|
![](static/image/common/ico_lz.png)
楼主 |
发表于 2010-12-10 09:36:59
|
显示全部楼层
第一个问题已经找到了答案并在keil下测试了!
1、51单片机调用函数的时候,比如A调用B,那么在进行保护现场也就是将A用到的CPU寄存器和临时变量压入堆栈的过程是怎样的?先后顺序?用汇编和用C有什么不同?A的PC地址是何时压入堆栈的
以下面两个函数为例:
void A(a,b,c)
{
INT8U a=2,b=3,c=4,d=5;
B(a,b,c);
}
void B(a,b,c)
{
a=b+c;
}
A调用B时,入栈情况是这样的:将三个参数a,b,c用通用寄存器保存下来,而局部变量d移入到堆栈空间,如果参数大于3个的话就利用堆栈空间来传递参数。但是这个过程中,是没有操作堆栈指针的! 在调用B之前,硬件是自动将A调用B的下一条PC地址压入了堆栈,顺序是先SP+1(到这里,第二个问题的为什么那个地址要减一已经很清楚了),压地址低字节,SP+1,再地址压高字节。这个过程,即使反汇编,用户是看不见的!
在调用B完后,需要返回A,执行RET指令的时候,硬件又自动从SP指向的栈顶位置弹出那两字节的PC,顺序是:弹出高8位地址,SP-1,再弹出低8位地址,SP-1,到这时候,栈顶栈底重合!指向OSStkStart。一般,keil编译器是把SP堆栈指针放在定义的变量之后的。
但是中断与子程序调用就不同,它除了压PC的时候需要操作SP指针,其他CPU寄存器也要操作SP指针压入堆栈!
因此,可以编写以下简单代码,执行任务切换(已测试):
#include"STC89C51RC_RD_PLUS.H"
#include "serial.h"
#define OFFSETOF(TYPE,MEMBER) (unsigned char)(&((TYPE*)0)->MEMBER) //或得结构体成员的偏移量
sbit Led1=P2^0;
sbit Led2=P2^1;
sbit Led3=P2^2;
sbit Led4=P2^3;
sbit Led5=P2^4;
sbit Led6=P2^5;
sbit Led7=P2^6;
sbit Led8=P2^7;
typedef struct
{
unsigned char Prio;
unsigned int DelayTime;
unsigned char TaskStat;
}OS_TCB;
OS_TCB TaskTcb;
unsigned char Task1Stk[16];
void Task1(void *Msg );
void Task2(void *Msg );
void TaskCreat(void (*Task)(void *Pdata),unsigned char *PtrTask);
void TaskDelay(unsigned int n );
/***************************************
主函数
***************************************/
int main(void)
{
unsigned char Offset=0;
UartInit();
SendStr("OS_Test:Usart Is Ok!\r\n",22);
TaskCreat(Task1,&Task1Stk[0]); //创建一个任务并执行
return 0;
}
/***************************************
创建任务并运行
***************************************/
void TaskCreat(void (*Task)(void *Pdata),unsigned char *PtrTask)
{
SP=&PtrTask[1]; //将堆栈指针指向人工堆栈栈顶
*PtrTask++=(unsigned int)Task; //将任务地址压入人工堆栈
*PtrTask++=(unsigned int)Task>>8;
}
/***************************************
任务延时
***************************************/
void TaskDelay(unsigned int n )
{
unsigned int i=0;
while(n--)
{
for(i=250;i>0;i--);
}
}
/***************************************
任务1
***************************************/
void Task1(void *Msg )
{
void *Message;
unsigned char i;
Message=Msg;
while(1)
{
if(i++>25)
{
i=0;
TaskCreat(Task2,&Task1Stk[0]);
}
Led1 = ~Led1;
TaskDelay(50);
}
}
/***************************************
任务2
***************************************/
void Task2(void *Msg )
{
void *Message;
unsigned char i;
Message=Msg;
while(1)
{
if(i++>15)
{
i=0;
TaskCreat(Task1,&Task1Stk[0]);
}
Led8 = ~Led8;
TaskDelay(50);
}
}
![](http://cache.amobbs.com/bbs_upload782111/files_35/ourdev_603687FA5FXG.jpg)
CX51关于函数调用的说明 (原文件名:q.jpg)
欢迎大家指正! |
|