STM32F103RCT6的基本定时器

1、定时器的分类

  STM32F103ZET6总共有8个定时器,它们是:TIM1~TIM8。STM32的定时器分为基本定时器、通用定时器和高等定时器。

  TIM6、TIM7是基本定时器。基本定时器是只能向上计数的16位定时器,基本定时器只能有定时的功能,没有外部IO口,所以没有捕获和比较通道。

  TIM2、TIM3、TIM4、TIM5是通用定时器。通用定时器是可以向上计数,也可以向下计数的16位定时器。通用定时器可以定时、输出比较、输入捕捉,每个通用定时器具有4个外部IO口。

  TIM1、TIM8是高等定时器。高等定时器是是可以向上计数,也可以向下计数的16位定时器。高等定时器可以定时、输出比较、输入捕捉、还可以输出三相电机互补信号,每个高等定时器有8个外部IO口。

  定时器分类图如下:

STM32F103RCT6的基本定时器

2、基本定时器

  基本定时器没有外部IO口,所以它只有定时的功能。

  基本定时器只能向上计数,也就是说基本定时器只能递增计数。

  基本定时器功能框图如下:

STM32F103RCT6的基本定时器

  从功能图的1中可以看到,基本定时器的时钟TIMxCLK来自内部时钟,该内部时钟为经过APB1预分频器分频后提供的。基本定时器跟APB1总线时钟的关系如下:

    • 如果APB1预分频系数为1,则基本定时器的时钟等于APB1总线时钟。
    • 如果APB1预分频系数不为1,则基本定时器的时钟等于APB1总线时钟经过分频后的2倍。

  比如APB1总线经过2分频后的时钟为36MHZ,那么基本定时器的时钟就是72MHZ3(36*2)。

  功能图中的2是一个预分频器,来自内部的时钟经过预分器分频后的时钟,用来驱动基本定时器的计数器计数。基本定时器的预分频器是一个16位的预分频器,预分频器可以对定时器时钟进行1~65536之间的任何一个数进行分频。计算方式如下:

  定时器工作时钟 = 来自APB1的时钟/(预分频系数+1)

  功能图中的3是一个16位的计数器,该计数器能能向上计数,最大计数值位65535。基本定时器的计数器从0开始向上计数,当计数器的值与自动重装载寄存器相等时产生更新事件,并清零从头开始计数。

  功能图中的4是一个16位的自动重装载寄存器。该寄存器装着计数器能计数的最大数值。当基本定时器的计数器计数到这个值的时候,如果使能了中断。定时器就会产生溢出中断。

  在定时器的参考资料中,很多地方都提到更新事件,其实更新事件就是计数器溢出。  

3、定时器的时间计算

  基本定时器每计数一次所经过的时间为:

  Time = (PSC + 1)/ TIMxCLK(us)

  PSC是定时器的分频系数,TIMxCLK是内部时钟。

  基本定时器的计数次数由自动重装载寄存器决定的,基本定时器的计数器从0开始向上计数,当计数器的值与自动重装载寄存器相等时,产生溢出。所以基本定时器的溢出时间计算公式如下:

  Time = (PSC+1)*(ARR)/ TIMxCLK(us)

  ARR是自动重装载寄存器的值。

  假设基本定时器TIMxCLK = 72MHZ,PSC = 71,ARR = 1000,那么定时器的溢出时间为:

  Time = (71+1)*1000/72 = 1000(us) = 1(ms)

