指针与寄存器
指针可以让我们申请内存空间,并使用指针读写指定的内存区域。但还有另外一种情况,我们想把数据放到指定的内存区域,或说在某些寄存器与内存统一编址的平台,想要访问某段寄存器,并更改寄存器的值。
通过查看芯片手册,知道设备寄存器地址,然后通过把地址值转换成指针对这段寄存器内存进行读写。
我们通过取地址符(&)来获得内存地址,同理也可以知道指定内存地址值,把地址值转换成指针。
指针在驱动程序中的角色
外设备控制寄存器,在驱动编写时常需要根据需对外设进行设置,这就需要配置外设的功能控制寄存器来实现。所谓功能控制寄存器,就是为了实现某些功能,需要很多配置选项来支持功能的多样性,功能越少,控制器越简单。比如简单的IO端口控制器,只是需要配置 IO端口的模式、速度、驱动强度、复用等几项。更复杂的功能比如LCD显示设备外设可能控制器就很复杂,需要配置很多的寄存器来实现这些各种显示功能。
某芯片GPIO端口寄存器配置地址图
以上是芯片GPIO端口复用寄存器地址段
- 第一列GPIO端口复用功能控制寄存器地址
- 第二列GPIO端口名称
- 第三列GPIO端口复用控制寄存器占用的位宽(32位)即4字节。
- 第四列GPIO端口控制寄存器是否可读写(R/W可读可写)
- 第五列GPIO端口控制寄存器默认值
- 第六列GPIO端口具体在手册中的章节及页码
具体GPIO端口复用控制寄存器某些位的功能配置
从红色标注可以看到,这是一个以0x2E00_0000为基地址段的内存空间。对基地址进行偏移(60H)从而读写具体的GPIO端口复用控制寄存器。这个与定义结构然后进行偏移读写变量是一个操作原理。
具体操作:
把 Address: 20E_0000h base + 60h offset = 20E_0060h 由于这个控制寄存器为4字节所以直接转换成int类型指针是变好的,int* p = (int*)20E_0060h,后面再使用位操作配置寄存器各个位(常使用读-改-写方式)。
所以我们经常会在UBOOT启动代码或其它底层寄存器操作驱动中有把寄存器值进宏定义等式如
/* 定义寄存器地址宏 */
define GPIO_IO01_MD 20E_0060h
define GPIO_IO02_MD 20E_0064h
/* 定义寄存器地址转成指针,并解引用获得内存空间 */
#define __REG(x) (*((volatile u32 *)(x)))
#define __REG16(x) (*((volatile u16 *)(x)))
#define __REG8(x) (*((volatile u8 *)(x)))
/* 对寄存器内存空间进行赋值 */
__REG(GPIO_IO01_MD) = 0xf;
只为理解指针进行的一个流程演示,具体芯片寄存器配置试依各芯片厂商提供的宏或函数来实现。
所以如果我们知道一个内存地址,那么通过把它转换成合适的指针类型,就可以通过这个指针对这段内存空间进行读写操作。
也就是说如果我们知道内存的某个很大内存空间没有人使用,那么我们完全可以把它转成指针,然后想怎么玩怎么样,但很容易出问题就是哈哈。