volatile

内容来自网络:

一:为什么要讲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 相关寄存器定义

 1 /**************************
 2 
 3 /* Vectored Interrupt Controller (VIC) */
 4 
 5 #define VICIRQStatus    (*((volatile unsigned long *) 0xFFFFF000))
 6 
 7 #define VICFIQStatus    (*((volatile unsigned long *) 0xFFFFF004))
 8 
 9 #define VICRawIntr      (*((volatile unsigned long *) 0xFFFFF008))
10 
11 #define VICIntSelect      (*((volatile unsigned long *) 0xFFFFF00C))
12 
13 #define VICIntEnable     (*((volatile unsigned long *) 0xFFFFF010))
14 
15 #define VICIntEnClr      (*((volatile unsigned long *) 0xFFFFF014))
16 
17 #define VICSoftInt       (*((volatile unsigned long *) 0xFFFFF018))
18 
19 #define VICSoftIntClear   (*((volatile unsigned long *) 0xFFFFF01C))
20 
21 #define VICProtection    (*((volatile unsigned long *) 0xFFFFF020))
22 
23 #define VICVectAddr     (*((volatile unsigned long *) 0xFFFFF030))
24 
25 #define VICDefVectAddr  (*((volatile unsigned long *) 0xFFFFF034))
26 
27 #define VICVectAddr0    (*((volatile unsigned long *) 0xFFFFF100))
28 
29 #define VICVectAddr1    (*((volatile unsigned long *) 0xFFFFF104))
30 
31 #define VICVectAddr2    (*((volatile unsigned long *) 0xFFFFF108))
32 
33 #define VICVectAddr3    (*((volatile unsigned long *) 0xFFFFF10C))
34 
35 #define VICVectAddr4    (*((volatile unsigned long *) 0xFFFFF110))
36 
37 #define VICVectAddr5    (*((volatile unsigned long *) 0xFFFFF114))
38 
39 #define VICVectAddr6    (*((volatile unsigned long *) 0xFFFFF118))
40 
41 #define VICVectAddr7    (*((volatile unsigned long *) 0xFFFFF11C))
42 
43 #define VICVectAddr8    (*((volatile unsigned long *) 0xFFFFF120))
44 
45 #define VICVectAddr9    (*((volatile unsigned long *) 0xFFFFF124))
46 
47 #define VICVectAddr10   (*((volatile unsigned long *) 0xFFFFF128))
48 
49 #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所指向的内容!!

比如:

1 {
2 Int a;
3 Int *p;
4 a=100;
5 p=&a
6 }

那么*p就等于100;

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

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

我们来做一个例比:

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

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

这里0xFFFFF010就对应到p本身的地址。  <why?!>

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

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

(unsigned long *) 0xFFFFF010

就是这样(也可以表示成(unsigned char *) 0xFFFFF010,

前者表示这个地方可以存放32位数据,

后者表示这个地址只能存放8位数据)[(unsigned long *)表示存放数据长度]

既然是地址,就像我们在超市里面的寄存包裹箱一样,是可以存放东西,而且可以取出东西的地方。我们叫可读写,当然有些地址是不能写的,只读的,比如这里面的VICIRQStatus..

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

*(volatile uint32 *)0x60001404 = 0x0000;

意思就是初始化0x60001404地址中的值为0x0000,一般在很多单片机程序中完成寄存器的操作时经常这样用,它相当于下面两步

   volatile uint32 * p = (volatile uint32 *)0x60001404;
   * p  = 0x0000;    (peak:把p指向的变量赋值0x0000)

  其中(volatile uint32 *)0x60001404,是利用c语言的强制类型转换把地址值转换为volatile uint32 * 类型的指针(相当于volatile uint32 * p = (volatile uint32 *)0x60001404),然后将0x0000赋给这个指针指向的地址(相当于 * p  = 0x0000),也就使0x60001404地址中的值为0x0000

posted @ 2020-12-14 21:00  Sean_hn  阅读(126)  评论(0编辑  收藏  举报