八、滴答定时器
1. 滴答定时器介绍
SysTick定时器可用作标准的下行计数器,是一个24位向下计数器,有自动重新装载能力,可屏蔽系统中断发生器。Cortex-M0+处理器内部包含了一个简单的定时器,所有基于M0+内核的控制器都带有SysTick定时器,这样就方便了程序在不同的器件之间的移植。SysTick定时器可用于操作系统,提供系统必要的时钟节拍,为操作系统的任务调度提供一个有节奏的“心跳”。
SysTick定时器设定初值并使能之后,每经过1个系统时钟周期,计数值就减1,减到0时,SysTick计数器自动重新装载初值并继续计数,同时内部的COUNTFLAG标志位被置位,触发中断(前提开启中断)。
2. 滴答定时器寄存器
在core_cm0plus.h中可查看相关寄存器其定义,
3. 滴答定时器函数
SysTick定时器的使用主要有:InitTick()函数
函数说明
- __WEAK关键字表明这是一个弱定义函数,意味着如果在其他地方有同名的强定义函数,那么会优先使用强定义版本。这通常用于提供默认的实现,允许用户在需要时替换或扩展功能。
- 参数HclkFreq是系统时钟频率,单位是Hz(赫兹),用于计算SysTick的重载值。
- SysTick_Config函数配置SysTick定时器,使其在每个uwTickFreq Hz频率下产生中断。uwTickFreq是你希望的SysTick中断频率,单位是Hz。
- HclkFreq / (1000U / uwTickFreq)计算每次中断间隔对应的SysTick重载值。HclkFreq是64MHz,且希望每毫秒产生一次中断(即uwTickFreq=1000Hz),则计算结果为64。
- 如果SysTick_Config返回值大于0,表示配置失败(例如,因为请求的中断频率超过SysTick的最大支持频率),则函数直接返回,不继续执行后续配置。
- 这部分代码尝试设置SysTick中断(SysTick_IRQn)的优先级为TICK_INT_PRIORITY。__NVIC_PRIO_BITS定义了可以设置的优先级位数,如果TICK_INT_PRIORITY的值有效(即小于最大优先级数),则使用NVIC_SetPriority函数设置优先级。
- 如果TICK_INT_PRIORITY超出允许范围,则函数同样提前返回,不更改中断优先级。
InitTick函数主要作用是初始化SysTick定时器,确保它能够在指定的频率(通过uwTickFreq定义)产生中断,并配置中断的优先级。
4. 滴答定时器配置
在board.c中找到board_init()这个函数中有滴答定时器的配置
说明
这个函数是为大家配置好的,直接用寄存器配置的。
SysTick->LOAD = (uint32_t)((64000000 / (1000U / uwTickFreq)) - 1UL); // 计算重载值
- 64000000 是假设的系统时钟频率(HCLK),即64MHz。
- uwTickFreq 是期望的SysTick中断频率,单位是Hz。
- (1000U / uwTickFreq) 计算每个tick频率对应的毫秒数。uwTickFreq为1000Hz(即每秒1000次中断),则此表达式结果为1,意在得到每1ms中断一次的配置。
- 整个表达式计算的是,为了在每uwTickFreq个周期产生一次中断,SysTick重载寄存器应设置的值。由于SysTick计数器在递减到0时产生中断并重载,所以实际设置的值需要比计算出的周期数少1。
SysTick->VAL = 0UL; // 清零计数器
- 这行代码将SysTick计数器的当前值(VAL)清零,确保计数器从0开始计数。
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_ENABLE_Msk; // 配置控制寄存器
2
- SysTick_CTRL_CLKSOURCE_Msk 位被设置,意味着SysTick定时器使用系统时钟作为时钟源。这是SysTick工作的一个基本选项,大多数情况下都是如此配置。
- SysTick_CTRL_ENABLE_Msk 位被设置,这一步使能了SysTick定时器,使其开始计数。
本段代码没有直接开启SysTick的中断功能(即没有设置SysTick_CTRL_TICKINT_Msk),这意味着SysTick定时器虽然会正常计数并重载,但不会产生中断。如果需要在每个tick时执行特定操作,还需要设置此位并提供相应的中断服务例程(ISR)。
5. 滴答定时器使用
我们写好了延时ms的函数,在board.c文件中,void delay_ms(uint32_t ms),有一个参数_ms,这个就是我们要延时的时间的ms数,延时1ms就是delay_ms(1),延时1s就是delay_ms(1000)。还有延时us的函数void delay_us(uint32_t us),也是一样的用法。
需要注意的是这个延时是阻塞延时,在延时的时间里一直在等待,比较浪费系统资源,慎用。
6. LED灯闪烁实验
前面我们学习了如何去使用滴答定时器,下面我们就用滴答定时器的延时去实现LED闪烁1s间隔。其实很简单就是先让LED引脚输出高电平,然后调用delay_ms(500),再让LED引脚输出低电平,再调用delay_ms(500),最后在while(1)函数里调用即可。
关于LED灯闪烁实验代码,在立创·地文星CW32F030C8T6开发板资料/第03章软件资料/代码例程/003滴答定时器灯闪烁。
烧写我们的代码之后,可以看到开发板LED将会0.5s亮0.5s灭。