本帖最后由 Cube 于 2011-3-29 16:42 编辑
新唐Cortex-M0通用头文件NUC1xxM051Seriescfg.h使用说明
菜农HotPower@163.com 2011.3.28 于雁塔菜地
菜农HotPower长期致力于MCU头文件的研究,头文件NUC1xxM051Seriescfg.h归属菜农的红杏系列。
它的宗旨是简化特殊寄存器的**,快捷地访问并可控制到位。
1.文件路径
NUC1xxM051Seriescfg.h包含了对新唐Cortex-M0两大系列NUC1xx和M05x所有寄存器的定义。可以
共用,也可分别引用。
对于NUC1xx系列:可将本文件拷贝到...\CMSIS\CM0\DeviceSupport\Nuvoton\NUC1xx内。
对于M05x系列: 可将本文件拷贝到...\CMSIS\CM0\DeviceSupport\Nuvoton\M051Series内。
2.工程引用
NUC1xxM051Seriescfg.h是对新唐头文件<NUC1xx.h>或<M051Series.h>的扩展和兼容,而非替代。
对于Nuc1xx用户:
#include "nuc1xx.h"
#include "NUC1xxM051Seriescfg.h"
对于M05x用户:
#include "M051Series.h"
#include "NUC1xxM051Seriescfg.h"
3.使用方法
现在以菜农新唐Cortex-M0助学开发板上的发光二极管的控制为例说明红杏头文件的使用。
该发光二极管L6位于NUC120RE3AN的GPA12.
在target.h中有如下宏定义:
#define PortLed PAs//Led端口GPIO
#define PmdLed PMD12//Led位Px.Pin12
#define PinLed Pin12//Led位Px.Pin12
故Led被绑定在GPA12上。其初始化在system.cpp中,Led初始化代码被添加在PortInit()函数中。
void SystemObj:ortInit(void)
{
PortLed.PMD.Bits.PmdLed = GPIO_PMD_OUTPUT;//设置LED为输出模式
}
此写法是红杏系列的手法,其特点就是不易出错,想错都难。
其功劳在于Bits位域结构与联合。Bits显式地指明其后的PmdLed是PortLed.PMD寄存器的一个位.
3.1 假若想用寄存器的结构方法写,可有如下多种表示方法:
PortLed.PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;
PAs.PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;
PORTs.Px[0].PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;//0-PA,1-PB,2...
PORTs.PA.PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;
3.2 假若想用位域的结构方法写,可有如下多种表示方法:
PortLed.PMD.Bits.PMD12 = GPIO_PMD_OUTPUT;
PAs.PMD.Bits.PMD12 = GPIO_PMD_OUTPUT;
PORTs.Px[0].PMD.Bits.PMD12 = GPIO_PMD_OUTPUT;//0-PA,1-PB,2...
PORTs.PA.PMD.Bits.PMD12 = GPIO_PMD_OUTPUT;
3.3 假若想用寄存器的结构指针方法写,可有如下多种表示方法:
Px(0)->MD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;//0-PA,1-PB,2...
PORTx(0)->MD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;//0-PA,1-PB,2...
3.4 假若想用位域的结构指针方法写,可有如下多种表示方法:
Px(0)->MD.Bits.PMD12 = GPIO_PMD_OUTPUT;//0-PA,1-PB,2...
PORTx(0)->MD.Bits.PMD12 = GPIO_PMD_OUTPUT;//0-PA,1-PB,2...
红杏方法太多,这里只列举常用的四种写法。
其中结构数组或结构指针数组适应于未知端口号的场合。
4.位域的安全性
红杏系列以Bits指出右值为位域的值,以Regs指出右值为寄存器的值。首先这样减少了出错的机会。
由于寄存器操作某个位实际是由“读、改、写”三个阶段组成的,故寄存器的写法可能会产生错误。
如上的PMD寄存器,它的位域PMDxx由2个位及4种IO方式组成:
GPIO_PMD_INPUT=0,GPIO_PMD_OUTPUT=1,GPIO_PMD_OPENDRAIN=2,GPIO_PMD_QUASI=3
寄存器写法:
PAs.PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;
此写法可能产品错误,因为“|=”是置位,应该先清除之。完整正确的方法如下:
PAs.PMD.Regs &= ~(0x03 << GPIO_PMD_PMD12);
PAs.PMD.Regs |= GPIO_PMD_OUTPUT << GPIO_PMD_PMD12;
而用位域则不需要这种考虑:
PAs.PMD.Bits.PMD12 = GPIO_PMD_OUTPUT;
故位域要比寄存器的写法直观和安全。
5.位域的效率低下
以上主要讲解了位域的优点,当需要对某个端口的数个位同时操作时,就显示出了位域的效率低下的
问题。
对于一位控制,下面的写法与寄存器的一样简洁:
PortLed.DOUT.Bits.PinLed = 1;//LED灭(高电平)
PortLed.DOUT.Bits.PinLed = 0;//LED亮(低电平)
但是对于超过一位控制或要求一个端口同时控制多个位时,显然必须用寄存器的写法:
PAs.DOUT.Regs |= ((1 << Bit0) | (1 << Bit1) | (1 << Bitn));
PAs.DOUT.Regs &= ~((1 << Bit0) | (1 << Bit1) | (1 << Bitn));
显然寄存器的写法效率明显要超过位域的写法,代码要优化很多。
6.红杏与库效率的差距
现在以库函数DrvGPIO_Open()为例,比较其差距。
int32_t DrvGPIO_Open(E_DRVGPIO_PORT port, int32_t i32Bit, E_DRVGPIO_IO mode)
{
volatile uint32_t u32Reg;
if ((i32Bit < 0) || (i32Bit > 16))
{
return E_DRVGPIO_ARGUMENT;
}
u32Reg = (uint32_t)&GPIOA->MD + (port*PORT_OFFSET);
if ((mode == E_IO_INPUT) || (mode == E_IO_OUTPUT) || (mode == E_IO_OPENDRAIN))
{
outpw(u32Reg, inpw(u32Reg) & ~(0x3<<(i32Bit*2)));
if (mode == E_IO_OUTPUT)
{
outpw(u32Reg, inpw(u32Reg) | (0x1<<(i32Bit*2)));
}else
if (mode == E_IO_OPENDRAIN)
{
outpw(u32Reg, inpw(u32Reg) | (0x2<<(i32Bit*2)));
}
}else
if (mode == E_IO_QUASI)
{
outpw(u32Reg, inpw(u32Reg) | (0x3<<(i32Bit*2)));
}else
{
return E_DRVGPIO_ARGUMENT;
}
return E_SUCCESS;
}
红杏的写法:
PORTs.Px[port].PMD.Regs |= mode << (16 + (i32Bit *2));
Px(port)->MD.Regs |= mode << (16 + (i32Bit *2));
|