1 寄存器地址的定义:
#define UART_BASE_ADRS (0x10000000) /* 串口的基地址 */
#define UART_RHR *(volatile unsigned char *)(UART_BASE_ADRS + 0) /* 数据接受寄存器 */
#define UART_THR *(volatile unsigned char *)(UART_BASE_ADRS + 0) /* 数据发送寄存器 */
2 寄存器读写操作:
UART_THR = ch; /* 发送数据 */
ch = UART_RHR; /* 接收数据 */
也可采用定义带参数宏实现
#define WRITE_REG(addr, ch) *(volatile unsigned char *)(addr) = ch
#define READ_REG(addr, ch) ch = *(volatile unsigned char *)(addr)
3 对寄存器相应位的操作方法:
定义寄存器
#define UART_LCR *(volatile unsigned char *)(UART_BASE_ADRS + 3) /* 线控制寄存器 */
定义寄存器相应位的值
#define CHAR_LEN_5 0x00
#define CHAR_LEN_6 0x01
#define CHAR_LEN_7 0x02
#define CHAR_LEN_8 0x03 /* 8 data bit */
#define LCR_STB 0x04 /* Stop bit control */
#define ONE_STOP 0x00 /* One stop bit! */
#define LCR_PEN 0x08 /* Parity Enable */
#define PARITY_NONE 0x00
#define LCR_EPS 0x10 /* Even Parity Select */
#define LCR_SP 0x20 /* Force Parity */
#define LCR_SBRK 0x40 /* Start Break */
#define LCR_DLAB 0x80 /* Divisor Latch Access Bit */
定义寄存器相应位的值另一种方法
#define CHAR_LEN_5 0<<0
#define CHAR_LEN_6 1<<0
#define CHAR_LEN_7 1<<1
#define CHAR_LEN_8 (1<<0)|(1<<1) /* 8 data bit */
#define LCR_STB 1<<2 /* Stop bit control */
#define ONE_STOP 0<<2 /* One stop bit! */
#define LCR_PEN 1<<3 /* Parity Enable */
#define PARITY_NONE 0<<3
#define LCR_EPS 1<<4 /* Even Parity Select */
#define LCR_SP 1<<5 /* Force Parity */
#define LCR_SBRK 1<<6 /* Start Break */
#define LCR_DLAB 1<<7 /* Divisor Latch Access Bit */
对寄存器操作只需对相应位或赋值
UART_LCR = CHAR_LEN_8 | ONE_STOP | PARITY_NONE; /* 设置 8位数据位,1位停止位,无校验位 */
4 对寄存器某一位置位与清零
对某一寄存器第7位置位
XX_CRTL |= 1<<7;
XX_CRTL &= ~(1<<7);
UART_LCR |= LCR_DLAB; /* 时钟分频器锁存使能 */
UART_LCR &= ~(LCR_DLAB); /* 禁止时钟分频器锁存 */
5 判断寄存器某一位是否置位或为0的方法
#define UART_LSR *(volatile unsigned char *)(UART_BASE_ADRS + 5) /* 线状态寄存器 */
#define LSR_DR 1<<0 /* Data Ready */
当UART_LSR的第0位为1时结束循环
while (!(UART_LSR & LSR_DR)) /* 等待数据接收完 */
=========================
例子:
rNFCONF = 0;
rNFCONF |= (1 << 1)|(1 << 0); //rNFCONF = 0x3
rNFCONF |= (1 << 2); //rNFCONF = 0x7
rNFCONF |= ~(1 << 3); //rNFCONF = 0xFFFFFFF7
rNFCONF =0xFFFFFFFF; //rNFCONF = 0xFFFFFFFF
rNFCONF &= ~(1<<0); //rNFCONF = 0xFFFFFFFE
rNFCONF = 0xFFFFFFFF; //rNFCONF = 0xFFFFFFFF
rNFCONF &= ~(0<<1); //rNFCONF = 0xFFFFFFFF
我这个天生愚钝的人,一直对这个搞的不是太明白,今天狠心把它搞个透彻。共有两种操作符,“&” 、“|”和“<<”。在程序中的操作形式有两种形式,一种是赋值、另一种就是对寄存器的位操作了。
赋值就是使用的“=”,而对寄存器的单独位操作就是使用“&=”和“|=”两种。
unsigned int i;
首先说赋值操作,i = ( 1 << 0) | ( 1 << 1); 执行完成后就是 i = 3;和i以前的值没有任何的关系。但是在表达式中 使用|(0 << 2),对结果是没有任何影响的。查看汇编程序前,一直以为是使用的另一个寄存器和当前的寄存器进行的与或操作,实际上编译器已经将处理成了 movl 0x03,exb 大概这样的形式。编译器已经将右值提前计算出来了。
另一种是i = (~(1 << 1))&(~(1 << 0)); i=0xfffffffc; 可以看出这两种操作的区别是一个是置位,另一个是清位,而两者的操作数是有区别的,分别对0x0和0xffffffff操作,不知道为什么。简单的记忆好了。
改变位的操作,分为清位和置位。在一行表达式中不要同时出现两种操作,它们是互斥的。
置位:i |= (1 << 0)| (1 << 1); 将第0位和第1位分别进行了置1的操作。
清位: i &= (~(1 << 1))&(~(1 << 0)); 将第0位和第1位分别进行了置零
对于那种一个功能包含两个数据位的寄存器,我们可以使用这种方式同时更改两位:
i |= (3 << (0*2))| (3 << (1*2)); 或者 i |= (3 << 0)| (3 << 2); i = 0x0f
i &= (~(3 << (0*2)))&(~(3 << (1*2));
也可以使用
i = 0xAA;
j = i;
j &= (~(0x03 << 0)) & (~(0x03 << 2));
j |= (0x01 << 0) | (0x01 << 2);
i = j;
这种形式可以保证只改变自己需要改变的寄存器。
简化写法为i = ( i & (~(0x03 << 0))) | (0x01 << 0);
或者直接写为 i = (i & 0xFFFFFFFC)| ();
下面来个更直接的 (_raw_readl(s3c2410_GPFCON) & (~(3 << 8)) | (1 << 8))
嵌入式系统头文件的寄存器定义
嵌入式系统在头文件中,经常看到如下定义
#define GPBCON (*(volatile unsigned long *)0x56000010)
volatile 是表示易变的,每次读取都从地址里读取,不优化;(volatile unsigned long *)是指针强制类型转换(注意这里是转换为指针,不是指针变量,一个地址可以看做是指针,而存放指针的是指针变量),表示指针指向unsigned long 类型;然后第二层括号是该指针的值,对应指针指向的地址为0x56000010;最后的取值符号*表示指向该地址,相当于读写该地址的值,读写的值是unsigned long 类型。 |