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