你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】stm32的systick原理与应用

[复制链接]
STMCU小助手 发布时间:2022-1-26 23:35
  1. /* SysTick滴答定时器
  2. 一、功能
  3. SysTick定时器是一个简单的定时器,CM3\CM4内核芯片都具备此定时器。SysTick定时器常用来做延时,采用实时系统时则用来做系统时钟。无论用作延时还是用作系统心跳时钟,不需要太复杂的功能,SysTick即可胜任。
  4. 二、实现原理
  5. SysTick定时器是一个24位的倒计数,当倒计数为0时,将从RELOAD寄存器中取值作为定时器的初始值,同时可以选择在这个时候产生中断(异常号:15)。
  6. 例如从RELOAD的值为999,那么当倒计数为0时,就会从复位为999继续倒计数。
  7.         只要不把它在SysTick控制及状态寄存器中的使能位清楚,就永不停息,即使在睡眠模式下也能继续工作。
  8. 三、SysTick寄存器(在 core_cm3.h 有定义,凡是 M3 内核的单片机都是一样的) */
  9. #define SysTick             ((SysTick_Type *)       SysTick_BASE)
  10. #define SysTick_BASE        (SCS_BASE +  0x0010)
  11. #define SCS_BASE            (0xE000E000)
  12. typedef struct
  13. {
  14.     __IO uint32_t CTRL;  // 控制及状态寄存器
  15.     __IO uint32_t LOAD;   // 重装载数值寄存器
  16.     __IO uint32_t VAL; // 当前计数数值寄存器
  17.     __I  uint32_t CALIB;  // 校准寄存器
  18. } SysTick_Type;
  19. /*
  20. SysTick->CTRL: (可通过 SysTick_CLKSourceConfig() 函数设置)
  21. COUNTFLAG(16)R: 计数标志位
  22. 当SysTick数到0,则该位被硬件置 1,当读取该位时,将被硬件清零

  23. CLKSOURCE(2)R/W: 时钟源设置
  24. 1 = 外部时钟源(STCLK) (AHB总线时钟的1/8(HCLK/8))
  25. 0 = 内核时钟(FCLK)  (AHB总线时钟的频率(HCLK))

  26. TICKINT(1)R/W: 中断使能位
  27. 1 = SysTick 倒数到0时产生 SysTick 异常请求
  28. 0 = 数到 0 时无动作

  29. ENABLE(0)R/W: SysTick 定时器使能位
  30. (当中断被使能后,需要关注 void SysTick_Handler(void) 函数)
  31. SysTick_Type->LOAD: (SysTick_Config() 函数会设置该寄存器)

  32. RELOAD(23:0)R/W: 重装载数值寄存器
  33. 当SysTick数到0,将被重装载的值

  34. SysTick_Type->VAL: (SysTick_Config() 函数会设置该寄存器)
  35. CURRENT(23:0)R/Wc: 当前计数数值寄存器
  36. 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。

  37. 四、库函数分析
  38. misc.c
  39. ---------------------------------------------------------------------------------- */
  40. #define SysTick_CLKSource_HCLK_Div8    ((uint32_t)0xFFFFFFFB)
  41. #define SysTick_CLKSource_HCLK         ((uint32_t)0x00000004)
  42. #define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \
  43. ((SOURCE) == SysTick_CLKSource_HCLK_Div8))
  44. void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
  45. {
  46.     /* Check the parameters */
  47.     assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));
  48.     if (SysTick_CLKSource == SysTick_CLKSource_HCLK)
  49.     {
  50.         SysTick->CTRL |= SysTick_CLKSource_HCLK; // 设置 CLKSOURCE 为 1
  51.     }
  52.     else
  53.     {
  54.         SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;      // 设置 CLKSOURCE 为 0
  55.     }
  56. }

  57. core_cm3.c
  58. ----------------------------------------------------------------------------------
  59. #define SysTick_LOAD_RELOAD_Pos             0
  60. #define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)
  61. typedef enum IRQn
  62. {
  63. //...
  64.     SysTick_IRQn                = -1,
  65. //...
  66. } IRQn_Type;
  67. #define __NVIC_PRIO_BITS          4
  68. #define SysTick_CTRL_CLKSOURCE_Pos          2
  69. #define SysTick_CTRL_CLKSOURCE_Msk         (1ul << SysTick_CTRL_CLKSOURCE_Pos)

  70. #define SysTick_CTRL_TICKINT_Pos            1
  71. #define SysTick_CTRL_TICKINT_Msk           (1ul << SysTick_CTRL_TICKINT_Pos)

  72. #define SysTick_CTRL_ENABLE_Pos             0
  73. #define SysTick_CTRL_ENABLE_Msk            (1ul << SysTick_CTRL_ENABLE_Pos)

  74. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
  75. {
  76.     if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
  77.     // 设置计数值为 ticks - 1
  78.     // 原因1:视频说是执行这些代码需要时间,所以减少一个节拍
  79.     // 原因2:我认为是因为 SysTick 的倒计数到 0,例如设置 1000 ,那么范围就应该是 999 ~ 0。
  80.     SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;
  81.     // 设置中断优先级
  82.     NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
  83.     SysTick->VAL   = 0;
  84.     // 设置时钟源为外部时钟源,同时开启中断、并使能 SysTick 定时器
  85.     SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
  86.                      SysTick_CTRL_TICKINT_Msk   |
  87.                      SysTick_CTRL_ENABLE_Msk;
  88.     return (0);
  89. }
  90. /* 五、延时应用
  91. 1、中断方式 */
  92. static __IO uint32_t TimingDelay;
  93. void Delay(__IO uint32_t nTime)
  94. {
  95.     TimingDelay = nTime;
  96.     while(TimingDelay != 0);
  97. }
  98. /* 中断服务函数 */
  99. void SysTick_Handler(void)
  100. {
  101.     if (TimingDelay != 0x00)
  102.     {
  103.         TimingDelay--;
  104.     }
  105. }
  106. int main(void)
  107. {
  108. // ...
  109.     if (SysTick_Config(SystemCoreClock / 1000)) // 注意,这里systick时钟为HCLK,中断时间间隔1ms
  110.     {
  111.         while (1);
  112.     }
  113.     while(1)
  114.     {
  115.         Delay(200);//2ms
  116. // ...
  117.     }
  118. }
  119. /* SysTick_Config(SystemCoreClock / 1000): (原代码这里假设是采用时钟源为 HCLK)
  120. 这里设置的是 72000000Hz / 1000 = 72000 ticks,也就是说 SysTick 从 (72000-1) 开始倒数。
  121. 每倒数完 72000 个节拍就触发一次中断。
  122. 一个节拍的时间为:72000000 / 72000 = 1000us == 1ms
  123. SysTick_Config((SystemCoreClock / 8000000) * 1000 * 1):
  124. SysTick_Config() 会设置时钟源为 HCLK/8 所以实际应用中不能按照上述代码的参数。
  125. SystemCoreClock / 8000000: 1us 的节拍数
  126. 1us的节拍数 * 1000: 则为 1ms 的节拍数
  127. 1ms 的节拍数 * 1: 设置 1ms 一个SysTick中断,即从 ((SystemCoreClock / 8000000) * 1000 * 1) - 1 开始倒数。
  128. 2、轮询方式 */
  129. static u8  fac_us=0; //us延时倍乘数
  130. static u16 fac_ms=0; //ms延时倍乘数
  131. void delay_init()
  132. {
  133.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8
  134.     fac_us = SystemCoreClock/8000000;         // 为系统时钟的1/8  1us = 72000000 / 8000000 =  9 个节拍
  135.     fac_ms = (u16)fac_us*1000; // 1ms 需要 9 * 1000 = 9000 个节拍
  136. }
  137. //延时 nus 微秒
  138. void delay_us(u32 nus)
  139. {
  140.     u32 temp;
  141.     SysTick->LOAD=nus*fac_us;  //时间加载
  142.     SysTick->VAL=0x00;          //清空计数器
  143.     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;                //开始倒数
  144.     do
  145.     {
  146.         temp=SysTick->CTRL;
  147.     }
  148.     while((temp&0x01)&&!(temp&(1<<16)));  //等待时间到达
  149.     SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
  150.     SysTick->VAL =0X00;                     //清空计数器
  151. }
  152. //延时nms
  153. //注意nms的范围
  154. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  155. //nms<=0xffffff*8*1000/SYSCLK
  156. //SYSCLK单位为Hz,nms单位为ms
  157. //对72M条件下,nms<=1864
  158. void delay_ms(u16 nms)
  159. {
  160.     u32 temp;
  161.     SysTick->LOAD=(u32)nms*fac_ms;   //时间加载(SysTick->LOAD为24bit)
  162.     SysTick->VAL =0x00;                       //清空计数器
  163.     SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  //开始倒数
  164.     do
  165.     {
  166.         temp=SysTick->CTRL;
  167.         //等待时间到达,这里使用了一个小技巧,通过(temp&0x01)检查 SysTick 的使能位,避免 Systick 定时器被关闭而导致无限循环
  168.     }
  169.     while((temp&0x01)&&!(temp&(1<<16)));
  170.     SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
  171.     SysTick->VAL =0X00;                      //清空计数器
  172. }
复制代码


SYSTick 定时器
CM3 内核的处理器,内部包含了一个 SysTick 定时器,(SysTick 的时钟源自HCLK的8分频,8个系统时钟周期systick跳一个,即8*1/72M=1/9 us)SysTick是一个24位的倒计数定时器,当计到0时,将从RELOAD寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息。
利用 STM32 的内部 SysTick 来实现延时的,这样既不占用中断,也不占用系统定时器。因为在 ucos 下 systic 不能再被随意更改,如果我们还想利用 systick 来做 delay_us 或者delay_ms 的延时,就必须想点办法了,这里我们利用的是时钟摘取法。以 delay_us 为例,比如delay_us (50),在刚进入 delay_us 的时候先计算好这段延时需要等待的 systick 计数次数,这里为 50*9 (假设系统时钟为 72Mhz,那么 systick 每增加 1,就是 1/9us) ,然后我们就一直统计systick的计数变化,直到这个值变化了50*9,一旦检测到变化达到或者超过这个值,就说明延时50us时间到了。———实质上就是不改变systick基本单位时长,以基本单位时长为基本元做多次到达摘取。
收藏 评论0 发布时间:2022-1-26 23:35

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版