原创 UCOSII AVR移植全过程及心得

2008-12-16 21:33 5122 10 11 分类: MCU/ 嵌入式

http://www.ic37.com/htm_bbs_dic/2003-8/134444_91048.htm


UCOSII AVR移植全过程及心得


音乐乐乐


Stonkson@163.com


Stonkson@yahoo.com.cn


经过近一个星期的战斗,现在的ucosii终于成功的移植到了avr单片机上,我发过帖子,觉得还是有很多朋友对这个比较感兴趣的,所以我把我记录的移植全过程及一些心得写出来与大家分享。


出于兴趣 ,本人也想再avr单片机上跑个操作系统玩玩,大家都知道,对于这样的单片机而言,ucosii当然是首选。


听说网上有现成的范例,偶用力搜搜搜,嘿嘿,结果搜到了两个例子,一个是8515的,另一个是103的,陶醉中…赶紧打开ICCAvr,编译…. Faint,出了一大堆错误,修改了半天也没弄好,没办法,还是自己移植吧。


先是买了邵贝贝翻译的那本第二版的书,躲在宿舍看了整整两天,先是完整的看一遍,从封面到封底,除8086移植范例外一字不漏,嘿嘿,发现我看得时候基本都明白,看完了好像都忘的差不多了 :( ,不过没关系,关键是移植嘛,对操作系统的运行原理有一些感性认识就可以了;好,现在开始专攻移植部分,又仔细的看了一遍第13章。


准备工作做好了接下来开始动手!ucos的任务调度机制网上有很多“专著”,我就不罗嗦了,其移植原理也比较简单,主要就是寄存器的保护合恢复。需要改写的有4个文件,OS_CPU.h,OS_CPU.c,OS_CPU_asm.s,OS_CFG.h, 再这里合理的设计这些contex的结构是很关键的一步。


需要修改的函数主要有一个C函数OSTaskStkInit();和4个汇编函数,OSStartHighRdy();OSCtxSw();OSIntCtxSw();OSTickISR();


一、误入歧途


这是我开始设计的contex结构:


OSTCB->SP——> |_________


|SREG


|R0


|R1


………….


|R27


|R30


|___R31____


根据这样的结构,相应的移植程序如下:


OS_STK*OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)


{


INT8U *stk;


INT8U i;


INT16U temp;


opt= opt;/* 'opt' is not used, prevent warning*/


stk= (INT8U *)ptos;/* Load stack pointer*/


temp=(unsigned int)task;


temp=*(const unsigned int*)temp; //////////////////////////////


*stk-- =(unsigned char)(temp&0xff);//save PC low


*stk--=(unsigned char)(temp>>8);//Pc high


for(i=0;i<12;i++)//32 General PurposeRegisters


{//Form R31R30,R27 to R18


*stk-- = 0;


}


*stk-- =(unsigned char)((unsigned int )pdata>>8);//pdata saves in R6,R17


*stk-- =(unsigned char)((unsigned int)pdata&0xff);


for(i=0;i<16;i++)


{


*stk--=0;//From R15 to R0


}


*stk-- = 0x80;//SREG initial value,Interrupt opened.


return ((OS_STK *)stk);


}


#define PUSH_R() \


asm("PUSH R31");\


asm("PUSH R30");\


asm("PUSH R27");\


asm("PUSH R26");\


asm("PUSH R25");\


asm("PUSH R24");\


asm("PUSH R23");\


asm("PUSH R22");\


asm("PUSH R21");\


asm("PUSH R20");\


asm("PUSH R19");\


asm("PUSH R18");\


asm("PUSH R17");\


asm("PUSH R16");\


asm("PUSH R15");\


asm("PUSH R14");\


asm("PUSH R13");\


asm("PUSH R12");\


asm("PUSH R11");\


asm("PUSH R10");\


asm("PUSH R9");\


asm("PUSH R8");\


asm("PUSH R7");\


asm("PUSH R6");\


asm("PUSH R5");\


asm("PUSH R4");\


asm("PUSH R3");\


asm("PUSH R2");\


asm("PUSH R1");\


asm("PUSH R0")


#define POP_R() \


asm("POP R0");\


asm("POP R1");\


asm("POP R2");\


asm("POP R3");\


asm("POP R4");\


asm("POP R5");\


asm("POP R6");\


asm("POP R7");\


asm("POP R8");\


asm("POP R9");\


asm("POP R10");\


asm("POP R11");\


asm("POP R12");\


asm("POP R13");\


