STM32CubeMX教程8 TIM 通用定时器 - 输出比较
读者可访问 GitHub - lc-guo/STM32CubeMX-Series-Tutorial 获取原始工程代码
1、准备材料
开发板(STM32F407G-DISC1)
STM32CubeMX软件(Version 6.10.0)
keil µVision5 IDE(MDK-Arm)
逻辑分析仪nanoDLA
2、实验目标
使用STM32CubeMX软件配置STM32F407通用定时器的输出比较通道,并将其输出到四个LED灯引脚实现LED灯流水灯效果
3、实验流程
3.0、前提知识
STM32F407的定时器通道均可以实现输出比较功能,输出比较功能是利用当前计数值CNT与捕获/比较寄存器CRR的值作比较,如果值相等就会产生输出比较结果,此时也会产生输出比较完成中断或DMA请求
定时器产生的输出比较结果可以输出到比较通道的具体引脚上,也可以直接内部使用不输出到引脚,具体输出的电平由比较模式和输出极性共同决定
输出比较模式有①冻结:保持当前电平、②匹配时输出有效电平、③匹配时输出无效电平、④匹配时翻转输出电平、⑤强制输出有效电平和⑥强制输出无效电平 六种模式
有效电平可以手动设置为高/低电平
3.1、CubeMX相关配置
3.1.0、工程基本配置
打开STM32CubeMX软件,单击ACCESS TO MCU SELECTOR选择开发板MCU(选择你使用开发板的主控MCU型号),选中MCU型号后单击页面右上角Start Project开始工程,具体如下图所示
开始工程之后在配置主页面System Core/RCC中配置HSE/LSE晶振,在System Core/SYS中配置Debug模式,具体如下图所示
详细工程建立内容读者可以阅读“STM32CubeMX教程1 工程建立”
3.1.1、时钟树配置
系统时钟使用8MHz外部高速时钟HSE,HCLK、PCLK1和PCLK2均设置为STM32F407能达到的最高时钟频率,具体如下图所示
3.1.2、外设参数配置
在Pinout & Configuration页面右边芯片引脚预览Pinout view中找到LED灯的四个控制引脚PD12、PD13、PD14和PD15,依次左键单击并配置其功能为TIM4_CHx
然后在页面左侧功能分类栏目中点开Timers栏目,单击栏目下的TIM4,并将其Channel1~4全部配置为Output Compare CHx
具体配置如下图所示
然后对启用的TIM4定时器的四个通道参数进行设置,下面对重要参数介绍
Prescaler 、 Counter Mode 、 Counter Period 和 auto-reload preload 等计数器参数的设置与之前的实验讲解一致,这里不再赘述,周期设为500ms,不分频不预装载
Output Compare Channel x / Mode (输出比较模式)均选择了Toggle on match,即当比较寄存器CCR的值和当前定时器计数值CNT匹配时翻转通道引脚输出状态
四个通道从上到下其 Pulse (比较寄存器的值CCR)依次为999、1999、2999和3999,这表示在一个时钟500ms周期内,通道1、2、3和4分别在100ms、200ms、300ms和400ms的时候翻转其对应通道引脚的状态
输出比较四个通道 Output compare preload (输出比较预装载)均为Disable, CH Polarity (通道输出有效状态)均为高电平,输出比较的其他模式如其字面意思较为好理解,这里不再过多阐述,具体的参数配置如下图所示
3.1.3、外设中断配置
在Pinout & Configuration页面左边System Core/NVIC中勾选TIM4全局中断,然后选择合适的中断优先级即可
3.2、生成代码
3.2.0、配置Project Manager页面
单击进入Project Manager页面,在左边Project分栏中修改工程名称、工程目录和工具链,然后在Code Generator中勾选“Gnerate peripheral initialization as a pair of 'c/h' files per peripheral”,最后单击页面右上角GENERATE CODE生成工程,具体如下图所示
详细Project Manager配置内容读者可以阅读“STM32CubeMX教程1 工程建立”实验3.4.3小节
3.2.1、外设初始化函数调用流程
在生成的工程代码主函数main()中调用了MX_TIM4_Init()函数完成了对TIM4基本定时器参数,输出比较通道1/2/3/4参数的配置
然后在HAL_TIM_OC_Init()函数中调用了HAL_TIM_OC_MspInit()函数对TIM4时钟和中断进行了使能,并对中断优先级进行了配置
最后在MX_TIM4_Init()函数末尾调用了HAL_TIM_MspPostInit()函数对TIM4的四个输出比较通道引脚进行了复用设置,从而完成了整个初始化过程
如下图所示为TIM4输出比较初始化的具体函数调用流程
3.2.2、外设中断函数调用流程
再来看看输出比较中断回调函数流程,使能定时器全局中断后在stm32f4xx_it.c中自动生成了TIM4的中断处理函数TIM4_IRQHandler
TIM4_IRQHandler调用了HAL库的定时器中断处理函数HAL_TIM_IRQHandler,这个函数负责处理所有的定时器相关中断
通过判断中断来源及相关寄存器,最终输出比较事件完成的回调函数为HAL_TIM_OC_DelayElapsedCallback()(或者是HAL_TIM_PWM_PulseFinishedCallback()) (注释1),该函数为虚函数,需要用户重新实现
如下图所示为TIM4输出比较中断回调的具体函数调用流程
该实验目标不需要动态修改输出比较的参数,因此笔者这里没有重新在tim.c中重新实现该回调函数
需要提醒的是,输出比较事件完成时会同时进入HAL_TIM_OC_DelayElapsedCallback()和HAL_TIM_PWM_PulseFinishedCallback()两个回调函数中,因此请注意此实验确保注释掉PWM输出实验中HAL_TIM_PWM_PulseFinishedCallback函数体内动态修改占空比的代码,否则将达不到预期效果
3.2.3、添加其他必要代码
最后只需要在主函数中启动定时器 (注释2),启动输出比较通道就可以实现本实验目标,启动代码如下图所示
源代码如下
HAL_TIM_Base_Start(&htim4);
HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_2);
HAL_TIM_OC_Start_IT(&htim4, TIM_CHANNEL_3);
HAL_TIM_OC_Start_IT(&htim4, TIM_CHANNEL_4);
4、常用函数
/*启动输出比较通道*/
HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
/*停止输出比较通道*/
HAL_StatusTypeDef HAL_TIM_OC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel)
/*以中断方式启动输出比较通道*/
HAL_StatusTypeDef HAL_TIM_OC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
/*停止以中断方式启动的输出比较通道*/
HAL_StatusTypeDef HAL_TIM_OC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
/*输出比较通道完成回调函数*/
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
5、烧录验证
烧录程序,上电单片机启动后会发现开发板上的四个LED灯,按照绿、橙、红和蓝的顺序依次点亮,然后不断循环,实际现象如下图所示
使用逻辑分析仪监测TIM4的四个输出通道引脚,可以看出TIM4的四个通道输出的电平翻转周期均为500ms,并且4个通道间每个通道均间隔100ms相继翻转状态
6、注释详解
注释1:在生成PWM波的实验中提到其使用的中断回调函数HAL_TIM_PWM_PulseFinishedCallback也可以换成输出比较完成的回调函数HAL_TIM_OC_DelayElapsedCallback,因为笔者发现在HAL库的定时器中断处理函数HAL_TIM_IRQHandler中,这两个函数是同时调用的,也就是说调用函数1,函数2也会跟着调用,所以笔者认为两个都可以使用
注释2:笔者发现在启动输出比较/输入捕获/PWM输出时,可以不调用定时器启动函数,执行HAL_TIM_xx_Start/HAL_TIM_xx_Start_IT函数时会自动启动定时器;另外HAL_TIM_OC_Start/HAL_TIM_OC_Start_IT两个输出比较启动函数和HAL_TIM_PWM_Start/HAL_TIM_PWM_Start_IT两个PWM输出启动函数,除了函数名和一些注释外其函数体内的代码一模一样