Chocore 发表于 2013-10-25 14:16:47

coocox的CoX库之GPIO介绍

附件的word文档,值得看。CoX统一ARM Cortex MCU软件接口标准, 这篇文章带你了解CoX是怎么诞生的,也可以帮助了解一些底层软件思想。

1.GPIO接口设计思想
1.1CoX.GPIO发展过程,历史版本
   CoX第一版从2009年开始,从CoX诞生开始,CoX的目标就是要做到在M系列的CPU上实现平滑移植。所以,GPIO的实现,第一版首先实现了,IO的模式配置(输入、输出)和管脚的上拉、下拉配置;然后,实现了IO管脚的状态读写。具体可以从接口定义清晰的看出来:
typedef struct {
COX_Status (*Init) (COX_PIO_Dev pio);
COX_Status (*Dir)(COX_PIO_Dev pio, uint8_t dir);
uint8_t   (*Out)(COX_PIO_Dev pio, uint8_t level);
uint8_t   (*Read) (COX_PIO_Dev pio);
COX_Status (*Cfg)(COX_PIO_Dev pio, uint8_t index, uint32_t arg, uint32_t *pre_arg);
} COX_PIO_PI_Def;
typedef const COX_PIO_PI_Def COX_PIO_PI;
复制代码
这样的实现,确实可以大大减小IO操作的移植,因为我们在每个厂商实现一套API,以新唐为例:
COX_PIO_PI pi_pio =
{
      NUC_GPIO_Init,
      NUC_GPIO_SetDir,
      NUC_GPIO_Out,
      NUC_GPIO_Read,
      NUC_GPIO_Cfg
};
复制代码
在使用的时候,我们仅仅需要使用pi_pio的指针就可以调用GPIO的API操作了,而且这个指针还可以被驱动嵌套使用。有兴趣的可以参考NUC140-LB Board的CoOS例程,这个在www.coocox.org官网可以下载到。
然而,第一版有几个明显的不足:
1.      实现的功能很少,只有IO的基本配置和读写操作,没有外部中断实现,没有多功能配置实现,以及一些其他特殊的功能。
2.      采用了结构体的形式,代码的可读性大大降低,效率也不高。
3.      CoX代码不能搞定所有基本的事情,在使用CoX库的使用还必须和厂商库配套使用。
4.      CoX在第一版更多的注重外设模块的移植,而忽略了系统。
    所以,CoX需要改进、升级。经常一年多时间的积累,在2011年开始推出CoX 2.0版本,这个版本解决了上述所有的缺点的同时,保留了CoX设计的初衷——那就是在M系类CPU上面的通用性。下面,详细介绍2.0版的CoX.GPIO接口。
1.2通用强制接口
通用强制接口是提取的一套ARM Cortex M0/M3所有厂商系列MCU都具有的功能接口。本篇以新唐M051为例讲解CoX.GPIO,其他系列大同小异, 提取GPIO通用接口的时候,是从以下角度出发考虑的:
配置一个GPIO管脚线
方向配置:
      输入   
      输出
      硬件功能
外围功能配置:
Pad配置:
      驱动能力大小(电流)
      开源/推挽
      弱上拉/下拉电阻
GPIO管脚数据控制
输出高/低电平
获取管脚输入值
输入中断(EXTI)
上升沿检测
下降沿检测
上/下沿检测
低电平检测
高电平检测
APIs分组完成以下几大功能:
      配置GPIO管脚线的函数:
      xGPIODirModeSet
      xGPIOSPinDirModeSet
      xGPIOPinConfigure
      读回GPIO管脚线模式配置的函数:
      xGPIODirModeGet
      还有很方便的函数,可以将GPIO配置成想要的功能:
      xGPIOSPinTypeGPIOInput
      xGPIOSPinTypeGPIOOutput
      xSPinTypeADC
      xSPinTypeI2C
      xSPinTypeSPI
      xSPinTypeTimer
      xSPinTypeUART
      xSPinTypeACMP
      处理GPIO中断的APIs
      xGPIOPinIntCallbackInit
      xGPIOPinIntEnable
      xGPIOSPinIntEnable
      xGPIOPinIntDisable
      xGPIOSPinIntDisable
      xGPIOPinIntStatus
      xGPIOPinIntClear
      xGPIOSPinIntClear
      处理GPIO Pin状态的APIs
      xGPIOPinRead
      xGPIOSPinRead
      xGPIOPinWrite
      xGPIOSPinWrite
复制代码
1.3通用非强制接口
通用非强制接口是一部MCU通有的功能,而不是所有MCU都具有的功能接口:
      xGPIOSPinTypeGPIOOutputOD
      xGPIOSPinTypeGPIOOutputQB
      xSPinTypePWM
      xSPinTypeEXTINT
      xSPinTypeEBI
