原创 [转]volatile关键字的用法

2008-11-23 16:44 1888 6 5 分类: MCU/ 嵌入式



volatile的本意是易变的”. 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化. 比如:

static int i = 0 ;

int main(void)

{

...

while (1)

{

if (i)

do_something() ;

}

}

 

/* Interrupt service routine. */

void ISR_2(void)

{

i = 1 ;

}

 

程序的本意是希望ISR_2中断产生时,main当中调用do_something函数,但是由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用. 如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行). 此例中i也应该如此说明.

一般说来,volatile用在如下的几个地方:

1.中断服务程序中修改的供其它程序检测的变量需要加volatile;

2.多任务环境下各任务间共享的标志应该加volatile;

3.存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;

另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了.

 

现举例说明(Keil-ca51为例,例子来自Keil FQA),看完例子后你应该明白volatile的意思了.


 

1.

void main (void)

{

volatile int i ;

int j ;

i = 1 ;  //1  不被优化 i="1"

i = 2 ;  //2  不被优化 i="2"

i = 3 ;  //3  不被优化 i="3"

 

j = 1;  //4  被优化

j = 2;  //5  被优化

j = 3;  //6  j = 3


}

2.

函数:

void func (void)

{

unsigned char xdata xdata_junk ;

unsigned char xdata *p = &xdata_junk ;

unsigned char t1, t2 ;

 

t1 = *p ;

t2 = *p ;

}

编译的汇编为:

0000 7E00    R     MOV     R6,#HIGH xdata_junk

0002 7F00    R     MOV     R7,#LOW xdata_junk

;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82          MOV     DPL,R7

0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意

0008 E0            MOVX    A,@DPTR

0009 F500    R     MOV     t1,A

000B F500    R     MOV     t2,A

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000D 22            RET     

 

将函数变为:

void func (void)

{

volatile unsigned char xdata xdata_junk ;

volatile unsigned char xdata *p = &xdata_junk ;

unsigned char t1, t2 ;

t1 = *p ;

t2 = *p ;

}

编译的汇编为:

0000 7E00    R     MOV     R6,#HIGH xdata_junk

0002 7F00    R     MOV     R7,#LOW xdata_junk

;---- Variable 'p' assigned to Register 'R6/R7' ----

0004 8F82          MOV     DPL,R7

0006 8E83          MOV     DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

0008 E0            MOVX    A,@DPTR

0009 F500    R     MOV     t1,A        a

000B E0            MOVX    A,@DPTR

000C F500    R     MOV     t2,A

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

000E 22            RET     

比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次,


如在a*p的内容有变化,t2得到的则不是真正*p的内容.

3.

volatile unsigned char bdata var ;  // use volatile keyword here

sbit var_0 = var^0 ;

sbit var_1 = var^1 ;

unsigned char xdata values[10];

 

void main (void) 

{

unsigned char i ;

for (i = 0; i < sizeof (values); i++)

{

var = values ;

if (var_0)

{

var_1 = 1 ; //a

values = var ;  // without the volatile keyword, the compiler

// assumes that 'var' is unmodified and does not

// reload the variable content.

}

}

}


在此例中,如在a处到下一句运行前,var如有变化则不会,var=0xff;则在values = var;得到的还是values = 1;

应用举例:

1.

#define DBYTE ((unsigned char volatile data  *) 0)


说明: 此处不用volatile关键字,可能得不到真正的内容.

2.

#define TEST_VOLATILE_C

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

// verwendete Include Dateien

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

#if __C51__ < 600

#error: !! Keil 版本不正确

#endif

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

// 函数 void v_IntOccured(void)

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

extern void v_IntOccured(void);

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

// 变量定义

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

char xdata cvalue1;          //全局xdata

char volatile xdata cvalue2; //全局xdata

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

// 函数: v_ExtInt0()

// 版本:

// 参数:

// 用途: cvalue1++,cvalue2++

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

void v_ExtInt0(void) interrupt 0

{

cvalue1++;

cvalue2++;

}

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

// 函数: main()

// 版本:

// 参数:

// 用途: 测试volatile

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

void main()

{

char cErg;

//1. 使cErg=cvalue1;

cErg = cvalue1;

//2. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++

if (cvalue1 != cErg)

v_IntOccured();

//3. 使cErg=cvalue2;

cErg = cvalue2;

//4. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++

if (cvalue2 != cErg)

v_IntOccured();

//5. 完成

while (1);

}

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

// 函数: v_IntOccured()

// 版本:

// 参数:

// 用途: 死循环

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

void v_IntOccured()

{

 while(1);

}

仿真可以看出,在没有用volatile,2,程序不能进入v_IntOccured(); 但在4处可以进入v_IntOccured().
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条