发新帖本帖赏金 100.00元(功能说明)我要提问
12下一页
返回列表
打印
[MM32生态]

EVB-L0136开发学习分享

[复制链接]
1903|24
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#   @21小跑堂




简介
MM32L0130系列MCU是灵动微电子新推出的低功耗产品,内核使用32位的高性能Arm® Cortex-M0+ 微控制器,最高工作频率可达到48MHz,内置高速存储器,丰富的增强型 I/O 端口和多种外设。可适用于空调遥控器、温控器、耳/额温枪、便携医疗设备、气/水/电等仪表、小家电等应用领域。


MM32L0130系列MCU主要特性
u  内核与系统
Ø  32-bit Arm® Cortex®-M0+
Ø  工作频率可达48MHz
u  存储器
Ø  多达 64KB 的 Flash 存储器
Ø  多达 8KB SRAM
Ø  Boot loader 支持片内 Flash 在线系统编程(ISP)
u  时钟、复位和电源管理
Ø  1.8V ∼ 5.5V 供电
Ø  上电/断电复位(POR/PDR) 、可编程电压监测器(PVD)
Ø  外部 4 ∼ 24MHz 高速晶体振荡器
Ø  外部 32.768KHz 低速振荡器(带 LSE Bypass 功能)
Ø  内置经出厂调校的8MHz 高速 RC 振荡器,全温度范围内偏差不超过 ±2.5%
Ø  PLL 支持 CPU 最高运行在 48MHz,支持多种分频模式
Ø  内置 16.384KHz 低速振荡器,全温度范围内频率偏差不超过 ±3.5%
u  低功耗
Ø  多种低功耗模式,包括:低功耗运行(Low Power Run)、睡眠(Sleep)、低功耗睡眠(Low Power Sleep)、停机(Stop)、深度停机(Deep Stop)、 待机(Standby)和关机(Shutdown)模式
u  1 个 5 通道 DMA 控制器,支持外设类型包括定时器、ADC、UART、LPUART、I2C、SPI 和SLCD
u  9 个定时器
Ø  2 个 16 位通用定时器(TIM3 /TIM4),有多达 4 个输入捕获/输出比较通道,可用于IR 控制解码
Ø  2 个 16 位基本定时器(TIM16/ TIM17),有 1 个输入捕获/输出比较通道,1 组互补输出, 支持死区生成,紧急停止, 调制器门电路用于 IR 控制
Ø  1 个低功耗定时器(LPTIM),可在除待机和关机模式外的所有模式唤醒 CPU
Ø  2 个看门狗定时器(独立型的 IWDG 和窗口型的 WWDG)
Ø  1 个 RTC 计数器,支持日历功能
Ø  1 个 Systick 定时器: 24 位自减型计数器
u  多达 57 个快速 I/O 端口:
Ø  所有 I/O 口可以映像到 16 个外部中断
Ø  所有端口均可输入输出电压不高于VDD 的信号
u  多达 6 个通信接口
Ø  2 个 UART 接口
Ø  1 个低功耗 UART 接口(LPUART)
Ø  1 个 I2C 接口
Ø  2 个 SPI 接口(2 个I2S 接口)
u  1 个红外信号调制模块(Infra-Red Modulator, IRM),支持 ASK/PSK/FSK 调制
u  1 个段码式液晶驱动模块(SLCD),可驱动 40x4 或 36x8 个段码
u  1 个 12 位模数转换器(ADC),1us 转换时间,多达 15 个外部输入通道,1个内部输入通道
Ø  转换范围:0 ∼ VDDA
Ø  支持采样时间和分辨率配置
Ø  片上温度传感器
Ø  片上电压传感器
u  1 个比较器
u  CRC 计算单元,8/16/32 位多项式可配置
u  96 位芯片唯一 ID(UID)
u  调试模式
Ø  串行调试接口(SWD) 接口
u  采用 LQFP64 和 LQFP48 封装


准备开发环境
1、EVB-L0130开发板
2、J-LINK调试下载器
3、USB转TLL工具
4、音箱、USB数据线
EVB-L0130属于MM32EVBoard系列开发板,板载芯片为MM32L0136C7P,带有丰富的外设资源:支持高达 4KV EFT 抗干扰能力、支持 SWD 下载调试接口、4个用户按键、1个复位按键、4个用户LED、2路UART 连接器、1个给开发板供电USB 连接器、1个8Mbit 的 SPI Flash 存储器、1个2048bit 的 I2C 存储器、4个功能选择开关(I2S或是LCD)、1个3.5mm 耳机插座,用于 I2SL/R 音频输出、1个无源扬声器、3路模拟输入电位器、1个板载段码 LCD显示屏……


准备相关资料
MM32L0130数据手册: DS_MM32L0130_SC.pdf (2.36 MB)
MM32L0130用户手册: UM_MM32L0130_SC.pdf (12.53 MB)
MM32L0130 KEIL芯片支持包: MindMotion.MM32L0130_DFP.0.0.2.pack.zip (36.45 KB)
EVB-L0130开发析用户手册: UG_EVB-L0136_SC.pdf (1.17 MB)
EVB-L0130开发板原理图: EVB-L0136_SCH.pdf (575.08 KB)
MM32L0130 MindSDK驱动程序: Windows mdk_evb-l0130 2022-08-15 1033.zip (9.04 MB)
MM32L0130 LibSample驱动程序: MM32L0130_LibSamples_V020.zip (4.19 MB)
段码LCD显示屏手册: GDC0689TP-11.pdf (896.84 KB)


