基于前后台设计的系统随着功能的递增变得越来越难以维护, 所以决定为STC15F2K单片机编写一个基于时分的非抢占式内核,方便进行任务模块的开发。
内核TCB结构定义如下:
typedef struct os_tcb {
void(*task)(void *); //任务处理函数
INT32U cpu_load; //任务分配CPU时间片
INT32U cpu_run; //任务当前运行CPU时间片
INT8U status; //任务状态
INT8U id; //任务ID
void *pd; //任务参数,方便数据共享交互
} OS_TCB;
通过STC的内部定时器0产生系统的tick,在定时器0的ISR中轮询TCB列表并对cpu_run进行计数,在main主循环中,轮询tcb中status为Ready的task,并进行调用。下面是简单的框架代码:
void timer0_isr (void) interrupt 1
{
OSSched();
}
void OSSched()
{
INT8U i;
for (i = 0; i < OS_TASK_NUM; i++) {
if ((OSTCBList[i].task != 0u) && (OSTCBList[i].status == OS_STAT_PEND)) {
OSTCBList[i].cpu_run++;
if (OSTCBList[i].cpu_run >= OSTCBList[i].cpu_load) {
OSTCBList[i].cpu_run = 0u;
OSTCBList[i].status = OS_STAT_RDY;
}
}
}
}
void main()
{
OSStart();
}
void OSStart()
{
INT8U i;
EA = 1;
while (OS_TRUE) {
for (i = 0; i < OS_TASK_NUM; i++) {
if ((OSTCBList[i].task != 0u) && (OSTCBList[i].status == OS_STAT_RDY)) {
OSTCBList[i].task(OSTCBList[i].pd);
OSTCBList[i].status = OS_STAT_PEND;
}
}
}
}
在实际使用中我创建了APPLedTask和APPKeyTask两个Task,每个Task单独运行功能都正常,但只要同时开启这两个Task,那么每个Task功能都会异样,有时候删除一行代码就正常,但我觉得问题觉得不出现在被删除代码处,所以怀疑是出现个函数指针Overlay的问题。
查看问题代码编译生成的m51文件
?PR?APPINIT?MAIN ----- -----
+--> ?PR?BSPGPIOINIT?GPIO
+--> ?PR?BSPLEDINIT?LED
?PR?APPTASKINIT?MAIN ----- -----
+--> ?PR?_APPKEYTASK?APP_KEY <--- 错误处
+--> ?PR?_OSTASKCREATE?OS
+--> ?PR?_APPLEDTASK?APP_LED <--- 错误处
?PR?_APPKEYTASK?APP_KEY 0029H 0003H <--- 错误处
+--> ?PR?BSPKEYSTATUS?KEY
?PR?_OSTASKCREATE?OS 0029H 0008H
?PR?_APPLEDTASK?APP_LED 0029H 0003H <--- 错误处
+--> ?PR?_BSPLEDLIGHT?LED
+--> ?PR?_BSPLEDDISPLAY?LED
?PR?_BSPLEDDISPLAY?LED ----- -----
+--> ?PR?_SEND_595?LED
+--> ?PR?UPDATE_595?LED
?PR?UPDATE_595?LED ----- -----
+--> ?CO?LED
+--> ?PR?_SEND_595?LED
BL51 BANKED LINKER/LOCATER V6.22 08/01/2017 14:01:54 PAGE 3
?PR?OSSTART?OS 0029H 0001H <--- 错误处
大家可以看到我标记出来的错误处。BL51错误的认为在函数: APPTASKINIT中调用了APPKeyTask, APPLedTask(实际上我只是对于函数指针进行赋值), 于是BL51把OSStart、APPKeyTask、APPLedTask都连接到了相同的Data memory地址0x0029,这就会导致在OSStart中调用Task函数时出现异常。
解决方案的话,在BL51 Misc的Overlay框中构建正确的调用树
?PR?APPTASKINIT?MAIN ~ ?PR?_APPKEYTASK?APP_KEY, ?PR?APPTASKINIT?MAIN ~ ?PR?_APPLEDTASK?APP_LED,
?PR?OSSTART?OS ! ?PR?_APPKEYTASK?APP_KEY, ?PR?OSSTART?OS ! ?PR?_APPLEDTASK?APP_LED
// ~: 删除调用关系 !: 插入调用关系
观察更新后m51文件,两个task已经被OSStart调用,并与OSStart处于不同的Data memory地址上
?PR?APPTASKINIT?MAIN ----- -----
+--> ?PR?_OSTASKCREATE?OS
?PR?_OSTASKCREATE?OS 0029H 0008H <-----
?PR?OSSTART?OS 0029H 0001H
+--> ?PR?_APPKEYTASK?APP_KEY
+--> ?PR?_APPLEDTASK?APP_LED
?PR?_APPKEYTASK?APP_KEY 002AH 0003H <-----
+--> ?PR?BSPKEYSTATUS?KEY
?PR?_APPLEDTASK?APP_LED 002AH 0003H <-----
+--> ?PR?_BSPLEDLIGHT?LED
+--> ?PR?_BSPLEDDISPLAY?LED
另外一种解决方案就是创建一个const code的函数指针对象,因为在code区,而BL51对于code区是另外对待的,所以不存在Overlay的情况,这样也省去了修改调用树的麻烦。
typedef struct os_task {
void(*task[OS_TASK_NUM])(void *);
} OS_TASK;
OS_TASK const code OSTaskList = {APPKeyTask, APPLedTask};