前面写了switch case做任务调度的应用,这边写一下如何实现.像一般的RTOS一样都有一个任务控制块(TCB)列表来管理所有的任务,所以这里也需要一个结构体来管理动作任务,这里叫做ACB吧.
动作任务管理结构体
这个结构体用来记录当前动作任务的当前步骤,函数地址,事件处理函数的函数地址,动作名称,运行时间等.
typedef struct _ACB
{
uint8_t nStep; // 分支动作步骤
uint8_t nChildID; //子动作ID
uint32_t nDelay; // 延时
uint32_t nStartTime;
uint32_t nUsedTime;
uint32_t nStatus; //当前状态
ActionEvent EventQueue[4]; //动作事件队列
uint8_t nEventCnt; //事件数量和个数
void (*AppCallBack)(); //业务动作函数指针
void (*EventCallback)(); //事件处理函数指针
void (*ActionCallback)(); //当前运行的函数指针
char ActionParam[10]; //动作参数
//17
char* pActionName; //动作名称
char* pErrorInfo; //错误信息
//8
struct _ACB* next; //上一个控制块 便于删除添加到就绪队列
struct _ACB* prev; //下一个控制块 便于添加删除到就绪队列
} ACB;
动作任务创建
//将OpApp业务函数指针和ACB结构体绑定
void AddAction(int nID,void (*OpApp)(),const char* pActionName)
{
if(nID<90)
{
mOS.ActionPool[nID].AppCallBack = OpApp;
mOS.ActionPool[nID].EventCallback = EventAction;
mOS.ActionPool[nID].ActionCallback = OpApp;
mOS.ActionPool[nID].nStatus = 0xff;
mOS.ActionPool[nID].nEventCnt = 0;
mOS.ActionPool[nID].nUsedTime = 0;
mOS.ActionPool[nID].nStartTime = 0;
mOS.ActionPool[nID].pActionName = (char*)pActionName;
}
}
动作任务执行
1.动作结构体初始化,添加到就绪队列
//根据ID启动动作任务
int8_t StartAction(uint8_t ActionId)
{
if(ActionId>90)
{
return false;
}
ACB* pAction = &mOS.ActionPool[ActionId];
pAction->ActionStartTime = mOS.SystemTime;
pAction->nStep = STEP1;
pAction->nEventCnt = 0;
pAction->nUsedTime = 0;
pAction->nChildID = 0;
pAction->EventCallback = EventAction;
pAction->ActionCallback = pAction->AppCallBack;//先指向业务函数指针
AddReadyActionToTail(pAction); //把当前的控制块添加到就绪队列
return true;
}
//将要运行的动作添加到就绪运行队列
void AddReadyActionToTail(ACB * pAction)
{
pAction->nStaus = 0;
pAction->next = NULL;
pAction->prev = mOS.tail; //
if(mOS.head==NULL)
{
mOS.head = pAction;
}
if(mOS.tail!=NULL) //当就绪链表没有动作时 tail为空
{
mOS.tail->next = pAction;
}
mOS.tail = pAction; //移动尾部指针
}
2.就绪队列遍历
inline void AppLoop()
{
mOS.current = mOS.head;
while(mOS.current!=NULL)
{
mOS.current->ActionCallback();
DelFinishAction(); //需要把完成的任务从就绪队列删除
mOS.current = mOS.current->next;
}
}
//动作完成后,并计算动作使用时间,从链表删除
inline void DelFinishAction()
{
if(mOS.current->nStatus==0) //动作任务正在运行
{
return ;
}
//统计任务耗时
mOS.current->nUsedTime = mOS.SystemTime - mOS.CurrentAction->nStartTime; //当前时间减去开始时间
if(mOS.head==mOS.tail) //只有一个元素
{
mOS.head= NULL;
mOS.tail = NULL;
return ;
}
if(mOS.current==mOS.head) //头部删除,需要将头部指针往后移动
{
mOS.head = mOS.current->next;
return ;
}
if(mOS.current==mOS.tail) //尾部删除,需要将尾部指针往前移动
{
mOS.tail = mOS.current->prev;
mOS.tail->next = NULL;
return ;
}
//中间删除
mOS.current->prev->next = mOS.current->next;
mOS.current->next->prev = mOS.current->prev;
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
Switch
+关注
关注
1文章
532浏览量
58098 -
RTOS
+关注
关注
21文章
809浏览量
119348 -
结构体
+关注
关注
1文章
129浏览量
10822
发布评论请先 登录
相关推荐
单片机工程师是程序员吗
程师做的工作就是程序开发,使用C语言来驱动单片机的硬件资源,以及完成一个逻辑任务,实现一定的功能的过程。但是单片机工程师又不是严格意义上的程序员,因为程序员是纯粹在电脑上写代码的一个职
发表于 11-09 09:14
多个任务时,如何保证单片机工作效率及每个任务完成的及时性?资料下载
电子发烧友网为你提供多个任务时,如何保证单片机工作效率及每个任务完成的及时性?资料下载的电子资料下载,更有其他相关的电路图、源代码、课件教程、中文资料、英文资料、参考设计、用户指南、解决方案等资料,希望可以帮助到广大的电子工程师
发表于 04-12 08:41
•3次下载
单片机工程师是程序员吗?真实的月薪到底有多少?
程师做的工作就是程序开发,使用C语言来驱动单片机的硬件资源,以及完成一个逻辑任务,实现一定的功能的过程。但是单片机工程师又不是严格意义上的程序员,因为程序员是纯粹在电脑上写代码的一个职
发表于 11-04 17:36
•16次下载
单片机工控事件
单片机工控通常有延时,电机状态,传感器状态等通用耗时操作,业务程序查询这些状态,就会产生大量的冗余代码,不简洁.使用事件则是把这些通用操作丢给系统去处理,系统操作完成后,则运行业务程序的下一个Step.
评论