学习UCOS II遇到的一些问题,希望得到大家的帮助和指点
最近在学习UCOS ii,平台是STC51+KEIL,网上流传的杨屹的移植版本。问题如下:
1、51单片机调用函数的时候,比如A调用B,那么在进行保护现场也就是将A用到的CPU寄存器和临时变量压入堆栈的过程是怎样的?先后顺序?用汇编和用C有什么不同?A的PC地址是何时压入堆栈的?
2、在OS_CPU_A.ASM文件中有如下一段代码:
?STACKSEGMENT IDATA
RSEG ?STACK
OSStack:
DS 40H
OSStkStart IDATA OSStack-1
这一段是表示堆栈需要重新定位,其预留空间是64字节?OSStack-1为什么这里要减一?编译器是怎么知道用户这里定义是给堆栈的?
3、
;OSTCBCur ===> DPTR获得当前TCB指针,详见C51.PDF第178页
MOVR0,#LOW (OSTCBCur) ;获得OSTCBCur指针低地址,指针占3字节。+0类型+1高8位数据+2低8位数据
INCR0
MOVDPH,@R0 ;全局变量OSTCBCur在IDATA中
INCR0
MOVDPL,@R0
上面的代码说指针是3字节?为什么很多教材或者网上的资料都说 8位机下 指针是2字节?
提的问题有点多,呵呵,希望得到大家的帮助!!先谢谢了! 3、keil C51的指针有可能占用:
1字节:存储限定idata
2字节:存储限定xdata, code
3字节:无存储限定,根据右值而变化指向空间。 回复【1楼】John_Lee
3、keil c51的指针有可能占用:
1字节:存储限定idata
2字节:存储限定xdata, code
3字节:无存储限定,根据右值而变化指向空间。
-----------------------------------------------------------------------
楼上说的无存储限定的是指void *ptr这种吧?
但是 OSTCBCur在定义的时候限定它在idata区,那不应该是1字节吗? 顶上去 回复【2楼】AVR_DIY 苹果的另一半
-----------------------------------------------------------------------
并不是说指针本身存储在哪个区域,而是指指针指向的区域限定。
例如:
OS_TCB* idata OSTCBCur; // 指针占用3字节,可以指向任意区域,本身存储在idata。
OS_TCB idata* OSTCBCur; // 指针占用1字节,限定指向idata区域,本身存储由编译器分配。
OS_TCB idata* idata OSTCBCur; // 指针占用1字节,限定指向idata区域,本身存储在idata。
你给的代码里是第1种情况。 无限定区域的指针就是keil C51手册中说的“generic pointer”,这种指针的操作比限定了区域的指针:如“idata pointer”,“xdata pointer”等,要耗费资源一些,如果在程序中能够事先确定数据的存储区域,建议在指针定义时,使用限定了存储区域的指针。 回复【5楼】John_Lee
无限定区域的指针就是keil c51手册中说的“generic pointer”,这种指针的操作比限定了区域的指针:如“idata pointer”,“xdata pointer”等,要耗费资源一些,如果在程序中能够事先确定数据的存储区域,建议在指针定义时,使用限定了存储区域的指针。
-----------------------------------------------------------------------
OS_EXTidata OS_TCB *OSTCBCur; /* Pointer to currently running TCB */
代码里面是这样定义的,貌似是你说的第二种哦 data unsigned char *ptr; 3个字节
unsigned char data *ptr; 1个字节
奇怪了,为什么上面的两种形式所占空间不一致?我在keil下测试了,关键字data放在前面,占3字节,放在后面占1个字节(这个好理解)。
data unsigned char ptr; 1个字节
unsigned char data ptr; 1个字节
而这样都是一样! 回楼上,是正常的现象。
data unsigned char ptr; 1个字节
unsigned char data ptr; 1个字节
这两条语句是相同意义,即定义一个unsigned char型变量ptr。
data unsigned char *ptr; 3个字节
unsigned char data *ptr; 1个字节
这两语句含义不同,
第一个是定义一个通用指针变量,此指针变量存放于data空间,可指向任意内存空间。
第二个是定义一个指向data空间的指针,此指针变量存放于默认空间,指向data内存空间。
因data空间只需一个字节即可描述内存地址,所以指针占用1字节。
通用指针需要三个字节,第一个字节表示指向那个内存空间,第二和第三字节保存地址。 回复【8楼】health
回楼上,是正常的现象。
data unsigned char ptr; 1个字节
unsigned char data ptr; 1个字节
这两条语句是相同意义,即定义一个unsigned char型变量ptr。
data unsigned char *ptr; 3个字节
unsigned char data *ptr; 1个字节
这两语句含义不同,
第一个是定义一个通用指针变量,此指针变量存放于data空间,可指向任意内存空间。
第二个是定义一个指向data空间的指针,此指针变量存放于默认空间,指向data内存空间。
因data空间只需一个字节即可描述内存地址,所以指针占用1字节。
通用指针需要三个字节,第一个字节表示指向那个内存空间,第二和第三字节保存地址。
-----------------------------------------------------------------------
data unsigned char *ptr; 和这个unsigned char *data ptr; 等效了啊? 对于其他单片机,比如说AVR 通用指针都是2字节吧?
因为他的内存,比如mega系列,都是大于256字节的 ,1个8位地址指不了那么多空间?
不知道是不是这样呢?对AVR的指针、堆栈不是很清楚,望大家赐教! 存储修饰符放在 * 之前和之后是两个不同的意思。
放在 * 之前表示修饰的是指向存储区域,这直接影响指针的宽度。
放在 * 之后表示修饰的是指针本身的存储区域,这不影响指针的宽度,但影响访问指针本身的操作。
像51类的CPU就是麻烦,各存储空间的最优访问方法是不同的,且效率差异较大,如果编译器全部采用统一方法(如“generic pointer”)的访问方法,运行效率肯定糟糕,如果只使用某一种(如“idata pointer”),而放弃其它数据空间,又不现实,折中的方案就是“增加方言”,搞几个存储修饰关键字(“idata, data, code, xdata”)让用户自己定义数据访问方式,但造成的问题,就是与标准C不兼容了。所谓“有利就有弊”。 你们说的都是针对51这类特殊的单片机吧?其他的单片机应该没有这些区分吧? 回复【11楼】John_Lee
存储修饰符放在 * 之前和之后是两个不同的意思。
放在 * 之前表示修饰的是指向存储区域,这直接影响指针的宽度。
放在 * 之后表示修饰的是指针本身的存储区域,这不影响指针的宽度,但影响访问指针本身的操作。
像51类的cpu就是麻烦,各存储空间的最优访问方法是不同的,且效率差异较大,如果编译器全部采用统一方法(如“generic pointer”)的访问方法,运行效率肯定糟糕,如果只使用某一种(如“idata pointer”),而放弃其它数据空间,又不现实,折中的方案就是“增加方言”,搞几个存储修饰关键字(“idata, data, code, xdata”)让用户自己定义数据访问方式,但造成的问题,就是与标准c不兼容了。所谓“有利就有弊”。
-----------------------------------------------------------------------
51 虽然比较麻烦,但是比较经典!呵呵 谢谢你的分析! 对了,你们可以分析分析我提出的前两个问题嘛? 我在网上找了好久都没有搞清楚具体51在调用子函数的时候压栈、出栈顺序,以及压哪些内容? avr也有这个问题。它的存储空间有两个:代码空间和数据空间,访问的指令也是不同的。
编译器方面,iar, icc, cv这几款都作了方言扩充。而gcc则是放弃了代码空间的访问。 回复【15楼】John_Lee
avr也有这个问题。它的存储空间有两个:代码空间和数据空间,访问的指令也是不同的。
编译器方面,iar, icc, cv这几款都作了方言扩充。而gcc则是放弃了代码空间的访问。
-----------------------------------------------------------------------
GCC也可以定义常量在CODE然后读取CODE里面的内容啊
我的意思是说,AVR有没有像51这样指针宽度会随着指向的空间不同而变化,像51指向data、idata就是1字节,指向xdata、code就是两字节,通用指针宽度为3字节... avr的指针总是16bits的宽度,如果超过了64K的空间,则要使用辅助寄存器扩展,但编译器不支持。
gcc可以访问代码空间?那不是在编译器层面实现的,不是编译器本身的功能,例如:你能定义一个整数在代码空间然后直接用右值表达式访问吗?
我们看到的所谓访问,那是在编译器之外,用inline汇编完成的,编译器本身不知道这个汇编指令的实际意义。 回复【14楼】AVR_DIY 苹果的另一半
对了,你们可以分析分析我提出的前两个问题嘛? 我在网上找了好久都没有搞清楚具体51在调用子函数的时候压栈、出栈顺序,以及压哪些内容?
-----------------------------------------------------------------------
没有做过51的ucos,无法回答你,如果你问avr 或者cortex,倒可以解答你的所有问题。 回复【17楼】John_Lee
avr的指针总是16bits的宽度,如果超过了64k的空间,则要使用辅助寄存器扩展,但编译器不支持。
gcc可以访问代码空间?那不是在编译器层面实现的,不是编译器本身的功能,例如:你能定义一个整数在代码空间然后直接用右值表达式访问吗?
我们看到的所谓访问,那是在编译器之外,用inline汇编完成的,编译器本身不知道这个汇编指令的实际意义。
-----------------------------------------------------------------------
赋值当然不行啊 ,keil也一样不能右值,我说的访问是只读访问,ReadOnly!
回复【18楼】John_Lee
回复【14楼】avr_diy 苹果的另一半
对了,你们可以分析分析我提出的前两个问题嘛? 我在网上找了好久都没有搞清楚具体51在调用子函数的时候压栈、出栈顺序,以及压哪些内容?
-----------------------------------------------------------------------
没有做过51的ucos,无法回答你,如果你问avr 或者cortex,倒可以解答你的所有问题。
-----------------------------------------------------------------------
谢谢你!以后移植UCOS到AVR 或者STM32的时候再找你!呵呵
对了你说的cortex是指M0 、M3都熟悉啊? 回复【19楼】AVR_DIY 苹果的另一半
-----------------------------------------------------------------------
右值!!不是左值。
Cortex-M,各型号差别不大。 回复【9楼】AVR_DIY 苹果的另一半
data unsigned char *ptr; 和这个unsigned char *data ptr; 等效了啊?
-----------------------------------------------------------------------
是这样的,按照keil c51手册的叙述,
data char *x; 等效于 char *data x;
推荐使用后者,前者是早期版本C51编译器的写法,目前两种写法都支持,但是将来可能不再支持前者。 回复【21楼】gamethink
很好奇,这个uc版本能否正常运行呢?
-----------------------------------------------------------------------
怎么不能正常运行啊?跑起来了 第一个问题已经找到了答案并在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 intDelayTime;
unsigned char TaskStat;
}OS_TCB;
OS_TCB TaskTcb;
unsigned char Task1Stk;
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); //创建一个任务并执行
return 0;
}
/***************************************
创建任务并运行
***************************************/
void TaskCreat(void (*Task)(void *Pdata),unsigned char *PtrTask)
{
SP=&PtrTask; //将堆栈指针指向人工堆栈栈顶
*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);
}
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);
}
Led8 = ~Led8;
TaskDelay(50);
}
}
http://cache.amobbs.com/bbs_upload782111/files_35/ourdev_603687FA5FXG.jpg
CX51关于函数调用的说明 (原文件名:q.jpg)
欢迎大家指正! c51好像不使用堆栈传递函数参数,
默认情况下,首先使用寄存器传递参数,寄存器不够用时再使用固定存储区。 回复【25楼】health
c51好像不使用堆栈传递函数参数,
默认情况下,首先使用寄存器传递参数,寄存器不够用时再使用固定存储区。
-----------------------------------------------------------------------
固定存储区也属于堆栈空间啊 不能这么说,
固定存储区可以是片内data,片外pdata,xdata。
即使是片内data空间,和堆栈也不是一回事。 回复【27楼】health
不能这么说,
固定存储区可以是片内data,片外pdata,xdata。
即使是片内data空间,和堆栈也不是一回事。
-----------------------------------------------------------------------
定义完所有的变量后,剩余的空间不就是堆栈空间吗?内存不设这么分配的? 没错,是这样。 MOVR0,#LOW (OSTCBCur) ;获得OSTCBCur指针低地址,指针占3字节。+0类型+1高8位数据+2低8位数据
INCR0
MOVDPH,@R0 ;全局变量OSTCBCur在IDATA中
INCR0
MOVDPL,@R0
楼上的技术不错 ,问一下上面的那段代码,C51不是大端模式吗?假如一个三字节的指针,最高字节应该是类型,次高字节是高8地址,低字节是低8位地址,那上面的那段怎么解释?
MOVR0,#LOW (OSTCBCur) 这句是获得低8位地址
INC R0;地址加一岂不是超越了这个指针的位置?? C51为大端模式,即内存低地址保存变量的高位。 回复【31楼】health
c51为大端模式,即内存低地址保存变量的高位。
-----------------------------------------------------------------------
是啊,那上面那个汇编怎么好像不是那样呢? 我明白了,是我自己理解错了!
MOVR0,#LOW (OSTCBCur) 这个命令是获得低地址不是低字节 谢谢! 记号
页:
[1]