搜索
bottom↓
回复: 54

KEIL MDK优化问题

  [复制链接]

出0入0汤圆

发表于 2010-8-17 13:47:57 | 显示全部楼层 |阅读模式
用KEIL MDK uVision4,如下程序,GPS_flg 是一个全局变量,当有GPS数据时就会中断被置1,结果我在while(1)外面判断GPS_flg就始终为0,进入死循环出不来,在while(1)里面就可以判断到GPS_flg为1,最后找不到问题,把优化level1改成level0就好了。折腾了半天时间,难道优化这么恐怖?

main()
{...       
  while(GPS_flg == 0)
  {;}
  while(1)
  {
    if(GPS_flg == 0)
      i++;
    if(GPS_flg == 1)
    {       
      xprintf("i = %d\r\n", i++);
      i=0;
    }
  ...
  }
}

阿莫论坛20周年了!感谢大家的支持与爱护!!

如果想吃一顿饺子,就得从冰箱里取出肉,剁馅儿,倒面粉、揉面、醒面,擀成皮儿,下锅……
一整个繁琐流程,就是为了出锅时那一嘴滚烫流油的热饺子。

如果这个过程,禁不住饿,零食下肚了,饺子出锅时也就不香了……《非诚勿扰3》

出0入0汤圆

发表于 2010-8-17 14:39:58 | 显示全部楼层
volatile

出0入0汤圆

 楼主| 发表于 2010-8-17 16:24:05 | 显示全部楼层
回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------

试过了思密达

出0入0汤圆

发表于 2010-8-17 17:06:41 | 显示全部楼层
看汇编斯密达。

Keil不优化的时候,所有变量用到时必然会从内存读。开了优化就可能寄存器。
考虑:更新编译器版本。

出0入0汤圆

发表于 2010-8-17 17:51:43 | 显示全部楼层
回复【2楼】gasbi Glen
回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------
试过了思密达
-----------------------------------------------------------------------

IAR如何?

出0入0汤圆

发表于 2010-8-17 21:57:46 | 显示全部楼层
中断要设置的变量 最好 volatile

出0入0汤圆

发表于 2010-8-17 22:16:41 | 显示全部楼层
中断要设置的变量 最好 volatile

这个是对的!呵呵!

出0入0汤圆

发表于 2010-9-25 02:46:54 | 显示全部楼层
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个变量是可能随时发生变化的,使得编译器编译程序的时候,每次都从RAM里面读取数据,而不是使用之前缓存到寄存器里面的值。

同样,对于多任务的程序,如果一个公共变量被多个任务用到也要加volatile修饰。

比如    volatile unsigned char GPS_flg;

程序写的严谨,什么编译器都没问题的。

出0入0汤圆

发表于 2011-3-11 23:56:28 | 显示全部楼层
大大的明白,思密达!

出0入0汤圆

发表于 2011-3-16 20:53:39 | 显示全部楼层
关键字volatile有什么含意?并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。
嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。

出0入0汤圆

发表于 2011-3-16 20:54:34 | 显示全部楼层
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
   return *ptr * *ptr;
}

出0入0汤圆

发表于 2011-3-20 20:30:57 | 显示全部楼层
volatile 正解

出0入0汤圆

发表于 2012-7-31 21:40:44 | 显示全部楼层
刚接触keil mdk 变量问题貌似是最大问题 解决变量问题,mdk就算精通了、吧

出0入0汤圆

发表于 2012-8-1 10:40:23 | 显示全部楼层
学习。。。

出0入0汤圆

发表于 2012-8-4 00:06:49 | 显示全部楼层
懂一点点的路过!

出0入0汤圆

发表于 2012-8-29 14:10:52 | 显示全部楼层
学习到了,
volatile变量的用法

出0入0汤圆

发表于 2012-9-18 23:12:30 | 显示全部楼层

不错,谢谢

出0入0汤圆

发表于 2012-9-23 21:07:24 | 显示全部楼层
学习了,以前没碰到这问题

出0入0汤圆

发表于 2012-9-26 15:19:53 | 显示全部楼层
本帖最后由 zhzhchang 于 2012-9-26 15:33 编辑

好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不规范的代码,才得以发现这个奇葩的事件。实在忍不住用了两个奇葩来形容。把过程简化一下,如下所述:

假如你的工程至少有两个.c文件,其中一个为timer.c,里面有个定时器中断程序,每10ms中断一次,定义一个变量来统计定时器中断次数:


