- 相关推荐
C语言访问MCU寄存器的三种方式
C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。那么C语言访问MCU寄存器的三种方式分别是怎样的呢?以下仅供参考!
1.对C编译器进行语法扩充
对C编译器进行语法扩充。例如MCS51系列单片机的C-51语法中扩充了sfr关键字,举例如下:
sfr P0 = 0x80;
这样操作0x80单元直接写P0即可。
又如Atmel的AVR系列单片机,其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); }
在这种设定下,同一个外设寄存器的结构体可以被多个外设实体共用,这样也使得程序维护变得容易。另外,由于立即数存储的减少,编译出的程序代码也会变小。
拓展:c语言的优点介绍
1、简洁紧凑、灵活方便
C语言一共只有32个关键字,9种控制语句,程序书写形式自由,区分大小写。把高级语言的基本结构和语句与低级语言的实用性结合起来。C 语言可以像汇编语言一样对位、字节和地址进行操作,而这三者是计算机最基本的工作单元。
2、运算符丰富
C语言的运算符包含的范围很广泛,共有34种运算符。C语言把括号、赋值、强制类型转换等都作为运算符处理。从而使C语言的运算类型极其丰富,表达式类型多样化。灵活使用各种运算符可以实现在其它高级语言中难以实现的运算。
3、数据类型丰富
C语言的数据类型有:整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类型等。能用来实现各种复杂的数据结构的运算。并引入了指针概念,使程序效率更高。
4、表达方式灵活实用
C语言提供多种运算符和表达式值的方法,对问题的表达可通过多种途径获得,其程序设计更主动、灵活。它语法限制不太严格,程序设计自由度大,如对整型量与字符型数据及逻辑型数据可以通用等。
5、允许直接访问物理地址,对硬件进行操作
由于C语言允许直接访问物理地址,可以直接对硬件进行操作,因此它既具有高级语言的功能,又具有低级语言的许多功能,能够像汇编语言一样对位(bit)、字节和地址进行操作,而这三者是计算机最基本的工作单元,可用来写系统软件。
6、生成目标代码质量高,程序执行效率高
C语言描述问题比汇编语言迅速,工作量小、可读性好,易于调试、修改和移植,而代码质量与汇编语言相当。C语言一般只比汇编程序生成的目标代码效率低10%~20%。
7、可移植性好
C语言在不同机器上的C编译程序,86%的代码是公共的,所以C语言的编译程序便于移植。在一个环境上用C语言编写的程序,不改动或稍加改动,就可移植到另一个完全不同的环境中运行。
8、表达力强
C语言有丰富的数据结构和运算符。包含了各种数据结构,如整型、数组类型、指针类型和联合类型等,用来实现各种数据结构的运算。C语言的运算符有34种,范围很宽,灵活使用各种运算符可以实现难度极大的运算。
C语言能直接访问硬件的物理地址,能进行位(bit)操作。兼有高级语言和低级语言的许多优点。
它既可用来编写系统软件,又可用来开发应用软件,已成为一种通用程序设计语言。
另外C语言具有强大的图形功能,支持多种显示器和驱动器。且计算功能、逻辑判断功能强大。
【C语言访问MCU寄存器的三种方式】相关文章:
C++ 的三种访问权限与三种继承方式06-07
C语言三种循环语句04-28
在C语言中函数调用方式的区别01-07
C语言中的三种预处理功能05-20
C语言中函数之间地址传递方式04-10
C语言字符数组的几种输出方式05-21
C语言main函数的三种形式实例分析06-22
C语言算法及三种基本程序结构03-21