实现功能
1.     基于EVB-L0130创建基础工程,移植MultiTime开源软件库,结合SysTick定时器实现系统的任务管理和调度
2.     移植Letter-shell_2.x开源软件库,结合板载的UART1接口,实现串口打印、在线联机调试的功能
3.     实现板载4颗LED灯的闪烁控制
4.     移植MultiButton开源软件库,结合板载的4个功能按键,实现按键扫描、检测和处理的功能
5.     通过DMA中断方式实现对板载3路模拟量输入的ADC采样,可通过实时看到当前的采样结果
6.     通过MCU的硬件I2C接口,对板载有EEPROM存储芯片进行读写操作
7.     移植SFUD开源软件库,再通过MCU的硬件SPI接口,对板载的SPIFLASH存储芯片进行读、写、擦除等操作
8.     通过TIM定时器的PWM输出功能,实现对板载蜂鸣器的控制,让其发出指定的提示音
9.     结合段位LCD显示屏的真值表、MCU与LCD的硬件连接,实现对板载LCD的显示驱动,通过查表方式,实现灵活显示,便程序更具有可读性、更便于移植和功能实现
10.   通过MCU内部自带的RTC模块,再结合段位LCD显示屏,实现实时时钟显示的功能,通过软件代码实现自动初始化日期、时间的功能
11.   通过板载的IRDA发送头和接收头,结合MCU内部自带的IRM模块实现红外数据的发送、接收功能
12.   通过实现XMODEM传输协议与SPI FLASH的读写操作,实现上位机软件将数据/文件写入到板载SPI FLASH存储芯片的功能,对于XMODEM的通讯操作使用了消息队列的处理方式
13.   通过I2S接口,实现播放SPI FLASH存储芯片中存放的WAV音频文件,通过音箱进行播放;在播放过程中我们使用DMA加双缓存,通过乒乓操作的方式进行,让音频播放更加流畅,不至于出现卡顿的效果


实验过程中需要注意点:
1、上述所有的功能均已实现,有基于MindSDK和LibSample标准库两个版本,根据个人习惯选择相应版本,对于作者而言,这两个其实都差不多,只是底层不太一样,上层的功能和应用没有太大影响
2、所有的功能可以单独测试,也可以组合测试,可以通过修改config.h头文件中的宏定义来开关相对应的功能
3、EVB-L0130开发板上有4个切换开关,是用来切换MCU的GPIO是用来控制LCD显示的,还是用来操作I2S播放音频的,所以在做LCD和I2S实验时,需要将这4个切换开关拨正确,这两个功能的实验也不能同时完成
4、通过EVB-L0130开发板的原理图,我们看到MCU与LCD的连接部分,LCD并不是所有的引脚都连接到MCU上的,这就导致LCD上的段位不可能显示完全
5、我手上的EVB-L0130是初代的版本,暂时还做不了低功耗的测试,这个官方应该会地后面的硬件版本上更新过来吧……


ADC & DMA功能实现:
#if ENABLE_ADC


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
MultiTimer ADC_MultiTimer;


/* Private variables ---------------------------------------------------------*/
float RV1_Voltage = 0.0;
float RV2_Voltage = 0.0;
float RV3_Voltage = 0.0;


/* Private variables ---------------------------------------------------------*/
uint16_t ADC_Flag  =  0;
uint16_t ADC_Buffer[30];


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url]      
* @param      
* @retval      
* [url=home.php?mod=space&uid=93590]@Attention[/url]   
*******************************************************************************/
void ADC_MultiTimerCallback(MultiTimer *timer, void *userData)
{
    uint32_t ADC0_Value = 0;
    uint32_t ADC1_Value = 0;
    uint32_t ADC2_Value = 0;

    if(ADC_Flag == 1)
    {
        ADC_Flag = 0;

        for(uint8_t i = 0; i < 30;)
        {
            ADC0_Value += ADC_Buffer[i++];
            ADC1_Value += ADC_Buffer[i++];
            ADC2_Value += ADC_Buffer[i++];
        }

        RV1_Voltage = ((float)(ADC0_Value) / 4096.0) * 3.3 / 10.0;
        RV2_Voltage = ((float)(ADC1_Value) / 4096.0) * 3.3 / 10.0;
        RV3_Voltage = ((float)(ADC2_Value) / 4096.0) * 3.3 / 10.0;

        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    }

    MultiTimerStart(&ADC_MultiTimer, 250, ADC_MultiTimerCallback, "ADC");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void mADC_Init(void)
{
    ADC_InitTypeDef  ADC_InitStructure;
    DMA_InitTypeDef  DMA_InitStruct;
    GPIO_InitTypeDef GPIO_InitStructure;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 2;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel1);

    DMA_StructInit(&DMA_InitStruct);
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
    DMA_InitStruct.DMA_MemoryBaseAddr     = (uint32_t)&ADC_Buffer;
    DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStruct.DMA_BufferSize         = 30;
    DMA_InitStruct.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
    DMA_InitStruct.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority           = DMA_Priority_High;
    DMA_InitStruct.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStruct.DMA_Auto_reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStruct);

    if(DMA_InitStruct.DMA_PeripheralBaseAddr == ((uint32_t)(&ADC1->DR)))
    {
        DMA_SetChannelMuxSource(DMA1_Channel1, DMA1_MUX_ADC1);

        if(DMA_GetChannelMuxSource(DMA1_Channel1) != DMA1_MUX_ADC1)
        {
            while(1);
        }
    }

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Channel1, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_ADC1, ENABLE);

    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.ADC_Resolution         = ADC_Resolution_12b;
    ADC_InitStructure.ADC_PRESCARE           = ADC_PCLK2_PRESCARE_16;
    ADC_InitStructure.ADC_Mode               = ADC_Mode_Continue;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv   = ADC1_ExternalTrigConv_T3_CC3;
    ADC_InitStructure.ADC_DataAlign          = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 0, ADC_Samctl_240_5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_Samctl_240_5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_Samctl_240_5);

    ADC_DMACmd(ADC1, ENABLE);
    ADC_Cmd(ADC1, ENABLE);

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    MultiTimerStart(&ADC_MultiTimer, 250, ADC_MultiTimerCallback, "ADC");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void DMA1_Channel1_IRQHandler(void)
{
    ADC_SoftwareStartConvCmd(ADC1, DISABLE);
    DMA_ClearITPendingBit(DMA1_IT_TC1);
    ADC_Flag = 1;
}


