这是一个基于时间片轮询的任务调度器,可以提供精准的任务执行周期
适用场合:
在多数实时控制系统中,可能需要定时地执行某些任务,当任务较多时,一般会采用配置一个定时器中断来提供一个基准,在该定时器中利用计数的方式,在需要的时间点调用指定的任务,而该系统可以自动的完成这一过程.
需要使用的资源:
- 定时器 x 1 用于提供任务调度中断
- 滴答计时器 x 1 用于提供标准时间
功能:
- 1、提供了 创建、销毁、休眠、唤醒任务函数。
- 2、在时间片不足,也就是CPU过载时调用TASK_CPU_OVERLOAD_DEBUG()报错,此时已非实时响应,属于异常情况
不适用场合:
对长耗时任务、死循环while任务等单次执行就超过任务调度周期的任务不能被加入此调度器,于这类建议采用前后台设计或直接使用成型的实时操作系统
不具备的功能:
该系统只提供定时调度和自适应耗时匹配,未作任何信号量、消息传递、内存管理等设计。
移植步骤:
- 1、在task.h中设置好默认参数,特别时两个需要的函数接口
- 2、根据环境调整task.c中的InitTaskSystem()任务初始化函数,主要是配置定时器及其中断以提供标准时基,中断时间为时间片长度,例如本例默认任务调度周期1000us,时间片数量50,因此时间片长度为20us,则中断为20us。
- 3、根据环境调整task.c中的TIM6_DAC_IRQHandler()中断服务函数。
其他
介绍待补充
详细代码:
file: task.h
/* * @Author : LuHeQiu * @Date : 2021-04-01 18:51:38 * @LastEditors : LuHeQiu * @Description : 这是一个基于时间片轮询的任务调度器,可以提供精准的任务执行周期 * 适用场合:在多数实时控制系统中,可能需要定时地执行某些任务,当任务较多时,一般会采用配置一个定时器中断来 * 提供一个基准,在该定时器中利用计数的方式,在需要的时间点调用指定的任务,而该系统可以自动的完成这一过程, * 需要使用的资源: 定时器 x 1 用于提供任务调度中断 * 滴答计时器 x 1 用于提供标准时间 * 功能:1、提供了 创建、销毁、休眠、唤醒任务函数。 * 2、在时间片不足,也就是CPU过载时调用TASK_CPU_OVERLOAD_DEBUG()报错,此时已非实时响应,属于异常情况 * 不适用场合:对长耗时任务、死循环while任务等单次执行就超过任务调度周期的任务不能被加入此调度器, * 对于这类建议采用前后台设计或直接使用成型的实时操作系统 * 不具备的功能:该系统只提供定时调度和自适应耗时匹配,未作任何信号量、消息传递、内存管理等设计。 * 移植步骤:1、在task.h中设置好默认参数,特别时两个需要的函数接口 * 2、根据环境调整task.c中的InitTaskSystem()任务初始化函数,主要是配置定时器及其中断以提供标准时基, * 中断时间为时间片长度,例如本例默认任务调度周期1000us,时间片数量50,因此时间片长度为20us,则中断为20us。 * 3、根据环境调整task.c中的TIM6_DAC_IRQHandler()中断服务函数。 * @FilePath : \CarBaseControl\SYSTEM\Task\task.h * @HomePage : https://www.luheqiu.com */ #ifndef __TASK_H #define __TASK_H #include "stm32f4xx.h" #include "delay.h" /* 包含 GetsystemRunningTime() 全局时间获取函数 */ #include "function.h" /* 包含 task_controller_error() 过载错误报错 */ /* 设置默认参数 ----------------------------------------------------*/ #define MAX_TASK_NUM (16) /* 任务栈最大任务数量 */ #define MAX_TICK_NUM (50) /* 时间片最大数目, */ #define TASKSYSTEM_PERIOD (1) /* 任务调度周期,单位ms,建议为整数 */ /* 一般建议任务调度周期是时间片的整数倍,例如50 * 20us = 1000us */ #define TASK_GET_SYSTEMTIME() GetsystemRunningTime() /* 全局时间获取函数,单位us */ #define TASK_CPU_OVERLOAD_DEBUG() task_controller_error() /* CPU过载错误报错 */ /* 定义系统变量 -------------------------------------------*/ typedef enum{ sleep, /* 休眠态 */ ready, /* 就绪态 */ running, /* 运行态 */ waiting, /* 等待态 */ release /* 释放态 */ }TaskState; typedef enum{ TASK_TO_WATE = 0 , /* 任务等待 */ TASK_TO_DESTROY /* 任务销毁 */ }TaskStateShift; typedef struct{ uint8_t (*taskFunction)(uint8_t); /* 任务函数指针 */ uint8_t taskTicks; /* 任务占用的时间片 */ uint16_t defaultPeriod; /* 任务默认周期 */ TaskState taskstate; /* 任务状态 */ uint16_t cTaskPerid; /* 当前任务周期 */ }Task; typedef struct{ Task tasks[MAX_TASK_NUM+1]; /* 任务栈 */ uint8_t maxTaskNum; /* 最大任务数量 */ uint8_t resTaskNum; /* 剩余任务数量 */ uint8_t maxTickNum; /* 最大时间片数量 */ uint8_t resTickNum; /* 剩余时间片数量 */ }TaskStack; /* 定义系统私有函数 ------------------------------------------*/ static void TaskScheduleLoop(void); static uint8_t RunningTask( uint8_t taskID,uint8_t* taskTicks); /* 定义任务系统接口 ------------------------------------------*/ /** * @brief 初始化任务调度系统 */ extern void InitTaskSystem(void); /** * @brief 启动任务调度器 */ extern void LaunchTaskSchedule(void); /** * @brief 关闭任务调度器 */ extern void CloseTaskSchedule(void); /** * @brief 创建一个任务并加入到任务栈中,任务默认处于sleep休眠态不会被运行 * 任务创建顺序决定了加入到任务栈的顺序,从某种意义上来说在一个周期内会先执行先创建的任务 * @param taskFunction 任务函数地址 * 需要周期性调度的任务的地址,一般对函数名取地址即可。 * @param ticks 预估需要的时间片数量 * 实际上系统会在每次的执行中测量消耗的时间并给任务分配更多时间片(如果有),因此参数不是特别重要 * 但还是建议预先进行恰当的分配 * @param period 任务执行周期 单位ms * 例如,设定为100,表示任务会每隔100ms执行一次 * @retval 创建结果代码 0 创建成功 * 1 任务数量已达最大值,无法创建 * 2 需要分配的任务片不足,无法创建 * 3 当前任务已经存在,不支持重入 */ extern uint8_t CreateTask( uint8_t (*taskFunction)(uint8_t),uint8_t ticks,uint16_t period); /** * @brief 从任务栈中销毁一个任务,此时该任务被彻底删除,需要重新调用CreateTask创建 * @param taskFunction 提供待销毁的任务函数地址 * @retval 结果代码 0 销毁成功 * 1 保留 * 2 保留 * 3 任务并不存在,无法销毁 */ extern uint8_t DestroyTask(uint8_t (*taskFunction)(uint8_t)); /** * @brief 唤醒一个sleep休眠态的任务, * 此时任务从sleep态切换到waiting阻塞态,并根据创建时预设的参数定时执行 * @param taskFunction 提供待唤醒的任务函数地址 * @retval 结果代码 0 唤醒成功 * 1 该任务未休眠,无法唤醒 * 2 保留 * 3 任务不存在,请先创建 */ extern uint8_t WakeUpTask(uint8_t (*taskFunction)(uint8_t)); /** * @brief 休眠一个未处于休眠态的任务 * 此时任务切换到sleep态,与DestroyTask不同的是,任务仍然存在任务栈中,也仍然占用预留的时间片 * 且可以随时使用WakeUpTask唤醒 * @param taskFunction 提供待休眠的任务函数地址 * @retval 结果代码 0 休眠成功 * 1 该任务已经休眠,无法重复休眠 * 2 保留 * 3 任务不存在,请先创建 */ extern uint8_t SleepTask(uint8_t (*taskFunction)(uint8_t)); #endif
file: task.c
/* * @Author : LuHeQiu * @Date : 2021-04-01 18:51:38 * @LastEditors : LuHeQiu * @Description : * @FilePath : \CarBaseControl\SYSTEM\Task\task.c * @HomePage : https://www.luheqiu.com */ /* Includes ------------------------------------------------------------------*/ #include "task.h" /* Private variables ---------------------------------------------------------*/ TaskStack taskStack; /** * @brief Initializes the TaskSystem according to the default parameters. * @param None * @retval None */ void InitTaskSystem(){ /* initialize the task stack */ taskStack.maxTaskNum=MAX_TASK_NUM; taskStack.resTaskNum=MAX_TASK_NUM; taskStack.maxTickNum=MAX_TICK_NUM; taskStack.resTickNum=MAX_TICK_NUM; /* Config TIM6 */ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_ClocksTypeDef RCC_Clocks; //RCC时钟结构体 NVIC_InitTypeDef NVIC_InitStructure; //NVIC中断向量结构体 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); RCC_GetClocksFreq(&RCC_Clocks); //获取系统时钟 TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (RCC_Clocks.PCLK2_Frequency / (1000/TASKSYSTEM_PERIOD*MAX_TICK_NUM*10) ) - 1; TIM_TimeBaseStructure.TIM_Period = 10-1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); /* Enable the TIM6 Update Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM6_DAC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ClearFlag(TIM6, TIM_FLAG_Update); TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //打开溢出中断 TIM_Cmd(TIM6,DISABLE); } /** * @brief Create a new task and add it to the taskStack. * @param taskFunction: Provides a handle to a task function through which the system can execute a task. * @param ticks: Determine how much time the task will take. * @param period: Determine the execution period of the task.(ms) * @retval None */ uint8_t CreateTask( uint8_t (*taskFunction)(uint8_t),uint8_t ticks,uint16_t period){ if(taskStack.resTaskNum<=0)return(1); /* The maximum number of tasks has been exceeded */ if(taskStack.resTickNum<=0)return(2); /* The maximum number of ticks has been exceeded */ for(uint8_t i=0;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ if(taskStack.tasks[i].taskFunction==taskFunction){ return(3); /* This task is already in the taskStack */ } } taskStack.tasks[taskStack.maxTaskNum - taskStack.resTaskNum].taskFunction = taskFunction; taskStack.tasks[taskStack.maxTaskNum - taskStack.resTaskNum].taskTicks = ticks; taskStack.tasks[taskStack.maxTaskNum - taskStack.resTaskNum].defaultPeriod = period; taskStack.tasks[taskStack.maxTaskNum - taskStack.resTaskNum].taskstate = sleep; taskStack.tasks[taskStack.maxTaskNum - taskStack.resTaskNum].cTaskPerid = period / TASKSYSTEM_PERIOD; taskStack.resTaskNum -= 1; taskStack.resTickNum -= ticks; (*taskFunction)(0); return(0); } /** * @brief Wake up a sleeping task which is in the taskStack. * @param taskFunction: Provides a handle to a task function through which the system can execute a task. * @retval the state : 0 succeed * 1 the task is not sleep , cannot to wake * 2 the task is not in the taskStack , please Create first */ uint8_t WakeUpTask(uint8_t (*taskFunction)(uint8_t)){ for(uint8_t i=0;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ if(taskStack.tasks[i].taskFunction==taskFunction){ if(taskStack.tasks[i].taskstate == sleep){ taskStack.tasks[i].taskstate = waiting; return 0; }else{ return 1; } } } return 3; } /** * @brief Sleep a task which is in the taskStack. * @param taskFunction: Provides a handle to a task function through which the system can execute a task. * @retval the state : 0 succeed * 1 the task is already sleep * 2 the task is not in the taskStack , please Create first */ uint8_t SleepTask(uint8_t (*taskFunction)(uint8_t)){ for(uint8_t i=0;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ if(taskStack.tasks[i].taskFunction==taskFunction){ if(taskStack.tasks[i].taskstate == sleep){ return 1; }else{ taskStack.tasks[i].taskstate = sleep; return 0; } } } return 3; } /** * @brief Launch a task which in the taskStack * @param taskFunction: Provides a handle to a task function through which the system can execute a task. * @retval None */ uint8_t RunningTask( uint8_t taskID,uint8_t* taskTicks){ if(taskID>=(taskStack.maxTaskNum-taskStack.resTaskNum))return(1); /* The task is not on the stack */ (*taskTicks)=taskStack.tasks[taskID].taskTicks; taskStack.tasks[taskID].taskstate=running; return(0); } /** * @brief Stop a task which in the taskStack * @param taskFunction: Provides a handle to a task function through which the system can execute a task. * @retval None */ uint8_t DestroyTask(uint8_t (*taskFunction)(uint8_t)){ for(uint8_t i=0;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ if(taskStack.tasks[i].taskFunction==taskFunction){ taskStack.tasks[i].taskstate=release; return 0; } } return 3; } /** * @brief Launch the task scheduling system * @param None * @retval None */ void LaunchTaskSchedule(){ TIM_Cmd(TIM6,ENABLE); } /** * @brief Close the task scheduling system * @param None * @retval None */ void CloseTaskSchedule(){ TIM_Cmd(TIM6,DISABLE); } /** * @brief Task polling * @param None * @retval None */ void TaskScheduleLoop(void){ static uint8_t taskID=0; /* 任务ID */ static uint8_t taskticks=0; /* 任务时间片数 */ static uint8_t periodticks=0; /* 当前时间片周期序数 */ static uint8_t isFree=1; /* 是否空闲,即没有任务执行 */ uint32_t lastTime = TASK_GET_SYSTEMTIME(); if(periodticks < taskStack.maxTickNum-taskStack.resTickNum && taskticks==0){ /* 任务轮询结束,返回任务栈栈首 */ if(taskID>=taskStack.maxTaskNum-taskStack.resTaskNum){ taskID=0; } /* 判断当前任务状态 */ switch(taskStack.tasks[taskID].taskstate){ /* 若任务为休眠态 */ case sleep: // 0 1 2 3 { /* 直接跳过该任务 */ if(isFree){ taskticks = taskStack.tasks[taskID].taskTicks; } break; } /* 若任务为就绪态 */ case ready: { /* 如果任务栈为空闲状态则切换当前任务为运行态,并分配时间片 */ if(isFree){ RunningTask(taskID,&taskticks); isFree=0; } } /* 若任务为运行态 */ case running: { taskticks = taskStack.tasks[taskID].taskTicks; if( taskticks==0 ){ taskStack.tasks[taskID].taskstate=waiting; break; } /* 以指令码 1 运行任务函数 并接收返回码 */ uint8_t res = (*taskStack.tasks[taskID].taskFunction)(1); /* 返回码为 0 表示任务暂时完成,进入等待态 */ if( res == 0 ){ taskStack.tasks[taskID].taskstate=waiting; taskStack.tasks[taskID].cTaskPerid=taskStack.tasks[taskID].defaultPeriod/TASKSYSTEM_PERIOD; /* 返回码为 3 表示任务全部完成,进入释放态,准备释放当前任务 */ }else if(res == 3){ taskStack.tasks[taskID].taskstate=release; } break; } /* 若任务为等待态 */ case waiting: { /* 跳过当前任务 */ if(isFree){ taskticks = taskStack.tasks[taskID].taskTicks; } break; } /* 若任务为释放态 */ case release: // 0 x 2 3 { uint8_t releasedTicks=taskStack.tasks[taskID].taskTicks; /* 释放的时间片数量 */ /* 任务前移 */ /* PS虽说链表更优雅就是了 */ for(uint8_t i=taskID;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ taskStack.tasks[i]=taskStack.tasks[i+1]; } taskStack.resTickNum+=releasedTicks; taskStack.resTaskNum+=1; isFree=1; break; } } } /* 真实时间片消耗测量 */ uint32_t divTime = TASK_GET_SYSTEMTIME() - lastTime; uint8_t usedTicks = divTime / (TASKSYSTEM_PERIOD * 1000 / MAX_TICK_NUM) + 1; if(periodticks < taskStack.maxTickNum-taskStack.resTickNum){ /* 如果有任务在运行,矫正运行时间 */ if( !isFree ){ if(taskticks > usedTicks){ taskticks -= usedTicks; }else if(taskticks == usedTicks){ taskticks -= usedTicks; taskID++; } else{ /* 时间片不足,尝试给任务增加时间片 */ if( usedTicks <= taskStack.resTickNum ){ taskStack.tasks[taskID].taskTicks = usedTicks; taskStack.resTickNum -= (usedTicks - taskticks); }else{ /* 系统剩余时间片不足,报超载错误 */ TASK_CPU_OVERLOAD_DEBUG(); } taskID++; taskticks = 0; } isFree = 1; }else{ /* 如果没有任务运行 */ /* 非紧缩策略 上一任务分配的时间片未用完,CPU空转 */ if(taskticks > 0){ taskticks--; } if(taskticks==0){ if(taskID < taskStack.maxTaskNum){ taskID++; } } } } periodticks += usedTicks; /* 任务时间片轮询结束,进行下周期轮询准备 */ if(periodticks==MAX_TICK_NUM){ for(uint8_t i=0;i<taskStack.maxTaskNum-taskStack.resTaskNum;i++){ /* 如果任务为等待态 */ if(taskStack.tasks[i].taskstate==waiting){ /* 等待周期减一 */ if(taskStack.tasks[i].cTaskPerid>0){ taskStack.tasks[i].cTaskPerid--; } /* 等待完成,变更为就绪态 */ if(taskStack.tasks[i].cTaskPerid==0){ taskStack.tasks[i].taskstate=ready; } } } periodticks=0; taskID = 0; }else if(periodticks > MAX_TICK_NUM ){ /* 系统剩余时间片不足,报超载错误 */ TASK_CPU_OVERLOAD_DEBUG(); } } /*以下为中断服务程*/ void TIM6_DAC_IRQHandler(void) { //是否有更新中断 if(TIM_GetITStatus(TIM6,TIM_IT_Update) != RESET) { //处理中断 TaskScheduleLoop(); //清除中断标志 TIM_ClearITPendingBit(TIM6,TIM_IT_Update); } }