原创 volatile

2012-8-10 13:55 3433 22 33 分类: 消费电子

翻译解析:下家山

一:为什么要讲volatile

       因为,很多”面试官”自己找不到能够测试应聘者的好的方式,所以就google了一下,发现了”嵌入式经典的0x10个面试题”,于是乎就拿来直接问了。我想第一个想到用这个来提问应聘者的人绝对是值得我们仰慕的。

二:Volatile官方说明

Indicates that a variable can be changed by a background routine.

Keyword volatile is an extreme opposite of const. It indicates that a variable may be changed in a way which is absolutely unpredictable by analysing the normal program flow (for example, a variable which may be changed by an interrupt handler). This keyword uses the following syntax:

volatile data-definition;

Every reference to the variable will reload the contents from memory rather than take advantage of situations where a copy can be in a register.

翻译:

     表示一个变量也许会被后台程序改变.

        关键字volatile是与const绝然对立的。它指示一个变量也许会被某种方式修改,这种方式按照正常程序流程分析是无法预知的(例如,一个变量也许会被一个中断服务程序所修改)。这个关键字使用下列语法定义:

      volatile data-definition;

      变量如果加了volatile修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。

三:实例分析

         Volatile应用比较多的场合,在中断服务程序和cpu相关寄存器的定义。

比如,下面就是lpc2136 相关寄存器定义

/**************************

【By 下家山 Q群 75303301        上海松江文汇路928号258室    松江大学城

上海索漫科技  http://www.xiajiashan.com  专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训】**************************************/

 

/* Vectored Interrupt Controller (VIC) */

#define VICIRQStatus    (*((volatile unsigned long *) 0xFFFFF000))

#define VICFIQStatus    (*((volatile unsigned long *) 0xFFFFF004))

#define VICRawIntr      (*((volatile unsigned long *) 0xFFFFF008))

#define VICIntSelect      (*((volatile unsigned long *) 0xFFFFF00C))

#define VICIntEnable     (*((volatile unsigned long *) 0xFFFFF010))

#define VICIntEnClr      (*((volatile unsigned long *) 0xFFFFF014))

#define VICSoftInt       (*((volatile unsigned long *) 0xFFFFF018))

#define VICSoftIntClear   (*((volatile unsigned long *) 0xFFFFF01C))

#define VICProtection    (*((volatile unsigned long *) 0xFFFFF020))

#define VICVectAddr     (*((volatile unsigned long *) 0xFFFFF030))

#define VICDefVectAddr  (*((volatile unsigned long *) 0xFFFFF034))

#define VICVectAddr0    (*((volatile unsigned long *) 0xFFFFF100))

#define VICVectAddr1    (*((volatile unsigned long *) 0xFFFFF104))

#define VICVectAddr2    (*((volatile unsigned long *) 0xFFFFF108))

#define VICVectAddr3    (*((volatile unsigned long *) 0xFFFFF10C))

#define VICVectAddr4    (*((volatile unsigned long *) 0xFFFFF110))

#define VICVectAddr5    (*((volatile unsigned long *) 0xFFFFF114))

#define VICVectAddr6    (*((volatile unsigned long *) 0xFFFFF118))

#define VICVectAddr7    (*((volatile unsigned long *) 0xFFFFF11C))

#define VICVectAddr8    (*((volatile unsigned long *) 0xFFFFF120))

#define VICVectAddr9    (*((volatile unsigned long *) 0xFFFFF124))

#define VICVectAddr10   (*((volatile unsigned long *) 0xFFFFF128))

#define VICVectAddr11   (*((volatile unsigned long *) 0xFFFFF12C))

也许有人看到这里有带出了跟本文无关的疑问,#define VICIntEnable     (*((volatile unsigned long *) 0xFFFFF010))

是什么语法。

这里也一并介绍了:

先总结一句话,#define VICIntEnable     (*((volatile unsigned long *) 0xFFFFF010))

其实就是定义一个指针变量。

那什么是指针变量呢,万变不离其宗!

我们看C里面对指针变量的定义:(大家可以去看谭浩强老师C语言第四版指针部分)

Int a;这里a是一个变量,是一个32位整形变量;

Int *p;同理,p也是一个变量,但他是一个指针变量,他可以存放一个地址,

如:p=&a,(p可以这样赋值),p存放的地址是变量a的地址。&是取地址符合。

那么*p是什么呢?

*p就是p所指向的内容。比如:

{

Int a;

Int *p;

a=100;

p=&a

}

那么*p就等于100;

但是,这里还有个问题,p本身的地址。如果想到了这里,我们就好介绍

,#define VICIntEnable     (*((volatile unsigned long *) 0xFFFFF010))

我们来做一个例比:

(*((volatile unsigned long *) 0xFFFFF010))=========*p

那么这里 ((volatile unsigned long *) 0xFFFFF010)========p

这里0xFFFFF010就对应到p本身的地址。

 

首先,大家应该知道这是一个C语言里面的宏定义;然后,0xFFFFF010这个32位数,这个数的来源在lpc2136 datasheet,52page/270

 

1.jpg

 

所以,这个32位数是一个寄存器地址,要把一个32位数表示成地址怎么表示呢?