#endif


BUZZER & TIM & PWM功能实现:
#if ENABLE_BUZZER


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
MultiTimer BUZZER_MultiTimer;


/* Private variables ---------------------------------------------------------*/
BUZZER_TypeDef BUZZER_TAB[] =
{
    {50, 100, 1},
    {50, 100, 1},
    {50, 100, 0},
};


/* Private variables ---------------------------------------------------------*/
volatile uint8_t BUZZER_Flag = 0;


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


#if 1


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BUZZER_MultiTimerCallback(MultiTimer *timer, void *userData)
{
    static uint8_t State = 0, Index = 0;

    if(State == 0)
    {
        State = 1;  BUZZER_Flag = 1;

        MultiTimerStart(&BUZZER_MultiTimer, BUZZER_TAB[Index].PlayMS, BUZZER_MultiTimerCallback, "BUZZER");
    }
    else
    {
        State = 0;  BUZZER_Flag = 0;

        if(BUZZER_TAB[Index].KeepOn)
        {
            MultiTimerStart(&BUZZER_MultiTimer, BUZZER_TAB[Index++].WaitMS, BUZZER_MultiTimerCallback, "BUZZER");
        }
        else
        {
            Index = 0;
        }
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BUZZER_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    NVIC_InitTypeDef        NVIC_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

    RCC_ClocksTypeDef  RCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = TIM16_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_TIM16, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Prescaler         = (RCC_Clocks.SYSCLK_Frequency / 1000000 - 1);
    TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_Period            = (500 - 1);
    TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM16, &TIM_TimeBaseStructure);

    TIM_ITConfig(TIM16, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM16, ENABLE);

    MultiTimerStart(&BUZZER_MultiTimer, 100, BUZZER_MultiTimerCallback, "BUZZER");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void TIM16_IRQHandler(void)
{
    static BitAction Value = Bit_RESET;

    if(TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET)
    {
        if(BUZZER_Flag == 1)
        {
            if(Value == Bit_RESET)  Value = Bit_SET;
            else                    Value = Bit_RESET;

            GPIO_WriteBit(GPIOA, GPIO_Pin_6, Value);
        }

        TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
    }
}


#else


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BUZZER_MultiTimerCallback(MultiTimer *timer, void *userData)
{
    static uint8_t State = 0, Index = 0;

    if(State == 0)
    {
        State = 1;  TIM_SetCompare1(TIM3, 500);

        MultiTimerStart(&BUZZER_MultiTimer, BUZZER_TAB[Index].PlayMS, BUZZER_MultiTimerCallback, "BUZZER");
    }
    else
    {
        State = 0;  TIM_SetCompare1(TIM3, 0);

        if(BUZZER_TAB[Index].KeepOn)
        {
            MultiTimerStart(&BUZZER_MultiTimer, BUZZER_TAB[Index++].WaitMS, BUZZER_MultiTimerCallback, "BUZZER");
        }
        else
        {
            Index = 0;
        }
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void BUZZER_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef       TIM_OCInitStructure;

    RCC_ClocksTypeDef  RCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_1);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_TIM3, ENABLE);

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
    TIM_TimeBaseStructure.TIM_Prescaler         = (RCC_Clocks.PCLK1_Frequency / 1000000 - 1);
    TIM_TimeBaseStructure.TIM_ClockDivision     = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_Period            = (1000 - 1);
    TIM_TimeBaseStructure.TIM_CounterMode       = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

    TIM_OCStructInit(&TIM_OCInitStructure);
    TIM_OCInitStructure.TIM_OCMode       = TIM_OCMode_PWM2;
    TIM_OCInitStructure.TIM_OutputState  = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
    TIM_OCInitStructure.TIM_Pulse        = 0;
    TIM_OCInitStructure.TIM_OCPolarity   = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);

    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
    TIM_ARRPreloadConfig(TIM3, ENABLE);

    TIM_Cmd(TIM3, ENABLE);

    TIM_CtrlPWMOutputs(TIM3, ENABLE);

    MultiTimerStart(&BUZZER_MultiTimer, 100, BUZZER_MultiTimerCallback, "BUZZER");
}


#endif


#endif


