【嵌入式学习笔记】任务调度器

前言

本系列学习笔记是本人跟随米醋电子工作室学习嵌入式的学习笔记,自用为主,不是教学或经验分享,若有误,大佬轻喷,同时欢迎交流学习,侵权即删。

一、概述

什么是任务调度器?

嵌入式系统中的"课程时间表",按预定时间间隔安排不同任务执行

二、基本概念

任务调度器的基本功能

定时执行任务:按精确时间间隔运行特定函数

周期性操作:实现循环执行功能

非阻塞式设计:避免延时函数造成系统阻塞

模块化结构:将不同功能分割为不同模块,提高代码移植性与可维护性

三、C语言结构体

本部分详见

【嵌入式学习笔记】基础拓展:C语言的结构体-CSDN博客

四、调度器实现

调度器的实现主要包含三个部分:任务数组、初始化函数和运行函数。

任务数组

cpp 复制代码
/ 全局变量,用于存储任务数量
uint8_t task_num;

// 静态任务数组,每个任务包含任务函数、执行周期(毫秒)和上次运行时间(毫秒)
static scheduler_task_t scheduler_task[] =
{
    {Led_Proc, 1, 0},    // LED控制任务:周期1ms
    {Key_Proc, 10, 0},   // 按键扫描任务:周期10ms
    {Sensor_Proc, 100, 0}, // 传感器读取任务:周期100ms
    {Comm_Proc, 50, 0}   // 通信处理任务:周期50ms
};

初始化函数

cpp 复制代码
**
 * @brief 调度器初始化函数
 * 计算任务数组的元素个数,并将结果存储在 task_num 中
 */
void scheduler_init(void)
{
    // 计算任务数组的元素个数,并将结果存储在 task_num 中
    task_num = sizeof(scheduler_task) / sizeof(scheduler_task_t);
}

运行函数

cpp 复制代码
/**
 * @brief 调度器运行函数
 * 遍历任务数组,检查是否有任务需要执行。如果当前时间已经超过任务的执行周期,则执行该任务并更新上次运行时间
 */
void scheduler_run(void)
{
    // 遍历任务数组中的所有任务
    for (uint8_t i = 0; i < task_num; i++)
    {
        // 获取当前的系统时间(毫秒)
        uint32_t now_time = HAL_GetTick();

        // 检查当前时间是否达到任务的执行时间
        if (now_time >= scheduler_task[i].rate_ms + scheduler_task[i].last_run)
        {
            // 更新任务的上次运行时间为当前时间
            scheduler_task[i].last_run = now_time;

            // 执行任务函数
            scheduler_task[i].task_func();
        }
    }
}

实际运行流程图

复制代码
主循环(无限循环)
    ↓
调用 scheduler_run()  // 检查所有任务
    ↓
检查任务1:LED任务到时间了吗?
    ↓
检查任务2:按键任务到时间了吗?
    ↓
检查任务3:传感器任务到时间了吗?
    ↓
检查任务4:通信任务到时间了吗?
    ↓
回到主循环开始

五、完整示例(配详细注释)

5.1 头文件 scheduler.h

cpp 复制代码
#ifndef __SCHEDULER_H
#define __SCHEDULER_H

#include "stm32f1xx_hal.h"  // HAL库头文件

// 任务结构体定义
typedef struct {
    void (*task_func)(void);  // 函数指针:指向任务函数
    uint32_t rate_ms;         // 执行周期(毫秒)
    uint32_t last_run;        // 上次执行时间(毫秒)
} scheduler_task_t;

// 函数声明
void scheduler_init(void);    // 调度器初始化
void scheduler_run(void);     // 调度器运行

#endif

5.2 源文件 scheduler.c

cpp 复制代码
#include "scheduler.h"

// 全局变量:任务数量
uint8_t task_num;

// 任务数组:所有要调度的任务都在这里
static scheduler_task_t scheduler_task[] =
{
    // 格式说明:
    // {任务函数名, 执行周期(ms), 上次执行时间(初始0)}
    
    // LED任务:1ms执行一次(快速闪烁)
    {Led_Proc, 1, 0},
    
    // 按键任务:10ms执行一次(防抖检测)
    {Key_Proc, 10, 0},
    
    // 传感器任务:100ms执行一次(不需要太快)
    {Sensor_Proc, 100, 0},
    
    // 通信任务:50ms执行一次(发送数据)
    {Comm_Proc, 50, 0}
};

