目录
现有问题
- 每次时钟节拍都需要扫描所有任务,比较耗时
- 不易支持多个任务具有相同优先级
延时队列设计
将所有需要延时的任务单独放置在一个队列中,每次发生系统节拍时,只需扫描该队列。
方式一:独立保存延时时间
- 插入延时任务比较简单,快速。
- 扫描整个队列比较简单,但较慢。
方式二:递增的延时队列
每一个任务的延时时间等于它前面所有任务的延时时间的和加上它自己的延时时间。
- 插入延时任务比较复杂,较慢。
- 扫描整个队列比较复杂,但较快。
设计实现
- 添加延时队列
- 延时队列的插入
- 延时队列的删除
- 时钟节拍扫描延时队列
main.c
#include "tinyOS.h"
#include "ARMCM3.h"
tTask *currentTask; //指示当前运行任务的指针
tTask *nextTask; //指向下一个任务的指针
tTask *idleTask; //空闲任务
tBitmap taskPrioBitmap; //优先级位图
tTask *taskTable[TINYOS_PRO_COUNT]; //任务数组列表
uint8_t schedLockCount; //调度锁计数器
tList tTaskDelayedList; //延时队列
/* 任务的初始化函数 */
//参数:tTask结构的指针,任务入口函数的地址,传递给任务的参数地址,堆栈地址
//任务初始运行时,会把栈里的内容依次弹出来,恢复到内核寄存器中。
void tTaskInit(tTask *task, void(*entry)(void *), void *param, uint32_t prio, tTaskStack *stack)
{
//初始化具体的堆栈内容
//传递堆栈的末端地址,内核本身的堆栈增长方式是满递减方式增长的,先进行递减操作
*(--stack) = (unsigned long)(1 << 24); //xPSR,设置T标志位
*(--stack) = (unsigned long)entry; //R15(PC),程序入口函数
*(--stack) = (unsigned long)0x14; //R14(LR),未用
*(--stack) = (unsigned long)0x12; //R12,未用
*(--stack) = (unsigned long)0x3; //R3,未用
*(--stack) = (unsigned long)0x2; //R2,未用
*(--stack) = (unsigned long)0x1; //R1,未用
*(--stack) = (unsigned long)param; //R0,程序的入口参数,函数第一个参数存入R0
*(--stack) = (unsigned long)0x11; //R11,未用
*(--stack) = (unsigned long)0x10; //R10,未用
*(--stack) = (unsigned long)0x9; //R9,未用
*(--stack) = (unsigned long)0x8; //R8,未用
*(--stack) = (unsigned long)0x7; //R7,未用
*(--stack) = (unsigned long)0x6; //R6,未用
*(--stack) = (unsigned long)0x5; //R5,未用
*(--stack) = (unsigned long)0x4; //R4,未用
task->stack = stack; //保存最终的值
task->delayTicks = 0; //初始化计数器
task->prio = prio; //初始化优先级
task->state = TINYOS_TASK_STATE_RDY; //任务状态初始化为就绪态
tNodeInit(&(task->delayNode)); //对延时结点初始化
taskTable[prio] = task;
tBitmapSet(&taskPrioBitmap, prio); //对位图置1,表明任务已经初始化好,可以占用CPU运行
}
/* 查找优先级位图函数 */
tTask *tTaskHighestReady(void)
{
uint32_t highestPrio = tBitmaoFirstSet(&taskPrioBitmap);
return taskTable[highestPrio];
}
/* 调度锁初始化函数 */
void tTaskSchedInit(void)
{
schedLockCount = 0; //初始计数值为0
tBitmapInit(&taskPrioBitmap); //对bitmap初始化
}
/* 上锁函数(禁止调度函数) */
void tTaskSchedDisable(void)
{
uint32_t status = tTaskEnterCritical();
//schedLockCount是全局变量需要临界区保护
if(schedLockCount < 255)//schedLockCount是8位的,防止溢出
{
schedLockCount++;
}
tTaskExitCritical(status);
}
/* 解锁函数(使能调度函数) */
void tTaskSchedEnable(void)
{
uint32_t status = tTaskEnterCritical();
if(schedLockCount > 0)
{
if(--schedLockCount == 0)
{
tTaskSched();//执行调度函数
}
}
tTaskExitCritical(status);
}
/* 就绪表插入任务 */
void tTaskSchedRdy(tTask *task)
{
taskTable[task->prio] = task;
tBitmapSet(&taskPrioBitmap, task->prio); //就绪表中设置任务就绪
}
/* 就绪表移除任务 */
void tTaskSchedUnRdy(tTask *task)
{
taskTable[task->prio] = (tTask *)0;
tBitmapClear(&taskPrioBitmap, task->prio); //位图位清零
}
/* 调度函数 */
//决定CPU在哪两个任务之间运行,采用什么规则,怎么分配
void tTaskSched()
{
tTask *tempTask;
uint32_t status = tTaskEnterCritical();
//判断调度器是否上锁
if(schedLockCount > 0)//上锁
{
tTaskExitCritical(status);//退出
return;
}
tempTask = tTaskHighestReady();//获取最高优先级需要占用CPU运行的任务
if(tempTask != currentTask)//最高优先级任务与当前任务不同
{
nextTask = tempTask;
tTaskSwitch();//任务切换函数
}
tTaskExitCritical(status);
}
/* 延时队列初始化 */
void tTaskDelayedInit()
{
tListInit(&tTaskDelayedList);//初始化延时链表
}
/* 延时队列插入任务 */
void tTimeTaskWait(tTask *task, uint32_t ticks)
{
task->delayTicks = ticks;
tListAddLast(&tTaskDelayedList, &(task->delayNode));//任务插入队列尾部
task->state |= TINYOS_TASK_STATE_DELAYED;
}
/* 延时队列移除任务 */
void tTimeTaskWakeUp(tTask *task)
{
tListRemove(&tTaskDelayedList, &(task->delayNode));
task->state &= ~TINYOS_TASK_STATE_DELAYED;//清除延时标志位
}
/* 时钟节拍处理函数 */
void tTaskSystemTickHandler()
{
tNode *node;
uint32_t status = tTaskEnterCritical();
//扫描延时队列
for(node = tTaskDelayedList.headNode.nextNode; node != &(tTaskDelayedList.headNode); node = node->nextNode)
{
tTask *task = tNodeParent(node, tTask, delayNode);//获取任务结构
if(--task->delayTicks == 0)//判断任务有没有延时到
{
tTimeTaskWakeUp(task);//从延时队列中移除
tTaskSchedRdy(task);//插入就绪表
}
}
tTaskExitCritical(status);
tTaskSched();//调度函数
}
/* 任务延时函数 */
void tTaskDelay(uint32_t delay)
{
uint32_t status = tTaskEnterCritical();
tTimeTaskWait(currentTask, delay);
tTaskSchedUnRdy(currentTask);//清除就绪位,将调用延时函数的任务从就绪表中删除掉
tTaskExitCritical(status);
tTaskSched();//调度函数
}
/* SysTick初始化函数 */
//参数:毫秒
void tSetSysTickPeriod(uint32_t ms)
{
SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;//初始化SysTick重载寄存器值,在SysTick计数器递减到0时将这个计数器的值加载到递减寄存器里
NVIC_SetPriority(SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);//
SysTick->VAL = 0;//递减寄存器
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | //控制寄存器
SysTick_CTRL_TICKINT_Msk | //产生中断使能的标志位
SysTick_CTRL_ENABLE_Msk; //使能计数器的标志位
}
/* SysTick中断处理函数 */
void SysTick_Handler()
{
tTaskSystemTickHandler();
}
//定义两个任务要执行的功能
int task1Flag;
void task1Entry(void *param)
{
tSetSysTickPeriod(10);//初始化
for(;;)//任务里是for的死循环
{
task1Flag = 0;
tTaskDelay(1);
task1Flag = 1;
tTaskDelay(1);
}
}
int task2Flag;
void task2Entry(void *param)
{
for(;;)
{
task2Flag = 0;
tTaskDelay(1);
task2Flag = 1;
tTaskDelay(1);
}
}
//定义两个任务,分别为它们配备独立的堆栈空间
tTask tTask1;
tTask tTask2;
tTaskStack task1Env[1024];
tTaskStack task2Env[1024];
tTask tTaskIdle; //空闲任务
tTaskStack idleTaskEnv[1024]; //空闲任务的堆栈
/* 空闲任务具体内容 */
void idleTaskEntry(void *param)
{
for(;;)
{
//空闲任务什么都不做
}
}
int main(void)
{
tTaskSchedInit(); //初始化调度锁
tTaskDelayedInit(); //初始化延时队列
//最后一个参数:传堆栈末端地址,因为堆栈是向下生长的,初始堆栈地址是堆栈空间最后一个单元地址的末端
tTaskInit(&tTask1, task1Entry, (void *)0x11111111, 0, &task1Env[1024]);
tTaskInit(&tTask2, task2Entry, (void *)0x22222222, 1, &task2Env[1024]);
//初始化任务数组
taskTable[0] = &tTask1;
taskTable[1] = &tTask2;
tTaskInit(&tTaskIdle, idleTaskEntry, (void *)0, TINYOS_PRO_COUNT - 1, &idleTaskEnv[1024]);//创建空闲任务
idleTask = &tTaskIdle;//空闲任务赋值
nextTask = tTaskHighestReady();//初始运行任务指向就绪表中优先级最高的任务
tTaskRunFirst();//切换到第0个任务运行
return 0;
//执行tTaskRunFirst函数后,CPU的控制权切换到任务里运行
//任务里是for循环会一直在任务里运行
//切换到另一个函数里面也是for循环
//永远不会返回
}
tinyOS.h
#ifndef __TINYOS_H
#define __TINYOS_H
#include <stdint.h>
#include "tLib.h"
#include "tConfig.h"
#define TINYOS_TASK_STATE_RDY 0 //任务就绪
#define TINYOS_TASK_STATE_DELAYED (1 << 1) //任务处于延时状态
//堆栈单元类型
typedef uint32_t tTaskStack;
//任务结构
typedef struct _tTask{
uint32_t *stack; //指向堆栈的指针
uint32_t delayTicks; //软延时计数器
tNode delayNode; //通用的结点结构
uint32_t prio; //优先级字段
uint32_t state; //任务状态
}tTask;
extern tTask *currentTask;
extern tTask *nextTask;
uint32_t tTaskEnterCritical(void);
void tTaskExitCritical(uint32_t status);
void tTaskSwitch(void); //和CPU相关,写在switch.c
void tTaskRunFirst(void);
void tTaskSchedInit(void);
void tTaskSchedDisable(void);
void tTaskSchedEnable(void);
void tTaskSched();
#endif
调试现象
标签:task,tTask,队列,void,任务,延时 From: https://blog.csdn.net/daybydayby/article/details/144365449