EEPROM & I2C功能实现:
#if ENABLE_EEPROM


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void I2C_GetBuffer(uint8_t Address, uint8_t *Buffer, uint8_t Length)
{
    uint8_t Flag = 0, Count = 0;

    I2C_SendData(I2C1, Address);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    for(uint8_t i = 0; i < Length; i++)
    {
        while(1)
        {
            if((I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFNF)) && (Flag == 0))
            {
                I2C_ReadCmd(I2C1);   Count++;
                if(Count == Length) Flag = 1;
            }

            if(I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_RFNE))
            {
                Buffer[i] = I2C_ReceiveData(I2C1);     break;
            }
        }
    }

    I2C_GenerateSTOP(I2C1, ENABLE);
    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void I2C_PutBuffer(uint8_t Address, uint8_t *Buffer, uint8_t Length)
{
    I2C_SendData(I2C1, Address);
    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    for(uint8_t i = 0; i < Length; i++)
    {
        I2C_SendData(I2C1, *Buffer++);
        while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
    }

    I2C_GenerateSTOP(I2C1, ENABLE);
    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void EEPROM_ReadData(uint8_t Address, uint8_t *Buffer, uint8_t Length)
{
    I2C_GetBuffer(Address, Buffer, Length);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void EEPROM_WriteData(uint8_t Address, uint8_t *Buffer, uint8_t Length)
{
    uint8_t Start, StartCount, PageNumber, FinalCount;

    if((Address % EEPROM_PAGE_SIZE) == 0)
    {
        StartCount = 0;
        PageNumber = Length / EEPROM_PAGE_SIZE;
        FinalCount = Length % EEPROM_PAGE_SIZE;
    }
    else
    {
        Start = Address % EEPROM_PAGE_SIZE;

        if(((Start + Length) / EEPROM_PAGE_SIZE) == 0)
        {
            StartCount = Length;
            PageNumber = 0;
            FinalCount = 0;
        }
        else
        {
            StartCount = EEPROM_PAGE_SIZE - Start;
            PageNumber = (Length - StartCount) / EEPROM_PAGE_SIZE;
            FinalCount = (Length - StartCount) % EEPROM_PAGE_SIZE;
        }
    }

    if(StartCount)
    {
        I2C_PutBuffer(Address, Buffer, StartCount);
        Address += StartCount;
        Buffer  += StartCount;

        printf("\r\nWait A Moument...");    SysTick_DelayMS(10);
    }

    while(PageNumber--)
    {
        I2C_PutBuffer(Address, Buffer, EEPROM_PAGE_SIZE);

        Address += EEPROM_PAGE_SIZE;
        Buffer  += EEPROM_PAGE_SIZE;

        printf("\r\nWait A Moument...");    SysTick_DelayMS(10);
    }

    if(FinalCount)
    {
        I2C_PutBuffer(Address, Buffer, FinalCount);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void EEPROM_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);

    I2C_StructInit(&I2C_InitStructure);
    I2C_InitStructure.I2C_Mode       = I2C_Mode_MASTER;
    I2C_InitStructure.I2C_OwnAddress = 0;
    I2C_InitStructure.I2C_Speed      = I2C_Speed_STANDARD;
    I2C_InitStructure.I2C_ClockSpeed = 100000;
    I2C_Init(I2C1, &I2C_InitStructure);

    I2C_Send7bitAddress(I2C1, EEPROM_I2C_ADDRESS, I2C_Direction_Transmitter);

    I2C_Cmd(I2C1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_3);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_3);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void EEPROM_Demo(void)
{
    uint8_t rBuffer[20], wBuffer[20];
    uint8_t Address = 0;
    uint8_t Length = 20;

    for(uint8_t i = 0; i < Length; i++)
    {
        rBuffer[i] = 0;
        wBuffer[i] = i;
    }

    printf("\r\n\r\nEEPROM Write Data : ");

    EEPROM_WriteData(Address, wBuffer, Length);

    printf("OK");   SysTick_DelayMS(10);

    printf("\r\n\r\nEEPROM Read  Data : \r\n");

    EEPROM_ReadData(Address, rBuffer, Length);

    for(uint8_t i = 0; i < Length; i++)
    {
        printf("0x%02x ", rBuffer[i]);

        if(((i + 1) % 10) == 0) printf("\r\n");
    }

    printf("\r\n\r\n");
}


#endif


SPI FLASH & SFUD功能实现:
/* Includes ------------------------------------------------------------------*/
#include <sfud.h>


/* Private typedef -----------------------------------------------------------*/


/* Private define ------------------------------------------------------------*/
#define SFUD_DEMO_BUFFER_SIZE      (512)


/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
uint8_t sfud_demo_buf[SFUD_DEMO_BUFFER_SIZE];


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
* @brief       SFUD demo for the first flash device test
* @param       addr flash start address
* @param       size test flash size
* @param       size test flash data buffer
* @retval      
* @attention   
*******************************************************************************/
void sfud_demo(uint32_t addr, size_t size, uint8_t *data)
{
    sfud_err result = SFUD_SUCCESS;

    const sfud_flash *flash = sfud_get_device_table() + 0;

    size_t i;

    /* prepare write data */
    for(i = 0; i < size; i++)
    {
        data[i] = i;
    }

    /* erase test */
    result = sfud_erase(flash, addr, size);

    if(result == SFUD_SUCCESS)
    {
        printf("\r\nErase the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
    }
    else
    {
        printf("\r\nErase the %s flash data failed.\r\n", flash->name);
        return;
    }

    /* write test */
    result = sfud_write(flash, addr, size, data);

    if(result == SFUD_SUCCESS)
    {
        printf("\r\nWrite the %s flash data finish. Start from 0x%08X, size is %d.\r\n", flash->name, addr, size);
    }
    else
    {
        printf("\r\nWrite the %s flash data failed.\r\n", flash->name);
        return;
    }

    /* read test */
    result = sfud_read(flash, addr, size, data);

    if(result == SFUD_SUCCESS)
    {
        printf("\r\nRead the %s flash data success. Start from 0x%08X, size is %d. The data is:\r\n", flash->name, addr, size);

        printf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\r\n");

        for(i = 0; i < size; i++)
        {
            if((i % 16) == 0)
            {
                printf("[%08X] ", addr + i);
            }

            printf("%02X ", data[i]);

            if(((i + 1) % 16 == 0) || (i == size - 1))
            {
                printf("\r\n");
            }
        }

        printf("\r\n");
    }
    else
    {
        printf("\r\nRead the %s flash data failed.\r\n", flash->name);
    }

    /* data check */
    for(i = 0; i < size; i++)
    {
        if(data[i] != i % 256)
        {
            printf("\r\nRead and check write data has an error. Write the %s flash data failed.\r\n", flash->name);
            break;
        }
    }

    if(i == size)
    {
        printf("\r\nThe %s flash test is success.\r\n", flash->name);
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void SPI_FLASH_Init(void)
{
    printf("\r\n");

    if(sfud_init() == SFUD_SUCCESS)
    {
        sfud_demo(0, sizeof(sfud_demo_buf), sfud_demo_buf);
    }
}


RTC & LCD功能实现:
#if ENABLE_RTC


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
MultiTimer RTC_MultiTimer;


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint8_t RTC_GetWeek(uint16_t Year, uint8_t Month, uint8_t Day)
{
    int w, c, y;

    /* Month 1 Or 2 of This Year Must Be As Last Month 13 Or 14 */
    if((Month == 1) || (Month == 2))
    {
        Month += 12;
        Year  -= 1;
    }

    w = 0;          /* Weekday */
    c = Year / 100; /* Century */
    y = Year % 100; /* Year    */

    w = y + (y / 4) + (c / 4) - (2 * c) + (26 * (Month + 1) / 10) + Day - 1;

    while(w < 0) w += 7;

    w %= 7;

    return w;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RTC_LoadDefault(RTCCAL_DateTypeDef *RTCCAL_Date, RTCCAL_TimeTypeDef *RTCCAL_Time)
{
    char    date[20], time[20];
    char    text[6][5];
    uint8_t index = 0, month = 0;

    memset(date, 0, sizeof(date));
    memset(time, 0, sizeof(time));
    memset(text, 0, sizeof(text));

    memcpy(date, __DATE__, sizeof(__DATE__));
    memcpy(time, __TIME__, sizeof(__TIME__));

    char *str;

    str = strtok(date, " ");

    while(str != NULL)
    {
        memcpy(text[index], str, strlen(str));
        index++;

        str = strtok(NULL, " ");
    }

    str = strtok(time, ":");

    while(str != NULL)
    {
        memcpy(text[index], str, strlen(str));
        index++;

        str = strtok(NULL, ":");
    }

#if 0
    for(uint8_t i = 0; i < index; i++)
    {
        printf("\r\n->%s", text[i]);
    }
#endif

    char *strMonth[12] =
    {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    };

    for(uint8_t i = 0; i < 12; i++)
    {
        if(strcmp(text[0], strMonth[i]) == 0)
        {
            month = i + 1;
        }
    }

    RTCCAL_Date->RTCCAL_Date    = atoi(text[1]);
    RTCCAL_Date->RTCCAL_Month   = month;
    RTCCAL_Date->RTCCAL_Year    = atoi(text[2]) - 2000;
    RTCCAL_Date->RTCCAL_WeekDay = RTC_GetWeek(RTCCAL_Date->RTCCAL_Year + 2000,
                                              RTCCAL_Date->RTCCAL_Month,
                                              RTCCAL_Date->RTCCAL_Date);

    RTCCAL_Time->RTCCAL_Hours   = atoi(text[3]);
    RTCCAL_Time->RTCCAL_Minutes = atoi(text[4]);
    RTCCAL_Time->RTCCAL_Seconds = atoi(text[5]);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RTC_MultiTimerCallback(MultiTimer *timer, void *userData)
{
    RTCCAL_TimeTypeDef RTCCAL_TimeStructure;
    RTCCAL_DateTypeDef RTCCAL_DateStructure;

    RTCCAL_GetTime(RTCCAL_Format_BIN, &RTCCAL_TimeStructure);
    RTCCAL_GetDate(RTCCAL_Format_BIN, &RTCCAL_DateStructure);

#if ENABLE_LCD
    LCD_DisplayNumber2(0, '0' + (RTCCAL_TimeStructure.RTCCAL_Hours   / 10), 0);
    LCD_DisplayNumber2(1, '0' + (RTCCAL_TimeStructure.RTCCAL_Hours   % 10), 0);
    LCD_DisplayNumber2(2, '0' + (RTCCAL_TimeStructure.RTCCAL_Minutes / 10), 0);
    LCD_DisplayNumber2(3, '0' + (RTCCAL_TimeStructure.RTCCAL_Minutes % 10), 0);
    LCD_DisplayNumber2(4, '0' + (RTCCAL_TimeStructure.RTCCAL_Seconds / 10), 0);
    LCD_DisplayNumber2(5, '0' + (RTCCAL_TimeStructure.RTCCAL_Seconds % 10), 0);
#else
    printf("\r\n%04d-%02d-%02d", RTCCAL_DateStructure.RTCCAL_Year + 2000, RTCCAL_DateStructure.RTCCAL_Month,  RTCCAL_DateStructure.RTCCAL_Date);

    switch(RTCCAL_DateStructure.RTCCAL_WeekDay)
    {
        case 0 : printf(" SUN "); break;
        case 1 : printf(" MON "); break;
        case 2 : printf(" TUE "); break;
        case 3 : printf(" WED "); break;
        case 4 : printf(" THU "); break;
        case 5 : printf(" FRI "); break;
        case 6 : printf(" SAT "); break;
        default: break;
    }

    printf("%02d:%02d:%02d\r\n", RTCCAL_TimeStructure.RTCCAL_Hours, RTCCAL_TimeStructure.RTCCAL_Minutes, RTCCAL_TimeStructure.RTCCAL_Seconds);
#endif

    MultiTimerStart(&RTC_MultiTimer, 500, RTC_MultiTimerCallback, "RTC");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void RTC_Init(void)
{
    RTCCAL_InitTypeDef RTCCAL_InitStructure;
    RTCCAL_TimeTypeDef RTCCAL_TimeStructure;
    RTCCAL_DateTypeDef RTCCAL_DateStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_PWR, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1ENR_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);

    RCC_LSEConfig(RCC_LSE_ON);
    while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_RTC, ENABLE);

    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    RCC_RTCCLKCmd(ENABLE);

    RTCCAL_EnterInitMode();

    RTCCAL_WaitForSynchro();

    RTCCAL_StructInit(&RTCCAL_InitStructure);
    RTCCAL_InitStructure.RTCCAL_HourFormat   = RTCCAL_HourFormat_24;
    RTCCAL_InitStructure.RTCCAL_AsynchPrediv = 0x7F;
    RTCCAL_InitStructure.RTCCAL_SynchPrediv  = 0xFF;
    RTCCAL_Init(&RTCCAL_InitStructure);

    RTC_LoadDefault(&RTCCAL_DateStructure, &RTCCAL_TimeStructure);
    RTCCAL_TimeStructure.RTCCAL_H12 = RTCCAL_H12_AM;
    RTCCAL_SetTime(RTCCAL_Format_BIN, &RTCCAL_TimeStructure);
    RTCCAL_SetDate(RTCCAL_Format_BIN, &RTCCAL_DateStructure);

    RTCCAL_WaitForSynchro();

    RTCCAL_ExitInitMode();

    MultiTimerStart(&RTC_MultiTimer, 500, RTC_MultiTimerCallback, "RTC");
}


#endif

I2S & DMA & WAV功能实现:
#if ENABLE_I2S


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
MultiTimer I2S_MultiTimer;


/* Private variables ---------------------------------------------------------*/
uint8_t  WAV_DataBuffer[2][WAV_BUFFER_SIZE];
uint8_t  WAV_NextIndex = 0;
uint8_t  WAV_PlayEnded = 0;
uint8_t  WAV_PlayState = 0;


/* Private variables ---------------------------------------------------------*/
uint32_t WAV_Offset   = 0;
uint32_t WAV_DataSize = 0;
uint32_t WAV_TxLength = 0;


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void I2S_MultiTimerCallback(MultiTimer *timer, void *userData)
{
    WAV_PlaySong();

    MultiTimerStart(&I2S_MultiTimer, 5000, I2S_MultiTimerCallback, "I2S");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void mI2S_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2S_InitTypeDef  I2S_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);

    I2S_InitStructure.I2S_Mode       = I2S_Mode_MasterTx;
    I2S_InitStructure.I2S_Standard   = I2S_Standard_Phillips;
    I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
    I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
    I2S_InitStructure.I2S_AudioFreq  = I2S_AudioFreq_44k;
    I2S_InitStructure.I2S_CPOL       = I2S_CPOL_Low;
    I2S_Init(SPI1, &I2S_InitStructure);

    SPI_DMACmd(SPI1, ENABLE);

    I2S_Cmd(SPI1, ENABLE);

    SPI1->GCTL |= (SPI_GCR_SPIEN | SPI_GCR_MODE | SPI_GCR_TXEN);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOC, ENABLE);

    GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_6);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_6);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_6);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_6);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 |
                                    GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOC, GPIO_Pin_2, Bit_RESET);

    MultiTimerStart(&I2S_MultiTimer, 5000, I2S_MultiTimerCallback, "I2S");
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void I2S_DMA_Transfer(uint16_t *Buffer, uint32_t BufferSize)
{
    DMA_InitTypeDef  DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    DMA_DeInit(DMA1_Channel1);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_DMA1, ENABLE);

    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(SPI1->TXREG);
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)Buffer;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize         = BufferSize;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_InitStructure.DMA_Auto_reload        = DMA_Auto_Reload_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    if(DMA_InitStructure.DMA_PeripheralBaseAddr == ((uint32_t)(&(SPI1->TXREG))))
    {
        DMA_SetChannelMuxSource(DMA1_Channel1, DMA1_MUX_SPI1_TX);

        if(DMA_GetChannelMuxSource(DMA1_Channel1) != DMA1_MUX_SPI1_TX)
        {
            while(1);
        }
    }

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE);

    DMA_Cmd(DMA1_Channel1, ENABLE);
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
uint8_t WAV_DecodeFile(WAV_TypeDef *pWav, uint32_t Address)
{
    ChunkRIFF_TypeDef *WAV_RIFF;
    ChunkFMT_TypeDef  *WAV_FMT ;
    ChunkFACT_TypeDef *WAV_FACT;
    ChunkDATA_TypeDef *WAV_DATA;

    uint8_t WAV_HeadBuffer[512];


    SPI_FLASH_FastRead(Address, WAV_HeadBuffer, 512);


    /* 获取RIFF块 */
    WAV_RIFF = (ChunkRIFF_TypeDef *)WAV_HeadBuffer;

    /* 是WAV格式文件 */
    if(WAV_RIFF->Format == 0x45564157)
    {
        /* 获取FMT块 */
        WAV_FMT  = (ChunkFMT_TypeDef *)(WAV_HeadBuffer+12);

        /* 读取FACT块 */
        WAV_FACT = (ChunkFACT_TypeDef *)(WAV_HeadBuffer+12+8+WAV_FMT->ChunkSize);

        if((WAV_FACT->ChunkID == 0x74636166) || (WAV_FACT->ChunkID == 0x5453494C))
        {
            /* 具有FACT/LIST块的时候(未测试) */
            pWav->DataStart=12+8+WAV_FMT->ChunkSize+8+WAV_FACT->ChunkSize;
        }
        else
        {
            pWav->DataStart=12+8+WAV_FMT->ChunkSize;
        }

        /* 读取DATA块 */
        WAV_DATA = (ChunkDATA_TypeDef *)(WAV_HeadBuffer+pWav->DataStart);

        /* 解析成功 */
        if(WAV_DATA->ChunkID == 0x61746164)
        {
            pWav->AudioFormat   = WAV_FMT->AudioFormat;     /* 音频格式 */
            pWav->nChannels     = WAV_FMT->NumOfChannels;   /* 通道数 */
            pWav->SampleRate    = WAV_FMT->SampleRate;      /* 采样率 */
            pWav->BitRate       = WAV_FMT->ByteRate*8;      /* 得到位速 */
            pWav->BlockAlign    = WAV_FMT->BlockAlign;      /* 块对齐 */
            pWav->BitsPerSample = WAV_FMT->BitsPerSample;   /* 位数,16/24/32位 */

            pWav->DataSize      = WAV_DATA->ChunkSize;      /* 数据块大小 */
            pWav->DataStart     = pWav->DataStart+8;        /* 数据流开始的地方 */

            printf("\r\npWav->AudioFormat   : %d", pWav->AudioFormat);
            printf("\r\npWav->nChannels     : %d", pWav->nChannels);
            printf("\r\npWav->SampleRate    : %d", pWav->SampleRate);
            printf("\r\npWav->BitRate       : %d", pWav->BitRate);
            printf("\r\npWav->BlockAlign    : %d", pWav->BlockAlign);
            printf("\r\npWav->BitsPerSample : %d", pWav->BitsPerSample);
            printf("\r\npWav->DataSize      : %d", pWav->DataSize);
            printf("\r\npWav->DataStart     : %d", pWav->DataStart);

            WAV_Offset   = pWav->DataStart + Address;
            WAV_DataSize = pWav->DataSize;
        }
    }

    return 0;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void WAV_PrepareData(void)
{
    if(WAV_NextIndex == 0)
    {
        SPI_FLASH_FastRead(WAV_Offset + WAV_TxLength, WAV_DataBuffer[0], WAV_BUFFER_SIZE);
    }
    else
    {
        SPI_FLASH_FastRead(WAV_Offset + WAV_TxLength, WAV_DataBuffer[1], WAV_BUFFER_SIZE);
    }

    WAV_TxLength += WAV_BUFFER_SIZE;

    if(WAV_TxLength > WAV_DataSize) WAV_PlayEnded = 1;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void WAV_PlayHandler(void)
{
    if(WAV_PlayEnded == 0)
    {
        I2S_DMA_Transfer((uint16_t *)&WAV_DataBuffer[WAV_NextIndex][0], (WAV_BUFFER_SIZE / 2));
    }
    else
    {
        DMA_Cmd(DMA1_Channel1,    DISABLE);

        printf("\r\nWAV Play Finish!\r\n");     WAV_PlayState = 0;
    }

    if(WAV_NextIndex == 0) WAV_NextIndex = 1;
    else                   WAV_NextIndex = 0;
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void WAV_PlaySong(void)
{
    WAV_TypeDef WaveFile;

    if(WAV_PlayState == 0)
    {
        if(WAV_DecodeFile(&WaveFile, 0x00000000) == 0)
        {
            if((WaveFile.BitsPerSample == 16) && (WaveFile.nChannels == 2) &&
               (WaveFile.SampleRate  > 44000) && (WaveFile.SampleRate < 48100))
            {
                WAV_NextIndex = 0;
                WAV_PlayEnded = 0;
                WAV_TxLength  = 0;
                WAV_PlayState = 1;

                printf("\r\n");
                printf("\r\nWAV Data Size : %d, Data Start : 0x%08x", WAV_DataSize, WAV_Offset);
                printf("\r\n");

                WAV_PrepareData();
                WAV_PlayHandler();
            }
            else
            {
                printf("\r\nWAV File Format Error!\r\n"); return;
            }
        }
        else
        {
            printf("\r\nNo WAV File!\r\n"); return;
        }
    }
    else
    {
        printf("\r\nThe Song Is Not Over Yet!\r\n");
    }
}


/*******************************************************************************
* @brief      
* @param      
* @retval      
* @attention   
*******************************************************************************/
void DMA1_Channel1_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC1) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_IT_TC1);

        WAV_PlayHandler();
    }

    if(DMA_GetITStatus(DMA1_IT_HT1) != RESET)
    {
        DMA_ClearITPendingBit(DMA1_IT_HT1);

        WAV_PrepareData();
    }
}