// 调度器初始化
void scheduler_init(void)
{
    // 计算任务数量
    // sizeof(数组):整个数组占多少字节
    // sizeof(结构体):一个任务结构体占多少字节
    // 相除得到:数组中有几个元素
    task_num = sizeof(scheduler_task) / sizeof(scheduler_task_t);
    
    // 举例:假设每个任务结构体占12字节,4个任务就是48字节
    // 48 ÷ 12 = 4个任务
}

// 调度器运行函数(在main循环中不断调用)
void scheduler_run(void)
{
    uint32_t now_time;  // 当前时间
    uint8_t i;          // 循环计数器
    
    // 遍历所有任务(4个任务都要检查)
    for (i = 0; i < task_num; i++)
    {
        // 获取当前系统时间(单位:毫秒)
        now_time = HAL_GetTick();
        
        // 判断任务是否需要执行
        // 条件:当前时间 ≥ 上次执行时间 + 执行周期
        if (now_time >= scheduler_task[i].last_run + scheduler_task[i].rate_ms)
        {
            // 更新"上次执行时间"为现在
            // 这样下次就会在 now_time + rate_ms 时再执行
            scheduler_task[i].last_run = now_time;
            
            // 执行任务函数
            // 这会调用相应的函数,比如 Led_Proc()
            scheduler_task[i].task_func();
        }
    }
}

5.3 任务函数示例

cpp 复制代码
// LED任务函数:1ms执行一次
void Led_Proc(void)
{
    static uint32_t counter = 0;
    counter++;
    
    // 每500ms翻转一次LED(500次 × 1ms = 500ms)
    if (counter >= 500)
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        counter = 0;
    }
}

// 按键任务函数:10ms执行一次
void Key_Proc(void)
{
    static uint8_t key_state = 0;
    
    // 检测按键是否按下
    if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET)
    {
        key_state++;
        if (key_state >= 5)  // 连续检测到5次(50ms)才算有效
        {
            // 执行按键动作
            key_state = 0;
        }
    }
    else
    {
        key_state = 0;
    }
}

// 传感器任务函数:100ms执行一次
void Sensor_Proc(void)
{
    // 读取温度传感器
    float temperature = read_temperature();
    
    // 可以在这里进行数据处理
    // ...
}

// 通信任务函数:50ms执行一次
void Comm_Proc(void)
{
    // 发送数据到上位机
    send_data_to_pc();
}

5.4 主函数 main.c

cpp 复制代码
#include "main.h"
#include "scheduler.h"

int main(void)
{
    // HAL库初始化
    HAL_Init();
    
    // 系统时钟配置
    SystemClock_Config();
    
    // 外设初始化(GPIO、串口等)
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    
    // 调度器初始化
    scheduler_init();
    
    // 无限循环(嵌入式程序特点)
    while (1)
    {
        // 运行调度器:检查并执行任务
        scheduler_run();
        
        // 注意:这里没有delay!
        // CPU会全速运行,不断检查任务
        
        // 如果CPU太快,可以加短暂延时避免过度占用
        // HAL_Delay(1);  // 可选:延时1ms
    }
}
相关推荐
耶叶2 小时前
查找算法学习总结2:代码分析篇
数据结构·学习·算法
笨蛋少年派2 小时前
Shell脚本编程(bash)简述
开发语言·bash
BD_Marathon2 小时前
Vue3_事件渲染命令
开发语言·javascript·ecmascript
倔强的小石头_2 小时前
Python 从入门到实战(十一):数据可视化(用图表让数据 “说话”)
开发语言·python·信息可视化
Brookty2 小时前
Java文件操作系列(一):从基础概念到File类核心方法
java·学习·java-ee·文件io
非凡ghost2 小时前
eDiary电子日记本(记录生活点滴)
windows·学习·生活·软件需求
StudyWinter2 小时前
【c++】thread总结
开发语言·c++·算法
小鸡脚来咯2 小时前
java泛型详解
java·开发语言
liuyouzhang2 小时前
备忘-国密解密算法
java·开发语言