正点原子学习小组
直播中

donatello1996

7年用户 687经验值
擅长:处理器/DSP 控制/MCU RF/无线
私信 关注
[经验]

【正点原子STM32探索者V3开发板体验】定时器非中断事件(手动或外部事件重置)按键输入

2

      在我自己的实际项目中,需要用到定时器进行精确计时,但是出于高实时性的考虑,不能让单片机频繁进入中断,哪怕只是几百毫秒的长间隔中断也不好,最好是一直在主循环中轮询定时器状态寄存器来查询溢出事件,这种用法常用于一些跟传感器有密切交互的场合,比如GPS模块,心跳传感器,这些场合下不单单只是接收传感器数据,还需要在接收到特定指令或数据后延时规定长度,比如100毫秒,500毫秒,1秒,所以使用非中断事件是最好的选择,但是我现在还没有往探索者V3板接入实际的传感器,所以重置定时器事件我用按键实现。

手动或外部事件重置定时器机制很简单,只需要用到CR1寄存器的CEN位即可:

10.jpg

当定时器计数值溢出后,SR状态寄存器的末位会有反应,也就是会变成1:
11.jpg


知道了这两点,想要实现按键手动重置就非常简单了,首先是初始化定时器,计数模式向上向下都可,没有区别,重要的是分频系数写8400-1,因为定时器6 7 12的满配时钟都是84MHz,分频系数写8400-1的话,也就是一秒钟计数10K次,即一万次,那么重装载值写10000就是1秒触发一次更新事件,5000就是0.5秒触发一次更新事件,以此类推,我这里分别设置定时器6 100毫秒触发一次更新事件,定时器7 500毫秒,定时器12 1秒:
  1. void tiM6_NO_INT_Init(uint16_t arr, uint16_t psc)
  2. {
  3.     __HAL_RCC_TIM6_CLK_ENABLE();
  4.    
  5.     htim6.Instance = TIM6;
  6.     htim6.Init.Period = arr;
  7.     htim6.Init.Prescaler = psc;
  8.     htim6.Init.CounterMode = TIM_COUNTERMODE_DOWN;
  9.     htim6.Init.ClockDivision = 1;
  10.     HAL_TIM_Base_Init(&htim6);
  11.    
  12.     TIM6->CNT = 65535;
  13.     TIM6->CR1 &= (~TIM_CR1_CEN);
  14.     //TIM6->CR1 |= TIM_CR1_CEN;
  15.     TIM6->SR &= ~TIM_FLAG_UPDATE;
  16. }
  17. void TIM7_NO_INT_Init(uint16_t arr, uint16_t psc)
  18. {
  19.     __HAL_RCC_TIM7_CLK_ENABLE();
  20.    
  21.     htim7.Instance = TIM7;
  22.     htim7.Init.Period = arr;
  23.     htim7.Init.Prescaler = psc;
  24.     htim7.Init.CounterMode = TIM_COUNTERMODE_DOWN;
  25.     htim7.Init.ClockDivision = 1;
  26.     HAL_TIM_Base_Init(&htim7);
  27.    
  28.     TIM7->CNT = 65535;
  29.     TIM7->CR1 &= (~TIM_CR1_CEN);
  30.     //TIM7->CR1 |= TIM_CR1_CEN;
  31.     TIM7->SR &= ~TIM_FLAG_UPDATE;
  32. }
  33. void TIM12_NO_INT_Init(uint16_t arr, uint16_t psc)
  34. {
  35.     __HAL_RCC_TIM12_CLK_ENABLE();
  36.    
  37.     htim12.Instance = TIM12;
  38.     htim12.Init.Period = arr;
  39.     htim12.Init.Prescaler = psc;
  40.     htim12.Init.CounterMode = TIM_COUNTERMODE_DOWN;
  41.     htim12.Init.ClockDivision = 1;
  42.     HAL_TIM_Base_Init(&htim12);
  43.    
  44.     TIM12->CNT = 65535;
  45.     TIM12->CR1 &= (~TIM_CR1_CEN);
  46.     //TIM12->CR1 |= TIM_CR1_CEN;
  47.     TIM12->SR &= ~TIM_FLAG_UPDATE;
  48. }
  49.     TIM6_NO_INT_Init(999 , 8399 - 1);
  50.     TIM7_NO_INT_Init(4999 , 8399 - 1);
  51.     TIM12_NO_INT_Init(9999 , 8399 - 1);
