前面写了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文章
515浏览量
57553 -
RTOS
+关注
关注
20文章
776浏览量
118798 -
结构体
+关注
关注
1文章
125浏览量
10750
发布评论请先 登录
相关推荐
单片机工程师是程序员吗
程师做的工作就是程序开发,使用C语言来驱动单片机的硬件资源,以及完成一个逻辑任务,实现一定的功能的过程。但是单片机工程师又不是严格意义上的程序员,因为程序员是纯粹在电脑上写代码的一个职
发表于 11-09 09:14
单片机工程师是程序员吗?真实的月薪到底有多少?
程师做的工作就是程序开发,使用C语言来驱动单片机的硬件资源,以及完成一个逻辑任务,实现一定的功能的过程。但是单片机工程师又不是严格意义上的程序员,因为程序员是纯粹在电脑上写代码的一个职
发表于 11-04 17:36
•16次下载
c语言实现任务调度器
一、介绍调度器是常用的一种编程框架,也是操作系统的拆分多任务的核心,比如单片机的裸机程序框架,网络协议栈的框架如can网关、485网关等等,使用场合比较多,是做稳定产品比较常用的编程技术
发表于 12-22 18:51
•8次下载
单片机工控事件
单片机工控通常有延时,电机状态,传感器状态等通用耗时操作,业务程序查询这些状态,就会产生大量的冗余代码,不简洁.使用事件则是把这些通用操作丢给系统去处理,系统操作完成后,则运行业务程序的下一个Step.
51单片机多任务执行例子
51单片机多任务同时执行。RTX51 tiny是一种实时操作系统(RTOS),可以用它来建立多个任务(函数)同时执行的应用(从宏观上看是同时
发表于 08-04 15:59
•0次下载
评论