当前位置: 首页 > article >正文

基于 SysTick 定时器实现任务轮询调度器

文章目录

  • 前言
  • 一、SysTick 定时器介绍
  • 二、SysTick 驱动设计
    • 1. 初始化方法
    • 2. SysTick 中断函数
    • 3. 时间类 API
  • 三、任务调度器设计
    • 1. 任务结构体
    • 2. 任务初始化
    • 3. 主调度器
    • 4. 调度器更新
  • 四、任务函数实现
  • 五、总结
    • 1. 优缺点分析
    • 2. 扩展建议


前言

在嵌入式系统中,对于资源受限、实时性要求较强的小型项目,使用一个轻量级的 轮询调度器(Polling Scheduler) 往往是比使用完整 RTOS 更合适的选择。本文将介绍如何基于 SysTick 定时器,构建一个简单、可配置、易于维护的任务轮询调度框架。

轮询调度器是一种按照固定频率循环调用多个任务的调度方式。与传统的 RTOS 使用任务优先级和时间片管理不同,轮询调度器结构简单,没有上下文切换开销,适用于嵌入式裸机系统。

SysTick 是 Cortex-M 内核自带的一个 24-bit 的定时器,非常适合实现毫秒级的系统节拍(系统心跳),可以定期触发中断来增加系统“时钟节拍数”。

一、SysTick 定时器介绍

SysTick 是 Cortex-M3/M4/M7 系列核内部自带的一个定时器,有如下特点:

  • 可以设置一个重装值,到值时触发一次中断

  • 可以选择不同的时钟源(如 HCLK/无分频或 HCLK/8)

  • 通常用于 RTOS 的时钟引擎,也可用于实时延时

在本组件中,我们把 SysTick 设置为 10kHz 高精度定时器,即 0.1ms 一次中断

二、SysTick 驱动设计

1. 初始化方法

void drv_systick_init(void)
{crm_clocks_freq_type clocks_struct;systick_clock_source_config(SYSTICK_CLOCK_SOURCE_AHBCLK_NODIV); // 使用 AHB 时钟crm_clocks_freq_get(&clocks_struct);uint32_t tick_cnt = clocks_struct.ahb_freq / SYSTICK_FREQUENCE;systick_interrupt_config(tick_cnt);
}

根据当前 AHB 主时钟,计算定时轮被动进制值,定时频率为 SYSTICK_FREQUENCE = 10000 即 10kHz。

2. SysTick 中断函数

void SysTick_Handler(void)
{++systick_count;
}

每 0.1ms 一次,代表一个精度较高的 “系统时钟基准” 增加。

3. 时间类 API

uint32_t get_systick_count(void)
{return systick_count;
}uint32_t get_systick_ms(void)
{return (uint32_t)((float)systick_count / ((float)SYSTICK_FREQUENCE * 0.001f));
}uint32_t get_systick_s(void)
{return (uint32_t)((float)systick_count / (float)SYSTICK_FREQUENCE);
}void delay_ms(uint32_t ms)
{uint32_t start_cnt = get_systick_count();while ( (get_systick_count() - start_cnt) < ms * SYSTICK_FREQUENCE * 0.001f) {}
}void delay_s(uint32_t s)
{uint32_t start_cnt = get_systick_count();while ( (get_systick_count() - start_cnt) < s * SYSTICK_FREQUENCE) {}
}
  • get_systick_count() :返回自己滴答数

  • get_systick_ms() :系统运行时间,单位ms

  • delay_ms() :延时操作,单位ms

三、任务调度器设计

核心思想: 每个任务记录上次执行时间 last_tick,按照预设频率 frequence 判断是否超时,如果是,就执行任务函数。

1. 任务结构体

struct loop_task_info {char *name;void (*function)(void);    // 任务函数指针uint32_t frequence;        // 任务执行频率(Hz)uint32_t tick;             // 已执行次数uint32_t last_tick;        // 上次执行时的系统节拍数
};

所有任务被组织在一个静态数组 task_info[] 中。

2. 任务初始化

INIT_LOOP_TAST(task_info[LOOP_TASK_A], "Task A", task_a,  10000, 0, 0); // 10000Hz
INIT_LOOP_TAST(task_info[LOOP_TASK_B], "Task B", task_b,  500, 0, 0);    // 500Hz

3. 主调度器

主循环不断轮询所有任务:

void loop_task_ontick(void)
{while (1) {for (i = 0; i < LOOP_TASK_MAX; ++i)loop_task_updata(&task_info[i]);}
}

4. 调度器更新

