- C函数调用前后的堆栈变化 。(看看编译后的汇编就知道了)
- 中断中堆栈的变化,特别是C编写中断程序时的情况。(看看I$$SAVE 和 I$$REST )
- 一个刚开始运行,但是还没有进入的C函数堆栈情况。(编一个用汇编调用C的例子看看)
其实来,主要的难点时对OS实现原理的理解。比如什么时候发生调度,调度的时候需要那些工作。这一部分主要在OSCtxSw()实现的软件中断,和定时器中断中调用的OSIntExit()->OSIntCtxSw()的过程中发生。而OSIntCtxSw和OSCtxSw的不同在DSP上只有栈顶位置的不同。所以,问题都归咎到了堆栈的处理上了。没有办法,只有汇编重要部分的代码,看看到底压入了什么内容,计算好距离即可。
这里先说一说DSP初始化堆栈的情况,也就是最前面说的第三种堆栈情况-当中断发生在写入了参数也调用了CALL,但是还没有执行这个C函数的那一刻,堆栈到底有什么内容。这就是函数OSTaskStkInit需要处理的内容了。
// -- 模拟C函数运行时堆栈的情况 --这里完成第三种堆栈情况的仿真工作,此时stk就是这个任务需要的AR1(SP)数值。(这里面的c_int0是调试种为了防止堆栈错误写的C语言入口,便于在出错的时候系统重新初始化。)
*stk++ = (UWORD)pdata; // 入口参数
*stk++ = (UWORD)c_int0; // 旧PC
// -- CPU寄存器内容备份 --
*stk++ = (UWORD)0x2000; // ST1
*stk++ = (UWORD)0x2200; // ST0
*stk++ = (UWORD)0x0000; // 累加器ACC高16位
*stk++ = (UWORD)0x0000; // 累加器ACC低16位
*stk++ = (UWORD)0x0000; // 乘法寄存器高16位
*stk++ = (UWORD)0x0000; // 乘法寄存器低16位
*stk++ = (UWORD)0x0000; // 临时寄存器TREG
*stk++ = (UWORD)ptos + 4; // AR0
*stk++ = (UWORD)0x0000; // AR2
*stk++ = (UWORD)0x0000; // AR3
*stk++ = (UWORD)0x0000; // AR4
*stk++ = (UWORD)0x0000; // AR5
*stk++ = (UWORD)0x0000; // AR6
*stk++ = (UWORD)0x0000; // AR7
// -- 硬件堆栈备份--
*stk++ = (UWORD)task; // 中断返回地址 = 任务其始地址
*stk++ = (UWORD)c_int0;
*stk++ = (UWORD)c_int0;
*stk++ = (UWORD)c_int0;
*stk++ = (UWORD)c_int0;
*stk++ = (UWORD)c_int0;
*stk++ = (UWORD)c_int0;
剩下的任务就非常简单了,只需要看着那本RMB$79的书,完全按照要求编写OSCtxSw函数即可。值得注意的就是保存和返回任务我们需要使用“CALL I$$SAVE”和“B I$$REST”来实现。而OSIntCtxSw的实现则更加简单,只需要参照你的定时器中断函数调用时栈顶到I$$SAVE调用后栈顶之间的距离,用AR1减去这个值,然后跳入到OSCtxSw在“CALL I$$SAVE”之后的内容即可。
接下来的事情就是编写你的C语言主函数和任务等等东西了。非常的简单,不过定时器的周期需要按照OS_TICKS_PER_SEC的设定设置好。然后顺序调用
OSTimeTick(); // uC/OS的系统定时这三个函数即可。
OSIntEnter();
OSIntExit(); // OSIntCtxSw包含enable()调用
值得注意的地方是:OSIntExit()将直接从中断处理中通过I$$REST进入到优先级最高、准备好的任务中,所以之后的代码不会被执行。
其他的就和x86平台上没有什么区别了。不过这里你只能通过一些二极管什么的显示一下活动的任务,比较惨吧!
没有评论:
发表评论