CoX.GPIO 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 xGPIODirModeSetl xGPIOSPinDirModeSetl xGPIOPinConfigureu 讀回 GPIO管腳線模式配置的函數:l xGPIODirModeGetu 還有很方便的函數,可以將 GPIO配置成想要的功能:l xGPIOSPinTypeGPIOInputl xGPIOSPinTypeGPIOOutputl xSPinType ADCl xSPinTypeI2Cl xSPinTypeSPIl xSPinTypeTimerl xSPinTypeUARTl xSPinTypeACMPu 處理 GPIO中斷的APIsl xGPIOPinIntCallbackInitl xGPIOPinIntEnablel xGPIOSPinIntEnablel xGPIOPinIntDisablel xGPIOSPinIntDisablel xGPIOPinIntStatusl xGPIOPinIntClearl xGPIOSPinIntClearu 處理 GPIO Pin狀態的APIsl xGPIOPinReadl xGPIOSPinReadl xGPIOPinWritel xGPIOSPinWrite
1.3通用非強制接口 通用非強制接口是一部MCU通有的功能,而不是所有MCU都具有的功能接口: l xGPIOSPinTypeGPIOOutputODl xGPIOSPinTypeGPIOOutputQBl xSPinType PWMl xSPinTypeEXTINTl 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 ); } }
|