然后加入按键例程代码,设计为按键重置定时器计数:
  1.    while(1)
  2.     {
  3.         ret = USER_KEY_Scan();
  4.         if (ret)
  5.         {
  6.             switch (ret)
  7.             {
  8.                 case KEY0_PRES:
  9.                 printf(\\\\\\\"KEY0_PRES\\\\\\\\n\\\\\\\");
  10.                 TIM6->CR1 |= TIM_CR1_CEN;
  11.                 break;
  12.                 case KEY1_PRES:
  13.                 printf(\\\\\\\"KEY1_PRES\\\\\\\\n\\\\\\\");
  14.                 TIM7->CR1 |= TIM_CR1_CEN;
  15.                 break;
  16.                 case KEY2_PRES:
  17.                 printf(\\\\\\\"KEY2_PRES\\\\\\\\n\\\\\\\");
  18.                 TIM12->CR1 |= TIM_CR1_CEN;
  19.                 break;
  20.                 default : break;
  21.             }
  22.         }
  23.         if(TIM6->SR & TIM_FLAG_UPDATE)
  24.         {
  25.             TIM6->SR &= ~TIM_FLAG_UPDATE;
  26.             TIM6->CR1 &= (~TIM_CR1_CEN);
  27.             printf(\\\\\\\"TIM6 TIM_FLAG_UPDATE TIM6->CNT = %d\\\\\\\\n\\\\\\\" , TIM6->CNT);
  28.         }
  29.         if(TIM7->SR & TIM_FLAG_UPDATE)
  30.         {
  31.             TIM7->SR &= ~TIM_FLAG_UPDATE;
  32.             TIM7->CR1 &= (~TIM_CR1_CEN);
  33.             printf(\\\\\\\"TIM7 TIM_FLAG_UPDATE TIM7->CNT = %d\\\\\\\\n\\\\\\\" , TIM7->CNT);
  34.         }
  35.         if(TIM12->SR & TIM_FLAG_UPDATE)
  36.         {
  37.             TIM12->SR &= ~TIM_FLAG_UPDATE;
  38.             TIM12->CR1 &= (~TIM_CR1_CEN);
  39.             printf(\\\\\\\"TIM12 TIM_FLAG_UPDATE TIM12->CNT = %d\\\\\\\\n\\\\\\\" , TIM12->CNT);
  40.         }
  41.     }
运行效果,当按键按下之后,三个定时器对应三个不同按键分别工作,在延时规定时间(100毫秒 500毫秒 1秒)后,触发更新事件:

12.jpg

成功使用了通用定时器进行轮询外部事件计时后,现在使用同样的原理实现延时,唯一区别就是这个延时是阻塞的,而计时是非阻塞的,用途不同,可以用在需要阻塞延时的场合,使用通用定时器13实现。
先看看代码:
  1. void TIM13_NO_INT_Init(uint16_t arr, uint16_t psc)
  2. {
  3.     __HAL_RCC_TIM13_CLK_ENABLE();
  4.    
  5.     htim13.Instance = TIM13;
  6.     htim13.Init.Period = arr;
  7.     htim13.Init.Prescaler = psc;
  8.     htim13.Init.CounterMode = TIM_COUNTERMODE_UP;
  9.     htim13.Init.ClockDivision = 1;
  10.     HAL_TIM_Base_Init(&htim13);
  11.    
  12.     //TIM13->CR1 &= (~TIM_CR1_CEN);
  13.     TIM13->CR1 |= TIM_CR1_CEN;
  14.     TIM13->SR &= ~TIM_FLAG_UPDATE;
  15. }
  16. void TIM13_Delay_us(unsigned int delay_us)
  17. {
  18.     TIM13->CNT = 0;
  19.     while (TIM13->CNT < delay_us);   
  20. }
  21. void TIM13_Delay_ms(uint32_t delay_ms)
  22. {
  23.     while (delay_ms--)
  24.     {
  25.         TIM13_Delay_us(1000);
  26.     }
  27. }
原理非常简单,也是使用通用定时器的计数寄存器CNT,当CNT计数未达到规定数时,使用死循环使CPU一直阻塞,systick也是类似的原理。

13.jpg

更多回帖

发帖
×
20
完善资料,
赚取积分