static void loop_task_updata(struct loop_task_info *loop_task)
{uint32_t tick_interval = get_systick_count() - loop_task->last_tick;if (tick_interval >= (get_systick_frequence() / loop_task->frequence)) {loop_task->last_tick = get_systick_count();++loop_task->tick;if(loop_task->function != NULL)loop_task->function();}
}

四、任务函数实现

任务函数内部可以再细分子任务,如在 Task A 中实现 10KHz、1KHz、1Hz 的子任务:

void task_a(void)
{if (get_loop_task_tick(LOOP_TASK_A) % (get_loop_task_frequence(LOOP_TASK_A) / 10000) == 0) {// 10000Hz 执行}if (get_loop_task_tick(LOOP_TASK_A) % (get_loop_task_frequence(LOOP_TASK_A) / 1000) == 0) {// 1000Hz 执行}if (get_loop_task_tick(LOOP_TASK_A) % (get_loop_task_frequence(LOOP_TASK_A) / 1) == 0) {// 1Hz 执行}
}

通过tick计数除以频率的方式,在一个任务函数中实现多频率分支处理,非常适合多粒度控制。

五、总结

1. 优缺点分析

优点:

  • 结构简单:无需操作系统,适合裸机项目。

  • 执行频率可控:每个任务可单独配置运行频率。

  • 任务切换无上下文开销:极大节省系统资源。

缺点:

  • 不适合实时性要求极高的任务:任务间是串行执行,容易被阻塞。

  • 不支持任务优先级:所有任务平等轮询。

  • 没有“抢占机制”:任务函数内部不能阻塞太久,否则影响整体调度精度。

2. 扩展建议

  • 支持任务优先级(按优先级或 deadline 排序执行)

  • 增加任务 Watchdog 功能,防止任务异常长时间卡死

  • 动态添加、删除任务支持

  • 基于时间轮的调度器优化版

相关文章:

基于 SysTick 定时器实现任务轮询调度器

文章目录 前言一、SysTick 定时器介绍二、SysTick 驱动设计1. 初始化方法2. SysTick 中断函数3. 时间类 API 三、任务调度器设计1. 任务结构体2. 任务初始化3. 主调度器4. 调度器更新 四、任务函数实现五、总结1. 优缺点分析2. 扩展建议 前言 在嵌入式系统中&#xff0c;对于资…...

【STM32】综合练习——智能风扇系统

目录 0 前言 1 硬件准备 2 功能介绍 3 前置配置 3.1 时钟配置 3.2 文件配置 4 功能实现 4.1 按键功能 4.2 屏幕功能 4.3 调速功能 4.4 倒计时功能 4.5 摇头功能 4.6 测距待机功能 0 前言 由于时间关系&#xff0c;暂停详细更新&#xff0c;本文章中&#xff0c;…...

MyBatis 动态 SQL 使用详解

&#x1f31f; 一、什么是动态 SQL&#xff1f; 动态 SQL 是指根据传入参数&#xff0c;动态拼接生成 SQL 语句&#xff0c;不需要写多个 SQL 方法。MyBatis 提供了 <if>、<choose>、<foreach>、<where> 等标签来实现这类操作 ✅ 二、动态 SQL 的优点…...

【重装系统】大白菜自制U盘装机,备份C盘数据,解决电脑启动黑屏/蓝屏

1. 准备 U 盘 U 盘容量至少 8G&#xff0c;备份 U 盘的数据&#xff08;后期会格式化&#xff09; 2. 从微软官网下载操作系统镜像 https://www.microsoft.com/zh-cn/software-download/windows11 3. 下载安装大白菜 https://www.alipan.com/s/33RVnKayUfY 4. 插入 U 盘&#…...

vue实现目录锚点且滚动到指定区域时锚点自动回显——双向锚点

最近在用vue写官网&#xff0c;别问我为什么用vue写官网&#xff0c;问就是不会jq。。。。vue都出现11年了。。。 左侧目录&#xff1a;点击时&#xff0c;右侧区域可以自动滚动到指定的位置。 右侧区域手动滚动时&#xff0c;左侧锚点可以自动切换到对应的目录上 从而实现…...

python——正则表达式

一、简介 在 Python 中&#xff0c;正则表达式主要通过 re 模块实现&#xff0c;用于字符串的匹配、查找、替换等操作。 二、Python的re模块 使用前需要导入&#xff1a; import re 三、常用方法 方法描述re.match(pattern, string)从字符串开头匹配&#xff0c;返回第一个匹…...

Flutter Invalid constant value.

0x00 问题 参数传入变量&#xff0c;报错&#xff01; 代码 const Padding(padding: EdgeInsets.all(20),child: GradientProgressIndicator(value: _progress), ),_progress 参数报错&#xff1a;Invalid constant value. 0x01 原因 这种情况&#xff0c;多发生于&#xff…...

libev实现Io复用及定时器事件服务器

客户端和服务器都绑定在了enp2s0网卡&#xff0c;需要SERVER_IP和SERVER_PORT改为其ip&#xff0c;注意不能是127.0.0.1&#xff0c;因为这个是lo虚拟网口。 安装libev sudo apt-get install libev-dev客户端&#xff1a; #include <iostream> #include <string>…...

【精品PPT】2025固态电池知识体系及最佳实践PPT合集(36份).zip

精品推荐&#xff0c;2025固态电池知识体系及最佳实践PPT合集&#xff0c;共36份。供大家学习参考。 1、中科院化学所郭玉国研究员&#xff1a;固态金属锂电池及其关键材料.pdf 2、中科院物理所-李泓固态电池.pdf 3、全固态电池技术研究进展.pdf 4、全固态电池生产工艺.pdf 5、…...

如何计算设备电池工作时长?

目录 【mAh&#xff08;毫安时&#xff09;计算方法】 【Wh&#xff08;瓦时&#xff09;计算方法】 【为什么仅用电流&#xff08;mA&#xff09;和时间&#xff08;h&#xff09;就能计算电池使用时长&#xff08;mAh&#xff09;&#xff1f;】 1. mAh 的本质是“电荷量…...

抽象类及其特性

目录 1、概念2、语法3、特性4、作用 1、概念 在面向对象中&#xff0c;所有对象都是通过类来描述的&#xff0c;但是并不是所有的类都可以用来描述对象。比如下述例子中的 Animal 类&#xff0c;Dog 类和 Cat 类是 Animal 类的子类&#xff0c;可以分别描述小狗和小猫&#xf…...

【教程】xrdp修改远程桌面环境为xfce4

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 xfce4 vs GNOME对比 配置教程 1. 安装 xfce4 桌面环境 2. 安装 xrdp 3. 配置 xrdp 使用 xfce4 4. 重启 xrdp 服务 5. 配置防火墙&#xff…...

利用python从零实现Byte Pair Encoding(BPE):NLP 中的“变形金刚”

BPE&#xff1a;NLP 界的“变形金刚”&#xff0c;从零开始的奇幻之旅 在自然语言处理&#xff08;NLP&#xff09;的世界里&#xff0c;有一个古老而神秘的传说&#xff0c;讲述着一种强大的魔法——Byte Pair Encoding&#xff08;BPE&#xff09;。它能够将普通的文本“变形…...

部署redis cluster

一。在所有的主机里面设置密码和文件地址 vi /etc/redis/6379.conf 注释&#xff1a;登陆则要使用auth 123456才可以进入redis 配置文件地址和超时时间 二。创建集群&#xff1a;上面主机为master&#xff0c;下面为slave&#xff0c;master和slave会随机分配 先写主节点&…...

Android 11 (API 30) 及更高版本中,查询的特定应用商店包,无需动态请求权限处理

在 Android 11 (API 30) 及更高版本中&#xff0c;通过在 AndroidManifest.xml 中添加 <queries> 元素声明需要查询的特定应用商店包名后&#xff1a; 1. 不需要额外请求权限 &#xff08;如 QUERY_ALL_PACKAGES &#xff09;即可查询这些应用的安装状态 2. 这是 Googl…...

基于springboot钻孔数据管理系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!

摘要 本钻孔数据管理系统采用B/S架构&#xff0c;数据库是MySQL&#xff0c;网站的搭建与开发采用了先进的Java语言、Hadoop、数据可视化技术进行编写&#xff0c;使用了Spring Boot框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。用户主要功能包括&…...

SpringBoot和微服务学习记录Day2

微服务 微服务将单体应用分割成更小的的独立服务&#xff0c;部署在不同的服务器上。服务间的关联通过暴露的api接口来实现 优点&#xff1a;高内聚低耦合&#xff0c;一个模块有问题不影响整个应用&#xff0c;增加可靠性&#xff0c;更新技术方便 缺点&#xff1a;增加运维…...

4.9复习记

1.地宫取宝--记忆化搜索&#xff0c;可以先写void dfs&#xff0c;然后在改成ll 形式的&#xff0c;边界条件return 0/1&#xff1b; 记忆化数组与dfs元素保持一致&#xff0c;记得记忆化剪枝 这个题特殊在value可能是0&#xff0c;不取的时候应该记为-1 https://mpbeta.cs…...

LinuxSocket套接字编程

1.介绍函数使用 1.创建套接字 int socket(int domain, int type, int protocol); domain&#xff1a;指定协议族&#xff0c;如AF_INET&#xff08;IPv4&#xff09;或AF_INET6&#xff08;IPv6&#xff09;。 type&#xff1a;指定套接字类型&#xff0c;如SOCK_DGRAM&#…...

动态科技感html导航网站源码

源码介绍 动态科技感html导航网站源码&#xff0c;这个设计完美呈现了科幻电影中的未来科技界面效果&#xff0c;适合展示技术类项目或作为个人作品集的入口页面&#xff0c;自适应手机。 修改卡片中的链接指向你实际的HTML文件可以根据需要调整卡片内容、图标和颜色要添加更…...

Java进阶版线程池(超详细 )

线程池 线程池工具类 Executors Executors 是 Java 提供的一个工具类&#xff0c;它包含了多个静态方法&#xff0c;能够方便地创建不同类型的线程池。 newFixedThreadPool 创建一个固定大小的线程池&#xff0c;线程池中的线程数量固定&#xff0c;当有新任务提…...

每日算法:洛谷U535992 J-C 小梦的宝石收集(双指针、二分)

题目描述 小梦有 n 颗能量宝石&#xff0c;其中第 i 颗的能量为 ai​&#xff0c;但这些能量宝石十分不稳定&#xff0c;随时有可能发生崩坏&#xff0c;导致他们全部消失&#xff01; 小梦想要留住宝石们&#xff0c;不希望他们发生崩坏&#xff0c;同时他发现&#xff1a;如…...

YOLOv11训练中精准率召回率与mAP@0.5的动态变化分析

目标检测模型的训练过程涉及多个关键性能指标和损失函数的变化&#xff0c;这些数据能够直观反映模型的收敛速度、最终精度以及改进效果。本文旨在通过绘制YOLOv11模型在训练过程中的精准率&#xff08;Precision&#xff09;、召回率&#xff08;Recall&#xff09;、mAP0.5 、…...

Java常用工具算法-6--秘钥托管云服务AWS KMS

前言&#xff1a; 之前我们介绍了一些常用的加密算法&#xff08;如&#xff1a;对称加密AES&#xff0c;非对称加密RSA&#xff0c;ECC等&#xff09;&#xff0c;不论是哪一种都需要涉及到秘钥的管理。通常的做法都是把秘钥放到配置文件中进行配置&#xff0c;但是对于一些高…...

11. Langchain输出解析(Output Parsers):从自由文本到结构化数据

引言&#xff1a;从"自由发挥"到"规整输出" 2025年某金融机构的合同分析系统升级前&#xff0c;AI生成的合同摘要需人工二次处理达47分钟/份。引入LangChain结构化解析后&#xff0c;处理时间缩短至3分钟。本文将详解如何用LangChain的解析器&#xff0c;…...

docker stack常用命令

1、Docker Stack介绍 Docker Stack管理swarm堆栈与Swarm协调器配合使用&#xff0c;是Docker Swarm环境中用于管理一组相关服务的工具。它使得在Swarm集群中部署、管理和扩展一组相互关联的服务变得简单。主要用于定义和编排容器化应用的多个服务。以下是Docker Stack的一些关…...

python reportlab模块----操作PDF文件

reportlab模块----操作PDF文件 一. 安装模块二. reportlab相关介绍三. 扩展canvas类四. 水平写入完整代码五. 垂直写入完整代码 一. 安装模块 pip install reportlab二. reportlab相关介绍 # 1. letter 生成A4纸张尺寸 from reportlab.lib.pagesizes import letter print(let…...

解锁基因密码之重测序(从测序到分析)

在生命科学的奇妙世界中&#xff0c;基因恰似一本记录着生命奥秘的“天书”&#xff0c;它承载着生物体生长、发育、衰老乃至疾病等一切生命现象的关键信息。而重测序技术&#xff0c;则是开启基因“天书”奥秘的一把神奇钥匙。 试想&#xff0c;你手中有一本经典书籍的通用版…...

TQTT_KU5P开发板教程---QSFP25G光口回环测试

文档实现功能介绍 本文档通过一个叫做ibert的IP&#xff0c;实现25G光口回环测试例子。工程新建方法请参考文档《流水灯》&#xff0c;其中只是将文件名进行修改。 Vivado 起始页&#xff08;或 file-->Project-->New 创建新工程(Create New Project) 向导起始页面 点…...

JVM虚拟机篇(七):JVM垃圾回收器全面解析与G1深度探秘及四种引用详解

JVM垃圾回收器全面解析与G1深度探秘及四种引用详解 JVM虚拟机&#xff08;七&#xff09;&#xff1a;JVM垃圾回收器全面解析与G1深度探秘及四种引用详解一、JVM有哪些垃圾回收器1. Serial回收器2. ParNew回收器3. Parallel Scavenge回收器4. Serial Old回收器5. Parallel Old回…...