4、定时器的寄存器   

  TIMx_CR1控制寄存器1

  Bit0位CEN是定时器的使能位,CEN=0时,计数器不工作;CEN=1是计数器开始计数。想要定时器工作,就要将CEN置1。

  Bit1位UDIS是禁止更新位,当UDIS=0时,定时器溢出后会把TIMx_SR寄存器的UIF置1;当UDIS=1时,定时器溢出后并不会置位UIF位,即不会产生中断。

  Bit2位URS是更新请求源,当URS=0时,计数器溢出、设置UG位、通过从模式控制器产生的更新都会产生中断;当URS=1时,只有在计数器溢出的情况下才会产生中断。

  Bit3位OPM可以设置定时器是工作一次还是重复工作,当OPM=0时,定时器产生中断后继续工作;当OPM=1时,定时器溢出后会清除CEN位,即定时器停止计数。

  Bit7位ARPE是自动重装载预装载使能,当ARPE=0时,TIMx_ARR寄存器没有缓冲;当ARPE=1时,TIMx_ARR寄存器具有缓冲。

  TIMx_DIER中断使能寄存器

  Bit0位UIE是更新中断使能位,当UIE=0时,定时器禁止溢出中断;当UIE=1时,定时器可以产生溢出中断。

  Bit8位UDE是更新DMA请求使能位,当UDE=0时,禁止更新DMA请求;当UDE=1时,使能更新DMA请求。

  TIMx_SR状态寄存器

  Bit1位UIF是溢出中断标志。当TIMX_CR1的UDIS=0,如果定时器产生溢出,UIF就会被置1,UIF不会被硬件自动清除,只能软件清除。当产生中断时,可以由该位判断是否是定时器的溢出中断。

  TIMx_EGR事件产生寄存器

  Bit0位UG是产生更新事件,UG位由软件置1,但由硬件自动清除。当UG写0时无作用;当UG写1时,如果TIMx_CR1寄存器的UDIS = 0,则会重新初始化定时器的计数器并产生对寄存器的更新,需要注意的是预分频器也被清除(但预分频系数不变)。

  TIMx_CNT计数器寄存器

  TIMx_CNT只有低16位有效,可以从0到65535之间计数。

  自动重装载寄存器TIMx_ARR

  如果TIMx_ARR的数值为0,则TIMx则会停止工作。 

  自动重装载寄存器是预加载的,每次读写TIMx_ARR寄存器时,实际上是通过读写预加载寄存器实现的。TIMx_ARR寄存器根据TIMx_CR1控制寄存器的bit7位ARPE来决定是带缓冲的或是不带缓冲的。

  当ARPE=0时,TIMx_ARR寄存器是不具有缓冲功能的,当改变TIMx_ARR寄存器的值时,立马就更新定时器的自动重装载寄存器,如下图。 

STM32F103RCT6的基本定时器

  从上图可以看到,在没改变TIMx_ARR寄存器的值之前自动重装载的寄存器的是FF,当在计数器跑到32的时候突然改变TIMx_ARR寄存器的值为36,那么自动重装载的寄存器的值也立马从FF变为了36,改变TIMx_ARR寄存器的值并不会影响计数器寄存器的值,计数器寄存器继续从32计数到36后溢出并产生更新中断。

  当ARPE=1时,TIMx_ARR寄存器是具有缓冲功能的,当改变TIMx_ARR寄存器的值时,并不会马上更新定时器的自动重装载寄存器,而是要等到下一次更新事件发生时,才会更新定时器的自动重装载寄存器的值,如下图。

 STM32F103RCT6的基本定时器

  从上图可以看到,在没改变TIMx_ARR寄存器的值之前自动重装载的寄存器的是F5,当在计数器跑到F1的时候突然改变TIMx_ARR寄存器的值为36时,虽然自动重装载的寄存器的值也变为了36,但是自动重载影子寄存器的值还是F5,并没有被改变,而且计数器寄存器的值是跟自动重载影子寄存器的值比较,只有当产生更新事件的时候,自动重载影子寄存器的值才变为了36。

  TIMx_PSC预分频寄存器

  TIMx_PSC只有低16位有效,用来设置定时器时钟的分频系数。

  TIMx_PSC预分频寄存器具有缓冲功能,如果在定时器运行的过程中更改TIMx_PSC寄存器的值,定时器的分频系数并不会立马发生更改,而是会等到下一个计数器溢出时新的预分频数值才会更改。如下图。

 STM32F103RCT6的基本定时器

  从图中可以看到,当计数器寄存器递增到F8的时候,TIMx_PSC的值被改写成了1,但是实际的预分频系数还没有被改变,还是0,只有当计数器寄存器溢出变为0时,实际的预分频系数才变为1。从图中还可以看出,当预分频系数为0的时候,CK_PSC = CK_CNT,当预分频系数为1的时候,CK_CNT = 2CK_PSC,也就是通过波形可以看出计数频率变了。

