• STM32 定时器介绍--基本定时器


    目录

    基本定时器

    1.功能框图

    1-时钟源

    2-控制器

    3-时基单元

    4-影子寄存器

    2.定时时间的计算

    3.时基初始化结构体

    4.实验设计

    1-配置时基初始化结构体

    2-开启定时器更新中断(即定时时间到了)

    3-编写main函数


    在我之前文章中说过一个系统定时器SysTick,它存在于内核,嵌套在NVIC中,所有的Cortex-M内核的单片机都具有这个定时器。而今天介绍的STM32定时器是相当于一个外设,功能更加丰富全面。

    SysTick定时器文章链接:

    STM32 系统定时器--SysTick_systick中断_Haohao fighting!的博客-CSDN博客

    定时器分类:基本定时器(TIM6和TIM7)、通用定时器(TIM2、TIM3、TIM4和TIM5)、高级定时器(TIM1和TIM8)

    功能:基本定时器:只能实现定时的功能

              通用定时器:定时、输出比较、输入捕获、互补输出

              高级定时器:包含了基本定时器和通用定时器的所有功能。

    STM32F103系列有2个高级定时器TIM1和TIM8,4个通用定时器TIM2/3/4/5,2个基本定时器TIM6和TIM7。

    基本定时器

    计数器16bit,只能从0开始向上计数。没有外部的GPIO,是内部资源,只能用来定时。时钟来自PCLK1,为72M,可实现1~65536分频。

    1.功能框图

    1-时钟源

    时钟源来自RCC的TIMx_CLK(属于内部的CK_INT)

    基本定时器和通用定时器是挂载到APB1总线上,由于APB1的预分频系数是2,所以频率要×2,也就是72M。

    2-控制器

    控制器用于控制定时器的:复位、使能、计数、触发DAC。涉及到的寄存器为:CR1/2、DIER(DMA/中断使能寄存器)、EGR(事件产生寄存器)、SR(状态寄存器)

    3-时基单元

    定时器最主要的就是时基部分:包括预分频器、计数器、自动重装载寄存器。这些所有定时器都有。

    预分频器PSC (寄存器 TIMx_PSC控制):

    结构框图中:CK_PSC就是从APB1得到的时钟(72M),预分频控制寄存器的值就是PSC里面的值,而CK_CNT就是分频最终得到的值。

    16位的预分频器PSC对内部时钟CK_PSC进行分频之后,得到计数器时钟CK_CNT=CK_PSC(预分频器时钟)/(PSC(预分频因子)+1)

    计数器CNT在计数器时钟的驱动下开始计数,计数一次的时间为1/CK_CNT。

    计数器、自动重装载寄存器

    定时器使能(CEN置1)后,计数器 CNT在CK_CNT 驱动下计数,当TCNT值(计数器的值)与ARR 的设定值(自动重装载寄存器的值,自动重装载寄存器是16位的,最大值是65535)相等时就自动生成事件(如果使能中断就产生中断)并 CNT 自动清零,然后自动重新开始计数,如此重复以上过程。

    4-影子寄存器

    功能框图上有个影子,PSC和ARR都有影子寄存器。

    什么是影子寄存器?

    这表示在物理上这个寄存器对应2个寄存器:一个是我们可以可以写入或读出的寄存器,称为预装载寄存器,另一个是我们看不见的、无法真正对其读写操作的,但在使用中真正起作用的寄存器,称为影子寄存器。

    每次发生更新事件(UEV)时,所有真正起作用的影子寄存器(shadowregister)可以同时被更新为预装载寄存器 (preloadregister)里面的值,这样可以保证多个通道的操作能够准确同步;如果没有影子寄存器(shadowregister),或者预装载寄存器 (preloadregister) 和影子寄存器 (shadowregister)是直通的,当软件更新了预装载寄存器 (preloadregister),也就立即更新了影子寄存器 (shadowregister),由于软件不能同时更新多个影子寄存器,这就会导致多个通道的时序不能同步。

    原文链接:

    https://blog.csdn.net/weixin_51703421/article/details/126500556

    影子寄存器的存在起到一个缓冲的作用,用户值->寄存器->影子寄存器->起作用。例如:自动重装载的影子寄存器(ARR寄存器):自动重载寄存器是预装载的。对自动重载寄存器执行写入或读取操作时会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器,也可以在每次发生更新事件 (UEV) 时传送到影子寄存器。也就是说,在CNT计数器计数的过程中,自动重装载寄存器的值可以随时更改,更改的数值可以立刻起作用,也可以在本轮计数完成之后再起作用。是否立刻起作用可以通过软件来进行控制。由控制寄存器 1(TIMx_CR1)的ARPE位来控制。0:TIMx_ARR寄存器没有缓冲 1:TIMx_ARR寄存器具有缓冲。

    而PSC的影子寄存器,只能在这一轮计数完成之后的下一轮才开始起作用。这个是不能通过软件来控制的

    如果不使用影子寄存器则用户值在写到寄存器之后则里面起作用。

    2.定时时间的计算

    PSC = 72-1,定时器内部时钟为72MHz,定时器频率=72M/(PSC+1)=1MHz,近似时间为1微秒。

    若我们设定ARR(自动重装载寄存器)= 1000-1,设定为向上计数模式,从0计数到999,则计了1000次。

    中断周期   T = 1000 *1/1MHz = 1ms

    3.时基初始化结构体

    时基初始化结构体 TIM_TimeBaseInitTypeDef 里面有5个成员,TIM6和TIM7的寄存器里面只有TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,另外三个成员是通用定时器和高级定时器才有。TIM_Prescaler对应预分频器(TIMx_PSC),TIM_Period对应自动重装载寄存器(TIMx_ARR)。

    1. typedef struct
    2. {
    3. TIM_Prescaler 都有
    4. TIM_CounterMode TIMx,x[6,7]没有,其他都有
    5. TIM_Period 都有
    6. TIM_ClockDivision TIMx,x[6,7]没有,其他都有
    7. TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有
    8. }TIM_TimeBaseInitTypeDef;

    4.实验设计

    基本定时器产生500MS定时,在主函数里面让LED反转

    设计思路:让基本定时器产生1ms的中断,到了1ms之后进入中断服务函数,中断服务函数里的变量自加,加到500之后代表到了500ms。我们可以在主函数内部判断变量值是否等于500,到了500让LED翻转一次。

    这个就与51或者蓝桥杯的定时器0、1、2的定时功能差不多,我们通过定时的功能,完成一个固定周期的重复事件。

    1-配置时基初始化结构体

    首先宏定义硬件,基本定时器的时钟,自动重装载的值,时钟分频因子,定时器的中断通道(在使用高级定时器的时候只更改这些即可)。

    1. /********************基本定时器TIM参数定义,只限TIM67************/
    2. #define BASIC_TIM6 // 如果使用TIM7,注释掉这个宏即可
    3. #ifdef BASIC_TIM6 // 使用基本定时器TIM6
    4. #define BASIC_TIM TIM6
    5. #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
    6. #define BASIC_TIM_CLK RCC_APB1Periph_TIM6
    7. #define BASIC_TIM_Period 1000-1 //自动重装载的值 1000次计数进一次中断,也就是1毫秒进一次中断
    8. #define BASIC_TIM_Prescaler 71 //时钟分频因子 控制计数一次的时间 72/(71+1) = 1M 也就是1微秒
    9. #define BASIC_TIM_IRQ TIM6_IRQn //中断源 所有的中断源在 stm32f10x.h文件中
    10. #define BASIC_TIM_IRQHandler TIM6_IRQHandler
    11. #else // 使用基本定时器TIM7
    12. #define BASIC_TIM TIM7
    13. #define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
    14. #define BASIC_TIM_CLK RCC_APB1Periph_TIM7
    15. #define BASIC_TIM_Period 1000-1
    16. #define BASIC_TIM_Prescaler 71
    17. #define BASIC_TIM_IRQ TIM7_IRQn
    18. #define BASIC_TIM_IRQHandler TIM7_IRQHandler
    19. #endif

    配置、初始化时基结构体,配置、初始化NVIC结构体。

    1. // 中断优先级配置
    2. static void BASIC_TIM_NVIC_Config(void)//涉及到中断就要配置、初始化 NVIC
    3. {
    4. NVIC_InitTypeDef NVIC_InitStructure;
    5. // 设置中断组为0
    6. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    7. // 设置中断来源
    8. NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;
    9. // 设置主优先级为 0
    10. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    11. // 设置抢占优先级为3
    12. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    13. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    14. NVIC_Init(&NVIC_InitStructure);
    15. }
    16. static void BASIC_TIM_Mode_Config(void)//初始化时基结构体
    17. {
    18. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    19. // 开启定时器时钟,即内部时钟CK_INT=72M
    20. BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
    21. // 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
    22. TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;
    23. // 时钟预分频数为
    24. TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;
    25. // 时钟分频因子 ,基本定时器没有,不用管
    26. TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    27. // 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
    28. TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
    29. // 重复计数器的值,基本定时器没有,不用管
    30. TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
    31. // 初始化定时器
    32. TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
    33. // 清除计数器中断标志位
    34. TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
    35. // 开启计数器中断
    36. TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
    37. // 使能计数器
    38. TIM_Cmd(BASIC_TIM, ENABLE);
    39. }
    40. void BASIC_TIM_Init(void)
    41. {
    42. BASIC_TIM_NVIC_Config();
    43. BASIC_TIM_Mode_Config();
    44. }

    2-开启定时器更新中断(即定时时间到了)

    具体哪个定时器,具体哪种中断。这里用到的是更新中断,计数器从n计数到m为一轮产生的中断就叫做更新中断。

    1. extern volatile uint32_t time;
    2. void BASIC_TIM_IRQHandler (void)
    3. {
    4. if ( TIM_GetITStatus( BASIC_TIM, TIM_IT_Update) != RESET )//判断是否产生中断
    5. {
    6. time++;
    7. TIM_ClearITPendingBit(BASIC_TIM , TIM_FLAG_Update);
    8. }
    9. }

    3-编写main函数

    1. int main(void)
    2. {
    3. /*在程序来到main函数这里的时候,系统时钟已经配置成72M*/
    4. LED_GPIO_Config();//初始化函数
    5. BASIC_TIM_Init();
    6. while(1)
    7. {
    8. if(time == 500)
    9. {
    10. time = 0;
    11. LED1_TOGGLE;/* LED1 取反 实现LED1的亮灭反转*/
    12. }
    13. }
    14. }

     完整程序下载链接:

    链接:https://pan.baidu.com/s/13eb3qHm4YVtHELPXYBJXdQ 
    提取码:hjaw 

  • 相关阅读:
    高通Android 12默认授权 不弹出投屏弹窗
    案例分享:Qt激光加工焊接设备信息化软件研发(西门子PLC,mysql数据库,用户权限控制,界面设计,参数定制,播放器,二维图,期限控制,参数调试等)
    【luogu CF1710C】XOR Triangle(数位DP)
    Scrapy设置代理IP方法(超详细)
    Junit
    Ubuntu安装SVN服务并结合内网穿透实现公网访问本地存储文件
    使用PYQT5简单制作动态仪表盘
    如何在视频中加水印?分享这些实用的加水印方法给你
    Flutter的专属Skia引擎解析+用法原理
    1073 Scientific Notation
  • 原文地址:https://blog.csdn.net/ChenWenHaoHaoHao/article/details/133185618