#endif


还有其它等等功能,由于篇幅太长就不这边一一列举出来,大家可以下载最后的软件工程源代码进行学习和研究。


软件工程源代码
最后我们附上完整的软件工程源代码,供大家下载学习!!!
MindSDK版本: EVB-L0136_MindSDK_20220901.zip (571.89 KB)
LibSample版本: EVB-L0136.zip (1.15 MB)


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2022-11-08
理由:恭喜通过原创审核!期待您更多的原创作品

评论
21小跑堂 2022-11-8 13:28 回复TA
大佬就是大佬呀,敢为人先,不走寻常路,以一人之力在不依靠官方例程的情况下玩转整个开发板,有实力,有耐心,值得肯定!文章的描写在完善点会得到更高的打赏哦~ 
沙发
xld0932|  楼主 | 2022-11-2 16:11 | 只看该作者
通过官方网站,下载到了MM32L0130的数据手册、用户手册、芯片支持包、EVB-L0130开发板用户手册、原理图,以及MindSKD驱动和示例程序;

但对于EVB-L0130开发板,并没有提供对应的软件代码,本文作者通过学习MM32L0130系列MCU的各个模块,再结合EVB-L0130开发带有功能,给大家分享了基于EVB-L0130开发板的软件工程,并提供了详细的源码!

