1/34STM32Cube官方例程学习指南STM32CubeMX是ST官方提供的一个代码生成工具。使用该工具,通过图形化的配置方法,就能快速生成STM32的各种片上外设的初始化代码。CubeMX生成的软件工程使用HAL库,HAL库是ST以后主推的外设驱动库。另外CubeMX还提供了FATFS、FreeRTOS、LWIP、USB库等中间件的支持,配置之后生成软件工程,工程文件就包含了相应代码。本文档以STM32F4系列为例,简要地分析官方提供的Cube例程。希望能够帮助CubeMX初学者快速掌握STM32的常用外设使用方法。文档不求全面,只讲常用的外设,对不常用的只进行概况性地描述。同时,文档只对例程进行直接分析,不对其他文件进行详述。第一部分准备工作首先是下载STM32CubeF4支持包,可以到与非网ST社区搜索STM32CubeF4,然后下载当前版本已经更新到V1.13.0。点击附件中的STM32CubeF4,转到下载链接地址。附件大小300M左右。本人当前使用的是V1.9.0版本的,例程相差不大,后面就用V1.9.0版本的例程进行分析。下载后解压,得到如下图的文件,其中例程放在Projects文件夹中:2/34打开Projects文件夹,可以看到前12个文件夹分别官方提供的12款评估板,后面我们仅以STM324xG_EVAL评估板的例程为讲解内容。STM324xG_EVAL文件夹中,Examples文件夹存放的就是片上外设的使用例程。(Applications文件夹是STM324xG_EVAL相关的一些高级应用例程,如FreeRTOS、FatFs、LwIP、USB等,有一定基础之后可以学习这里面的内容。本文不作分析。)Examples文件夹提供了27个外设对应文件夹,每个文件夹包含若干个例程,后面将对常用的外设例程(不是全部)进行简要分析。3/34第二部分例程分析下面将挑选常用外设的例程进行分析,顺序是从简单的到复杂的。第一章:GPIOGPIO共有两个例程:外部中断和IO翻转。1.GPIO_IOToggle打开…\GPIO_IOToggle\MDK-ARM文件夹下的MDK工程,打开main.c文件。如main.c文件开头的描述,本例程描述如何配置GPIO和通过HALAPI函数使用GPIO。看main函数:4/34分析:从注释可以看出,GPIO的控制只需要3个步骤,使能GPIO时钟、配置GPIO模式、控制GPIO状态。前两个步骤的代码不需要用户手动输入,完全有STM32CubeMX生成,用户只需要在CubeMX中用图形化界面进行配置。(详细操作步骤可参考本人编写的STM32Cube学习笔记,或者其他STM32Cube入门教程。)步骤3的重点在HAL_GPIO_TogglePin()函数,在控制GPIO反转的。其他控制GPIO输出状态的函数还有HAL_GPIO_WritePin()。2.GPIO_EXTI该例程演示如何使用GPIO的外部中断功能。打开…\GPIO_EXTI\MDK-ARM文件夹下的MDK工程,打开main.c文件。先看main函数:main函数只有个语句,分3个部分。第一部分是系统时钟配置。第二部分是板级支持配置,本例中是初始化两个LED控制口。第三部分是配置外部中断。这三个部分的代码功能都可以通过CubeMX配置生成,不需要用户输入。最后,要在回调函数中实现中断响应的功能代码。HAL_GPIO_EXTI_Callback()函数是HAL库的外部中断回调函数,所有的外部中断都是使用该函数。然后在处理时,判断是哪个引脚号对应的中断。第二章:UARTUART共有三个例程。5/341.UART_Printf打开…\UART_Printf\MDK-ARM文件夹下的MDK工程,打开main.c文件。如main.c文件开头描述,本例演示了如何将printf()函数的输出功能映射到串口上。看main函数:main函数分为3部分。第一部分是系统初时钟配置。第二部分是初始化UART。第三部分是使用printf()函数输出一个语句。在126~133行,就是实现过程。6/34其中PUTCHAR_PROTOTYPE是一个宏,该宏已经在main文件开头给出。因此,对于MDK-ARM使用的编译器,126~133的函数等效于。实现该函数并包含stdio.h文件之后,程序中就可以用printf()通过UART输出字符串了。2.UART_Hyperterminal_IT打开…\UART_Hyperterminal_IT\MDK-ARM文件夹下的MDK工程,打开main.c文件。该例程演示如何使用串口发送和接收中断。看main函数:在main函数中已经标注了各个步骤的序号。步骤1就是初始化串口,和上一个例子形式差不多。步骤2是演示HAL_UART_Transmit_IT()函数的用法,该函数的功能是将aTxStartMessage[]数组的TXSTARTMESSAGESIZE个字节数据发送出去,并使能发送完成中断,当发送完成后会调用一次HAL_UART_TxCpltCallback()回调函数。步骤3是演示HAL_UART_Receive_IT()函数的用法,用法和发送函数类似。功能是使能UART接收中断,接收的数据存入缓冲数组aRxBuffer[],在接收数据量达到RXBUFFERSIZE字节时调用一次HAL_UART_RxCpltCallback()回调函数。用户可以在回调函数中添加数据处理的代码。7/34步骤4是等待串口空闲。步骤5是再次发送数据。步骤6、7、8,过程和前面类似,注释已经说明清楚。本例中回调函数的内容很简单,就是点亮LED。3.UART_Hyperterminal_DMA该例程演示如何使用串口DMA发送和接收及中断,和UART_Hyperterminal_IT结构完全相同,只是把函数的后缀都改成了_DMA。而且回调函数都是一样的。8/34第三章:TIMTIM共有18个例程。定时器是STM32中用途最多变的外设。下面分析几个典型应用例程。1.TIM_TimeBase打开…\TIM_TimeBase\MDK-ARM文件夹下的MDK工程,打开main.c文件。该例程是定时器最基本的应用,即定时中断功能。看main函数:一共只有两个步骤。步骤1是配置定时器,步骤2是启动定时器并使能中断。步骤1是通过CubeMX配置生成代码。步骤2需要用户手动添加。9/34HAL_TIM_PeriodElapsedCallback()是定时器Update中断回调函数。所有定时器的更新中断都使用该回调函数接口。因此,如果开启了多个定时器更新中断时,应该对中断源进行判断,如下图:2.TIM_PWMOutput打开…\TIM_PWMOutput\MDK-ARM文件夹下的MDK工程,打开main.c文件。该例程演示怎么使用定时器的PWM模式产生4路PWM信号。看main函数:步骤1是配置TIM外设。步骤2是配置PWM通道。这两步骤的代码可由CubeMX配置生。10/34步骤3是启动各个PWM通道,就是调用HAL_TIM_PWM_Start()函数。该步骤的代码要用户添加。经过上述3个步骤,就可以在相应引脚输出PWM信号了。3.TIM_InputCapture打开…\TIM_InputCapture\MDK-ARM文件夹下的MDK工程,打开main.c文件。该例程演示TIM的输入捕获功能,用该功能测量信号的频率或周期。看main函数:步骤1配置TIM外设,步骤2配置输入捕获通道。11/34步骤3启动输入捕获功能,并使能相应中断。对捕获值的处理在中断回调函数中进行。一共捕获两次,两次捕获值的差值就是乘以定时器时钟周期,就得到信号的周期,其倒数就是信号频率。4.TIM_PWMInput该例程演示用TIM的输入捕获功能能测量PWM信号的占空比。打开…\TIM_PWMInput\MDK-ARM文件夹下的MDK工程。12/34关键步骤就是同一个定时器开启了两个输入捕获通道,一个捕获上升沿,另一个捕获下降沿。在捕获中断回调函数中进行数据处理。两次相邻的上升沿的时间差就是PWM信号周期,这两次上升沿之间有个下降沿,该下降沿和第一次上升沿的时间差就是PWM信号的占空比。5.TIM_OCActive该例程演示用TIM的比较匹配功能输出4路PWM信号。打开…\TIM_OCActive\MDK-ARM文件夹下的MDK工程。步骤1配置TIM外设。步骤2配置各通道的输出比较参数,包括输出模式、极性、比较值等。13/34步骤3点亮LED1,这一步无关紧要。步骤4启动各通道比较匹配输出功能。6.TIM_OCInactive该例程和TIM_OCActive的步骤完全相同,只是PWM输出信号的进行了取反,并且在步骤4中启动个通道时使能了比较匹配中断。最后在中断函数中分别控制LED的熄灭。7.TIM_OCToggle该例程演示如何用TIM的比较输出功能的翻转模式,输出4路频率不同的50%占空比的方波信号。打开…\TIM_OCToggle\MDK-ARM文件夹下的MDK工程。步骤1配置TIM外设。14/34步骤2配置输出比较通道参数,包括输出模式、极性、比较值等。步骤3启动各通道的输出比较功能,并使能中断。关键的步骤是中断回调函数中。因为其实TIM硬件并不能同时输出多路不同频率的PWM信号,这里是通过不断修正各个通道的比较值,才实现了4路不同频率的方波信号。8.TIM_OnePulse该例程演示如何用TIM输出单脉冲信号。通过通道2输入信号,上升沿触发通道1产生脉冲信号。打开…\TIM_OnePulse\MDK-ARM文件夹下的MDK工程。15/34看main函数:步骤1配置TIM外设。步骤2配置通道1为单脉冲模式,输入通道是通道2。步骤3启动单脉冲通道2。从步骤1的注释中,详细描述了单脉冲的的产生过程和参数计算。16/349.TIM_DMA该例程演示通过TIM更新请求触发DMA更新TIM的输出比较值,以改变PWM的占空比。打开…\TIM_DMA\MDK-ARM文件夹下的MDK工程。看main函数:步骤1配置TIM外设。在此RepetitionCounter是一个重要参数,等于3就是3个周期触发一次更新请求。步骤2配置PWM通道参数,包括输出模式、极性、脉冲宽度。步骤3以DMA模式启动PWM输出,注意HAL_TIM_PWM_Start_DMA()函数的最后两个参数,一个是数据地址,一个是数据量。程序运行的过程:TIM的定时周期由变量uhTimerPeriod确定,由于RepetitionCounter=3,TIM经过3个定时周期就触发一次DMA传输。DMA传输是将aCCValue_Buffer[]的数据传入TIM的CHANNEL_3的比较匹配寄存器中,即改变PWM的占空比。第一次DMA传输的是aCCValue_Buffer[0],第二次传输aCCValue_Buffer[1],第三次传输aCCValue_Buffer[2]。三次DMA传输就完成了整个循环周期。第四次又传输aCCValue_Buffer[0],如此往下循环。10.TIM_DMABurst该例程演示通过TIM更新请求触发DMA突发传输更新TIM的ARR、RCR、CCR1寄存器的值,以改变PWM的周期、占空比。打开…\TIM_DMABurst\MDK-ARM文件夹下的MDK工程。17/34步骤1配置TIM外设。步骤2配置PWM通道。步骤3启动PWM输出。步骤4启动DMA突发传输。注意HAL_TIM_DMABurst_WriteStart()中几个被标注的参数,他们只有可用的有效取值,查看该函数在stm32f4xx_hal_tim.c的位置,有详细的说明。该例程的运行过程,在…\TIM_DMABurst文件夹的readme.txt文件有描述。“每次更新的DMA请求,DMA就传输3个HalfWord到定时器的寄存器,起始寄存器为ARR。在DMA更新请求中,0x0FFF被传入ARR,0x0000传入RCR,0x0555传入C