Chinaunix首页 | 论坛 | 博客
  • 博客访问: 304354
  • 博文数量: 85
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 800
  • 用 户 组: 普通用户
  • 注册时间: 2014-10-18 15:21
文章分类

全部博文(85)

文章存档

2017年(1)

2016年(19)

2015年(55)

2014年(10)

我的朋友

分类: 嵌入式

2014-12-21 13:45:34

本文转载自http://www.cnblogs.com/heiyue/p/3373178.html

3.最新的keil MDK(V4.54)在编辑界面中已经可以支持中文编码了,所以可以在编辑器中直接输入汉字和中文标点符号,再也不会显示乱码或者不显示了。虽然乱写汉字和中文标点在编译时依然会报错,但好歹能显示,也从侧面说明中国市场的崛起。开启方法见http://blog.csdn.net/zhzht19861011/article/details/7741928 不再贴了。

我还清楚的记得自己在大学刚开始用Keil C51那会,一次不小心在一行代码后面用了个中文分号,在当时这个中文分号是不被显示的,然后编译,编译器报错,我双击报错信息定位到报错的代码行,却怎么也检查不出来错误来,当时着急的心情现在想想还很好笑的,那个时候只能将错误代码行用双斜杠注释掉,才能看到那个中文分号。但从V4.54之后,就应该再不会遇到我当时的情况了。
//---------------------------------------------------------------------------------------------
转载后注:
想复制文本到其他文档上的时候,改成中文编码还是有帮助的,这样就不会出现乱码。若果还是标准ANSI,在复制到word等文档文档文本上时候就会出现乱码。
//---------------------------------------------------------------------------------------------

9.好个一丝不苟的编译器

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

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

 

  1. unsigned int unIdleCount;  

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

 

 

  1. extern unsigned int unIdleCount;  

 

 

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

 

 

  1. unIdleCount=0;  
  2.       
  3. while(unIdleCount!=200);   //延时2S钟  

keil MDK V5.54下编译,默认优化级别,编译后下载到硬件平台。你会发现,代码在

  1. while(unIdleCount!=200);  

处陷入了死循环。反汇编,代码如下:

 

 

  1.    122:     unIdleCount=0;   
  2.    123:        
  3. 0x00002E10  E59F11D4  LDR       R1,[PC,#0x01D4]  
  4. 0x00002E14  E3A05000  MOV       R5,#key1(0x00000000)  
  5. 0x00002E18  E1A00005  MOV       R0,R5  
  6. 0x00002E1C  E5815000  STR       R5,[R1]  
  7.    124:     while(unIdleCount!=200);   //延时2S钟   
  8.    125:        
  9. 0x00002E20  E35000C8  CMP       R0,#0x000000C8  
  10. 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中我是这样定义统计定时器中断次数变量的:

 

  1. unsigned int volatile unIdleCount;  

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

 

 

  1. extern unsigned int unIdleCount;  

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

 

 

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

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

 

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

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

 

  1. extern unsigned int volatile unIdleCount;  

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

 

 

  1.    122:     unIdleCount=0;   
  2.    123:        
  3. 0x00002E10  E59F01D4  LDR       R0,[PC,#0x01D4]  
  4. 0x00002E14  E3A05000  MOV       R5,#key1(0x00000000)  
  5. 0x00002E18  E5805000  STR       R5,[R0]  
  6.    124:     while(unIdleCount!=200);   //延时2S钟   
  7.    125:        
  8. 0x00002E1C  E5901000  LDR       R1,[R0]  
  9. 0x00002E20  E35100C8  CMP       R1,#0x000000C8  
  10. 0x00002E24  1AFFFFFC  BNE       0x00002E1C  

看最后三句汇编代码,发现多了一个载入汇编指令LDR,这个指令在每次循环中都将变量unIdleCount从RAM中读出到寄存器R1中,然后R1的值再和0xC8比较。这才是符合逻辑的需要的代码。

 

其实如果好好看看编译原理的书,是不会犯这么低级的错误的,编译器是分文件编译,然后链接,文件A使用了文件B中定义的变量,在编译的时候,文件A是完全不知道文件B里面有什么东西的,只能通过文件B的接口文件(.h文件)来获得使用变量的属性.

以这个为例子,着重说明下关键字volatile,同时也要掌握编译原理的知识,用好手中的工具.


MDK优化:
关于MDK的优化请查看
http://blog.163.com/zhaojun_xf/blog/static/300505802011291384721/
为了保证你的代码在优化后能正确运行,一定要注意在不能被优化的变量前加上:volatile。

阅读(4835) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~