通过数组和枚举简化GPIO操作编码
????????在工作中,經(jīng)常遇到大量使用GPIO作為數(shù)字量輸入輸出來控制設(shè)備或采集狀態(tài),每次定義操作不同的GPIO針腳既麻煩又容易出錯,于是就想要簡化操作過程。對于數(shù)字量輸入來說就是采集對應(yīng)針腳的狀態(tài);而輸出則是根據(jù)邏輯關(guān)系置位或復(fù)位對應(yīng)得針腳。
????????為了使用方便,我們按可復(fù)用和經(jīng)常變化的部分叫軟件的實現(xiàn)劃分為2個部分。相對固定的部分我們封裝成操作函數(shù)供調(diào)用,對于經(jīng)常變化的部分(如硬件配置等)我們另外實現(xiàn),并調(diào)用前面封裝的函數(shù)實現(xiàn)功能。
????????現(xiàn)在我們只要實現(xiàn)了通用性較好的函數(shù)封裝,剩下就是調(diào)用來實現(xiàn)具體控制的問題。那么怎么封裝這些函數(shù)呢?
????????我們首先定義兩個枚舉類型分別定義如下:
//定義數(shù)字量輸出通道枚舉類型,規(guī)定通道的范圍 typedef enum {DOChannel1,DOChannel2,DOChannel3,DOChannel4,DOChannel5,DOChannelNum } DigitalOutput;//定義數(shù)字量輸入通道枚舉類型,規(guī)定通道的范圍 typedef enum {DIChannel1,DIChannel2,DIChannel3,DIChannel4,DIChannel5,DIChannelNum } DigitalInput;????????數(shù)字量輸入輸出的枚舉主要是為了方便操作和識別,通道數(shù)量出現(xiàn)變化時只需要增加枚舉兩種的通道定義即可。此處數(shù)字量輸入輸出均定義了5個通道。枚舉量的最后一個成員代表了通道的數(shù)量,在枚舉全部通道時能夠很好的避免超出范圍的錯誤。
????????同時還要定義如下的結(jié)構(gòu)體,用于定義需要操作GPIO目標。
//定義用于針腳操作的目標針腳類型 typedef struct{GPIO_TypeDef* GPIOx;uint16_tGPIO_Pin; }TargetPin;????????有了上述的定義則可以實現(xiàn)前面設(shè)想的操作了,接下來我們還需要定義兩個數(shù)字量輸入輸出通道的TargetPin類型的數(shù)組,用于存放想要操作的目標通道,和前面枚舉兩種定義的通道一致,此處也是5個通道。
//定義DI通道的全部目標針腳數(shù)組 TargetPindiPin[]={{GPIOE,GPIO_Pin_2},{GPIOE,GPIO_Pin_3},{GPIOE,GPIO_Pin_4},{GPIOE,GPIO_Pin_5},{GPIOE,GPIO_Pin_6}};//定義DO通道的全部目標針腳數(shù)組 TargetPindoPin[]={{GPIOD,GPIO_Pin_3},{GPIOD,GPIO_Pin_4},{GPIOD,GPIO_Pin_5},{GPIOD,GPIO_Pin_6},{GPIOD,GPIO_Pin_7}};????????有了以上2個數(shù)組就可以在避免在操作過程中大量使用條件分支語句(Switch或if語句),簡化編碼和避免在增加通道時號要修改函數(shù)的情況。現(xiàn)在如果通道數(shù)量出現(xiàn)變化則只需要修改枚舉量和數(shù)組的值就可。或者操作的管腳出現(xiàn)變化則只需要修改數(shù)組的值就可以了。而不需要去修改函數(shù)體,而且函數(shù)體的編碼也非常簡單。
????????對數(shù)字量輸出的操作如下,在操作全部通道時,以枚舉變量作為循環(huán)變量,以枚舉的最后定義的數(shù)量來控制,并以枚舉量的取值作為數(shù)組下標,有效避免出現(xiàn)超出范圍的錯誤,同時在通道數(shù)量和通道對應(yīng)的具體針腳發(fā)生變化時,無需修改函數(shù)。
//操作全部繼電器DO通道 //輸入?yún)?shù)TargetPin *doPin為要操作的DO通道列表 //輸入?yún)?shù)BOOL *commands欲寫給DO通道的值列表 void OperationAllRelayChannel(TargetPin*doPin,BOOL *commands) {DigitalOutputDOChannel;for(DOChannel=DOChannel1;DOChannel<DOChannelNum;DOChannel++){OperationSingleRelayChannel(doPin[DOChannel],commands[DOChannel]);} }//操作單個繼電器DO通道 //輸入?yún)?shù)TargetPin doPin為要操作的DO通道 //輸入?yún)?shù)BOOL command欲寫給DO通道的值 void OperationSingleRelayChannel(TargetPindoPin,BOOL command) {if(command==True){GPIO_SetBits(doPin.GPIOx,doPin.GPIO_Pin);}else{GPIO_ResetBits(doPin.GPIOx,doPin.GPIO_Pin);} }????????對數(shù)字量輸入的操作函數(shù)的編寫采用與數(shù)字量輸出相同的思路。對于枚舉之所以可以用作數(shù)組下標,是因為枚舉沒被指定值時,總是從0開始向上累加,正好與數(shù)組下標是一致的。這要做還有一個好處是,通道與具體的GPIO引腳是由TargetPin數(shù)組的賦值順序決定的,修改非常方便。
//獲取全部DI量狀態(tài)輸入值 //輸入?yún)?shù)TargetPin *diPin為需要讀取的DI通道列表 //輸入?yún)?shù)BOOL *result為讀取的通道值返回列表 void GetAllDIStatusInput(TargetPin *diPin,BOOL*result) {DigitalInputDIChannel;for(DIChannel=DIChannel1;DIChannel<DIChannelNum;DIChannel++){result[DIChannel]=GetSingleDIStatusInput(diPin[DIChannel]);} }//獲取單個DI量狀態(tài)輸入值 //輸入?yún)?shù)TargetPin diPin是需要讀取的DI通道 //返回值為讀取的通道值 BOOL GetSingleDIStatusInput(TargetPin diPin) {uint8_treadValue;readValue= GPIO_ReadInputDataBit(diPin.GPIOx,diPin.GPIO_Pin);return(readValue>0)?True:False; }????????通過以上的編碼操作DI、DO已經(jīng)很方便了,但在操作單個DO通道的函數(shù)中還有一個if…else語句給人的感覺比較不太好。因為操作簡單就是置位和復(fù)位,所以我們定義一個指向函數(shù)的指針數(shù)組,如下:
/*定義操作GPIO管腳的函數(shù)指針*/ void (*OperationGPIOBits[])(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin)={GPIO_ResetBits,GPIO_SetBits};????????有了這個指向函數(shù)的指針數(shù)組我們可以將上面的操作單個DO通道的函數(shù)簡化為如下:
//操作單個繼電器DO通道 //輸入?yún)?shù)TargetPin doPin為要操作的DO通道 //輸入?yún)?shù)BOOL command欲寫給DO通道的值 void OperationSingleRelayChannel(TargetPindoPin,BOOL command) {OperationGPIOBits[command](doPin.GPIOx,doPin.GPIO_Pin); }????????其中command是一個布爾變量取值為0和1,正好與指向函數(shù)的指針數(shù)組對應(yīng),實現(xiàn)在command取不同值時,調(diào)用復(fù)位或置位函數(shù)。
????????以上代碼在IAR EWARM和STM32F103VET平臺測試正確。
總結(jié)
以上是生活随笔為你收集整理的通过数组和枚举简化GPIO操作编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 外设驱动库开发笔记20:BME280压力
- 下一篇: 外设驱动库开发笔记37:S1336-5B