asm("POP R14");\


asm("POP R15");\


asm("POP R16");\


asm("POP R17");\


asm("POP R18");\


asm("POP R19");\


asm("POP R20");\


asm("POP R21");\


asm("POP R22");\


asm("POP R23");\


asm("POP R24");\


asm("POP R25");\


asm("POP R26");\


asm("POP R27");\


asm("POP R30");\


asm("POP R31")


void OSStartHighRdy(void)


{


unsigned char *stk_temp,*pdest;//The Middle SP Variable


unsigned char s0,s1;


OSTaskSwHook();


OSRunning=1;//set flag


stk_temp=OSTCBHighRdy->OSTCBStkPtr;


s0=(unsigned char)((unsigned int)stk_temp&0xff);//sp low 8bit


s1=(unsigned char)((unsigned int)stk_temp>>8);//sp high 8bit


asm(


"OUT 0x3d, %s0\n"


"OUT 0x3e, %s1");


asm("POP R0\n"//Restore SREG


"OUT 0x3f, R0");


POP_R();//Restore R0--R31


}


/***************************************************/


voidOSCtxSw(void)


{


unsigned char i,j;


unsigned char *psrc,*next_sp;


unsigned int temp;


PUSH_R();//save R31--R0


asm("IN R0, 0x3f\n"


"PUSH R0");//save SREG


asm("IN %i, 0x3d\n"


"IN %j, 0x3e");


temp=(j<<8)+i;


OSTCBCur->OSTCBStkPtr=(unsigned char*)temp; //SAVEcurrent task spin TCBcur


OSTaskSwHook();


OSTCBCur=OSTCBHighRdy;


OSPrioCur=OSPrioHighRdy;


next_sp=OSTCBHighRdy->OSTCBStkPtr;


i=(unsigned char)((unsigned int)next_sp&0xff);


j=(unsigned char)((unsigned int)next_sp>>8);


asm("OUT 0x3d, %i\n"//Get sp


"OUT 0x3e,%j" );


asm("POP%i \n"/////////Restore SREG


"OUT 0x3f,%i" );


POP_R();///////////Restore R


}


/*******************************************************************/


voidOSIntCtxSw(void)


{


unsigned char i,j;


unsigned char*next_sp;


OSTaskSwHook();


OSTCBCur=OSTCBHighRdy;


OSPrioCur=OSPrioHighRdy;


next_sp=OSTCBHighRdy->OSTCBStkPtr;


i=(unsigned char)((unsigned int)next_sp&0xff);


j=(unsigned char)((unsigned int)next_sp>>8);


asm("OUT 0x3d, %i\n"//Get sp


"OUT 0x3e,%j" );


asm("POP%i \n"/////////Restore SREG


"OUT 0x3f,%i" );


POP_R();///////////Restore R


}


/*************************************************/


#pragma interrupt_handlerOSTickISR:TIMER_INT_VECTOR


#pragma ctask OSTickISR


voidOSTickISR(void)


{


unsigned char i,j;


unsigned int sptemp;


PUSH_R();//save R31--R0


TCNT0 = 0xb2; //reload counter value


asm("IN R0, 0x3f\n"


"PUSH R0");//save SREG


///i=j=0;This is used ONLY TO AVOID THE WARNING.


OSIntNesting++;


asm("IN %i,0x3d\n"//Get SP Value


"IN %j,0x3e");


sptemp=(j<<8)+i;


if(OSIntNesting==1)


{


OSTCBCur->OSTCBStkPtr=(unsigned char*)sptemp;


}


OSTimeTick();


OSIntExit();


asm("POP %i\n"//Restore SREG


"OUT 0x3f, %i");


POP_R();//Restore R0--R31


}


我写了个测试程序:其中自己建立了两个任务,LED()用于控制数码管的亮与暗,key_scan()用与判断是否按下了PA0,为了调试程序的方便,没有用OS提供的通讯机制,直接用了一个全局变量 key_press;再任务key_scan里,当检测到按键按下时设置key_press=1,否则设置为0;而任务LED()则根据key_press的值控制LED的亮与暗。


源程序如下:


#include "includes.h"


#define LED_ON() PORTB|=0x1


#define LED_OFF() PORTB&=0xfe


#define key_scan_StkSize 50


#define LED_StkSize 50


OS_STK LEDStk[LED_StkSize];


OS_STK key_scanStk[key_scan_StkSize];


unsigned char key_press;


void LED(void*pdata);


void key_scan(void*pdata);


void main(void)


