合作式调度器

合作式调度器

优缺点:

  • 同一时间只有一个任务运行
  • 容易调试,任务好管理
    缺点:
  • 任务阻塞可能会饿死其他任务
  • 只适合小型项目

代码解析:

句柄数据结构:

1
2
3
4
5
6
7
8
9
10
#define SCH_MAX_TASKS 5

typedef struct
{
uint8_t run; //1调度,0挂起
uint16_t TimeCount; //时间片周期
uint16_t TimeRload; //用于重载时间片
void (*pTaskFunc)(void); //函数指针,保存任务函数地址
} TaskStruct_t;

调度器:

调度器本身是一个中断函数,先设定一定的时基,比如100us进一次中断,累计中断次数就知道过了多少时间了。然后判断TimeCount 是否为0,若为0则表示时间到开始运行任务,否则继续递减。TimeRload表示若任务需要隔一段时间运行,则该变量储存这段时间的具体值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SCH_inter()
{
for (uint8_t i = 0; i < SCH_MAX_TASKS; i++)
{
if(TaskNum[i].TimeCount > 0) // 仅处理时间片>0的任务
{
TaskNum[i].TimeCount--; // 先递减
if(TaskNum[i].TimeCount == 0) // 递减后判断是否为0
{
TaskNum[i].run = 1; // 置调度标志
if(TaskNum[i].TimeRload > 0) // 周期任务重载
{
TaskNum[i].TimeCount = TaskNum[i].TimeRload;
}
}
}
}
}

最后,在while(1)中循环的是管理器,它用来执行任务并准备好下一次的运行。

1
2
3
4
5
6
7
8
9
10
11
12
void SCH_update()
{
for(uint8_t i=0;i<SCH_MAX_TASKS;i++)
{
if(TaskNum[i].run == 1) //若任务处于调度状态,开始调度
{
TaskNum[i].run = 0; //清标志位
TaskNum[i].pTaskFunc(); //运行任务
}
}
}

最后是添加任务到消息队列,若对某个i,函数地址为空,则表示队列里面有空闲位置可以加入函数,只需要更改制定数据结构即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void SCH_addTask(void (*pFunction)(void), uint32_t delay, uint32_t period)
{
for(uint8_t i=0;i<SCH_MAX_TASKS;i++)
{
if(TaskNum[i].pTaskFunc == 0) //找到空闲任务位
{
TaskNum[i].pTaskFunc = pFunction; //保存任务函数地址
TaskNum[i].TimeCount = delay; //设置延时
TaskNum[i].TimeRload = period; //设置周期
TaskNum[i].run = 0; //清调度标志
break;
}
}
}

同时,我把printf函数重定向到串口,只需要初始化(我给的是PA9和PA10)

1
2
3
4
5
6
7
8
9

//在uart.c文件中键入
#include <stdio.h>

int fputc(int ch, FILE *f)
{
send_data((uint8_t)ch);
return ch;
}

即可在使用printf函数的时候自动输出到端口。

简单运用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "stm32f10x.h"
#include "timer.h"
#include "scheduler.h"
#include "uart.h"

void test_task(void);
void task_2s(void);

int main(){

uart_init();
Timer_Init();
printf("A\r\n");
SCH_addTask(test_task,10000,10000); //1s
SCH_addTask(task_2s,20000,20000); //2s
while(1){
SCH_update();
};
}

void TIM2_IRQHandler() //调度器,100us
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) // 检查TIM2更新中断发生
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志
// 在这里添加你的中断处理代码
SCH_inter(); //调用调度器
}
}

void test_task(void)
{
printf("this is a time of 1s test task\r\n");
}

void task_2s(void)
{
printf("this is a time of 2s test task\r\n");
}