先说结论:
1、 C51中mian()并不是被调用的,而是跳转进去的;
2、 在main()中调用return语句,将会从栈中弹出SP大小的数据到SP中,进而程序会运行到弹出数据指向的地址,C51中可能就是SP指针返回0x0000。
昨天和我合作一个工程的同事在调试的时候,发现单片机程序会经常出现重启的现象,当时我认为可能是数组溢出、指针越界导致的程序跑飞。
后来同事将有问题的语句定位到某个分支中的“return 0;”语句上,我一看这居然是在main()函数上使用了“return 0;”,认为程序会在调用这个语句后,
PC指针离开main(),继续执行main()函数后面的代码导致程序跑飞(同事估计是受到VC等PC编译环境的影响,汗~)。
不过,回到坐位后却想起return对应的指令,会负责将栈中的值取出来赋值给PC指针,这样的话执行“return 0;”后应该返回到调用main()函数的地方。
既然有疑问就动手吧,首先向同事拿到了出问题的工程,在main()开始的地方设置了两个“return 0;”(分别是第25行和32行)。
然后进入仿真模式,如下图,可以观察到sp指针指向起始值0x07,PC指针指向0x0000,PC指针的长度为2。
图8.jpg (34.86 KB, 下载次数: 0)
下载附件
2013-12-4 09:44 上传
图8
在9.0版本上,定义成int main(),并且没有while(1)的话就报错:MAIN.C(28): warning C290: missing return value
定义成void main(),并且没有while(1)的话就不会报错。
在实验的时候发现,两者的代码没有什么不同。
两者都会因为没有while(1) 在执行到main()最后的时候,让PC指针返回0x0000
(注:如果mian()最后是一个函数,那么这个函数就不是被调用的,而是使用LJMP跳进去的,直到遇到函数最后的RET指令)
我就说之前怎么会有网友问我为什么不加while(1)也能做流水灯,当时我以为是PC指针越过main()后,执行了后面的未知汇编代码偶尔导致
的单片机重启,现在看来C51本来就是会让它重启的~~
但这只能说明当前的调试环境的情况,而且软件仿真的时候 相同的代码 偶尔也有没跑回0x0000,到处乱飞的情况,不知道是不是仿真的BUG
什么不懂捏~科普下CALL和RET指令的作用
在C51中,PC指针代表了单片机当前运行的位置,而SP指针对应的就是栈的指针。
调用函数对应的指令为LCALL(还有SCALL之类的),这个指令会自动将当前PC指针+N的和压入到栈中,N的值就是LCAL之下的一条指令的地址
如主贴中图7所示,图7中Register窗口中PC的值为C:0x02EF,C说明这是CODE区的地址。刚好指向了LCALL MCU_Init(),而下一个指令的
地址为0x02F2,当执行LCALL MCU_Init()指令后,单片机自动将0x02F2这个值压入到栈中,同时SP指针+2,变成了0x2A
再接下来PC指针就被MCU_Init()这个常量赋值了,程序运行到了MCU_Init()这个函数里面。
return语句对应的是ret指令,它的作用是从栈中弹出两个字节的数据到PC指针上,也就是让程序运行到C:0x02F2上,这就是程序返回。
加个变量就可以了啊
if(condition_true)
{
//执行代码
conditon_true = false;
}
一周热门 更多>