KEIL MDK优化问题
用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;
}
...
}
} volatile 回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------
试过了思密达 看汇编斯密达。
Keil不优化的时候,所有变量用到时必然会从内存读。开了优化就可能寄存器。
考虑:更新编译器版本。 回复【2楼】gasbi Glen
回复【1楼】june4th 朴正欢
-----------------------------------------------------------------------
试过了思密达
-----------------------------------------------------------------------
IAR如何? 中断要设置的变量 最好 volatile 中断要设置的变量 最好 volatile
这个是对的!呵呵! 楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个变量是可能随时发生变化的,使得编译器编译程序的时候,每次都从RAM里面读取数据,而不是使用之前缓存到寄存器里面的值。
同样,对于多任务的程序,如果一个公共变量被多个任务用到也要加volatile修饰。
比如 volatile unsigned char GPS_flg;
程序写的严谨,什么编译器都没问题的。 大大的明白,思密达! 关键字volatile有什么含意?并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。
嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
} volatile 正解 刚接触keil mdk 变量问题貌似是最大问题 {:dizzy:} 解决变量问题,mdk就算精通了、吧 学习。。。 懂一点点的路过! 学习到了,
volatile变量的用法
不错,谢谢
学习了,以前没碰到这问题 本帖最后由 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:
0x00002E10E59F11D4LDR R1,
0x00002E14E3A05000MOV R5,#key1(0x00000000)
0x00002E18E1A00005MOV R0,R5
0x00002E1CE5815000STR R5,
124: while(unIdleCount!=200); //延时2S钟
125:
0x00002E20E35000C8CMP R0,#0x000000C8
0x00002E241AFFFFFDBNE 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:
0x00002E10E59F11D4LDR R1,
0x00002E14E3A05000MOV R5,#key1(0x00000000)
0x00002E18E1A00005MOV R0,R5
0x00002E1CE5815000STR R5,
124: while(unIdleCount!=200); //延时2S钟
125:
0x00002E20E35000C8CMP R0,#0x000000C8
0x00002E241AFFFFFDBNE 0x00002E20
可以看出,这个反汇编代码居然和没加volatile关键字的时候一模一样!!代码还是会在while出陷入死循环。
现在,应该知道我要表达的意思了吧,如果引用的变量声明中没有使用volatile关键字修饰,即便定义这个变量的时候使用了volatile关键字修饰,MDK编译器照样优化掉它!
将timer.h中的声明更改为:
extern unsigned int volatile unIdleCount;
同样环境下编译,查看反汇编代码,如下所示:
122: unIdleCount=0;
123:
0x00002E10E59F01D4LDR R0,
0x00002E14E3A05000MOV R5,#key1(0x00000000)
0x00002E18E5805000STR R5,
124: while(unIdleCount!=200); //延时2S钟
125:
0x00002E1CE5901000LDR R1,
0x00002E20E35100C8CMP R1,#0x000000C8
0x00002E241AFFFFFCBNE 0x00002E1C
看最后三句汇编代码,发现多了一个载入汇编指令LDR,这个指令在每次循环中都将变量unIdleCount从RAM中读出到寄存器R1中,然后R1的值再和0xC8比较。这才是符合逻辑的需要的代码。
以这个为例子,一是说明关键字volatile,另外也提下这个有趣的编译器,不得不说,她好认真。再另外,我要买本编译原理的书看看了.
摘自个人博客---有趣的MDK细节:http://blog.csdn.net/zhzht19861011/article/details/7745151
现则反过来想想,原因还是很简单的,MDK编译多个文件时是分别编译,最后再用链接器链接,当编译的时候一个模块引用另外一个模块的变量,完全是靠的变量声明,如果声明都不加volatile,那么引用的模块肯定会把变量当成普通变量的,再反推一下,如果原变量没有加volatile,但是声明的时候加了volatile,是不是引用的模块会将这个变量当成volatile型变量呢.
zhzhchang 发表于 2012-9-26 15:19 static/image/common/back.gif
好个一丝不苟的编译器
这是个十分奇葩的问题,碰巧被我遇到了,我承认是我代码写的不够规范,但正是这个不 ...
你这是对C编译原理的不了解导致的。
如果我告诉你一个最简单的基础知识,你就会对这些现象融汇贯通了:C编译器是以每个C文件作为基本编译单元的,称为模块,被编译为obj;而模块之间的函数或变量访问都是通过标号来实现的,标号本身没有任何属性,只是提供给链接器使用的一个符号名称而已,标号的属性完全就靠调用的地方的原型声明来决定的!
因此,你在timer模块中定义为volatile,仅仅是在timer模块中告诉编译器不要优化而已,在另外的模块内使用了这个变量,而它们是不知道该变量是什么属性的,所以只有靠原型声明来告诉编译器这些信息了。
最后简单的给你总结一下:别小看原型声明,它是联系各个模块的桥梁,是各种输出符号的属性信息所在。 很好!表示学习! 是不是因为中断始终慢于main函数中非死循环部分的语句? mark{:smile:}
好经验,收藏了 学习了! GPS_flg 这个变量在while以前应该被用到了,所以会存在寄存器中;如果while是main的第一条语句,不会出现这个问题吧。 huchunlei 发表于 2010-9-25 02:46 static/image/common/back.gif
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...
写的太具体了!!学习了!! 非常的好 还是的看看编译原理的书籍了 mark……
顶一个… mark很好学习了 huchunlei 发表于 2010-9-25 02:46 static/image/common/back.gif
楼上正解!
一个变量,如果你的主程序要用到,同时中断还要用到,要加volatile修饰。 目的是告诉编译器这个 ...
受教啦 今天 刚碰到这个问题
mark 一下 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, //这条语句是在下面那个判断之前突然出现的,用来获得最新的值
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的值,但是他也会每次都读取最新的值,
于是我真的有点感慨,真的很智能,(他的理念或许就是这样),完全没意义的事就不用每次都浪费时间和代码空间读取最新的值了,但是
真的有事要做的话就得严谨地每次读取最新的值看是不是满足条件,应不应该执行了,哈哈,说的优点多
volatile 的使用{:smile:} 路过学习了,很好! 学习了,不过20楼的意思没怎么明白,还望再解释下 mark!学习备用,虽然还是有点没明白 不错。MARK一下。 优化当然恐怖,一个好的程序难就男在优化上了 qiushui 发表于 2011-3-16 20:54
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么 ...
看来我就是个C程序员。记住了volatile。 学习了,以前没碰到这问题 做个标记,有时候程序不知道原因跑错。 mark一下学习~~~~~ volatile的用法这么有讲究,回头好好研究一下 volatile的用法很有讲究,大家好好理解呀。 对于没有用于左值运算的变量,程序会进行优化,此时就应该加VOLATILE了。空循环也是容易被优化的。请问楼主的问题找到了吗?我的程序也是没有优化的时候就正常,优化后有些功能不异常。 volatile的用法,谢谢了。 优化,下午试一下。。从来没有优化过代码 受教了
养分很足,电脑上再去好好看 有时候全局变量在其它中断程序里面有修改的话。也会出现一些奇怪的问题。 好帖子,mark ,volatile ~~ 学习了,感谢分享 有时候还是搞不懂,程序跑的好好的,为什么还要优化 volatile 正解
mark
好经验,收藏了
页:
[1]