C语言访问MCU寄存器的三种方式

发布时间:2019-1-22 10:37    发布者:xyd2018
关键词: C语言、寄存器
1.对C编译器进行语法扩充
对C编译器进行语法扩充。例如MCS51系列单片机的C-51语法中扩充了sfr关键字,举例如下:
sfr P0 = 0x80;
这样操作0x80单元直接写P0即可。

又如AtmelAVR系列单片机,其ICCAVR和GCCAVR编译器都没有定义新的数据类型,只能采用标准C的强制类型转换和指针来实现访问MCU的寄存器。而IAR和CodeVisionAVR编译器对ANSI C进行了扩充,定义了新的数据类型,使C语言可以直接访问MCU的有关寄存器,例如在IAR中可以使用:

SFR_B(DDRB, 0x28);

CodeVisionAVR中可以使用:
sfrb DDRB = 0x28;



2.使用标准C的强制类型转换和指针来实现


采用标准C的强制转换和指针的概念来实现访问MCU的寄存器,例如:
#define DDRB (*(volatile unsigned char *)0x25)

分析如下:

1.(unsigned char *)0x25中的0x25只是个值,前面加(unsigned char *)表示把这个值强制类型转换为unsigned char型的指针。再在前面加”*”,即*(volatile unsigned char *)0x25表示对这个指针解引用,相当于
(unsigned char *)0x25是一个指针p,而这个宏定义为#define DDRB *p。
这样当读/写以0x25为地址的寄存器时,直接书写DDRB即可,即写:
DDRB = 0xff;

相当于:
unsigned char *p, i; p = 0x25; i = *p;        //把地址为0x25单元中的数据读出送入i变量*p = 0xff;     //向地址为0x25的单元中写入0xff

这样经过一层宏定义的封装就变得直观和方便的多了。

2.关键字volatile确保本指令不会以为C编译器的优化而被省略,且要求每次直接读值。例如使用while(*(unsigned char *)0x25)时,有时系统可能不能真正去读0x25的值,而是用第一次读出的值,如果这样,这个循环可能就是个死循环。用了volatile则要求每次都去读0x25的实际值。

GCCAVR工具链中就使用了这样的方式,例如在iomx8.h文件中一个定义如下:
#define PORTB _SFR_IO8(0x25)

而在sfr_defs.h中可以找到如下两个宏定义:

#define _SFR_IO8(io_addr)     _MMIO_BYTE((io_addr)+0x20)#define _MMIO_BYTE(mem_addr)  (*(volatile unit8_t *)(mem_addr))


实质上与直接的强制类型转换和指针定义是一样的。


3.使用结构体实现


使用指针的方式来访问特殊功能寄存器的优势在于完全符合标准的ANSI-C,而无需扩展语法,形成“方言”,拥有更好的兼容性和可移植性。

这种方式适合简单的应用程序,而当系统用到多个同种外设时,就需要为每一个这种外设定义寄存器,这样就会使程序的维护变得非常困难。而且,由于每次寄存器操作都会有对应的常量存储在程序Flash里,为每个寄存器定义单独的指针还会增加程序代码。

为了简化程序代码,可以将寄存器组定义为结构体,而将外设当做指向这个结构体的指针。例如:

typedef struct {    volatile unsigned long DATA;    //0x00    volatile unsigned long RSR;     //0x04    unsigned long RESERVED0[4];     //0x08-0x14    volatile unsigned long FLAG;    //0x18    ... }UART_TypeDef;#define Uart0 ((UART_Type *)0x40003000)#define Uart1 ((UART_Type *)0x40004000)#define Uart2 ((UART_Type *)0x40005000)int getkey(UART_TypeDef * uartptr) {    while((uartptr->FLAG & 0x40) == 0);    //无数据,等待    return uartptr->DATA;                  // 读取字符}int main(void) {    unsigned long data;    data = getkey(Uart0); }


在这种设定下,同一个外设寄存器的结构体可以被多个外设实体共用,这样也使得程序维护变得容易。另外,由于立即数存储的减少,编译出的程序代码也会变小。

本文来自bill_20106029的博客

想要了解嵌入式物联网相关技术的可以联系宋工企鹅号三五二四六五九零八八
Tel:173--1795--1908
免费试听C语言、电子PCBSTM32LinuxFPGA、JAVA、安卓等。
想学习的你和我联系预约就可以免费听课了。



本文地址:https://www.eechina.com/thread-559893-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表