热度 22
2012-9-12 09:26
2148 次阅读|
2 个评论
转载请注明出处! 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通用接口的时候,是从以下角度出发考虑的: u 配置一个GPIO管脚线 l 方向配置: n 输入 n 输出 n 硬件功能 l 外围功能配置: l Pad配置: n 驱动能力大小(电流) n 开源/推挽 n 弱上拉/下拉电阻 u GPIO管脚数据控制 l 输出高/低电平 l 获取管脚输入值 u 输入中断(EXTI) l 上升沿检测 l 下降沿检测 l 上/下沿检测 l 低电平检测 l 高电平检测 APIs分组完成以下几大功能: u 配置GPIO管脚线的函数: l xGPIODirModeSet l xGPIOSPinDirModeSet l xGPIOPinConfigure u 读回GPIO管脚线模式配置的函数: l xGPIODirModeGet u 还有很方便的函数,可以将GPIO配置成想要的功能: l xGPIOSPinTypeGPIOInput l xGPIOSPinTypeGPIOOutput l xSPinTypeADC l xSPinTypeI2C l xSPinTypeSPI l xSPinTypeTimer l xSPinTypeUART l xSPinTypeACMP u 处理GPIO中断的APIs l xGPIOPinIntCallbackInit l xGPIOPinIntEnable l xGPIOSPinIntEnable l xGPIOPinIntDisable l xGPIOSPinIntDisable l xGPIOPinIntStatus l xGPIOPinIntClear l xGPIOSPinIntClear u 处理GPIO Pin状态的APIs l xGPIOPinRead l xGPIOSPinRead l xGPIOPinWrite l xGPIOSPinWrite 1.3通用非强制接口 通用非强制接口是一部MCU通有的功能,而不是所有MCU都具有的功能接口: l xGPIOSPinTypeGPIOOutputOD l xGPIOSPinTypeGPIOOutputQB l xSPinTypePWM l xSPinTypeEXTINT l 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的实现方式如下: #define xGPIOPinWrite(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 ); } } 更多请访问: http://www.coocox.org/COX.html