unsigned int unIdleCount;还有一个timer.h文件,里面是一些timer.c模块的封装,其中变量unIdleCount就被封装在里面:


extern unsigned int unIdleCount;


在main.c函数中,包含timer.h文件,并利用定时器变量unIdleCount来精确延时2秒,代码如下:


unIdleCount=0;
   
while(unIdleCount!=200);   //延时2S钟
keil MDK V5.54下编译,默认优化级别,编译后下载到硬件平台。你会发现,代码在while(unIdleCount!=200);处陷入了死循环。反汇编,代码如下:


   122:     unIdleCount=0;
   123:      
0x00002E10  E59F11D4  LDR       R1,[PC,#0x01D4]
0x00002E14  E3A05000  MOV       R5,#key1(0x00000000)
0x00002E18  E1A00005  MOV       R0,R5
0x00002E1C  E5815000  STR       R5,[R1]
   124:     while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E20  E35000C8  CMP       R0,#0x000000C8
0x00002E24  1AFFFFFD  BNE       0x00002E20
重点看最后两句汇编代码,寄存器R0是当前变量unIdleCount的值,汇编指令CMP为比较指令,如果R0中的内容与0xC8不等,则循环。但是这里并没有更新寄存器R0的代码,也就是说变量unIdleCount的值虽然在变化,但跟0xC8一直比较的却是内容不变的R0。因为之前变量unIdleCount被清零,所以R0的内容也是0,永远不等于0xC8,永远不会跳出循环。

看到这里,也许你已经笑翻了:你这个小白,这很明显是没用volatile修饰变量unIdleCount造成的!!!不错,比起从RAM中读写数据,ARM或其它硬件从寄存器读取数据要快的多的多的多...因此编译器会“自作主张”的将某些变量读到寄存器中,再次运算时也优先从寄存器中读取,上面的例子就是这样。解决这样的方法是用关键字volatile修饰你不想让编译器优化的变量,明白的告诉编译器:你不准优化我,每次使用我你都要本本分分的从RAM中读取或写入RAM。

所以先不要笑,我是不会犯这种错误的,之所以从这里说起,是为了照顾下还不知道volatile关键字的。。。

其实在timer.c中我是这样定义统计定时器中断次数变量的:


unsigned int volatile unIdleCount;但是,在timer.h中,我确偷了个懒,声明这个变量的代码如下:

extern unsigned int unIdleCount;
没有使用关键字volatile,在keil MDK V5.54下编译,默认优化级别,然后查看代码的反汇编,如下所示:


   122:     unIdleCount=0;
   123:      
0x00002E10  E59F11D4  LDR       R1,[PC,#0x01D4]
0x00002E14  E3A05000  MOV       R5,#key1(0x00000000)
0x00002E18  E1A00005  MOV       R0,R5
0x00002E1C  E5815000  STR       R5,[R1]
   124:     while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E20  E35000C8  CMP       R0,#0x000000C8
0x00002E24  1AFFFFFD  BNE       0x00002E20

可以看出,这个反汇编代码居然和没加volatile关键字的时候一模一样!!代码还是会在while出陷入死循环。

现在,应该知道我要表达的意思了吧,如果引用的变量声明中没有使用volatile关键字修饰,即便定义这个变量的时候使用了volatile关键字修饰,MDK编译器照样优化掉它!

将timer.h中的声明更改为:


extern unsigned int volatile unIdleCount;

同样环境下编译,查看反汇编代码,如下所示:


   122:     unIdleCount=0;
   123:      
0x00002E10  E59F01D4  LDR       R0,[PC,#0x01D4]
0x00002E14  E3A05000  MOV       R5,#key1(0x00000000)
0x00002E18  E5805000  STR       R5,[R0]
   124:     while(unIdleCount!=200);   //延时2S钟
   125:      
0x00002E1C  E5901000  LDR       R1,[R0]
0x00002E20  E35100C8  CMP       R1,#0x000000C8
0x00002E24  1AFFFFFC  BNE       0x00002E1C
看最后三句汇编代码,发现多了一个载入汇编指令LDR,这个指令在每次循环中都将变量unIdleCount从RAM中读出到寄存器R1中,然后R1的值再和0xC8比较。这才是符合逻辑的需要的代码。

以这个为例子,一是说明关键字volatile,另外也提下这个有趣的编译器,不得不说,她好认真。再另外,我要买本编译原理的书看看了.

摘自个人博客---有趣的MDK细节:http://blog.csdn.net/zhzht19861011/article/details/7745151

现则反过来想想,原因还是很简单的,MDK编译多个文件时是分别编译,最后再用链接器链接,当编译的时候一个模块引用另外一个模块的变量,完全是靠的变量声明,如果声明都不加volatile,那么引用的模块肯定会把变量当成普通变量的,再反推一下,如果原变量没有加volatile,但是声明的时候加了volatile,是不是引用的模块会将这个变量当成volatile型变量呢.

出870入263汤圆

发表于 2012-10-7 13:14:17 | 显示全部楼层
zhzhchang 发表于 2012-9-26 15:19
好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不 ...

你这是对C编译原理的不了解导致的。
如果我告诉你一个最简单的基础知识,你就会对这些现象融汇贯通了:C编译器是以每个C文件作为基本编译单元的,称为模块,被编译为obj;而模块之间的函数或变量访问都是通过标号来实现的,标号本身没有任何属性,只是提供给链接器使用的一个符号名称而已,标号的属性完全就靠调用的地方的原型声明来决定的!
因此,你在timer模块中定义为volatile,仅仅是在timer模块中告诉编译器不要优化而已,在另外的模块内使用了这个变量,而它们是不知道该变量是什么属性的,所以只有靠原型声明来告诉编译器这些信息了。
最后简单的给你总结一下:别小看原型声明,它是联系各个模块的桥梁,是各种输出符号的属性信息所在。

出0入0汤圆

发表于 2012-10-11 11:07:00 | 显示全部楼层
很好!表示学习!

出0入0汤圆

发表于 2012-10-11 12:29:59 | 显示全部楼层
是不是因为中断始终慢于main函数中非死循环部分的语句?

出0入0汤圆

发表于 2012-12-24 16:41:23 | 显示全部楼层
mark
好经验,收藏了

出0入0汤圆

发表于 2013-1-1 20:00:18 | 显示全部楼层
学习了!

出0入0汤圆

发表于 2013-1-2 10:07:45 | 显示全部楼层
GPS_flg 这个变量在while以前应该被用到了,所以会存在寄存器中;如果while是main的第一条语句,不会出现这个问题吧。

出0入0汤圆

发表于 2013-1-8 15:43:15 | 显示全部楼层
huchunlei 发表于 2010-9-25 02:46
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...

写的太具体了!!学习了!!

出0入0汤圆

发表于 2013-1-10 23:20:30 | 显示全部楼层
非常的好   还是的看看编译原理的书籍了

出0入0汤圆

发表于 2013-8-12 20:11:19 来自手机 | 显示全部楼层
mark……
顶一个…

出0入0汤圆

发表于 2013-9-10 21:28:05 | 显示全部楼层
mark  很好  学习了

出0入0汤圆

发表于 2013-9-12 14:15:30 | 显示全部楼层
huchunlei 发表于 2010-9-25 02:46
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...

受教啦                                             

出0入0汤圆

发表于 2013-11-28 19:11:53 | 显示全部楼层
今天 刚碰到这个问题   
mark 一下

出0入0汤圆

发表于 2014-2-24 01:16:00 | 显示全部楼层
zhzhchang 发表于 2012-9-26 15:19
好个一丝不苟的编译器

这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不 ...

1、你好,我发现如果那个while(unIdleCount!=200);   不是一个空等待的循环,(当然,里面不是的环就不是等待2Ms了,而是在这2MS里一直执行某个操作了),这样的话就不会出现这种情况了,在比较之前
   会有一个LDR的操作,保证读取的是RAM里的数据,而这时我并没有用volatile修饰他,而且我的优化等级选的是最高的3也没关系

2、另一个,我发现,只要unIdleCount=0不是紧挨着下面那个判断语句,两句之间隔了一些函数,就算这些函数没有操作unIdleCount,在比较之前也会有那个LDR命令获得unIdleCount最新的值

   22:         TIM3_Int_Init(500,7199);//10Khz的计数频率,计数到5000为500ms   
    23:           
0x08000A08 8820      LDRH     r0,[r4,#0x00]   //这条语句是在下面那个判断之前突然出现的,用来获得最新的值  
    24:         while(unIdleCount!=10);
    25:                  
0x08000A0A 280A      CMP      r0,#0x0A
0x08000A0C D1FD      BNE      0x08000A0A

同样,这时我也没有用volatile修饰unIdleCount,那应该就是这时寄存器里面没有保存unIdleCount了,(因为这之前的操作保存不了了),所以会重新读,

3、后来我又NC的做了几个实验,发现在unIdleCount=0;和while(unIdleCount!=10);之间相隔的有东西的时候,不管是函数(需要跳转)还是简单运算,只要编译器有办法用一个寄存器保存unIdleCount 的值
   同时又能完成这之间的运算,他就会直接比较寄存器里面的值,(此时我任然选的是最高优化级别)我的是4.7版本的MDK。
   于是我又重复了1里面说的,发现只要while里面有东西,哪怕简单得足以让可以保存unIdleCount的值,但是他也会每次都读取最新的值,
   于是我真的有点感慨,真的很智能,(他的理念或许就是这样),完全没意义的事就不用每次都浪费时间和代码空间读取最新的值了,但是
    真的有事要做的话就得严谨地每次读取最新的值看是不是满足条件,应不应该执行了,哈哈,说的优点多


出0入0汤圆

发表于 2014-4-8 16:38:37 | 显示全部楼层
volatile 的使用

出0入0汤圆

发表于 2014-4-10 16:46:45 | 显示全部楼层
路过学习了,很好!

出0入0汤圆

发表于 2014-5-16 21:40:06 | 显示全部楼层
学习了,不过20楼的意思没怎么明白,还望再解释下

出0入0汤圆

发表于 2014-5-22 21:50:56 | 显示全部楼层
mark!学习备用,虽然还是有点没明白

出0入0汤圆

发表于 2014-6-24 15:13:33 | 显示全部楼层
不错。MARK一下。

出0入0汤圆

发表于 2014-9-18 08:50:54 | 显示全部楼层
优化当然恐怖,一个好的程序难就男在优化上了

出0入0汤圆

发表于 2014-9-18 09:04:40 | 显示全部楼层
qiushui 发表于 2011-3-16 20:54
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么 ...

看来我就是个C程序员。记住了volatile。

出0入0汤圆

发表于 2014-11-19 14:37:51 | 显示全部楼层
学习了,以前没碰到这问题

出0入0汤圆

发表于 2014-11-19 15:08:34 | 显示全部楼层
做个标记,有时候程序不知道原因跑错。

出0入0汤圆

发表于 2014-11-19 16:45:45 | 显示全部楼层
mark一下  学习~~~~~

出0入0汤圆

发表于 2014-11-24 14:18:47 | 显示全部楼层
volatile的用法这么有讲究,回头好好研究一下

出0入0汤圆

发表于 2014-11-24 15:27:45 | 显示全部楼层
volatile的用法很有讲究,大家好好理解呀。

出0入0汤圆

发表于 2014-11-25 11:13:06 | 显示全部楼层
对于没有用于左值运算的变量,程序会进行优化,此时就应该加VOLATILE了。空循环也是容易被优化的。请问楼主的问题找到了吗?我的程序也是没有优化的时候就正常,优化后有些功能不异常。

出0入0汤圆

发表于 2014-11-27 14:12:43 | 显示全部楼层
volatile的用法,谢谢了。

出0入0汤圆

发表于 2014-11-28 07:29:25 | 显示全部楼层
优化,下午试一下。。从来没有优化过代码

出0入0汤圆

发表于 2014-11-28 20:06:54 | 显示全部楼层
受教了

出5入42汤圆

发表于 2014-11-28 20:43:16 来自手机 | 显示全部楼层
养分很足,电脑上再去好好看

出0入0汤圆

发表于 2015-1-4 11:22:32 | 显示全部楼层
有时候全局变量在其它中断程序里面有修改的话。也会出现一些奇怪的问题。

出0入0汤圆

发表于 2015-2-8 18:32:15 | 显示全部楼层
好帖子,mark ,volatile ~~

出0入10汤圆

发表于 2016-4-22 16:51:31 | 显示全部楼层
学习了,感谢分享

出0入0汤圆

发表于 2016-5-9 13:34:40 | 显示全部楼层
有时候还是搞不懂,程序跑的好好的,为什么还要优化

出0入0汤圆

发表于 2016-5-10 13:45:41 | 显示全部楼层
volatile 正解

出0入0汤圆

发表于 2016-11-11 18:27:16 | 显示全部楼层

mark
好经验,收藏了
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-28 23:46

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表