(unsigned long *) 0xFFFFF010,就是这样(也可以表示成(unsigned char *) 0xFFFFF010,前者表示这个地方可以存放32位数据,后者表示这个地址只能存放8位数据)。既然是地址,就像我们在超市里面的寄存包裹箱一样,是可以存放东西,而且可以取出东西的地方。我们叫可读写,当然有些地址是不能写的,只读的,比如这里面的VICIRQStatus..

    寄存器地址为什么要加volatile修饰呢,是因为,这些寄存器里面的值是随时变化的。比如,我们这里的中断状态寄存器VICIRQStatus ,当某个中断发生的时候,我们无法知道,那么这个状态寄存器的内容也是无法预知的。我们读取的时候,CPU就直接到内存里面取值,而不是到cache里面取值。

 写于上海松江

作者:下家山(请尊重原创, 转载请注明)  http://www.xiajiashan.com,有什么问题可与我联系:ximenpiaoxue4016@sina.com

 

文章评论11条评论)

登录后参与讨论

用户1326533 2012-6-21 08:03

舀汤很有意思 博主讲的也很详细

用户1406868 2012-6-20 13:13

学习了!!!

用户1634956 2012-6-20 13:01

楼上烧汤的比喻很好

用户1167151 2012-6-20 00:16

Rn如果指寄存器的话,那就一定不会"在内存中". 至于调试,也应该是根据编译生成的调试信息来确定该变量在堆栈中还是寄存器中, 如: .stabs "nTimeout:r1",64,0,148,20 说明变量nTimeout是类型1(int), 放在寄存器20中. 对不起,我比较喜欢抬杠.

用户1443342 2012-6-19 16:46

受教了,虽然没有完全看明白。

用户1534834 2012-6-19 16:27

主要用于编译时的优化和调试,JohnXia说了优化部分(作者的cache也就是编译时产生的Rn或临时变量,它们都在内存中),调试部分也会对volatile 地址每次直接再读取,而不读取保留的中间变量或Rn.

用户1406868 2012-6-19 11:36

受教了

用户1602177 2012-6-19 09:42

"因为,很多”面试官”自己找不到能够测试应聘者的好的方式,所以就google了一下,发现了”嵌入式经典的0x10个面试题”,于是乎就拿来直接问了。我想第一个想到用这个来提问应聘者的人绝对是值得我们仰慕的。"文章开头就很吸引人,好好学习一下~~

用户1190942 2012-6-19 09:06

hao

用户1167151 2012-6-19 07:34

补充一下: “我们读取的时候,CPU就直接到内存里面取值,而不是到cache里面取值。”这个说法并不是很正确。 CPU读的时候究竟是从内存中读还是从cache里面取值并不由程序本身决定。(事实上C语言没有办法强制让CPU从内存中或cache中取值,除非平台定义了专门的cacheable和non-cacheable区域。)请注意volatile这个修饰符是在编译时起作用的,也就是说有没有此修饰符的编译结果是不一样的。那究竟有什么不一样呢? 一般来说C编译器都会对生成的代码进行优化。有些常用的变量就会用寄存器来保存,在循环中就不需要每次从内存(或者cache)中载入。对一般的变量当然没有问题,但对于有一些特殊的变量(博主所说硬件寄存器是一例,还包括可能被中断服务程序或其他线程修改的全局变量), 这样优化就有问题。讲一个笑话:有个人下厨烧汤,舀一勺汤尝了一下,不够咸。抖了一些盐在锅里,又拿起刚才舀的那勺汤尝了一下,还是不够咸, 于是继续放盐... 这种可能被硬件或其他程序改变的变量,就必须加上volatile修饰符,好让编译器知道每次要重新舀汤,不要用原来舀好的样品。
相关推荐阅读
用户1037328 2016-04-17 17:59
好的理论可以避免犯错——读《丈量世界》有感!
     《丈量世界》,早就因为仰慕高斯而想看这本书      1.殊途同归      这本书写的是高斯和洪堡两个人,为什么要把这两个人放在一起写呢?因为高斯是一个宅男,不喜欢什么旅游,考察!他觉得...
用户1037328 2016-04-05 09:10
叛逆在爱因斯坦性格中的重要性(一)
叛逆在爱因斯坦性格中的重要性(一) 《爱因斯坦传》读书笔记      昨天晚上,读完了2016年书单的第二部书,《爱因斯坦传》。我想梳理一下自己读这本书的收获,或者说感悟!在读这...
用户1037328 2016-04-05 09:09
叛逆在爱因斯坦性格中的重要性(二)
叛逆在爱因斯坦性格中的重要性(二) 《爱因斯坦传》读书笔记                                四  叛逆性 在爱因斯坦的同时期科学家中,我想比他IQ...
用户1037328 2014-07-09 19:55
RTEMS系统时钟原理
作者,下家山(转载请注明作者及出处,请尊重原创)   专注嵌入式(ARM7,Cortex-M0,Cortex-M3,ARM9,linux)培训 一:从应用看原理      每个...
用户1037328 2014-07-09 19:47
RTEMS在S3C2410上的移植(一)
作者:下家山(转载,请注明) 1:RTEMS开发环境建立 1.1开发环境建立前的准备工作 因为RTEMS开发环境主要用的是GNU的工具链,所以需要一台装有Linux的HOST. 1.2...
用户1037328 2014-07-09 19:45
深入浅出学Makefile<三,如何自动生成Makefile>
(请尊重原创,转载请注明,作者:下家山) 五: 运行make编译得到可执行文件 root@parson-desktop:/home/parson/tmp/automk# make gcc ...
我要评论
11
22
关闭 站长推荐上一条 /2 下一条