{


OSInit();


OSTaskCreate(LED,(void*)0,&LEDStk[LED_StkSize-1],0);


OSTaskCreate(key_scan,(void*)0,&key_scanStk[key_scan_StkSize-1],8);


OSStart();


}


void LED(void*pdata)


{


pdata=pdata;


//OSStatInit();


while(1)


{


if(key_press)


LED_ON();


else


LED_OFF();


OSTimeDly(2);


}


}


void key_scan(void*pdata)


{


while(1)


{


if(PINA&0x1)


key_press=1;


else


key_press=0;


OSTimeDly(1);


}


}


好,开始把程序下载到双龙SL-DIY02-1开发板上运行,嘿嘿,It works!又是陶醉中…※%……%¥#¥×)(×※……¥#¥◎哎呀,完了!死机了!!不到十分钟就死了!按下按键LED死活不动!


再Studio上模拟跟踪反汇编后发现,#pragma ctask 对中断程序不起作用!我们来看看这会引起什么也的后果:


当执行定时器中断的时候,编译器会保存一些寄存器的值,如果再ISR中没有任务切换,那么中断执行完会弹出堆栈,没有什么问题,但是如果再ISR中发生了任务切换,那么这些被保存的寄存器就得不到恢复!使得这个堆栈变的越来越大!最后系统崩溃!!!


这样看来,没办法,这个OSTickISR必须用汇编写了。改后的汇编如下:


TCNT = $32


.macroPUSH_ALL


push R31


push R30


push R27


push R26


push R25


push R24


push R23


push R22


push R21


push R20


push R19


push R18


push R17


push R16


push R15


push R14


push R13


push R12


push R11


push R10


push R9


push R8


push R7


push R6


push R5


push R4


push R3


push R2


push R1


push R0


inR0,0x3f //Save SREG


push R0


.endmacro


.macro POP_ALL


popR0


out0x3f,R0//Restore SREG


popR0


popR1


popR2


popR3


popR4


popR5


popR6


popR7


popR8


popR9


popR10


popR11


popR12


popR13


popR14


popR15


popR16


popR17


popR18


popR19


popR20


popR21


popR22


popR23


popR24


popR25


popR26


popR27


popR30


popR31


.endmacro


_OSTickISR::


PUSH_ALL


ldiR19,0xf1;0xb2


outTCNT,R19


ldsR16, _OSIntNesting //OSIntNesting++;


incR16


sts_OSIntNesting, R16


cpiR16,1


brneNO_SAVE_SP


inR16,0x3d//save sp to current tcb


inR17,0x3e


sts_OSTCBCur, R16


sts(_OSTCBCur+1),R17


NO_SAVE_SP:


rcall _OSTimeTick


rcall _OSIntExit


POP_ALL


reti


重新编译运行。嘿嘿,这次好了,一天也没有死机!本以为这下子可以轻松一下,享受一下OS的快乐了,可没想到。。。。


二、错误纠正


哎呀,光是一个按键,一个LED亮暗多没意思啊,于是乎我做了个稍微有意思一点的测试程序。


程序利用OS的内建任务OSTaskStat统计CPU的利用率,再用6个数码管显示出来,格式为USE ××(以前做好的LED模块是6个LED)。


源程序如下:


#include "includes.h"


#define LED_ON() PORTB|=0x1


#define LED_OFF() PORTB&=0xfe


#define LED_DAT PORTA


#define CS_LED PORTB


#define key_scan_StkSize 70


#define LED_StkSize 90


OS_STK LEDStk[LED_StkSize];


//OS_STK key_scanStk[key_scan_StkSize];


//OS_STK key_scanStk_1[40];


unsigned char buff[6];


unsigned char key_press;


