最近在使用Nordic最新的蓝牙芯片nRF52832开发过程中,遇到了一些需要对内存地址进行操作的测试。其中使用了(*(volatile unsigned int *)0xE000EDFC)
的语法,并通过宏定义进行解析。本文将对这种用法进行分析。
代码解析
首先,让我们看下面这段代码:
#define ARM_CM_DEMCR (*(volatile unsigned int *)0xE000EDFC)
#define ARM_CM_DWT_CTRL (*(volatile unsigned int *)0xE0001000)
#define ARM_CM_DWT_CYCCNT (*(volatile unsigned int *)0xE0001004)
对这段代码进行结构分析。由于nRF52832是Cortex-M4内核,在ARM处理器中,只能将其识别为一个十六进制数值,而无法自动区分其具体是数据还是地址。
使用(unsigned int *)0xE000EDFC
进行强制类型转换,表示该数值为一个无符号整型地址指针值。同时,关键字volatile
告诉编译器指针指向的内容是易变的,可能会被硬件等意外因素修改。
而*(volatile unsigned int *)0xFFE00000
表示获取指针所指向地址处的内容。
采用#define
宏的方式,在用户程序中对ARM_CM_DEMCR
的操作等同于在地址0xE000EDFC
上进行读写操作。
应用
如前所述,在32位处理器中,如果要对一个32位的内存地址进行访问和操作,可以使用类似上述的语法。通过强制类型转换和指针操作,可以对指定地址的内存内容进行读写。
tmp = ARM_CM_DEMCR;//读
ARM_CM_DEMCR = 0x55;//写
使用volatile修饰是因为它的值可能会改变,我们假设在一个循环操作中需要不停地判断一个内存数据,例如要等待ARM_CM_DEMCR的flag标志位置位,因为ARM_CM_DEMCR是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把ARM_CM_DEMCR读取到寄存器中,然后不停地判断寄存器相应位,而不会再读取ARM_CM_DEMCR.
而实际工程中,程序例如中断事件会改变ARM_CM_DEMCR,而寄存器相应位没有更新,会造成死循环了。如果volatile来修饰,那每次要操作一个变量的时候会都从内存中读取一次。
嵌入式系统编程中要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。
对于不同的计算机体系结构,设备可能是端口映射,也可能是内存映射的。如果系统结构支持独立的IO地址空间,并且是端口映射,就必须使用汇编语言完成实际对设备的控制,因为C语言并没有提供真正的“端口”的概念。
volatile关键字的用途
volatile的意思是告诉编译器,在编程源代码时,对这个变量不要使用优化,C语言中可能会优化一些运算过程中的变量值导致最后的结果不对,这里就不多说了。
volatile还可以防止编译器优化去掉某些语句,在arm中假如需要写1清中断,举例子如下:
#define INTPAND *(volatile unsigned int *)0x560012300
INTPAND = INTPAND; // 清中断
INTPAND = INTPAND;
这种操作,如果没有volatile修饰,编译器就很有可能会去掉INTPAND = INTPAND;
,相当于没这句话了。
在嵌入式编程中,当地址是io端口的时候,读写这个地址是不能对它进行缓存的(有cache才),比如写这个io端口的时候,如果没有volatile修饰,编译器会先把值先写到一个缓冲区,到一定时候再写到io端口,这样就不能使数据及时的写到io端口,有了volatile修饰就会直接写到io端口,从而避免了读写io端口的延时。
编译器对代码的优化
再说说编译器的优化,CPU在执行的过程中,因为访问内存的速度远没有cpu的执行速度快,为了提高效率,引入了高速缓存cache。
C编译器在编译时如果不知道变量会被其它外部因素(操作系统、硬件或者其它线程)修改,那么就会对该变量进行优化(当然也有些IDE可以设置优化等级)
,这个变量在CPU的执行过程中会被放到高速缓存cache去,进而达到对变量的快速访问。
在一些寄存器变量或数据端口的使用中,因为寄存器变量本身也是靠cache来处理,为了避免引起错误,也可以使用volatile修饰符。
如果变量是被外部因素改变,那么cpu就无法判断出这个变量已经被改变,那么程序在执行的过程中如果使用到该变量,还会继续使用cache中的变量(已经改变)
,需要到内存地址中更新,所以变量在执行的过程中不能被放到cache中。
总结
使用volatile的目的就是让对volatile变量的存取不能缓存到寄存器,每次使用时需要重新存取。在嵌入式开发中这种用法很常见也很关键,需要掌握。
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !