STM32的HAL库开发—TIMER(定时器) — 基本定时器

一、定时器概述

1.1 软件定时原理

使用纯软件(CPU死等)的方式实现定时(延时)功能。就是delay_us()函数进行延迟,延迟时间是不精准的,同时CPU还在被占用,浪费资源。

1.2 定时器定时定时原理

使用精准的时基,通过硬件的方式,实现定时功能,定时器核心就是计数器。

STM32系统时钟线CLK经过预分频器,得到定时器TIM_CLK工作时钟,每来一个时钟信号,计数器CNT就变化1,这个计数器可以递增,也可以递减,当计数器CNT溢出的时候就代表时间到了,时间到了之后可以产生中断也可以产生事件,同时自动重装载值就会重新装在到计数器里边。

1.3 STM32定时器分类

 1.4 STM32定时器特性表

计时器位数为16,就是计数器的值可以是0-65535之间。

计时器模式:

  • 递增:每来一个时钟信号,计时器的值加一。
  • 递减:每来一个时钟信号,计时器的值减一。
  • 中央对齐计时:
  • 预分频系数:必须为整数,就是将系统时钟信号进行分频,分频就是除一个系数。

    产生DMA请求:当计时器溢出时,就是时间到了,可以产生DMA请求。

    捕获/比较通道:基本定时器没有,只有通用和高级定时器有。

    互补输出:只有高级定时有。

    1.5 STM32基本、通用、高级定时器功能整体的区别

    基本定时器只能用作定时功能,,没有输入输出通道,通用定时器具有基本定时器的功能,同时还可以输入捕获/输出比较。高级定时器具备通用定时器的左右功能,同时还有互补输出等功能。

    二、基本定时器

    2.1 基本定时器简介

    基本定时器时TIM6和TIM7,有一个16位的递增计数器,不能递减,计数值为0~65535。

    有一个16位预分频器,分频系数为1~65536。

    可触发DAC,当计数器溢出的时候,可进行一次数模转化。

    在更新事件(计数器溢出)时,可产生中断/DMA请求,这个月由用户自己设置。

    2.2 基本定时器框图

    1、时钟源来自内部时钟

    2、时钟信号经过控制器来到预分频器,经过预分频器PSC分频,得到基本定时器的工作时钟CK_CNT,每来一个时钟,计数器的值就加一。

    3、当计数器CNT的值与自动重装载寄存器ARR的影子寄存器的值相同时,达到溢出条件,发生溢出。

    图中可以看到,自动重装载寄存器和预分频器都有影子,这俩都有影子寄存器。影子寄存器是实际
    起作用的寄存器,不可直接访问。例如不可以直接访问自动重装载寄存器的影子寄存器,但是可以访问自动重装载寄存器,这个寄存器起到缓冲的作用。

    而写入ARR寄存器的值不能马上起作用,必须转移到影子寄存器里边才能起作用。转移到影子寄存器里需要有条件,就是事件。PSC寄存器也是一样的,必须转移到影子寄存器里边才能起作用。

    这个是图解, U事件就是Update事件,当计数器CNT溢出的时候,除了产生更新事件,还可以产生中断和DMA输出。默认情况下U事件是默认产生的,可以配置成不产生。而中断和DMA输出默认是不产生的,可以配置成产生。

    除了计数器溢出的时候产生更新事件,设置UG位也可以软件产生更新事件。

    产生U更新事件后,预装载寄存器的值会加载到对应的影子寄存器里边,包括自动重装载寄存器和PSC预分频器。这里边可以设置ARR寄存器有没有缓冲功能,设置APRE位,置1就是有缓冲功能,写入ARR寄存器不会立即转移到影子寄存器里边。置0会就是无缓冲作用,会立即转移到影子寄存器里边。

    控制部分:

    可以在控制寄存器设置复位、使能、计数等。触发控制器就是触发DAC产生一次DAC转换,TRGO信号在计数器溢出的时候产生。

    时钟源:

     TIM1和TIM8是挂在在APB2上面的,TIM2-TIM7是挂载在APB1上面的,其中APB2最大频率是72MHz,APB1最大稳定频率为36MHz。但是实际定时器最大时钟频率不是这么确定的,看下面时钟树。

     程序里边有一个HAL初始化配置,将APB1预分频系数配置成2,那么满足前面时钟数里边说的的”如果APB1预分频系数=1则频率不变,否则频率x2“,分频系数不是1,所以乘2得到72MHz时钟,前提是PCLK1时钟是36MHz。

    同理,程序里边设置的APB2预分频系数为1,那个满足时钟树里边说的"如果APB2预分频系数=1则频率不变,否则频率x2",分频系数为1,所以TIM1和TIM8时钟频率为72MHz,前提是PCLK2时钟为72MHz。

    2.3 STM32定时器计数模式及溢出条件

    递增计数模式:溢出条件为计数器的值与ARR影子寄存器值相同。 发生溢出之后计数器从0开始重新计数。

    递减计数模式:溢出条件为CNT值为0,发生溢出之后计时器从ARR重新开始递减。

    中心对齐模式:溢出条件有两个,一个是CNT值为ARR-1,另一个是CNT的值为1。计时器从0开始递增,递增到ARR-1发生溢出。然后从ARR开始递减,递减到1发生溢出。就是递增的时候在ARR-1发生溢出,递减的时候在1发生溢出。

     

     

    递增和递减的就不说了,很简单 这里讲一下中心对齐模式的时序图。

    其中CK_PSC是定时器的系统时钟信号。

    CNT_EN是计时器使能,开启之后计数器才开始工作。 

    CK_CNT是计数器时钟,由CK_PSC经过预分频器得到的时钟,由于PSC为0,需要加1,也就是1分频,就是不分频。如果PSC的值为1,就是二分频,每来两个CK_PSC,定时器时钟CK_CNT来一个,计数器的值变化一个。

    最开始计数器从04开始递减,递减到01的时候,满足下溢条件,产生更新事件,同时更新中断标志置1。然后递减到0,开始递增,递增到ARR-1了,满足上溢条件,再次产生更新事件,同时更新中断标志置1,如果程序里边开中断了,那么会进入中断服务函数,需要手动讲这个更新中断标志清0;

    2.4 定时器中断实验相关寄存器

    2.4.1 TIM6 和TIM7 控制寄存器 1(TIMx_CR1)

    位 7 ARPE:置1则ARR寄存器有缓冲功能,需要等更新事件来了,才能将ARR寄存器的值写入到ARR影子寄存器里边。置0,则没有缓冲功能,不需要更新事件,立即写入影子寄存器。

    例子:让一个LED亮1s,再灭2s

    亮1s,就是使用定时器设置ARR寄存器的值,例如ARR寄存器设置100,为1s,那么当CNT计数器的值等于100的时候,发生溢出,此时在中断函数里边将LED灭,同时需要修改ARR寄存器的值,因为灯需要灭2s,如果没有缓冲功能,那么修改ARR寄存器的值时间被计入进去了,时间不准确。如果开启了ARR缓冲功能,那么在ARR到达100之前,就可以写入ARR寄存器的值,等到更新时间发生时,就可以自动写入到影子寄存器里边,去掉了无缓冲写入ARR寄存器的时间,定时时间更加精准,无误差。如果是亮1s,再灭1s,那么就不需要更改ARR寄存器的值了,就没有这个说法了。

    位 0 CEN:计数器使能位 ,置1则开启计数器,置0关闭计数器。默认是关闭的。

    用于设置ARR寄存器是否具有缓冲,使能/关闭计数器。

    2.4.2 TIM6 和TIM7 DMA/中断使能寄存器(TIMX_DIER)

    位8 UDE:置0 关闭更新DMA请求 ,置1开启更新DMA请求。

    位0 UIE:置0关闭更新中断,置1开启更新中断。

    2.4.3 TIM6 和TIM7 状态寄存器(TIMX_SR)

    位0 UIF: 更新中断标志位,当计数器发生溢出时,这位会被硬件置1,如果使用更新中断,需要再中断函数里边使用软件将这一位清除。

    用于判断是否发生了更新中断,由硬件置1,软件清零。

    2.4.4 TIM6 和TIM7 计数器(TIMx_CNT)

    16位计数器,存储计数器的值,范围为0-65535。可读可写,在计数器运行当中,也可以对它进行读写操作。

    2.4.5 TIM6 和TIM7 预分频器(TIMx_PSC)

    设置定时器时钟分频系数的,实际计算分频的时候,需要将这个寄存器的值加1。在每一次更新事件时,PSC的数值被传送到实际的预分频寄存器中。

    2.4.5 TIM6 和TIM7 自动重装载寄存器(TIMX_ARR)

    设置自动重装载值得,ARR的数值将传送到实际的自动重装载寄存器中。

    2.5 定时器溢出时间计算方法

    这个公式很简单,可以自己推一下,主要就是先计算计数器计一个数需要的时间,在去乘总共计多少个数。这里的(PSC + 1)表示分频系数加一才是真正的分频数,比如PSC寄存器设置的为0,那么他是1分频,不可能是0分频。(ARR + 1)表示计数总个数,如果设置为0,那么它计一个数就溢出了,不可能是计0个数。 

    2.6 定时器中断实验配置步骤

    1、HAL_TIM Base_Init()函数,配置定时器基础工作参数。

    2、HAL_TIM_Base_MspInit()函数,定时器基础MSP初始化,配置NVIC、CLOCK等。

    3、HAL_TIM_Base_Start_IT()函数,使能更新中断并启动计数器。

    4、HAL_NVIC_SetPriority()、 HAL_NVIC_EnablelRQ()函数设置优先级,使能中断。

    5、TIMx_IRQHandler()>HAL_TIM_IRQHandler()函数,编写中断服务函数。

    6、HAL_TIM_PeriodElapsedCallback()函数,编写定时器更新中断回调函数。

    2.7 定时器实验中断

    实验:使用定时器6,实现500ms定时器更新中断,在中断里翻转LED0。

    定时器产生中断有两种方式,一种是软件设置UG位,一种是计数器溢出时,产生中断。

    思路:定时500ms带入前面的公式,TIM6是挂载在APB1总线上面的,APB1的时钟为36MHz,APB1的分频系数为2,不为1,所以TIM6的时钟为36MHz乘2,就是72MHz。剩下ARR和PSC两个未知数,任取一个计算出另一个,一般是取PSC,计算出ARR的值。例如设置PSC的值为7199,那么计算出ARR的值为4999。

    btim.h头文件

    #ifndef __BTIM_H
    #define __BTIM_H
    
    #include "stm32f1xx.h"
    
    void TIM_Init(uint16_t arr ,uint16_t psc);
    #endif
    

    btim.c源程序

    #include "./BSP/TIMER/btim.h"
    #include "./BSP/LED/led.h"
    
    TIM_HandleTypeDef htim;
    
    void TIM_Init(uint16_t arr ,uint16_t psc)
    {
    	htim.Instance = TIM6;
    	//设置开启ARR寄存器缓冲功能  这里设置固定500ms 如果定时时间不同  需要修改ARR的值  需要开启
    	htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
    	//基本定时器只有向上计数模式  这个不设置也可以
    	htim.Init.CounterMode =TIM_COUNTERMODE_UP;
    	//设置自动重装载值
    	htim.Init.Period = arr;
    	//分频系数
    	htim.Init.Prescaler = psc;
    	HAL_TIM_Base_Init(&htim);
    	
    	HAL_TIM_Base_Start_IT(&htim);
    }
    	
    //这个函数由HAL_TIM_Base_Init自动调用
    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
    {
    	//这个函数是所有定时器公用的 所以需要判断 如果使用了多个定时器
    	if(htim->Instance == TIM6)
    	{
    		//开启定时器6时钟
    		__HAL_RCC_TIM6_CLK_ENABLE();
    		
    		//设置定时器6优先级
    		HAL_NVIC_SetPriority(TIM6_IRQn, 2, 2);
    		
    		//开启定时器中断
    		HAL_NVIC_EnableIRQ(TIM6_IRQn);
    	}
    }	
    
    //定时器6中断处理函数
    void TIM6_IRQHandler(void)
    {
    	//定时器6HAL库中断公共处理函数
    	HAL_TIM_IRQHandler(&htim);
    	
    	
    }
    
    //定时器TIM Update event回调函数 HAL_TIM_IRQHandler里边自动调用
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    	//这个函数是所有定时器公用的 所以需要判断 如果使用了多个定时器
    	if(htim->Instance == TIM6)
    	{
    		LED0_TOGGLE();
    	}
    }
    

     main.c主函数

    #include "./SYSTEM/sys/sys.h"
    #include "./SYSTEM/usart/usart.h"
    #include "./SYSTEM/delay/delay.h"
    #include "./BSP/LED/led.h"
    #include "./BSP/TIMER/btim.h"
    
    int main(void)
    {
        HAL_Init();                         /* 初始化HAL库 */
    	sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
        delay_init(72);                     /* 延时初始化 */
        led_Init();                         /* LED初始化 */
    	TIM_Init(5000 - 1,7199); //初始化定时器6 ARR设置成4999 ,PSC设置成 7199
    	while(1)
        { 
    	
        }
    }

    现象: 正电平时间500ms

    作者:猿~~~

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32的HAL库开发—TIMER(定时器) — 基本定时器

    发表回复