QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1484|回复: 0

你今天volatile了吗?--准确定位

[复制链接]
发表于 2006-8-29 11:47:53 | 显示全部楼层 |阅读模式
  在前一篇关于volatile文章《你今天volatile了吗?--慎重使用》(在后面,简称《慎重使用》)中提到,volatile定义的对象其内容可能会忽然的变化。换句话讲,如果你定义了一个volatile对象,就等于你告诉编译器该对象的内容可能会改变,即使代码中没有任何语句去改变该对象。编译器访问非volatile对象和volatile对象的方式很不一样。对于前者(经优化后),它先将非volatile对象的内容读到CPU寄存器中,等操作CPU寄存器一段时间后,才最终将CPU寄存器的内容写回volatile对象。然而,对于volatile对象就没有这种优化操作。这时候编译器有些“笨”,代码要求它读取或写入volatile,它就马上如实地去做。前一篇《慎重使用》主要讲述如何明智地正确使用volatile,本篇文章通过一些实际应用进一步阐述volatile在解决嵌入式问题中的一些微妙作用并继续深入探讨使用volatile要注意的一些细节问题。

1.构造内存映射的设备寄存器
  许多处理器使用内存映射的I/O设备。这些设备将寄存器映射到普通内存空间的某些固定地址。这些基于内存映射的设备寄存器看起来与一般的数据对象没啥两样。在《慎重使用》中提到ARM Evaluator-7T 的特殊寄存器的定义为:
typedef uint32_t special_register;
在嵌入式应用中,许多设备有时候不仅仅与一个寄存器打交道,有时可能与多个寄存器的集合同时打交道。在Evaluator-7T板子上,串口UART就是一个很好的例子。在这个板子上有两个UART,UART0和UART1。每个UART都由6个特殊寄存器控制。我们可以通过一个数据结构来表示这些寄存器的集合:

注意:数据结构UART和标识符UART的不同使用方法和位置。

typedef struct UART UART;
struct UART
{
  special_register ULCON;
  special_register UCON;    /*控制*/
  special_register USTAT;     /*状态*/
  special_register UTXBUF;    /*发送缓冲*/
  special_register URXBUF;    /*接收缓冲*/
  special_register UBRDIV;   
};
UART0对应的特殊寄存器被映射到0x03FFD000。我们有两种方法来访问该寄存器,一种是《智用篇》中提到过的宏定义方法:
#define UART0 ((UART *)0x03FFD000)
另一种是通过常量指针:
UART *const UART0  = (UART *) 0x03FFD000;

2.使用volatile
  《慎重使用》提到,如果你不希望编译器对你的代码作优化以防止出现你预想不到的情况,那么使用volatile是不二之选。显然,要访问串口的设备寄存器,我们必须要关掉编译器优化。现在,volatile可以大显身手了。我们修改前面的定义为:
#define UART0 ((UART volatile *) 0x03FFD000)
或:
UART volatile *const UART0  = (UART *) 0x03FFD000;
如果使用后者(常量指针),就建议做强制转化:
UART volatile *const UART0  = (UART volatile *)0x03FFD000;
但这并不是必须。对于任意类型T,c编译器提供T指针到volatile T指针的标准内置转化,就如同T指针到const T指针的转化,整个过程自动完成。另外,将一个对象定义为volatile类型,那么该对象中的所有成员也将成为volatile类型。显然,在UART0前面加volatile类型,不可避免在其它地方也必须要加上volatile。
比如,我们有下面的函数实现串口的字符串输出:
void put(char const *s, UART *u);
如果UART0是指向UART对象的volatile指针,那么如下调用会有什么问题呢:
put("hello, world\n", UART0);   
编译出错通不过!因为编译器不会将volatile UART指针转化为UART指针,所以我们能做的就是将其强制转化:/*UART == struct UART*/
put("hello, world\n", (UART *)UART0);/*volatile UART -> UART*/
这个强制转化虽然骗过了编译器,但在运行态(run time)可能会出问题。因为这时编译器将volatile类型UART0当做非volatile类型使用。为了避免这个缺陷,可以这样声明:
void put(char const *s, UART volatile *u);

注意:在这里加了volatile之后,在其它相关的地方别忘了也要加上volatile!

2.准确地构造寄存器
请转到http://blog.sina.com.cn/u/4a317b790100051o
继续阅读剩余部分
您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-5-13 10:15 , Processed in 0.061934 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

快速回复 返回顶部 返回列表