复制代码
CoX的宏定义的参数和APIs都是以' x '开头的, 体现出CoX接口的特征。比如将 GPIOA Pin0配置成输出模式, 代码如下:
xGPIODirModeSet(xGPIO_PORTA_BASE, xGPIO_PIN_0, xGPIO_DIR_MODE_OUT);
复制代码
函数和形式参数都是x开头。
1.4厂商库特色接口
特色接口是包括了通用性接口,和MCU特有功能的接口。比如:
void GPIOPinDebounceEnable(unsigned long ulPort, unsigned long ulPins);
复制代码
并不是通用强制型或者通用非强制型,而是MCU特有的功能,就是在厂商库特色接口这一组。

另外厂商库接口也实现了MCU其他所有的功能,比如:
void GPIOPinWrite(unsigned long ulPort, unsigned long ulPins, unsigned char ucVal);
复制代码
也实现了GPIO管脚线模式的配置,这个在CoX接口的xGPIOPinWrite也是这个功能。其实这个时候xGPIOPinWrite的实现方式如下:
#definex GPIOPinWrite(ulPort,ulPins,ucVal)                              \
      GPIOPinWrite(ulPort, ulPins, ucVal)
复制代码
进行了一次宏定义包装罢了,对应的参数也是进行的一次宏定义比如:
#define xGPIO_PIN_0             GPIO_PIN_0
复制代码
2.设计技巧简介
GPIO的CoX接口创新性的提出了Short Pin,比如PA0 是GPIOA的Pin0脚,它的定义如下:
#define PA0                     PA0
复制代码
自从有了Short Pin之后,对GPIO的操作简单多了,例如比如将 GPIOA Pin0配置成输出模式,并输出高电平, 代码如下:
xGPIODirModeSet(xGPIO_PORTA_BASE, xGPIO_PIN_0, xGPIO_DIR_MODE_OUT);
xGPIOPinWrite(xGPIO_PORTA_BASE, xGPIO_PIN_0, 1);
复制代码
现在用Short Pin作为参数,上面的功能可以这样实现:
xGPIOSPinTypeGPIOOutput(PA0);
xGPIOSPinWrite(PA0, 1);
复制代码
上面的Short Pin到底是如何实现的呢?看起来很神奇,以xGPIOSPinWrite为例说明它的实现过程:
#define xGPIOSPinWrite(eShortPin, ucVal)                                 \
      GPIOSPinWrite(eShortPin, ucVal)
