"); //-->
这是我两年多前写的一篇文章,与各位共享
MCS-51系列单片机的软件复位方法
在单片机系统的应用中,我们经常需要用到复位技术来实现抗干扰。有的单片机(如8098)有专门的复位指令,某些增强型MCS-51系列单片机虽然没有复位指令,但片内集成了WATCHDOG电路,可以很容易实现复位。而普及型MCS-51系列单片机(如8031和8032)既无复位指令,又不带硬件WATCHDOS,如果不外接硬件WATCHDOG,就必须采用软件复位技术。所谓软件复位就是用一系列指令来模仿复位操作。在MCS-51系列单片机中,只要用指令使程序从起始地址(0x0000)开始执行,就可以复位单片机。本文介绍三种用C语言实现软件复位的简单方法。
方法一:
void Reset(void)
{ unsigned char code rst[ ]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32};
(*((void (*)(void))(rst)))();
}
先来看一下这段程序编译后的汇编码:
C:0x0015 E4 CLR A //清除ACC=0
C:0x0016 C0E0 PUSH ACC(0xE0) //压0到堆栈——8位
C:0x0018 C0E0 PUSH ACC(0xE0) //再压0到堆栈——再8位
C:0x001A 32 RETI // 清除中断激活标志并返回到0x0000执行
C:0x001B 020015 LJMP C:0015
可以发现,数组rst[]中的内容恰恰是上面前四行的汇编机器码,即程序中将代码当作数组的数据来存储。再来研究后面的那句函数调用(*((void (*)(void))(rst)))(),rst是数组名(即数组首元素地址),(void(*)(void))是函数指针的强制类型转换运算,(void(*)(void))(rst)是将数组名rst强制转换成一个无参数无返回值的函数的指针,指向rst的首地址。只需调用(*((void (*)(void))(rst)))(),即可将数组中的数据当作函数代码来运行,因为无论是数据还是代码都是以二进制存储的,本质上是相同的。
方法二:
void Reset(void)
{ ( * ( void (*)( ) )0 ) ( );
}
这段程序摘自《C缺陷与陷阱》,比方法一中的更为简洁。与方法一类似,它也是使用函数指针的强制类型转换运算将函数指针指向一个非函数的地址,但不同的是它直接指向程序起始地址0x0000,方法一先指向数组rst,再利用数组中的机器码使程序跳转到0x0000。它编译后的汇编只有一句LCALL C_STARTUP(C:0000)。
方法三:
void Reset(void)
{ VoidFunc(); //请注意,函数VoidFunc()在程序中未定义
}
上面的VoidFunc()函数虽然没有定义,但在Keil环境中编译时只是警告,并不报错。编译后的汇编码为LJMP C_STARTUP(C:0000),同方法二极为相似,使程序跳转到0x0000开始执行,同样实现了软件复位的功能。这种做法最为简单,但不符合ANSI C标准中函数应先定义后调用的要求,在其它某些环境中可能无法编译通过,因此不推荐。
总结
我们知道,在MCS-51单片机的所有指令中,只有RETI指令能清除中断请求标志。因此只有方法一能在中断子程序中被调用,方法二和方法三都不能,否则系统复位后,中断请求标志仍在,可能造成系统刚复位就错误地进入了中断子程序。实际应用中应根据实际情况,选择合适的方法。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。