SysTick:系统定时器,和普通定时器类似,它也可以定时,但是却是有特殊用途。它有24bits,定时器最大数值是2^24,并且计数时是向下递减计数,每次递减1,递减一次的时间是1/SYSCLK,一般来说SYSCLK为72MHz。当寄存器中的值减少到0时,会产生一个中断信号,进而执行相应的中断服务函数
基于CM3内核的单片机,都会有SysTick定时器。这使得使用SysTick的软件都很容易移植。SysTick除了可以做正常的定时器使用以外,还有一个最重要的特殊作用:给操作系统提供时基,为操作系统提供必要的时钟节拍,从而为操作系统的任务调度提供一个稳定的心跳
与SysTick相关的寄存器有4个:
但是一般使用情况下,只使用到前三个寄存器。最后一个校准数值寄存器不经常使用
使用SysTick产生1s的定时,并设计程序让LED每1s闪烁一次
硬件上只需要LED以及自带的SysTick定时器
软件需要依赖于标准库函数中的core_m3.c和core_m3.h,这两个文件中有用于设置SysTick的函数。
同时需要建立两个文件bsp_systick.c和bsp_systick.h,用于编写函数
使用SysTick定时器的简要流程:
库文件 core_cm3.h 中的SysTick配置库函数代码主体部分
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
// 判断ticks有没有超过定时器的2^24最大值
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL);
}
// 设置重装载寄存器
SysTick->LOAD = (uint32_t)(ticks - 1UL);
// 设置中断优先级
NVIC_Setriority(SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
// 设置当前数值寄存器
SysTick->VAL = 0UL;
// 设置SysTick定时器的时钟源为AHBCLK 72MHz
// 使能SysTick定时器中断
// 使能SysTick定时器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
return (0UL);
}
其中有一个用于设置优先级的函数NVIC_Setriority(),这个函数也是在core_m3.h中定义的
NVIC_Setriority()代码主体部分
__STATIC_INLINE void NVIC_SetPririty(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)IRQn < 0)
{
SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] =
(uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uiny32_t)0xFFUL);
}
else
{
NVIC->IP[((uint32_t)(int32_t)IRQn)] =
(uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
函数解析
判断IRQn的大小,如果小于0,则此中断为系统中断,大于0,则此中断为外设中断。系统中断的优先级是由内核外设SCB的寄存器SHPRx(x=1~3)控制,而外设中断是由内核外设NVIC中的寄存器IPx控制
系统中断的优先级和外设中断的优先级比较
对于SysTick定时器,由于它是系统级的外设,那么在优先级设置方面会有特殊优待吗?
没有的
对于SysTick定时器产生的中断,它的优先级由内核外设SCB中的寄存器SHPRx控制,SHPRx是32位的寄存器,并且只能按字节进行访问,同时在STM32F103中只使用到寄存器单个字节的前4位,这就意味着SysTick定时器的中断优先级仅能设置为0~15,转换为二进制也就0000~1111
无论是系统中断还是外设中断,统一都是由NVIC进行管理,所以说,在进行优先级比较时,可以将寄存器SHPRx和寄存器IPRx中前四位进行比较,数值越小的中断优先级越高,也遵循之前讨论的优先级比较逻辑
在了解完 SysTick_Config()函数之后,可以通过调用它来配置定时器达到想要的效果
但在此之前要了解SysTick时间的相关计算:
AHBCLK时钟频率为72MHz
SysTick定时器减1所用的时间Tsys = 1 / SYSCLK = 1 / 72 000 000 (s)
所以当重装载数值为720时,产生一次中断信号的时间就为720 * Tsys = 720 / 72 000 000 = 1 / 100 000 (s) = 10us
所以首先,要先给SysTick定时器,赋上重装载数值SystemCoreClock/100000,SystemCoreClock的值通过宏定义定义为72M,即72 000 000
代码实现如下
// SysTick定时器初始化
void SysTick_Init(void)
{
if (SysTick_Config(SystemCoreClock/100000))
{
// ERROR
while (1);
}
}
编写定时函数,输入参数为定时的时间,只有指定时间过后,代码才会继续执行
代码实现如下
// 定时函数
void Delay_nTime_10us(__IO u32 nTime)
{
if (nTime < 0)
{
return;
}
DelayTime = nTime; // DelayTime是一个全局变量,并且和SysTick中断服务函数相关联
while (DelayTime > 0);
}
编写SysTick中断服务函数,用于联系SysTick中断信号和定时函数
代码实现如下
// 中断服务函数
void SysTick_Handler(void)
{
DelayTime_Decrement(); // 用于计算中断产生次数的,每产生一次中断,DelayTime递减1
}
// DelayTime_Decrement()函数
void DelayTime_Decrement(void)
{
while (DelayTime > 0)
{
DeylayTime--;
}
}
最后通过调用Delay_nTime_10us()函数可以实现定时指定时间后执行代码,例如Delay_nTime_10us(100000)),那么100000* 10us (1s)后,代码会继续执行
main.c中的代码实现如下
int main (void)
{
// 配置LED引脚
LED_GPIO_Config();
// SysTick定时器初始化
SysTick_Init();
// 业务代码
while (1)
{
// 红绿蓝三色灯,间隔1s换颜色
LED_RED;
Delay_nTime_10us(100000));
LED_GREEN;
Delay_nTime_10us(100000));
LED_BLUE;
Delay_nTime_10us(100000));
}
}
链接:资源下载