打印
[AVR单片机]

转载:关于一个WinAVR编译优化问题的分析

[复制链接]
5331|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
omat|  楼主 | 2009-5-13 19:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
原文地址: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变量有关的运算,不要进行编译优化,以免出错。

相关帖子

沙发
omat|  楼主 | 2009-5-13 20:14 | 只看该作者

自己先顶一下。

自己先顶一下。

使用特权

评论回复
板凳
void_c| | 2009-5-14 18:11 | 只看该作者

结论竟然是关闭优化。

结论竟然是关闭优化。

——————————————
严重误导别人。(分析并没有分析到点上)

使用特权

评论回复
地板
omat|  楼主 | 2009-5-18 21:37 | 只看该作者

PDF文件写了用volatile

帖子里没有更新,抱歉!

使用特权

评论回复
5
zhousd| | 2009-5-20 09:51 | 只看该作者

老外写的例程无一例外都是OS级别,

牛皮的很!俺调试时都是先关闭优化,功能正常以后再开通优化级别。

使用特权

评论回复
6
omat|  楼主 | 2009-5-20 21:25 | 只看该作者

good idea

使用特权

评论回复
7
areshan| | 2011-5-30 16:11 | 只看该作者
今天遇到了一个很奇怪的问题,在用AVRSTUDIO,加winavr时,要配置pll,默认的是os优化选择可以写入时钟选择(有写保护),但是我调成00无优化却不让写入系统时钟选择,怎的很奇怪,还好这个问题没有浪费我太多的时间!!!!!!!!要是在这个问题上卡死了,可以真要命,因为你的系统时钟永远都起不来!!!!!!我用的是xmega32a4!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

3

主题

19

帖子

0

粉丝