在一個嵌入式系統中,可能存在許多輸入或輸出的IO口,輸入有霍爾傳感器、紅外對管等,輸出有LED、電源控制開關等。如果說硬件可以一次成型,那么隨便一份代碼都可以完成IO的配置工作,但研發階段的產品,硬件各種修改是難免的,每一次 IO 的修改,對于底層開發人員來說,可能都是一次挑戰。因為一旦有某一個 IO 配置錯誤,或者原來的配置沒有修改正確(比如一個 IO 在原來的硬件適配中是輸入,之后的硬件需要修改成輸出),那么你很難查出來這是什么問題,因為這個時候不僅硬件修改了,軟件也修改了,你需要先定位到底是軟件問題還是硬件問題,所以一個好用的 IO 的配置框架就顯得很有必要了。
有道友會說,不如使用 CubeMx 軟件進行開發吧。
1、這個軟件適用于 ST 單片機,以前還能用,現在,除非你家里有礦,不然誰用的起STM32?基本上都國產化了(雖然有些單片機號稱兼容,但到底還是有些差異的)。2、公司原本的代碼就是使用標準庫,只是因為IO 的變化,你就需要把整個庫換掉嗎?時間上允許嗎?你確定修改后不會出現大問題?3、國產化的芯片可沒有所謂的標準庫和HAL庫供你選擇,每一家都有各自的庫,如果你的產品臨時換方案怎么辦?4、HAL 效率問題。今天魚鷹介紹一個簡單實用的框架,可用于快速增加或修改IO配置,甚至修改底層庫。假設有3個 LED 作為輸出、3 個霍爾傳感器作為輸入:輸入配置代碼:調試的時候,我們可以很方便的查看每個 IO 的狀態是怎樣的,而不用管 0 或 1 到底代表什么意思:#defineGPIOx_DefGPIO_TypeDef*#define GPIOMode_Def GPIOMode_TypeDeftypedef struct{GPIOx_Def gpio;uint16_t msk;GPIOMode_Def pull_up_down;} bsp_input_pin_def;#define _GPIO_PIN_INPUT(id, pull, gpiox, pinx) [id].gpio = (GPIOx_Def)gpiox, [id].msk = (1 << pinx), [id].pull_up_down = (GPIOMode_Def)pull#define GPIO_PIN_INPUT(id, pull, gpiox, pinx) _GPIO_PIN_INPUT(id, pull, gpiox, pinx)#define bsp_pin_get_port(gpiox) ((uint16_t)((GPIO_TypeDef *)gpiox)->IDR)#define bsp_pin_get_value(variable,id) do{ bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk ? variable |= (1 << id) : 0;} while(0)typedef enum{PIN_INPUT_HALL_0 = 0, // 輸入 IO 定義PIN_INPUT_HALL_1,PIN_INPUT_HALL_2,PIN_INPUT_MAX}bsp_pin_input_id_def;static const bsp_input_pin_def bsp_input_pin [PIN_INPUT_MAX] ={GPIO_PIN_INPUT(PIN_INPUT_HALL_0, BSP_GPIO_PUPD_NONE, GPIOA, 0),GPIO_PIN_INPUT(PIN_INPUT_HALL_1, BSP_GPIO_PUPD_NONE, GPIOB, 8),GPIO_PIN_INPUT(PIN_INPUT_HALL_2, BSP_GPIO_PUPD_NONE, GPIOE, 9),};// 單個 IO 初始化函數void bsp_pin_init_input(GPIOx_Def gpiox, uint32_t msk, GPIOMode_TypeDef pull_up_down){uint32_t temp;assert_param((msk & 0xffff0000) == 0 && gpiox != 0);temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);/* enable the led clock */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = (GPIOMode_Def)pull_up_down;GPIO_InitStruct.GPIO_Pin = msk;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);}// 所有 IO 初始化void gpio_input_init(){bsp_input_pin_def *info;info = (bsp_input_pin_def *)&bsp_input_pin;for(int i = 0; i < sizeof(bsp_input_pin)/sizeof(bsp_input_pin[0]); i++){bsp_pin_init_input(info->gpio, info->msk, info->pull_up_down);info++;}}// 最多支持 32 個 IO 輸入uint32_t bsp_input_all(void){uint32_t temp = 0;bsp_pin_get_value(temp, PIN_INPUT_HALL_0);bsp_pin_get_value(temp, PIN_INPUT_HALL_1);bsp_pin_get_value(temp, PIN_INPUT_HALL_2);return temp;}// 讀取單個 IO 狀態uint32_t bsp_input_level(bsp_pin_input_id_def id){return (bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk) ? 1 : 0;}typedef enum{HW_HAL_LEVEL_ACTIVE = 0, // 可直接修改為 0 或 1,另一個枚舉值自動修改為相反值HW_HAL_LEVEL_NO_ACTIVE = !HW_HAL_LEVEL_ACTIVE,}hw_input_hal_status_def;typedef struct{hw_input_hal_status_def hal_level0;uint8_t hal_level1;uint8_t hal_level2;}bsp_input_status_def;bsp_input_status_def bsp_input_status;int main(void){USRAT_Init(9600);//必須,進入調試模式后點擊全速運行gpio_input_init();while(1){uint32_t temp = bsp_input_all();bsp_input_status.hal_level0 = (hw_input_hal_status_def)((temp >> PIN_INPUT_HALL_0) & 1);bsp_input_status.hal_level1 = ((temp >> PIN_INPUT_HALL_1) & 1);bsp_input_status.hal_level2 = ((temp >> PIN_INPUT_HALL_2) & 1);}}
輸出配置代碼:這個框架有啥好處呢?1、自動完成 GPIO 的時鐘初始化工作,也就是說你只需要修改引腳即可,不必關心時鐘配置,但對于特殊引腳(比如PB3),還是得另外配置才行。2、應用和底層具體 IO 分離,這樣一旦修改了 IO,應用代碼不需要進行任何修改。3、增加或刪減 IO 變得很簡單,增加 IO時,首先加入對應枚舉,然后就可以添加對應的 IO 了。刪除 IO時,只要屏蔽對應枚舉值和引腳即可。4、參數檢查功能, IO 刪除時,因為屏蔽了對應的枚舉,所以編譯時可以幫你發現問題,而增加 IO 時,它可以幫你在運行時檢查該 IO是否進行配置了,可以防止因為失誤導致的問題。#define GPIOx_Def GPIO_TypeDef*#define GPIOMode_Def GPIOMode_TypeDeftypedef struct{GPIOx_Def gpio;uint32_t msk;uint32_t init_value;} bsp_output_pin_def;#define _GPIO_PIN_OUT(id, gpiox, pinx, init) [id].gpio = gpiox, [id].msk = (1 << pinx), [id].init_value = init#define GPIO_PIN_OUT(id, gpiox, pinx, init) _GPIO_PIN_OUT(id, gpiox, pinx, init)#define _bsp_pin_output_set(gpiox, pin) (gpiox)->BSRR = pin#define bsp_pin_output_set(gpiox, pin) _bsp_pin_output_set(gpiox, pin)#define _bsp_pin_output_clr(gpiox, pin) (gpiox)->BRR = pin#define bsp_pin_output_clr(gpiox, pin) _bsp_pin_output_clr(gpiox, pin)typedef enum{PIN_OUTPUT_LED_G,PIN_OUTPUT_LED_R,PIN_OUTPUT_LED_B,PIN_OUTPUT_MAX}bsp_pin_output_id_def;static const bsp_output_pin_def bsp_output_pin [PIN_OUTPUT_MAX] ={GPIO_PIN_OUT(PIN_OUTPUT_LED_G, GPIOA, 0, 0),GPIO_PIN_OUT(PIN_OUTPUT_LED_R, GPIOF, 15, 0),GPIO_PIN_OUT(PIN_OUTPUT_LED_B, GPIOD, 10, 0),};void bsp_pin_init_output(GPIOx_Def gpiox, uint32_t msk, uint32_t init){uint32_t temp;assert_param((msk & 0xffff0000) == 0 && gpiox != 0);temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);/* enable the led clock */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode = (GPIOMode_Def)GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = msk;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);if(init == 0){bsp_pin_output_clr(gpiox, msk);}else{bsp_pin_output_set(gpiox, msk);}}void bsp_output_init(){bsp_output_pin_def *info;info = (bsp_output_pin_def *)&bsp_output_pin;for(int i = 0; i < sizeof(bsp_output_pin)/sizeof(bsp_output_pin[0]); i++){bsp_pin_init_output(info->gpio, info->msk, info->init_value);info++;}}void bsp_output(bsp_pin_output_id_def id, uint32_t value){assert_param(id < PIN_OUTPUT_MAX);if(value == 0){bsp_pin_output_clr(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);}else{bsp_pin_output_set(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);}}int main(void){USRAT_Init(9600);//必須,進入調試模式后點擊全速運行bsp_output_init();while(1){bsp_output(PIN_OUTPUT_LED_G, 1);bsp_output(PIN_OUTPUT_LED_B, 0);bsp_output(PIN_OUTPUT_LED_R, 1);}}
5、更改庫時可以很方便,只需要修改對應的宏即可,目前可以順利在 GD32 和 STM32 庫進行快速更換。6、對于輸入 IO 而言,可以方便的修改有效和無效狀態,防止硬件修改有效電平。對于輸出 IO 而言,可以設定初始 IO 電平狀態。7、代碼簡單高效,盡可能的復用代碼,增加一個 IO 只需要很少的空間。8、缺點就是,只對同種配置的 IO 可以這樣用。
審核編輯 :李倩
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
單片機
+關注
關注
6076文章
45495瀏覽量
670391 -
霍爾傳感器
+關注
關注
28文章
797瀏覽量
66241
原文標題:簡單實用IO輸入輸出框架
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
熱點推薦
MTK Android 13狀態欄耳機圖標“失蹤”?這個SystemUI配置修改幫你找回
在 Android ROM 定制或設備調試中,SystemUI(系統界面)的配置修改往往直接影響用戶對設備的視覺感知 —— 比如狀態欄圖標顯示 / 隱藏、通知樣式、導航欄布局等。今天我們就以 MTK
RK3588平臺串口配置修改指南:切換至串口8
在嵌入式開發中,串口作為基礎調試接口至關重要。本文檔針對 RK3588 平臺,詳細介紹如何將系統默認串口修改為串口 8(UART8),包括 U-Boot 階段和內核階段的配置調整。該修改適用于
碩博電子IO模塊修改波特率和節點ID操作步驟
正確設置波特率與節點ID,是保證IO模塊與控制器穩定通信的關鍵。配置不當易引發設備失聯、數據錯亂等故障。本文將詳細講解波特率與節點ID的規范修改步驟,輕松搞定配置難題。
linux-arm開發環境的簡單配置
linux-arm開發環境簡單配置
關于linux-arm開發環境簡單配置是ARM學習的第一步,很多初學者會在這問題上糾結很久都不能配置好
發表于 01-13 07:56
利用EasyGo DeskSim快速完成Modbus協議通訊交互
EasyGo DeskSim是一款配置型的實時仿真軟件,它允許用戶將 Simulink 算法程序快速部署到 EasyGo 實時仿真機上。實時仿真機支持選配不同的 FPGA 芯片和 IO 模塊,能夠處理高速信號,并通過
利用EasyGo DeskSim快速實現PWM波信號采集
EasyGo DeskSim是一款配置型的實時仿真軟件,它允許用戶將 Simulink 算法程序快速部署到 EasyGo 實時仿真機上。實時仿真機支持選配不同的 FPGA 芯片和 IO 模塊,能夠處理高速信號,并通過
利用EasyGo DeskSim快速實現PWM波信號輸出
EasyGo DeskSim是一款配置型的實時仿真軟件,它允許用戶將 Simulink 算法程序快速部署到 EasyGo 實時仿真機上。實時仿真機支持選配不同的 FPGA 芯片和 IO 模塊,能夠處理高速信號,并通過
GraniStudio:IO初始化以及IO資源配置例程
1.文件運行 導入工程 雙擊運行桌面GraniStudio.exe。 通過引導界面導入IO初始化以及IO資源配置例程,點擊導入按鈕。 打開IO初始化以及
利用EasyGo DeskSim快速實現信號采集
EasyGo DeskSim是一款配置型的實時仿真軟件,它允許用戶將 Simulink 算法程序快速部署到 EasyGo 實時仿真機上。實時仿真機支持選配不同的 FPGA 芯片和 IO 模塊,能夠處理高速信號,并通過
干貨分享 | TSMaster IO功能使用指南—基于同星帶IO設備的配置與操作步驟
IO模塊是一種用于連接計算機系統或控制系統與外部設備之間的接口模塊。數字IO模塊用于處理二進制信號的輸入和輸出,它們可以接收和發送數字信號,
利用EasyGo DeskSim快速實現信號輸出
EasyGo DeskSim是一款配置型的實時仿真軟件,它允許用戶將 Simulink 算法程序快速部署到 EasyGo 實時仿真機上。實時仿真機支持選配不同的 FPGA 芯片和 IO 模塊,能夠處理高速信號,并通過
Linux系統中iptables防火墻配置詳解
iptables是Linux內核中用于配置防火墻規則的工具。它基于Netfilter框架,可以對通過網絡接口的數據包進行過濾、修改等操作。通過設置一系列規則,iptables能夠控制哪
MCU是否可以使用普通IO口和數據總線控制cy7c68013a,用異步slavefifo模式增加一個與PC通信的USB口?
cy7c68013a的固件已經配置成異步slave模式,是否有MCU用IO口控制cy7c68013a通過數據總線來實現與PC的USB口通信,這樣應用的示例程序或教程?
目的就是在現有的MCU系統中
發表于 05-30 06:32
簡單實用的框架,可用于快速增加或修改IO配置
評論