使用特权

评论回复
板凳
xld0932|  楼主 | 2022-11-4 10:41 | 只看该作者
20块开发板免费送~灵动MM32L0136C7P等你来申请!
https://bbs.21ic.com/icview-3262804-1-1.html?fromuser=xld0932
(出处: 21ic电子技术开**坛)

感觉给这个活动打了个好样呢

使用特权

评论回复
地板
chenjun89| | 2022-11-5 12:56 | 只看该作者
还有音箱,那必须得加个WIFI模块啊。

使用特权

评论回复
5
xld0932|  楼主 | 2022-11-6 00:17 | 只看该作者
chenjun89 发表于 2022-11-5 12:56
还有音箱,那必须得加个WIFI模块啊。

所有的功能都是基于EVB-L0136开发板的板载资源实现的,还没有扩展外接的模块哦

使用特权

评论回复
6
duo点| | 2022-11-8 16:33 | 只看该作者
没有WIFI模块的音响是不完美的,最好再搞个蓝牙模块

使用特权

评论回复
7
NOo02| | 2022-11-8 18:26 | 只看该作者
博主能否出一篇使用YModem或者XMODEM传输协议到SPI FLASH的读写操作的文章,一直对这个协议传输到Flash没搞明白

使用特权

评论回复
8
xld0932|  楼主 | 2022-11-8 22:12 | 只看该作者
NOo02 发表于 2022-11-8 18:26
博主能否出一篇使用YModem或者XMODEM传输协议到SPI FLASH的读写操作的文章,一直对这个协议传输到Flash没搞 ...

