Skip to content

封装版多任务调度

仍然是一种不使用 FreeRTOS 来实现简单的多任务调度的方式,使用 MSPM0G3507 作为示例。

首先请确保 SysTick 内的 Calculated Period 为 1ms,这样才能确保调度精确。当然也可以选择使用一个 1ms 定时器中断来代替 SysTick 中断。

嵌入式开发内的命名较乱,我目前除了 OLED_Init 这种操作外设的函数和 delay_ms 这种特殊的函数之外,主要还是用驼峰法 (camelCase) 进行命名。

SysTick 中断

delay.c 内编写延时函数,并通过 SysTick 中断来处理延时。

c
volatile uint32_t utick = 0;

// 1ms 执行一次
void SysTick_Handler(void) {
    SysTick->CTRL &= ~(1 << 16); // 清除中断标志位
    utick++;
}

以上中断会使得 utick 每 1ms 自增一次,因此可用 utick 来表示系统时间。

经典 delay_ms 函数

TI 似乎没有内置的阻塞型延时,需要利用 SysTick 中断实现阻塞型延时,相当于 STM32 HAL 库中的 HAL_Delay

c
void delay_ms(uint32_t ms) {
    uint32_t now = utick;

    while (utick < (now + ms)) {
    }
}

getTick 函数

定义一个 getTick 函数使得 utick 可被外部访问

c
void getTick(void) {
    return utick;
}

封装多任务调度

tasks.c 内封装多任务调度。首先在头文件中声明任务结构体:

c
typedef struct {
    uint32_t interval; // 任务持续时间
    uint32_t lastCall; // 上次执行时间
    void (*task)(void); // 任务函数指针
} Task;

定义任务数组,用于存放所有的定时任务

c
void task1(void) {
    // do something
}

void task2(void) {
    // do something
}

Task tasks[2] = {
    {20, 0, task1},
    {100, 0, task2}
};

int tasksLen = 2;

实现一个 iter 函数,该函数需在 main 函数中主动调用,以实现任务调度

c
void iter(void) {
    for (int i = 0; i < tasksLen; i++) {
        uint32_t now = getTick();

        if (now - tasks[i].lastCall >= tasks[i].interval) {
            tasks[i].task();
            tasks[i].lastCall = now;
        }
    }
}

在 main 函数主循环中调用 iter 来实现任务调度

c
#include "ti_msp_dl_config.h"

int main(void) {
    SYSCFG_DL_init();

    while (1) {
        iter();

        // 其他主逻辑
    }
}