"); //-->
用C语言函数写ARM寄存器预定义分析
我们在用C语言函数写ARM寄存器的预定义时,会用到这样的格式,如:
#define GPFCON (*(volatile unsigned *)0x56000050)
#define GPFDAT (*(volatile unsigned *)0x56000054)
一定要用这种格式吗,其中关键字volatile['vɔlətail]是什么意思呢,是必须的吗?我们一点点来看。
首先这条语句写成
编译是通不过的
int关键字的大小
这里解释一下为什么用int可以不会溢出,其实int多大,取决于你的系统,以及编译器,你如果是16位的系统,一个int 就是16位,也就是占2个字节,无符号整数最大就是2^16,32位就是2^32,这里ARM是32位的,因此用int是不会溢出。
关键字volatile
再看一下关键字volatile的作用
volatile ['vɔlətail] 的英文解释如下
· adj. 爆炸性的;不稳定的;挥发性的;反覆无常的
· n. 挥发物;有翅的动物
其是嵌入式开发所常用到的一种变量,一个定义为volatile的变量是说这变量可能会被意想不到的改变,这样,编译器就不会去假设这个变量的值。也就是说,优化器在使用到这个变量时必须每次都小心的重新读取这个变量的值,而不是使用保存在寄存器里的备份。例如:
Ø 并行设备的硬件寄存器(如状态寄存器)
Ø 一个中断服务子程序中会访问到的非自动变量(Non-automati variable)
Ø 多线程应用中被几个任务共享的变量
嵌入式系统程序员经常同硬件、中断、TROS等打交道,都要求volatile变量。
探究volatile变量,我们来回答下面三个问题
1. 一个参数可以既是const还是volatile吗?
2. 一个指针可以使volatile吗?
3. 下面的函数有错误码
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
我们来一一作答
1. 一个参数可以既是const又是volatile。例:只读的状态寄存器,它是volatile因为它可以被”意想不到的”改变;它是const因为程序不应该试图去修改它;
2. 指针可以是volatile,尽管这并不常见。例:当一个中断服务子程序修改一个指向buffer的指针时。
3. 这段代码是个恶作剧,其目的是用来返回*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a*b;
}
由于*ptr的值被“意想不到的”改变,因此a和b可能是不同的。因此这段代码返回的可能不是你所期望的平方值。正确的代码如下:
int square(volatile int *ptr)
{
int a;
a = *ptr;
return a*a;
}
再有volatile关键字告诉编译器不要持有变量的临时性拷贝。一般用在多线程程序中,以避免在其中一个线程操作该变量时,将其拷贝入寄存器。如:
A线程将变量复制入寄存器,然后进入循环,反复检测寄存器的值是否满足一定条件(它期望B线程改变变量的值),在这种情况下,当B线程改变变量的值时,已改变的值对其在寄存器中的值没有影响,所以A线程进入死循环。
那在这里volatile的作用是什么呢?
以 #define GPFDAT (*(volatile unsigned *)0x56000054) 为例
volatile告诉编译器这个GPFDAT的内容是随时都有可能发生变化的,每次使用它的时候必须从其中读取,因而编译器生成的可执行代码会重新从GPFDAT中读出数据进行操作。
如果不用volatile,那优化的做法就是由于编译器发现两次从GPFDAT中读出的数据之间的代码很可能没有对GPFDAT进行操作(这是很有可能的,在这里对GPFCON没有影响,但是对于GPFDAT,当将GPFCON配置成IN模式时,读GPFDAT就有可能出现错误),它会自动使用上次读的数据,而不是重新从GPFDAT中读取,这样就很容易出现错误。所以说volatile可以保证对特殊地址的稳定访问,不会出错。
本文在前人的基础上写成,感谢那些给予我帮助的童鞋们
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。