5、基本定时器的配置流程

  使能基本定时的时钟,不然无法使用基本定时器。

  设置基本定时器的预分频系数和自动重装载寄存器值,通用定时器和高等定时器还要设置计数方向,因为基本定时器只能向上计数,所以不用设置。

  开启基本定时器。

  通过NVIC配置基本定时器的中断。

  编写基本定时器的中断服务函数,在中断服务函数中,可以通过基本定时器的状态寄存器的值来判断此次产生的中断属于什么类型。

6、HAL库操作基本定时器

  在使用HAL操作基本定时器之前,需要将stm32f1xx_hal_tim.c和stm32f1xx_hal_tim_ex.c文件添加到功能目录,还要在stm32f1xx_hal_conf.h文件中使能HAL_TIM_MODULE_ENABLED宏。如下图。

STM32F103RCT6的基本定时器

  STM32F103RCT6的基本定时器

  以操作基本定时器TIM6为例,初始化代码如下:

STM32F103RCT6的基本定时器
 1 TIM_HandleTypeDef TIM6_Handler_Init;
 2 
 3 void TIM6_Init(void)
 4 {
 5     __HAL_RCC_TIM6_CLK_ENABLE();
 6 
 7     TIM6_Handler_Init.Instance = TIM6;
 8     TIM6_Handler_Init.Init.Prescaler = 36000-1;
 9     TIM6_Handler_Init.Init.Period = 1000;
10     HAL_TIM_Base_Init(&TIM6_Handler_Init);
11     
12     HAL_TIM_Base_Start_IT(&TIM6_Handler_Init);
13      
14     HAL_NVIC_SetPriority(TIM6_IRQn,2,1);
15    HAL_NVIC_EnableIRQ(TIM6_IRQn);
16              
17 }
STM32F103RCT6的基本定时器

  第1行首先定义一个全局变量TIM6_Handler_Init,这是一个句柄变量,用来操作TIM6。

  第5行通过__HAL_RCC_TIM6_CLK_ENABLE()宏使能TIM6的时钟,如果不是能时钟,是无法操作TIM6的。

  第7行是将TIM6_Handler_Init句柄变量指向TIM6。

  第8行是将TIM6的分频系数设为36000,如果TIM6的时钟为72M,那么分频后的值为72000000/36000 = 2000HZ。

  第9行是将TIM6的自动重载在的值设为1000,即TIM6的计数器计数1000次后溢出,根据分频系数可以得出溢出时间为1000 * 36000 /72000000 = 1000 / 2000 = 0.5s,即TIM6的计数器每0.5秒溢出一次。

  第10行是通过HAL_TIM_Base_Init函数配置TIM6定时器。

  第12行是通过HAL_TIM_Base_Start_IT函数使能溢出中断,并启动定时器从0开始向上计数。

  第14行是通过HAL_NVIC_SetPriority函数设置TIM6的中断优先级。

  第15行是通过HAL_NVIC_EnableIRQ函数使能TIM6中断,只有使能了中断,才能进入中断服务函数。

  初始化完TIM6后,编写中断服务函数,代码如下:

STM32F103RCT6的基本定时器
 1 void TIM6_IRQHandler(void)
 2 {
 3     HAL_TIM_IRQHandler(&TIM6_Handler_Init);
 4 }
 5 
 6 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 7 {
 8     if(htim == &TIM6_Handler_Init)
 9     {
10         HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_6);
11     }
12 }
STM32F103RCT6的基本定时器

  第1行是TIM6的中断服务函数。

  第2行是调用HAL库编写的定时器中断处理函数,将定时器的句柄变量通过参数传入其中。

  第6行是定时器的中断回调函数,通过这个函数实现定时器的中断处理。

  第8行是通过句柄判断需要处理的是什么类型的中断。

STM32F103RCT6的基本定时器

上一篇:[Flutter-28]DashedLine 虚线实现


下一篇:markdown简单入门