MCU中的特殊功能寄存器SFR,实际上就是SRAM地址已经确定的SRAM单元,在C语言环境下对其访问归纳起来有3种方法。
<strong>1.对C编译器进行语法扩充</strong>
对C编译器进行语法扩充。例如MCS51系列单片机的C-51语法中扩充了sfr关键字,举例如下:
<center>sfr P0 = 0x80;</center>
这样操作0x80单元直接写P0即可。
又如Atmel的AVR系列单片机,其ICCAVR和GCCAVR编译器都没有定义新的数据类型,只能采用标准C的强制类型转换和指针来实现访问MCU的寄存器。而IAR和CodeVisionAVR编译器对ANSI C进行了扩充,定义了新的数据类型,使C语言可以直接访问MCU的有关寄存器,例如在IAR中可以使用:
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
SFR_B(DDRB, 0x28);
</pre>
</div>
</br>
CodeVisionAVR中可以使用:
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
sfrb DDRB = 0x28;</pre>
</div>
</br>
<strong>2.使用标准C的强制类型转换和指针来实现</strong>
采用标准C的强制转换和指针的概念来实现访问MCU的寄存器,例如:
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
#define DDRB (*(volatile unsigned char *)0x25)
</pre>
</div>
</br>
<strong>分析如下: </strong>
1.(unsigned char *)0x25中的0x25只是个值,前面加(unsigned char *)表示把这个值强制类型转换为unsigned char型的指针。再在前面加”*”,即*(volatile unsigned char *)0x25表示对这个指针解引用,相当于
(unsigned char *)0x25是一个指针p,而这个宏定义为#define DDRB *p。
这样当读/写以0x25为地址的寄存器时,直接书写DDRB即可,即写:
<center>DDRB = 0xff; </center>
<strong>相当于:</strong>
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
unsigned char *p, i;
p = 0x25;
i = *p; //把地址为0x25单元中的数据读出送入i变量
*p = 0xff; //向地址为0x25的单元中写入0xff
</pre>
</div>
</br>
这样经过一层宏定义的封装就变得直观和方便的多了。
2.关键字volatile确保本指令不会以为C编译器的优化而被省略,且要求每次直接读值。例如使用while(*(unsigned char *)0x25)时,有时系统可能不能真正去读0x25的值,而是用第一次读出的值,如果这样,这个循环可能就是个死循环。用了volatile则要求每次都去读0x25的实际值。
GCCAVR工具链中就使用了这样的方式,例如在iomx8.h文件中一个定义如下:
<center>#define PORTB _SFR_IO8(0x25)</center>
而在sfr_defs.h中可以找到如下两个宏定义:
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr)+0x20)
#define _MMIO_BYTE(mem_addr) (*(volatile unit8_t *)(mem_addr))
</pre>
</div>
</br>
实质上与直接的强制类型转换和指针定义是一样的。
<strong>3.使用结构体实现</strong>
使用指针的方式来访问特殊功能寄存器的优势在于完全符合标准的ANSI-C,而无需扩展语法,形成“方言”,拥有更好的兼容性和可移植性。
这种方式适合简单的应用程序,而当系统用到多个同种外设时,就需要为每一个这种外设定义寄存器,这样就会使程序的维护变得非常困难。而且,由于每次寄存器操作都会有对应的常量存储在程序Flash里,为每个寄存器定义单独的指针还会增加程序代码。
为了简化程序代码,可以将寄存器组定义为结构体,而将外设当做指向这个结构体的指针。例如:
<div style="padding:10px;background:#f2f2f2;border:1px solid #595959;width:80%;margin:0 auto;font-size:14px;">
<pre style="white-space: pre-wrap; /*css-3*/ ;white-space: -moz-pre-wrap; /*Mozilla,since1999*/ ;white-space: -pre-wrap; /*Opera4-6*/ ;white-space: -o-pre-wrap; /*Opera7*/ ;word-wrap: break-word; /*InternetExplorer5.5+*/ ">
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);
}
</pre>
</div>
</br>
在这种设定下,同一个外设寄存器的结构体可以被多个外设实体共用,这样也使得程序维护变得容易。另外,由于立即数存储的减少,编译出的程序代码也会变小。
<strong>参考: </strong>
- <a href="http://blog.csdn.net/liming0931/article/details/7752248" target="_blank">http://blog.csdn.net/liming0931/article/details/7752248</a> <br />
- 《ARM Cortex-M0 权威指南》Joseph 著
本文转载自:<a href="http://blog.csdn.net/elegant__/article/details/48194281">bill_20106029的…;
转载地址:http://blog.csdn.net/elegant__/article/details/48194281
声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有,如涉及侵权,请联系小编邮箱:cathy@eetrend.com 进行处理。