本文目录
寄存器操作通常使用较少,因为已经有系统的库函数接口供给我们使用。本文主要为了了解如何使用寄存器来编写驱动,这里要根据实际的开发板手册进行编写。
一、c语言位操作
在操作寄存器前,我们需要复习一下c语言中的位操作。方便我们后续编写gpio寄存器。
1. 按位逻辑运算符
(1) &(按位与):有0为0,全1为1。
int a=153; // 1001 1001
int b=60; //0011 1100
a &= b; //a=a&b;
//(1001 1001) & (0011 1100)
结果为:
a=24 //0001 1000;
(2)|(按位或):有1为1,全0为0。
int a=153; //1001 1001
int b=60; //0011 1100
a |= b; // a= a | b;
//(1001 1001) | (0011 1100)
结果为:
a=189 //1011 1101;
(3)~(非):所有位取反,1变0,0变1。
~(0001 1100) //表达式
(1110 0011) // 结果值
(4)^(异或):位值不同为1,相同为0。
(1100 0001) ^ (0101 1011)
结果为:
(1001 1010)
2. 位移运算符
(1)左移:<<
int val = 1; //0000 0001
val <<= 2; //(0000 0001) << 2
结果为:
(0000 0100)
(2)右移:>>
int val = 16; //0001 0000
val >>=3; //(0001 0000) >> 3
结果为:
(0000 0010)
3. 寄存器位操作用法
(1)置1位( |= ): 将寄存器MODER第8位置1,第21位置1。
GPIOC->MODER |= (1<<8);
GPIOA ->MODER |= (1<<21);
(2)清0位( &=~ ): 将寄存器OTYPER第4位置0。
GPIOC->OTYPER &=~ (1<<4);
(3)切换位( ^= ): 是指打开已关闭的位,或关闭已打开的位。可以使用按位异或运算符(^)切换位。
val为0000 1111 ,MASK是1011 0110
val ^= MASK;
(0000 1111) ^ (1011 0110)
结果为
(1011 1001)
(4)掩码: 隐藏了val中的其他位。
int val=0xff; // 1111 1111
int MASK=2; // 0000 0010
val &= MASK
//val中除了1号位以外的所有位都设置为0。
//1号位的值不变。这个过程叫做使用掩码,掩码中的0隐藏了val中的其他位。
二、gpio寄存器编写流程(如操作PG_13口。实现功能:按下key按键时,蜂鸣器响,灯亮。)
具体需要根据开发板手册来使用。这里为了演示如下:
1. 需要操作的寄存器
(1)使得gpio正常使用,一般配置以下寄存器即可:
a.功能寄存器(Port Controller Register)。 //模式
b.数据寄存器 (data Register)。 //读写数据
(2)以下寄存器不配置,使用默认即可。
a.驱动等级寄存器。(Multi-Driving Register) //默认等级1级即可
b.上下拉寄存器。(PULL Register) //默认无上下拉即可。
2. 获取相应模块内存映射的基地址。(端口控制寄存器的基地址如下图所示)
由于端口寄存器都在PIO模块下,所以需要获取PIO模块的地址作为基地址。
#define PIO (0X01C20800) //PIO的基地址。
3. 获取PG寄存器的起始地址
如:GPIOG_13端口在PG第1组寄存器 PG Configure Register 1中。要操作GPIOG_13就要获取PG Configure Register 1寄存器的地址。
寄存器地址:基地址+偏移量。
PG Configure Register 1的地址为:0X01C20800 + 0XDC
。
4. 地址映射:将物理地址映射为虚拟地址。
设备的内存或寄存器可能位于物理内存的某个位置,而内核需要通过虚拟地址来访问这些设备内存。ioremap 函数将物理地址空间的某一块区域映射到内核的虚拟地址空间,使得内核可以通过该虚拟地址来访问物理设备的内存区域。
内核通常使用虚拟地址来访问内存,因为虚拟地址具有更好的管理和保护机制,直接使用物理地址会带来很多复杂的管理工作。尤其是对设备寄存器或共享内存的访问,通常是通过内存映射的方式进行的。ioremap
是访问这些 I/O 内存区域的标准方法。
(1)由于我们可能操作多个PG口,所有我们不妨将整个的PG组进行地址映射。将PG第0组的地址映射即可。
此时PG第0组寄存器起始地址为:0X01C20800 + 0XD8
。将该地址作为上述函数的addr参数。
(2)映射内容的大小:PG组共有9
个寄存器。则需要映射大小为36
个字节。将该字节数36
大小传入size参数中。
#define PIO (0X01C20800) //PIO的基地址。
#define PG_BASE ((PIO)+ 0XD8