原文地址:http://www.lazybee.cn/bbs/read.php?tid=572&u=2
本文由LazyBEE整理,欢迎转载,但请注明出处。
更多技术资料和经典教程请登陆http://www.LazyBEE.cn。
点击下载PDF格式文件
在使用WinAVR编写程序时,发现一处因编译器优化而引起的程序异常,以下就程序异常原因进行简单的分析。
在程序中,有如下一段代码。
#define ADC_CONVERT_FINISH 3 ADCEnable(ADCChannel); //使能某一个通道的ADC转换 /*主程序*/ Void main(void) { /*等待ADC中断服务程序中对g_StatusREG 的ADC_CONVERT_FINISH 进行置位*/ while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0) { ;//reserved } }
/*ADC中断服务程序*/ ISR(ADC_vect) {
cli(); ADCDataLowByte = ADCL; ADCDataHighByte = ADCH; ADCData = ADCDataHighByte*256 + ADCDataLowByte; g_StatusREG |= (1<<ADC_CONVERT_FINISH); sei(); }
其中g_StatusREG 为程序定义的一个状态寄存器。
按照正常的程序,ADC_CONVERT_FINISH在进入中断服务程序后会被置位,main函数退出 while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0){ ;//reserved }
然而按照缺省的makefile文件,编译出来的结果与所预期的的不一致,程序无法退出while循环,后通过调整makefile文件中的优化级别解决此问题。将优化等级由缺省的等级S(s = optimize for size)调整为0(0 = turn off optimization)。
以下通过两种优化设置下编译生成的汇编代码来分析问题产生的原因。
while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0) { ;//reserved } 按照不同的优化等级编译出的汇编代码如下所示。
按缺省的优化等级S(optimize for size)编译: +0000014C: 9180010E LDS R24,0x010E oad direct from data space //读取g_StatusREG +0000014E: 2799 CLR R25 Clear Register +0000014F: 7088 ANDI R24,0x08 Logical AND with immediate +00000150: 7090 ANDI R25,0x00 Logical AND with immediate 122: while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0) +00000151: 9700 SBIW R24,0x00 Subtract immediate from word +00000152: F3F1 BREQ PC-0x01 Branch if equal
分析:R24保存的是g_StatusREG,在此种优化下,程序只在最开始读取一次g_StatusREG的值,就不断重复判断它的ADC_CONVERT_FINISH是否改变。此种情况下,即便ADC的中断服务程序改变了g_StatusREG的值,在这一循环中,也不会更新while循环中用于判断是否推出循环的g_StatusREG值,因此程序无法退出while循环。
关闭优化后,编译的结果为: 122: while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0) +00000264: 91800100 LDS R24,0x0100 Load direct from data space //g_StatusREG +00000266: 2799 CLR R25 Clear Register +00000267: 7088 ANDI R24,0x08 Logical AND with immediate +00000268: 7090 ANDI R25,0x00 Logical AND with immediate +00000269: 9700 SBIW R24,0x00 Subtract immediate from word +0000026A: F409 BRNE PC+0x02 Branch if not equal +0000026B: CFF8 RJMP PC-0x0007 Relative jump
分析:从这一段汇编代码可以看出,当(g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0)时,程序是重新读取g_StatusREG的值,因此,只要ADC中断服务程序一更改g_StatusREG的值,程序就能立即退出while((g_StatusREG &(1<<ADC_CONVERT_FINISH))== 0) 循环。
解决办法
办法一:直接在makefile中关闭优化。 可以解决此问题,但同时整个程序也没有进行优化,这样会影响程序的效率。
方法二: 使用volitale关键词。 将uchar g_StatusREG;定义改为 volitale uchar g_StatusREG。这样即使使用优化也不会引起程序异常。Volitale主要是告诉编译器,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错。
|