你看我之前发的帖子呢,有专门讲YMODEM、XMODEM协议传输的分享

使用特权

评论回复
9
昨天| | 2022-11-11 15:38 | 只看该作者
这是给大家提前出考试的答案吗?……

使用特权

评论回复
10
tpgf| | 2022-12-1 17:36 | 只看该作者
这个开发板是不是带了一个小小的显示屏啊 请问是什么接口呢

使用特权

评论回复
11
xld0932|  楼主 | 2022-12-2 09:10 | 只看该作者
tpgf 发表于 2022-12-1 17:36
这个开发板是不是带了一个小小的显示屏啊 请问是什么接口呢

段码液晶显示屏,L0136带有一个SLCD,可以驱动段码屏显示……帖中准备相关资料中,已经附上了段码屏的手册了哈……

使用特权

评论回复
12
qcliu| | 2022-12-2 13:09 | 只看该作者
代码风格不错 不过要是能多加点解释就更好了

使用特权

评论回复
13
drer| | 2022-12-2 13:18 | 只看该作者
chenjun89 发表于 2022-11-5 12:56
还有音箱,那必须得加个WIFI模块啊。

请问为什么加上音箱的话就需要外扩wifi模块呢

使用特权

评论回复
14
coshi| | 2022-12-2 13:29 | 只看该作者
音频输出效果如何呀  噪音会不会比较大呢