#define GPIOSPinWrite(eShortPin, ucVal)                                  \
      GPIOPinWrite(G##eShortPin, ucVal)
复制代码
关于##, 其实是宏定义里面的高级用法,它是一个连接符,遇到此连接符,宏会一直展开下去,直到不能展开为止。G##eShortPin其实会连接为GPA0,而GPA0同样是个宏定义,如下:
#define GPA0                  GPIO_PORTA_BASE, GPIO_PIN_0
复制代码
GPIOPinWrite(GPA0, ucVal)
复制代码
会进一步展开为
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_0, ucVal),
复制代码
这个函数在厂商库里面定义了的,所以实现了Pin写的功能。
另外Short Pin对GPIO管脚的外设多功能复用操作也带来了极大的方便,比如配置PD5为I2C的clock脚功能,如下:
xSPinTypeI2C(I2C0SCK, PD5);
复制代码
是不是很简单!!!上面的实现如下:
#define xSPinTypeI2C(ePeripheralPin, eShortPin)                           \
do                                                                                                   \
{                                                                                                   \
         GPIOSPinConfigure(ePeripheralPin, eShortPin);                        \
         GPIOSPinFunctionSet(GPIO_FUNCTION_I2C,eShortPin);         \
}                                                                                                      \
while(0)
    #define GPIOSPinConfigure(ePeripheralPin, eShortPin)                  \
       GPIOPinConfigure(GPIO_##eShortPin##_##ePeripheralPin)
复制代码
上面的会连接成这样GPIOPinConfigure(GPIO_PD5_I2C0SCK), GPIO_PD5_I2C0SCK宏定义如下:
#define GPIO_PD5_I2C0SCK      0x00003510
这个是根据多功能复用进行的编码,视不同的芯片,这个编码方式灵活多变。

还有一些接口完全是为了移植方便性而产生的,比如xGPIOSPinToPort, 这个接口是由Short Pin就可以得到这个Pin所对应的PORT Base,而在CoX.SYSCTL中有一个接口xSysCtlPeripheralEnable2是使用外设地址Base为参数使能这个外设 (在SYSCTL中维护了一个外设BASE-ID-INT的表),它实现的时候,是进行了一个Base到外设ID的一个转换,最终还是调用xSysCtlPeripheralEnable使能外设的。但是有了这个功能,也很利于基于CoX驱动组件的移植性,例如在AD7415温度传感器是通过I2C接口进行数据通信的,在这个驱动组件头文件中, 只需要考虑一下四个元素就可以平滑的使得这个驱动组件移植到其他的MCU上(比如NUC1xx,或者STM32F1xx):
//
//! Config the device i2c Address
//
#define AD7415_I2C_ADDRESS      0x48

//
//! Config the devide i2c bus master
//
#define AD7415_MASTER_BASE   xI2C0_BASE

//
//! Config the i2c SDA pin
//
#define AD7415_PIN_I2CSDA      PA8

//
//! Config the i2c SCL pin
//
#define AD7415_PIN_I2CSCK      PA9
复制代码
因为有了xI2C0_BASE在驱动中就可以使能这个I2C外设,有了连接的管脚也就可以使能对应的GPIO PORT, 如xSysCtlPeripheralEnable2(AD7415_MASTER_BASE); 这里不在需要给出I2C0的外设使能ID,或者GPIOA的外设使能ID, 用户移植的时候只需要从硬件连接角度出发,用了那个I2C, 管脚是怎么连接的,而不需要考虑其他的元素。
3. GPIO接口使用示例与移植
下面给出一个CoX.GPIO的示例,都是使用的通用强制型的接口,因此下面的例子在所有Cortex M0/M3上都是平滑移植的, 类似一个简单的电灯程序。
void Blinky(void)
{
    unsigned long i;
      
    //
    // Initionalize system clock.
    //
    xSysCtlPeripheralClockSourceSet( 12000000,xSYSCTL_XTAL_12MHZ );
      
    //
    // Set GPIO port c pin 0 , 1 output mode.
    //
    xGPIODirModeSet( xGPIO_PORTC_BASE, xGPIO_PIN_0, xGPIO_DIR_MODE_OUT );
    xGPIODirModeSet( xGPIO_PORTC_BASE, xGPIO_PIN_1, xGPIO_DIR_MODE_OUT );
      
    while (1)
    {
       //
      // Delay some time.
      //
      for( i = 0; i < 0x1FFFF; i++ )
               
       //
      // Output high level.
      //
      xGPIOPinWrite( xGPIO_PORTC_BASE, xGPIO_PIN_0 | xGPIO_PIN_1, 1 );
               
      for( i = 0; i < 0x1FFFF; i++ )
               
         //
      // Output low level.
      //
      xGPIOPinWrite( xGPIO_PORTC_BASE, xGPIO_PIN_0 | xGPIO_PIN_1, 0 );
      }
      
}
复制代码

shinelovexi 发表于 2013-10-26 09:24:23

顶一个,好好,学习中

Chocore 发表于 2013-10-28 08:52:49

shinelovexi 发表于 2013-10-26 09:24 static/image/common/back.gif
顶一个,好好,学习中

其他的外设,uart, spi , i2c, timer ,pwm, adc等外设的cox通用接口标准都类似提取出来了。

hqgboy 发表于 2013-10-28 09:15:07

帮顶。

还在用寄存器的路过。

Chocore 发表于 2013-10-28 09:19:46

hqgboy 发表于 2013-10-28 09:15 static/image/common/back.gif
帮顶。

还在用寄存器的路过。

寄存器效率是比较高些。不过话说每个系列的Cox库也提供了寄存器接口,将各个寄存器的地址及每一位的定义也好了,方便用户直接操作寄存器或者实现自己需要的API。

McuPlayer 发表于 2013-10-28 09:41:40

CoX库使用了函数指针做struct member的方式
干脆一步到位,做成C++的得了,现在ARMCC和GCC都对C++支持的还不错
C++中没用到的member function会被编译器自动优化掉,而struct member可能就没这么幸运了

我现在正在把以前的代码从C转移到C++,由此获得的可维护性提升非常有意义

Shampoo 发表于 2013-10-28 09:43:05

求申请的套件。。。

Chocore 发表于 2013-10-28 09:50:19

Shampoo 发表于 2013-10-28 09:43 static/image/common/back.gif
求申请的套件。。。

你申请的哪个?coocox用户名是啥?

Chocore 发表于 2013-10-28 09:54:29

McuPlayer 发表于 2013-10-28 09:41 static/image/common/back.gif
CoX库使用了函数指针做struct member的方式
干脆一步到位,做成C++的得了,现在ARMCC和GCC都对C++支持的还 ...

使用函数指针做struct member的方式是我们CoX第一版本的哦, 后来的版本改变了,摒弃了这个方式。正如上面所说的:
第一版有几个明显的不足:
1.      实现的功能很少,只有IO的基本配置和读写操作,没有外部中断实现,没有多功能配置实现,以及一些其他特殊的功能。
2.      采用了结构体的形式,代码的可读性大大降低,效率也不高。
3.      CoX代码不能搞定所有基本的事情,在使用CoX库的使用还必须和厂商库配套使用。
4.      CoX在第一版更多的注重外设模块的移植,而忽略了系统。

最主要的就是CoX库不仅提供所有ARM Cortex MCU的通用API, 也需要提供MCU特有的API。 在保证库功能完整性基础之上,提取出的一套通用接口便于移植性。

Shampoo 发表于 2013-10-28 09:58:29

Chocore 发表于 2013-10-28 09:50 static/image/common/back.gif
你申请的哪个?coocox用户名是啥?

用户名:shaopus
GPRS模块

Chocore 发表于 2013-10-28 13:43:45

Shampoo 发表于 2013-10-28 09:58 static/image/common/back.gif
用户名:shaopus
GPRS模块

嗯我看到你申请了好几个模块。话说请告之一个会最先使用开发的。并最好在2周之内开发好驱动并开源分享。我会优先安排寄送此模块给你。
页: [1]
查看完整版本: coocox的CoX库之GPIO介绍