首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

C语言自定义寄存器操作的一些方法

C语言自定义寄存器操作的一些方法

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 类型。
返回列表