**************************************************************
_OSCtxSw::
PUSH_ALL
PUSH_SREG
PUSH_SP
lds r30, _OSTCBCur //Save software sp in the current TCB
lds r31, _OSTCBCur+1
st Z+, r28
st Z, r29
_OSIntCtxSw::
rcall _OSTaskSwHook//
lds r30, _OSTCBHighRdy //OSTCBCur=OSTCBHighRdy
lds r31, _OSTCBHighRdy+1
sts _OSTCBCur, r30
sts _OSTCBCur+1, r31
lds r16, _OSPrioHighRdy//OSPrioCur=OSPrioHighRdy
sts _OSPrioCur, r16
ld r28, Z+//restore the software sp_Y to be used
ld r29, Z
POP_SP
POP_SREG
POP_ALL
ret
********************************************************
_OSTickISR::
PUSH_ALL
PUSH_SREG
PUSH_SP
ldi R19, 0xf1;0xb2
out TCNT, R19
lds R16, _OSIntNesting //OSIntNesting++;
inc R16
sts _OSIntNesting, R16
cpi R16, 1
brne NO_SAVE_Y
lds r30, _OSTCBCur
lds r31, _OSTCBCur+1
st Z+, r28
st Z, r29
NO_SAVE_Y:
rcall _OSTimeTick
rcall _OSIntExit
POP_SP
POP_SREG
POP_ALL
reti
编译、下载、运行~!哈哈,孔子笑了…………………………
三、 结束语
ucos的移植相对还是比较简单的,但由于第一次做这样的移植,难免会走一些弯路(尤其是忽略了编译器的一些编译机制),我想一定也有不少朋友遇到和我类似的问题。我把移植全过程及“勘误表”都列出来,希望能对大家有所帮助,少走些弯路。
同时一个良好的系统需要经过严格的测试,我移植后的OS只做了一些简单的测试,所以可能有一些BUG,我也希望大家能对把使用过程中发现的问题提出来一起讨论, 多交流、指教!
音乐乐乐
时钟中断程序中
_OSTickISR::
PUSH_ALL
PUSH_SREG
PUSH_SP
............
错误分析:
由于在进中断时avr清了SREG中的I位,所以PUSH_SREG保存的SREG是错误的
改正方法:
.macro PUSH_SREG_INT
IN R16, 0x3f
ORI R16, 0x80 ;恢复SREG中的I位再保存!
ST -Y, R16
.endmacro
_OSTickISR::
PUSH_ALL
PUSH_SREG_INT
PUSH_SP
........
修改部分很简单,之所以把它定义成宏是考虑到以后写其他的中断服务程序会用到。
编译完后下载程序(我高喊:同学们,激动人心的时刻到啦~~~!),100%完成,哇,有了,显示USE 04(我狂喜)。。。。。
恩?怎么回事,LED偶尔会闪烁,闪烁瞬间显示的04左移了两位!哎呀郁闷ING(这时候电脑说话了——节哀顺便吧.晕!)~~~!
没办法,调试吧!因为数字会突然的瞬间左移,这说明I值和对应的buff不相应,很可能是I的值被改变了,
LED_DAT=buff[i];
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
.macro PUSH_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
LD R31,Y+
LD R30,Y+
LD R27,Y+
LD R26,Y+
LD R25,Y+
LD R24,Y+
LD R23,Y+
LD R22,Y+
LD R21,Y+
LD R20,Y+
LD R19,Y+
LD R18,Y+
LD R17,Y+
LD R16,Y+
LD R15,Y+
LD R14,Y+
LD R13,Y+
LD R12,Y+
LD R11,Y+
LD R10,Y+
LD R9,Y+
LD R8,Y+
LD R7,Y+
LD R6,Y+
LD R5,Y+
LD R4,Y+
LD R3,Y+
LD R2,Y+
LD R1,Y+
LD R0,Y+
.endmacro
.macro PUSH_SP
IN R16, 0x3d; SPL//Save SPL then SPH
ST -Y, R16
IN R16, 0x3e; SPH
ST -Y, R16
.endmacro
.macro PUSH_SREG
IN R16, 0x3f;SREG//Save SREG
ST -Y, R16
.endmacro
.macro POP_SP
LD R16, Y+
OUT 0x3e, R16
LD R16, Y+
OUT 0x3d, R16
.endmacro
.macro POP_SREG
LD R16, Y+
OUT 0x3f, R16
.endmacro
******************************************************
_OSStartHighRdy::
rcall _OSTaskSwHook //OSTaskSwHook();
lds r16, _OSRunning //OSRunning=1;
ldi r16, 1
sts _OSRunning, R16
lds r30, _OSTCBHighRdy //Restore Y,the software SP
lds r31, _OSTCBHighRdy+1
ld R28, Z+
ld r29, Z
POP_SP
POP_SREG
POP_ALL
ret
二、 错误纠正
哎呀,光是一个按键,一个LED亮暗多没意思啊,于是乎我做了个稍微有意思一点的测试程序。
程序利用OS的内建任务OSTaskStat统计CPU的利用率,再用6个数码管显示出来,格式为USE ××(以前做好的LED模块是6个LED)。
源程序如下:
#i nclude "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[i];
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
void OSTaskStatHook (void)
{
buff[4]=(~bcd[OSCPUUsage/10])^1;
buff[5]=(~bcd[OSCPUUsage%10])^1;
}
#endif
(取反是因为字模和我的LED相反,异或是因为数码管的第0位驱动坏了,显示值相反,呵呵)