请保留-> 【原文: https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】
(代码仓库地址:https://gitee.com/luobing4365/yie002-explorer
具体参考博客:YIE002开发探索-Gitee代码仓库说明)
定时器的最基本的功能是用来周期性的定时,当然,它还用来实现如下功能:
1) 输入捕获。脉冲计数,在上升沿或下降沿检测,已经PWM的输入检测;
2) 输出比较。脉冲输出和步进电机的控制;
3) PWM生成。电压输出控制、直流减速电机控制等;
4) 编码器接口、霍尔传感器接口等。
当然,根据所用芯片不同,定时器还能提供更多的功能,比如与DMA的配合等。本篇准备用定时器产生的时间中断,周期性地控制LED灯的亮灭,以期对定时器的编程有基本的理解。
1 STM32的定时器
YIE002-STM32所用的芯片为STM32F103C8T6,属于STM32F1系列,在此系列,STM32提供了大量的定时器:2个基本定时器(TIM6、TIM7)、4个通用定时器(TIM2、TIM3、TIM4、TIM5)、2个高级控制定时器(TIM1和TIM8),以及4个特定功能定时器(SysTick、IWDG、WWDG、RTC),共有12个。另外,超大容量系列的产品中,又增加了5个通用寄存器TIM9~TIM14。
从功能上来说,高级控制通用器的功能是通用定时器的超集、通用定时器的功能是基本定时器的超集。比较通用定时器和高级控制定时器,可知它们的功能非常接近,高级控制定时器只是针对电动机的控制增加了一些功能。
因此,本篇准备使用通用定时器TIM3,构建演示程序。
1.1 定时器的时钟
TIM1和TIM8是高级控制定时器,时钟由APB2输出产生;TIM2至于TIM5是通用定时器,TIM6和TIM7是基本定时器,时钟由APB1输出产生。
从前几篇中可以知道,F103C8T6的系统时钟为72MHZ。如图1所示的时钟树,给出了定时器的时钟频率。
图1是以STM32F103C8T6为例绘制的。APB1为36MHZ,APB2为72MHZ。在我们的配置中,APB1 Prescaler是2分频;APB2 Prescaler是1分频。从图中可以看出,不论是通过挂在APB1下、还是挂在APB2下的时钟,其时钟都是72MHZ。
也即TIMxCLK均为72MHZ,与系统时钟等同。
1.2 定时器的中断时间(TIM3)
STM32F1的通用定时器(本例中使用的TIM3)是通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成,其基本的配置过程包括:
1) TIM3时钟使能;
2) 初始化定时器参数,设置自动重装值、分配系数、计数方式等;
3) 允许TIM3更新中断;
4) 设置TIM3的中断优先级;
5) 使能TIM3;
6) 编写中断服务函数。
使用CubeMX编程,上述6个步骤的前5个都是通过图形配置、自动生成代码的,只有中断服务函数需要编写。
编写代码之前,我们需要考虑定时器以多长的时间定时中断。计算中断时间,与TIMx_PSC寄存器(预分频寄存器)和TIMx_ARR寄存器(自动重装载寄存器)有关。具体的细节可以查看《STM32参考手册》,我们主要关注CubeLibrary中如何操作。在stm32f1xx_hal_tim.h中定义了定时器初始化的数据结构:
/** @defgroup TIM_Exported_Types TIM Exported Types
* @{
*/
/**
* @brief TIM Time base Configuration Structure definition
*/
typedef struct
{
uint32_t Prescaler;
uint32_t CounterMode;
uint32_t Period;
uint32_t ClockDivision;
uint32_t RepetitionCounter;
uint32_t AutoReloadPreload;
} TIM_Base_InitTypeDef;
数据结构TIM_Base_InitTypeDef的成员变量Prescaler对应预分频寄存器、成员变量Period对应自动重装载寄存器。注意,这两个值可设置的值为[0,65535],注意不要越界。
假设TimClk为定时器时钟频率(单位:MHZ),TimOut为中断时间(也即溢出时间,单位:秒),则计算公式为:
TimOut = ((Prescaler + 1) * (Period + 1)) /TimClk;
TIM3的时钟频率TimClk为72MHZ,如果需要1秒的中断时间,则可以设置
Prescaler=7200-1;
Period=10000-1;
计算可得TimOut = 1 秒。
常用的几个溢出时间:
Period =9999 Prescaler =7199 1000ms
Period =4999 Prescaler =7199 500ms
Period =2499 Prescaler =7199 250ms
Period =1999 Prescaler =7199 200ms
Period =999 Prescaler =7199 100ms
有了这些基本概念,就可以进入编程步骤了。
2 YIE002-STM32的定时器编程
如之前一样,分为图形配置然后自动生成代码,以及手动编写代码。
2.1 Cube MX的图形配置
本篇的例子,是在03ExtIO上修改的。原来的配置没有修改,主要添加定时器TIM3的配置。如图2所示。
在Pinout & Configuration菜单下,选择Timers中的TIM3,将其“Internal Clock”的选项勾选上。同时设置Prescaler值为7200-1,Counter Period值设置为10000-1。
然后设置TIM3中断的优先级,可在System Core的NVIC中设置,如图3所示。
之前设置的外部中断的优先级,可以不必修改,保留即可。
完成设置后,生成代码。
2.2 编写TIM3中断服务函数
定时的中断服务处理函数,在startup_stm32f103xb.s中可以看到。
EXPORT TIM1_BRK_IRQHandler
EXPORT TIM1_UP_IRQHandler
EXPORT TIM1_TRG_COM_IRQHandler
EXPORT TIM1_CC_IRQHandler
EXPORT TIM2_IRQHandler
EXPORT TIM3_IRQHandler
EXPORT TIM4_IRQHandler
我们需要处理的中断函数是TIM3_IRQHandler。跟踪此函数,可以知道,Cube Library提供了回调函数HAL_TIM_PeriodElapsedCallback()供用户处理定时器中断。
因此需要处理的工作包括两项:
一是在初始化函数MX_TIM3_Init()中,添加对TIM3的中断使能函数,代码如下:
/* USER CODE BEGIN TIM3_Init 2 */
//robin add 20210725
if(HAL_TIM_Base_Start_IT(&htim3)!=HAL_OK)
{
Error_Handler();
}
/* USER CODE END TIM3_Init 2 */
二是添加对回调函数HAL_TIM_PeriodElapsedCallback()的实现,代码如下(添加在USER CODE BEGIN 4中):
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim ==(&htim3))
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_12);
}
编译下载,就可以看到实际效果了。所实现的代码,将以1秒为周期,驱动灯LED1亮灭。
1,093 total views, 1 views today