const unsigned char bcd[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//


void delay_us(unsigned int i);


void LED(void*pdata);


void key_scan(void*pdata);


//void key_scan_1(void*pdata);


void main(void)


{


OSInit();


OSTaskCreate(LED,(void*)0,&LEDStk[LED_StkSize-1],0);


//OSTaskCreate(key_scan,(void*)0,&key_scanStk[key_scan_StkSize-1],8);


OSStart();


}


void LED(void*pdata)


{


unsigned char i;


pdata=pdata;


buff[0]=0b00111111;//U


buff[1]=0b1101100;//S


buff[2]=0b1111000;//E


buff[3]=1;//Black


CS_LED=0x1;


OSStatInit();


//OSTaskCreate(key_scan_1,(void*)0,&key_scanStk_1[39],9);


while(1)


{


for(i=0;i<6;i++)


{


LED_DAT=buff;


OSTimeDly(1);


CS_LED<<=1;


if(i==5)


CS_LED=0x1;


}


}


}


在OSTaskStatHook函数里修改显示缓存BUFF的值:


extern unsigned char buff[];


extern const unsigned char bcd[];


#if OS_CPU_HOOKS_EN > 0


voidOSTaskStatHook (void)


{


buff[4]=(~bcd[OSCPUUsage/10])^1;


buff[5]=(~bcd[OSCPUUsage%10])^1;


}


#endif


(取反是因为字模和我的LED相反,异或是因为数码管的第0位驱动坏了,显示值相反,呵呵)


编译完后下载程序(我高喊:同学们,激动人心的时刻到啦~~~!),100%完成,哇,有了,显示USE 04(我狂喜)。。。。。


恩?怎么回事,LED偶尔会闪烁,闪烁瞬间显示的04左移了两位!哎呀郁闷ING(这时候电脑说话了——节哀顺便吧.晕!)~~~!


没办法,调试吧!因为数字会突然的瞬间左移,这说明I值和对应的buff不相应,很可能是I的值被改变了,


LED_DAT=buff;


OSTimeDly(1);


只可能是在调用OSTimeDly()的过程中变的,于是单步执行这个函数,果然!有时候执行后I的值会变,而且超出了6!!??


还是小米加步枪好啊,关键的时候还是要靠老古董——汇编。跟踪调用OSTimeDly以后的反汇编,噢~~~~~~~~~~!恍然大悟!


重大错误:在函数第一次调用OSTimeDly时,R值由编译器保存到软堆栈,该堆栈是以Y为指针的,由于在移植时用了sp作为任务堆栈指针,而Y始终为软件堆栈即全局堆栈,所以在压进去的R值可能被其他任务修改!!!换句话说,用这种方法(SP为任务指针)保存的任务变量不在自己的任务模块中!!


解决办法:用Y作为任务堆栈指针,SP仅仅作为函数返回地址指针,即硬件指针!


新的contex结构:


OSTCB-->Y——> |_________


|SP


|SREG


|R31


|R30


|R27


………….


|___R0____


更改后的移植程序如下(由前面的移植可以看到,由于后来改了一个函数OSTickISR为汇编,宏定义显得很罗嗦,所以干脆我把OSCtxSw,OSIntCtxSw,OSStartHighRdy和OSIntExit函数都用汇编写):


TCNT = $32


.macroPUSH_ALL


ST-Y,R0


ST-Y,R1


ST-Y,R2


ST-Y,R3


ST-Y,R4


ST-Y,R5


ST-Y,R6


ST-Y,R7


ST-Y,R8


ST-Y,R9


ST-Y,R10


ST-Y,R11


ST-Y,R12


ST-Y,R13


ST-Y,R14


ST-Y,R15


ST-Y,R16


ST-Y,R17


ST-Y,R18


ST-Y,R19


ST-Y,R20


ST-Y,R21


ST-Y,R22


ST-Y,R23


ST-Y,R24


ST-Y,R25


ST-Y,R26


ST-Y,R27


ST-Y,R30


ST-Y,R31


.endmacro


.macro POP_ALL


LDR31,Y+


LDR30,Y+


LDR27,Y+


LDR26,Y+


LDR25,Y+


LDR24,Y+


LDR23,Y+


LDR22,Y+


LDR21,Y+


LDR20,Y+


LDR19,Y+


LDR18,Y+


LDR17,Y+


LDR16,Y+


LDR15,Y+


LDR14,Y+


LDR13,Y+


LDR12,Y+


LDR11,Y+


LDR10,Y+


LDR9,Y+


LDR8,Y+


LDR7,Y+


LDR6,Y+


LDR5,Y+


LDR4,Y+


LDR3,Y+


LDR2,Y+


LDR1,Y+


LDR0,Y+


.endmacro


.macro PUSH_SP


INR16,0x3d; SPL//Save SPL then SPH


ST-Y,R16


INR16,0x3e; SPH


ST-Y,R16


.endmacro


.macro PUSH_SREG


INR16,0x3f;SREG//Save SREG


ST-Y,R16


.endmacro


.macro POP_SP


LDR16,Y+


OUT0x3e,R16


LDR16,Y+


OUT0x3d,R16


.endmacro


.macro POP_SREG


LDR16,Y+


OUT0x3f,R16


.endmacro


;******************************************************


_OSStartHighRdy::


rcall_OSTaskSwHook//OSTaskSwHook();


ldsr16,_OSRunning//OSRunning=1;


ldir16,1


sts_OSRunning,R16


ldsr30,_OSTCBHighRdy //Restore Y,the software SP


ldsr31,_OSTCBHighRdy+1


ldR28,Z+


ldr29,Z


POP_SP


POP_SREG


POP_ALL


ret


;**************************************************************


_OSCtxSw::


PUSH_ALL


PUSH_SREG


PUSH_SP


ldsr30,_OSTCBCur //Save software sp in the current TCB


ldsr31,_OSTCBCur+1


stZ+,r28


stZ,r29


_OSIntCtxSw::


rcall_OSTaskSwHook//


ldsr30,_OSTCBHighRdy //OSTCBCur=OSTCBHighRdy


ldsr31,_OSTCBHighRdy+1


sts_OSTCBCur,r30


sts_OSTCBCur+1, r31


ldsr16,_OSPrioHighRdy//OSPrioCur=OSPrioHighRdy


sts_OSPrioCur,r16


ldr28,Z+//restore the software sp_Y to be used


ldr29,Z


POP_SP


POP_SREG


POP_ALL


ret


;********************************************************


_OSTickISR::


PUSH_ALL


PUSH_SREG


PUSH_SP


ldiR19,0xf1;0xb2


outTCNT,R19


ldsR16, _OSIntNesting //OSIntNesting++;


incR16


sts_OSIntNesting, R16


cpiR16,1


brneNO_SAVE_Y


ldsr30,_OSTCBCur


ldsr31,_OSTCBCur+1


stZ+,r28


stZ,r29


NO_SAVE_Y:


rcall _OSTimeTick


rcall _OSIntExit


POP_SP


POP_SREG


POP_ALL


reti


编译、下载、运行~!哈哈,孔子笑了…………………………


三、结束语


ucos的移植相对还是比较简单的,但由于第一次做这样的移植,难免会走一些弯路(尤其是忽略了编译器的一些编译机制),我想一定也有不少朋友遇到和我类似的问题。我把移植全过程及“勘误表”都列出来,希望能对大家有所帮助,少走些弯路。


同时一个良好的系统需要经过严格的测试,我移植后的OS只做了一些简单的测试,所以可能有一些BUG,我也希望大家能对把使用过程中发现的问题提出来一起讨论, 多交流、指教!


音乐乐乐

PARTNER CONTENT

文章评论1条评论)

