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

【经验分享】STM32 基础重点—SysTick定时器

[复制链接]
STMCU小助手 发布时间:2022-4-27 23:04
11.1 关于SysTick定时器
SysTick定时器(又名系统滴答定时器)是存在于Cortex-M3的一个定时器,只要是ARM Cotex-M系列内核的MCU都包含这个定时器。使用内核的SysTick定时器来实现延时,可以不占用系统定时器,节约资源。由于SysTick是在CPU核内部实现的,跟MCU外设无关,因此它的代码可以在不同厂家之间移植。

本章将使用系统滴答定时器实现延时函数,注意SysTick用于了HAL库的毫秒级延时函数“HAL_Delay()”,不建议日常使用SysTick去作为其它用途,这里只作为演示。

SysTick定时器是一个24位递减定时器,即计数器可以从最大值224开始,每个时钟周期减1,当减到0时,会产生Systick异常,同时再自动重载定时初值,开始新一轮计数。通过设置这个定时初值,就可以实现得到指定时间。如下图 11.1.1 所示,y为定时器初值,然后随着时间增加,值逐渐减小,直至为0,再重新加载初值,如此往复,x1、x2、x3这些时间段,就是我们需要的延时时间。

[[_9E}~4BAPVH2QA1JFFR@C.png

图 11.1.1 Systick定时器工作示意图
假设STM32F103工作在72MHz,即72000000Hz,意味着1s时间内,会计数72000000次。那么1ms则计数72000000/1000=72000次。这个72000就可以作为系统滴答定时器的初始值,将这个值写入系统滴答定时器,定时器在每个时钟周期减1,减到0时,就刚好是1ms,同时产生中断通知,再次加载72000如此反复。HAL库提供“HAL_SYSTICK_Config()”函数去设置这个初始值。

系统滴答定时器控制寄存器比较少,整体比较简单,借助本次机会详细分析一下寄存器和HAL之间是调用关系。系统滴答定时器只有四个控制寄存器:STK_CTRL,STK_LOAD,STK_VAL和STK_CALIB。因为系统滴答定时器属于Cotex-M3内核的外设,相关寄存器介绍不在《参考手册》,而在《3_STM32F10xx Cortex-M3编程手册》,后简称《编程手册》。

系统滴答定时器控制和状态寄存器(STK_CTRL)


[3{6M[FR`KH03Z%FP{FP]@I.png

重点关注Bit[0],用于使能系统滴答定时器,Bit[1]使能系统滴答定时器中断,Bit[2]系统滴答时钟的时钟来源。

系统滴答定时器加载值寄存器(STK_LOAD)

E9VBHEX31HP7TKJ2}KS5[%H.png

Bit[23:0],一共24位,用来设置系统滴答定时器的初始值,因此范围为1~ 16777216。

系统滴答定时器当前值寄存器(STK_VAL)

I)3RM7IWY)]A6S1$VNQX3QV.png

Bit[23:0],一共24位,用来获取当前系统滴答定时器的计数值。

系统滴答定时器校准值寄存器(STK_CALIB)


ES27833[KFM_W@NFVV34YAX.png

这个寄存器没用到,可以不用管。此外,当处理器在调试期间被暂停(halt)时,系统滴答定时器也将暂停运作。

在理解系统滴答定时器的工作方式,了解系统滴答定时器的寄存器基本信息后,就可以尝试编写程序了。


11.2 硬件设计
系统滴答定时器属于Cortex-M3内核资源,不涉及外部硬件电路。实验中会用到LED灯,电路设计参考前面LED点灯实验。


11.3 软件设计
11.3.1.1 软件设计思路
实验目的:使用系统滴答定时器实现自定义延时。

1) 分析HAL库的系统滴答定时器配置函数;

2) 初始化系统滴答定时器(设置计数初值、使能等);

3) 封装延时函数,设置系统滴答定时器中断处理函数;

4) 主函数调用验证;

本实验配套代码位于“5_程序源码\4_基础重点—SysTick定时器”。

11.3.1.2 软件设计讲解

1) 分析HAL库的系统滴答定时器配置函数

在HAL库中,使用“HAL_SYSTICK_Config()”函数配置SysTick的初始值。

代码段 11.3.1 SysTick配置函数(stm32f1xx_hal_cortex.c)

  1. /**

  2.   * @brief  Initializes the System Timer and its interrupt, and starts the System Tick Timer.

  3.   *         Counter is in free running mode to generate periodic interrupts.

  4.   * @param  TicksNumb: Specifies the ticks Number of ticks between two interrupts.

  5.   * @retval status:  - 0  Function succeeded.

  6.   *                  - 1  Function failed.

  7.   */

  8. uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)

  9. {

  10.    return SysTick_Config(TicksNumb);

  11. }
复制代码

该函数调用“SysTick_Config()”函数,函数内容如下代码段 11.3.2所示。

代码段 11.3.2 SysTick配置函数(core_cm3.h)

  1. /* ##################################    SysTick function  ############################################ */
  2. /**
  3.   \ingroup  CMSIS_Core_FunctionInterface
  4.   \defgroup CMSIS_Core_SysTickFunctions SysTick Functions
  5.   \brief    Functions that configure the System.
  6.   @{
  7. */



  8. #if defined (__Vendor_SysTickConfig) && (__Vendor_SysTickConfig == 0U)


  9. /**
  10.   \brief   System Tick Configuration

  11.   \details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
  12.            Counter is in free running mode to generate periodic interrupts.
  13.   \param [in]  ticks  Number of ticks between two interrupts.



  14.   \return          0  Function succeeded.

  15.   \return          1  Function failed.

  16.   \note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
  17.            function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
  18.            must contain a vendor-specific implementation of this function.
  19. */
  20. __STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)

  21. {

  22.   if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  23.   {
  24.     return (1UL);                                                   /* Reload value impossible */
  25.   }

  26.   SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */

  27.   NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  28.   SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  29.   SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
  30.                    SysTick_CTRL_TICKINT_Msk   |
  31.                    SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  32.   return (0UL);                                                     /* Function successful */
  33. }

  34. #endif
复制代码


24~27行:判断传入的SysTick初始值是否大于最大值224;

29行:设置SysTick初始值;

30行:设置SysTick中断的优先级,默认为最低;

31行:将SysTick当前计数值清零;

32~34行:设置SysTick的控制和状态寄存器,展开对应的宏,值为“(1<<2) | (1<<1) | (1)”,结合前面STK_CTRL寄存器介绍,可知这里使能了SysTick,使能了SysTick中断,时钟源为AHB。当系统时钟为72MHz时,AHB不分频,也为72MHz,则SysTick的时钟也为72MHz。

通过对“HAL_SYSTICK_Config()”函数分析,可知只需要传入SysTick初始值,其它的都默认已经设置完成了。


2) 初始化系统滴答定时器


假设当MCU工作在72MHz,SysTick也工作在72MHz。时钟在1s内完成周期性变化的次数叫做频率(单位:Hz),因此72MHz则表示1秒SysTick计数72000000次,即1毫秒计数72000次。

因此,如果将72000传入“HAL_SYSTICK_Config()”函数,则SysTick从72000减到0,花费时间为1毫秒,创建函数“SysTickInit()”初始化系统滴答定时器,如代码段 11.3.3 所示。

代码段 11.3.3 初始化SysTick(driver_systick.c)

  1. /*

  2. *  函数名:void SysTickInit(uint32_t cycle)

  3. *  输入参数:cycle,设置系统滴答时钟周期

  4. *  输出参数:无

  5. *  返回值:无

  6. *  函数作用:初始化系统滴答时钟的频率和中断优先级

  7. */

  8. void SysTickInit(uint32_t cycle)

  9. {

  10.     uint32_t init_t = 0;



  11.     init_t = SystemCoreClock/cycle;



  12.     /* 时间(单位:s)=1/频率(单位:HZ)

  13.      * SystemCoreClock频率: 72MHz = 72,000,000

  14.      * 即MCU 1秒会计数72,000,000次

  15.      *       1ms则计数 72MHz/1000 = 72000次

  16.      * 72000就是滴答时钟的初始值,它向下计数72000次,计数将变为0,就会产生一次中断

  17.      * 滴答时钟初始值范围:1~16777216

  18.      *

  19.      * SystemCoreClock/1000:    1ms中断一次

  20.      * SystemCoreClock/100000:  10us中断一次

  21.      * SystemCoreClock/1000000: 1us中断一次

  22.      */

  23.     if(HAL_SYSTICK_Config(init_t) != HAL_OK)

  24.     {

  25.         Error_Handler();

  26.     }



  27.     // 设置滴答定时器中断优先级:最高

  28.     HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

  29.     // 使能滴答定时器中断

  30.     HAL_NVIC_EnableIRQ(SysTick_IRQn);

  31. }
复制代码


12行:使用HAL库提供的全局变量“SystemCoreClock”获取当前系统时钟,再根据传入的cycle,计算出SysTick的初始值; 

25~28行:使用“HAL_SYSTICK_Config()”函数设置SysTick的初始值,并检测是否设置成功;

31行:设置滴答定时器中断优先级,这里设置为最高。前面分析“HAL_SYSTICK_Config()”函数,知道该函数也会设置中断优先级,这里重新设置为最高优先级,在当前示例里,SysTick中断的优先级不重要;

33行:使能SysTick中断;这里是使能NVIC,而“HAL_SYSTICK_Config()”函数使能的是SysTick;

为了方便修改SysTick的初始值,这里定义几个常见的延时周期,如代码段 11.3.4 所示。当需要延时周期为1毫秒时,传入“CYCLE_1MS”给“SysTickInit()”,则SysTick计数到零花费1毫秒

代码段 11.3.4 定义延时周期(driver_systick.h)

  1. #define CYCLE_100MS   10

  2. #define CYCLE_10MS    100

  3. #define CYCLE_1MS     1000

  4. #define CYCLE_100US   10000

  5. #define CYCLE_10US    100000

  6. #define CYCLE_1US     1000<span style="background-color: rgb(255, 255, 255);">000</span>
复制代码

3) 封装延时函数,设置系统滴答定时器中断处理函数

创建延时函数“SysTickDelay()”,在该函数里设置自定义全局变量systick_t的初始值,SysTick每计数完一次则进入SysTick中断,将全局变量systick_t的值减1,如代码段 11.3.6 所示。一直到systick_t变为零,结束延时,如代码段 11.3.5 所示。

代码段 11.3.5 SysTick延时函数(driver_systick.c )

  1. /*

  2. *  函数名:void SysTickDelay(uint16_t m)

  3. *  输入参数:m-延时时间

  4. *  输出参数:无

  5. *  返回值:无

  6. *  函数作用:滴答定时器实现的延时函数

  7. */

  8. void SysTickDelay(uint32_t m)

  9. {

  10.     systick_t = m;

  11.     while(systick_t != 0);

  12. }


  13. 代码段 11.3.6 SysTick中断处理函数(stm32f1xx_it.c)

  14. /*

  15.   * @brief  This function handles SysTick Handler.

  16.   * @param  None

  17.   * @retval None

  18.   */

  19. void SysTick_Handler(void)

  20. {

  21.     HAL_IncTick();

  22.     if(systick_t)

  23.     {

  24.         systick_t--;

  25.     }

  26. }
复制代码

4) 主函数调用验证


代码段 11.3.7 SysTick延时点灯(main.c)

  1. /*

  2. * 初始化滴答时钟

  3. * 通过改变传入参数改变滴答时钟的频率,即SysTickDelay(1)的时长

  4. */

  5. SysTickInit(CYCLE_1MS);



  6. // 初始化LED

  7. LedGpioInit();

  8. while(1)

  9. {

  10.     /* 通过延时一段时间让LED亮灭实现LED闪烁,可以通过示波器打LED的引脚反转周期,精确看时间是否与设置的一致*/

  11.     BLED(ON);             // 点亮LED

  12.     SysTickDelay(1000);   // 延时CYCLE_1MS*1000=1s

  13.     BLED(OFF);            // 熄灭LED

  14.     SysTickDelay(1000);   // 延时CYCLE_1MS*1000=1s

  15. }
复制代码


5行:初始化SysTick,这里传入CYCLE_1MS,则延时函数“SysTickDelay()”的单位为1毫秒;

7~16行:初始化LED,调用延时函数“SysTickDelay()”,传入1000,则延时为1秒;



11.4 实验效果

本实验对应配套资料的“5_程序源码\4_基础重点—SysTick定时器\”。打开工程后,编译,下载,可以看到蓝色LED灯间隔1秒,交替闪烁。读者可修改代码段 11.3.7 中的第5行时钟周期,或者13、15行的延时时间,改变LED灯的闪烁间隔时间。

通过LED展示SysTick的延时结果不够严谨,有条件的读者可以使用示波器或逻辑分析仪,触碰LED灯焊盘的引脚,测试翻转时间,如图 11.4.1 所示,分别修改延时时间10us、1ms、1s后逻辑分析仪测量值。

X@5HN2ZMEBP{BBH3JG2[_IT.png

图 11.4.1 逻辑分析仪测试SysTick延时


收藏 评论0 发布时间:2022-4-27 23:04

举报

0个回答

所属标签

相似分享

官网相关资源

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