使用特权

评论回复
15
kxsi| | 2022-12-2 14:33 | 只看该作者
请问芯片支持包里边含有一些代码工程吗

使用特权

评论回复
16
xld0932|  楼主 | 2022-12-2 15:42 | 只看该作者
kxsi 发表于 2022-12-2 14:33
请问芯片支持包里边含有一些代码工程吗

你说的是Keil Pack的话,没有……这个KEIL PACK看原厂是怎么封装的了……你说的是LibSample或者是MindSDK的话,不仅有示例程序,也有底层驱动……

使用特权

评论回复
17
wiba| | 2022-12-3 08:19 | 只看该作者
xld0932 发表于 2022-12-2 15:42
你说的是Keil Pack的话,没有……这个KEIL PACK看原厂是怎么封装的了……你说的是LibSample或者是MindSDK ...

请问一下  您说的这两个都需要用户自主进行下载吗

使用特权

评论回复
18
xld0932|  楼主 | 2022-12-5 09:12 | 只看该作者
wiba 发表于 2022-12-3 08:19
请问一下  您说的这两个都需要用户自主进行下载吗

是的

使用特权

评论回复
19
xu@xupt| | 2022-12-8 23:40 | 只看该作者
学习啦

使用特权

评论回复
20
春娇霹雳娃| | 2022-12-9 17:29 | 只看该作者
学习啦

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

67

主题

2993

帖子

29

粉丝