STC15分时内核中函数指针的使用问题

    基于前后台设计的系统随着功能的递增变得越来越难以维护, 所以决定为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};

猜你喜欢

转载自my.oschina.net/u/2007478/blog/1498685