登录后参与讨论

用户1579481 2008-12-16 21:41

顶一下 留待参考
相关推荐阅读
用户1579481 2013-02-01 08:45
评论:@专注的力量 博客中提到的“[博客大赛]我在搞嵌入式 我有罪”
有些领悟...
用户1579481 2011-04-11 20:51
For DSP无源晶体和有源晶振---->明确一下总是好的!
无源晶体与有源晶振的区别、应用范围及用法:1、无源晶体——       无源晶体需要用DSP片内的振荡器,在datasheet上有建议的连接方法。无源晶体没有电压的问题,信号电平是可变的,也就是说是根...
用户1579481 2009-12-12 20:49
相变存储器PCM简介及部分论文
闲来无事,参加一下活动,了解一点知识 http://baike.baidu.com/view/955777.htm?fr=ala0   相变存储器(phase change memory),简称PCM...
用户1579481 2009-09-03 22:06
ARM,AVR,MSP430,Coldfire,DSP,FPGA,which is your cho
ARM vs AVR 1我以为这样比没有意义,做嵌入式系统最大特征是“嵌入”二字,也就是说你的控制系统是嵌入于你的控制对象之中,所以首先是服从于对象的需求和特征,脱离对象空论谁好谁坏有何依据?每个MC...
用户1579481 2009-08-30 08:43
[转]PCB敷铜的利与弊
敷铜作为PCB设计的一个重要环节,不管是国产的青越锋PCB设计软件,还国外的一些Protel,PowerPCB都提供了智能敷铜功能,那么怎样才能敷好铜,我将自己一些想法与大家一起分享,希望能给同行带来...
用户1579481 2009-08-23 09:47
【转】32个最热CPLD-FPGA论坛
原文来自:http://blog.ednchina.com/workman/197389/Message.aspx都沉默了 我来顶一下【转】32个最热CPLD-FPGA论坛32个最热CPLD-FPGA...
EE直播间
更多
我要评论
1
10
关闭 站长推荐上一条 /3 下一条