news 2026/5/7 4:11:09

【STM32】SysTick操作系统延时函数的讲解,手撸带操作系统的延时函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【STM32】SysTick操作系统延时函数的讲解,手撸带操作系统的延时函数

1. 流水灯模拟多线程
用一个流水灯小实验学习systick,模拟多线程

1.1 main.c
先看main.c文件,main函数中实现两个灯进行不同的任务
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "tasks.h"

int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init(); /* 初始化LED灯 */

while(1)
{
task1();
task2();
// led1_on();
// led2_off();
// delay_ms(500);
// led1_off();
// led2_on();
// delay_ms(500);
}
}

AI写代码
cpp
运行

HAL_Init()中:使用systick作为时基源,并默认配置1ms滴答(重置后的默认时钟为HSI)

即1ms中断一次

然后就只需要在中断服务函数中实现功能就好了

但是中断服务函数中最好不要写很长,最好把线程写在一个文件中,于是有了task.c

1.2 重点task.c
重点是task.c,要在这里实现多线程,main主函数里的task1()和task2()正是在这里实现

systick模拟多线程实验时,为什么两个小灯不同频率闪烁的代码写在中断服务函数里,就能实现功能呢?

答:SysTick 通常配置为每 1ms 产生一次中断

每次中断时,这个函数自动被调用,执行完中断程序再回到断点

函数内部维护两个任务的计时器,实现:

任务1:每秒执行一次(1000ms)
任务2:每0.5秒执行一次(500ms)
在main函数的HAL_Init()函数中默认设置的是每过1ms调用一下中断函数systick_isr(),相当于每过1ms都会检测task1和task2的cnt计数情况。就是每过1ms调用一次中断函数systick_isr(),这1ms后就实现systick_isr()中1000ms的led1和500ms的led2闪烁

#include "tasks.h"
#include "led.h"

uint32_t task1_cnt = 0;
uint32_t task2_cnt = 0;

uint8_t task1_flag = 0;
uint8_t task2_flag = 0;

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();
}
AI写代码
cpp
运行

在main函数中一直调用task1和task2,如果task1_flag == 0,即task1还没记完数,就return继续运行;否则,跳过return执行task1_flag = 0(没记完数时都为0,现在否则了,说明task1_flag = 1,所以需要重新置零,不能让它后面一直都是1进不去循环了); led1_toggle()(翻转灯);

别忘了中断函数再调用void systick_isr(void)
别忘了在.h文件中补充函数声明
1.3 老演员led.c
#include "led.h"
#include "sys.h"

//初始化GPIO函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//打开时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟

//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; // 两个LED对应的引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
gpio_initstruct.Pull = GPIO_PULLUP; // 上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭LED
led1_off();
led2_off();
}

//点亮LED1的函数
void led1_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); // 拉低LED1引脚,点亮LED1
}

//熄灭LED1的函数
void led1_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); // 拉高LED1引脚,熄灭LED1
}

//翻转LED1状态的函数
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
}

//点亮LED2的函数
void led2_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); // 拉低LED2引脚,点亮LED2
}

//熄灭LED2的函数
void led2_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); // 拉高LED2引脚,熄灭LED2
}

//翻转LED2状态的函数
void led2_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
}
AI写代码
cpp
运行

实现的是led1和led2不一样频率的闪烁,led1以1000ms闪烁,led2以500ms闪烁。

2. 什么是SysTick?
Systick,即滴答定时器,是内核中的一个特殊定时器,用于提供系统级的定时服务。该定时器是一个24位的递减计数器,具有自动重载值寄存器的功能。当计数器到达自动重载值时,它会自动重新加载并开始新的计数周期。
在使用Systick定时器进行延时操作时,可以设定初值并使能后,每经过一个系统时钟周期,计数值就减1。当计数到0时,Systick计数器自动重装初值并继续计数,同时内部的COUNTFLAG标志会置位,触发中断 (如果中断使能)。这样,可以在中断处理函数中实现特定的延时逻辑。

3. 手撸带操作系统的延时函数


根据流程图看懂以下代码不难

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;
}
}
}
AI写代码
cpp
运行

写在delay.c

学过C语言的都知道->是用指针访问结构体成员,systick是结构体指针,VAL是它访问的结构体成员,其实它是一个寄存器

4. systick寄存器
SysTick控制及状态寄存器(CTRL)

SysTick重装载数值寄存器(LOAD)

SysTick当前数值寄存器(VAL)

————————————————
版权声明:本文为CSDN博主「逆小舟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/2301_76153977/article/details/154233968

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 12:58:11

学霸同款!8款AI论文工具测评:开题报告与文献综述必备

学霸同款&#xff01;8款AI论文工具测评&#xff1a;开题报告与文献综述必备 2026年AI论文工具测评&#xff1a;精准匹配学术需求的实用指南 随着人工智能技术在学术领域的广泛应用&#xff0c;越来越多的研究生开始依赖AI工具提升论文写作效率。然而&#xff0c;面对市场上琳琅…

作者头像 李华
网站建设 2026/4/30 17:36:57

从600万到3000万:揭秘中小商家的增长密码

引言 中小商家最头疼的问题&#xff1a;产品不错&#xff0c;但没钱做推广。 今天看更关键的部分——如何从600万做到3000万。这背后不是靠运气&#xff0c;而是一套可复制的商业架构。 一、传统生意困局&#xff1a;酒香也怕巷子深 大多数商家面临三重困境&#xff1a; 广告…

作者头像 李华
网站建设 2026/4/28 4:34:33

Java之构造方法

什么是构造方法&#xff1f;构造方法是 Java 中一种特殊的方法&#xff0c;它的核心作用是&#xff1a;在创建对象&#xff08;使用new关键字&#xff09;时&#xff0c;初始化该对象的成员变量构造方法解决什么问题&#xff1f;构造方法解决给对象初始化的问题构造方法怎么使用…

作者头像 李华
网站建设 2026/5/3 23:14:22

想 2026 转行网络安全?前景、工作内容及薪资水平一次说透

如果你计划在2026年转行到网络安全领域&#xff0c;以下是一些建议&#xff0c;可以帮助你顺利过渡并打下坚实的基础 1、薪资情况 初级职位&#xff08;0-3年经验&#xff09; 薪资范围&#xff1a;大约 8k-15k/月&#xff08;根据地区、公司规模和工作内容有所不同&#xff…

作者头像 李华
网站建设 2026/4/28 15:32:30

【干货】2026年AI大模型趋势预测:AI智能体将如何改变编程开发?附ChatGPT+DeepSeek学习资料,小白程序员必看!

AI大模型领地 报告&#xff1a;**2026年中国AI智能体营销趋势与发展报告蓝皮书&#xff08;191页&#xff09; AI 智能体作为具备自主感知、决策与执行能力的 “数字业务伙伴”&#xff0c;正重塑商业格局与营销范式。2026 年&#xff0c;AI 智能体已从被动工具升级为主动参与者…

作者头像 李华