精通STM32 HAL库:打造Systick定时器,实现多线程操作系统
寄存器地址功能描述CTRL0xE000E010控制寄存器:使能计数器、中断、选择时钟源等。LOAD0xE000E014重装载值寄存器:设定计数器的初始值(最大值0xFFFFFF)。VAL0xE000E018当前值寄存器:读取当前计数值,写入任意值会清零计数器。CALIB0xE000E01C校准值寄存器:包含出厂预定义的校准值(STM32中通常无需手动配置)。关键位说明(CTRL寄存器):Bit 0
SysTick:STM32F103C8T6的精准延时与多任务调度核心
SysTick介绍
什么是SysTick?
SysTick(System Tick Timer)是ARM Cortex-M内核内置的一个24位系统定时器,用于提供精确的时间基准。在STM32中,SysTick常用于实现延时函数、RTOS(实时操作系统)的时基,甚至简单的多任务调度。它的核心优势在于其与内核紧密集成,无需依赖外部外设即可实现高精度定时。
SysTick工作原理
SysTick是一个递减计数器,工作流程如下:
初始化:设定重装载值(LOAD寄存器),计数器从该值开始递减。
计数:每个时钟周期计数器减1。
中断触发:当计数器减至0时,触发SysTick中断(若使能),并自动重载初始值,循环往复。
时钟源:默认使用内核时钟(HCLK,STM32F103C8T6通常为72MHz),也可配置为HCLK的1/8(需修改CTRL寄存器)。
SysTick寄存器介绍
SysTick通过4个寄存器控制:
| 寄存器 | 地址 | 功能描述 |
|---|---|---|
| CTRL | 0xE000E010 | 控制寄存器:使能计数器、中断、选择时钟源等。 |
| LOAD | 0xE000E014 | 重装载值寄存器:设定计数器的初始值(最大值0xFFFFFF)。 |
| VAL | 0xE000E018 | 当前值寄存器:读取当前计数值,写入任意值会清零计数器。 |
| CALIB | 0xE000E01C | 校准值寄存器:包含出厂预定义的校准值(STM32中通常无需手动配置)。 |
关键位说明(CTRL寄存器):
Bit 0:使能计数器(1=启动,0=关闭)
Bit 1:中断使能(1=计数到0时触发中断)
Bit 2:时钟源选择(1=HCLK,0=HCLK/8)
延时函数代码实现
HAL库提供了HAL_Delay(),但若需更高精度或非阻塞延时,可自定义实现:
- 初始化SysTick(HAL库已默认配置)
// HAL库启动时已初始化SysTick,用户无需重复配置
// 时钟源为HCLK(72MHz),中断频率1kHz(LOAD=72000-1)
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
- 微秒级延时(操作系统中可用)
/**
* @brief 微秒级延时
* @param nus 延时时长,范围:0~233015
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t tcnt = 0, told, tnow;
uint32_t reload = SysTick->LOAD; //重装载值
ticks = nus * 72; //需要计的节拍数
told = SysTick->VAL; //刚进入while循环时计数器的值
while(1)
{
tnow = SysTick->VAL;
if(tnow != told)
{
if(tnow < told)
tcnt += told - tnow;
else
tcnt += reload - (tnow -told);
told = tnow; //下次进入while循环时,当前VAL的值作为told
if(tcnt >= ticks) //已计的数超过/等于需要计的数时,退出循环
break;
}
}
}
- 毫秒级延时
/**
* @brief 毫秒级延时
* @param nms 延时时长,范围:0~4294967295
* @retval 无
*/
void delay_ms(uint32_t nms)
{
while(nms--)
delay_us(1000);
}
/**
* @brief 秒级延时
* @param ns 延时时长,范围:0~4294967295
* @retval 无
*/
void delay_s(uint32_t ns)
{
while(ns--)
delay_ms(1000);
}
/**
* @brief 重写HAL_Delay函数
* @param nms 延时时长,范围:0~4294967295
* @retval 无
*/
void HAL_Delay(uint32_t nms)
{
delay_ms(nms);
}
Systick模拟多线程
通过SysTick中断实现时间片轮转调度,模拟多线程任务切换:
由于Systick的中断在初始化时,HAL_Init()函数中,已经打开,我们只需要在中断处理函数中,添加一个我们的函数。
void SysTick_Handler(void)
{
HAL_IncTick();
systick_isr();
}
该中断函数可在.s文件中可找到。
添加的函数
由此,Systick会每一毫秒进入该函数一次。在该函数中,在该函数中,我们只做简单的计数任务,然后达到计数值则将flag值置1。在主函数中一直调用任务函数。
void systick_isr(void)
{
if (task1_cnt < 1000)
task1_cnt++;
else
{
task1_flag = 1;
task1_cnt = 0;
}
if (task2_cnt < 500)
task2_cnt++;
else
{
task2_flag = 1;
task2_cnt = 0;
}
}
任务函数
void task1(void)
{
if(task1_flag == 0)
return;
task1_flag = 0;
led1_toggle();
}
void task2(void)
{
if(task2_flag == 0)
return;
task2_flag = 0;
led2_toggle();
}
总结
SysTick作为Cortex-M内核的核心定时器,为STM32提供了精准的时间管理能力。通过灵活配置寄存器,开发者可实现从微秒级延时至多任务调度的复杂功能。在实际项目中,结合HAL库的封装和自定义逻辑,SysTick能够显著提升系统的实时性和效率。
更多推荐



所有评论(0)