本帖最后由 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));
|