在一個嵌入式系統(tǒng)中,可能存在許多輸入或輸出的IO口,輸入有霍爾傳感器、紅外對管等,輸出有LED、電源控制開關(guān)等。如果說硬件可以一次成型,那么隨便一份代碼都可以完成IO的配置工作,但研發(fā)階段的產(chǎn)品,硬件各種修改是難免的,每一次 IO 的修改,對于底層開發(fā)人員來說,可能都是一次挑戰(zhàn)。因為一旦有某一個 IO 配置錯誤,或者原來的配置沒有修改正確(比如一個 IO 在原來的硬件適配中是輸入,之后的硬件需要修改成輸出),那么你很難查出來這是什么問題,因為這個時候不僅硬件修改了,軟件也修改了,你需要先定位到底是軟件問題還是硬件問題,所以一個好用的 IO 的配置框架就顯得很有必要了。
有道友會說,不如使用 CubeMx 軟件進行開發(fā)吧。
1、這個軟件適用于 ST 單片機,以前還能用,現(xiàn)在,除非你家里有礦,不然誰用的起STM32?基本上都國產(chǎn)化了(雖然有些單片機號稱兼容,但到底還是有些差異的)。2、公司原本的代碼就是使用標準庫,只是因為IO 的變化,你就需要把整個庫換掉嗎?時間上允許嗎?你確定修改后不會出現(xiàn)大問題?3、國產(chǎn)化的芯片可沒有所謂的標準庫和HAL庫供你選擇,每一家都有各自的庫,如果你的產(chǎn)品臨時換方案怎么辦?4、HAL 效率問題。今天魚鷹介紹一個簡單實用的框架,可用于快速增加或修改IO配置,甚至修改底層庫。假設(shè)有3個 LED 作為輸出、3 個霍爾傳感器作為輸入:輸入配置代碼:調(diào)試的時候,我們可以很方便的查看每個 IO 的狀態(tài)是怎樣的,而不用管 0 或 1 到底代表什么意思:輸出配置代碼:#defineGPIOx_DefGPIO_TypeDef*
#define GPIOMode_Def GPIOMode_TypeDef
typedef 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 初始化函數(shù)
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 狀態(tài)
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);//必須,進入調(diào)試模式后點擊全速運行
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 的時鐘初始化工作,也就是說你只需要修改引腳即可,不必關(guān)心時鐘配置,但對于特殊引腳(比如PB3),還是得另外配置才行。2、應用和底層具體 IO 分離,這樣一旦修改了 IO,應用代碼不需要進行任何修改。3、增加或刪減 IO 變得很簡單,增加 IO時,首先加入對應枚舉,然后就可以添加對應的 IO 了。刪除 IO時,只要屏蔽對應枚舉值和引腳即可。4、參數(shù)檢查功能, IO 刪除時,因為屏蔽了對應的枚舉,所以編譯時可以幫你發(fā)現(xiàn)問題,而增加 IO 時,它可以幫你在運行時檢查該 IO是否進行配置了,可以防止因為失誤導致的問題。5、更改庫時可以很方便,只需要修改對應的宏即可,目前可以順利在 GD32 和 STM32 庫進行快速更換。6、對于輸入 IO 而言,可以方便的修改有效和無效狀態(tài),防止硬件修改有效電平。對于輸出 IO 而言,可以設(shè)定初始 IO 電平狀態(tài)。7、代碼簡單高效,盡可能的復用代碼,增加一個 IO 只需要很少的空間。8、缺點就是,只對同種配置的 IO 可以這樣用。#define GPIOx_Def GPIO_TypeDef*
#define GPIOMode_Def GPIOMode_TypeDef
typedef 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);//必須,進入調(diào)試模式后點擊全速運行
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);
}
}
審核編輯 :李倩
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
單片機
+關(guān)注
關(guān)注
6030文章
44489瀏覽量
631998 -
霍爾傳感器
+關(guān)注
關(guān)注
26文章
699瀏覽量
62997
原文標題:簡單實用IO輸入輸出框架
文章出處:【微信號:zhuyandz,微信公眾號:FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
華納云監(jiān)視Linux磁盤IO性能命令:iotop,iostat,vmstat,atop,dstat,ioping
。 top命令可用于查看CPU是否正在等待磁盤操作完成?!皐a”度量標準顯示IO等待,CPU等待IO完成所花費的時間(以百分比
io口和串口的區(qū)別 單片機有多少個io口
等。而串口,即串行通信接口(Serial Communication Interface),是一種數(shù)據(jù)通信方式,通過一條數(shù)據(jù)線按照順序傳送數(shù)據(jù)。IO口和串口在功能和用途上存在顯著區(qū)別: 通信方式 : IO口 :實現(xiàn)簡單的輸入輸出
【教程】比Modbus控制更簡單!S7 200 SMART利用Profinet網(wǎng)關(guān)擴展IO
PLC自帶IO方法控制擴展IO。一配置Profinet網(wǎng)關(guān)首先導入Profinet網(wǎng)關(guān)的GSD文件,用于STEP7-MicroWINSMART組態(tài)軟件
單片機io口怎么配置成輸出口
單片機IO口配置成輸出口的過程通常涉及對單片機內(nèi)部寄存器的操作。不同型號的單片機在寄存器配置和編程方式上可能有所不同,但基本原理是相似的。以下是一個通用的步驟,用于將單片機
遠程IO與分布式IO的區(qū)別
在工業(yè)自動化和控制系統(tǒng)設(shè)計中,遠程IO(Input/Output)和分布式IO是兩個重要的概念。它們各自具有獨特的特點和優(yōu)勢,適用于不同的應用場景。本文將詳細探討遠程IO與分布式
暖通控制系統(tǒng)可用鋇錸分布式IO系統(tǒng)BL207
可編程邏輯控制功能,可替代DDC部分功能。 BACnet/IP IO系統(tǒng)BL207提供雙網(wǎng)口支持交換機級聯(lián)功能,支持標準的BACnet/IP協(xié)議,也可用于接入BACnet/IP控制器,可適配江森、霍
自制測試框架(設(shè)置界面密碼1)
(設(shè)置界面密碼為1)
目前支持的指令如下:
1.沒有任何編程基礎(chǔ)的人快速實現(xiàn)簡易自動化制作;
2.測試后可以直接把數(shù)據(jù)和結(jié)果直接寫入到MES;
3.可持續(xù)增加新的功能及模塊;
4.快速實現(xiàn)點
發(fā)表于 03-02 19:33
EtherCAT IO的接線方法和流程是怎樣的?
EtherCAT IO的接線方法和流程是怎樣的? EtherCAT是一種用于實時以太網(wǎng)通信的開放式通信協(xié)議,具有低延遲和高帶寬的優(yōu)勢。 EtherCAT IO是EtherCAT網(wǎng)絡中連接到IO
激光焊接行業(yè)的智能化選擇鋇錸分布式IO
和穩(wěn)定性。 鋇錸技術(shù)分布式IO系統(tǒng)具有良好的擴展性和靈活性。隨著激光焊接技術(shù)的發(fā)展,對于設(shè)備控制的需求也在不斷變化。鋇錸技術(shù)的分布式IO系統(tǒng)可以通過簡單地增加
TQT507開發(fā)板如何修改和保存內(nèi)核配置
本文檔介紹如何在開發(fā)時修改和保存內(nèi)核配置,適用于開發(fā)板TQT507。 1.修改內(nèi)核配置 編譯時系統(tǒng)會先檢測當前內(nèi)核源碼目錄下是否存在.con
T507開發(fā)板如何修改和保存內(nèi)核配置
本文檔介紹如何在開發(fā)時修改和保存內(nèi)核配置,適用于開發(fā)板TQT507。1.修改內(nèi)核配置編譯時系統(tǒng)會先檢測當前內(nèi)核源碼目錄下是否存在.confi
springboot框架介紹
Spring Boot 是一個開源的、用于開發(fā)微服務的框架,它基于 Java 平臺。它提供了一種快速、敏捷的方式來構(gòu)建獨立的、可部署的、生產(chǎn)級別的 Spring 應用程序。Spring Boot
linux修改網(wǎng)卡ip配置文件
Linux是一種開源的操作系統(tǒng),因此,它給用戶提供了很高的自由度,可以根據(jù)個人需要進行各種定制和配置。其中,修改網(wǎng)絡接口配置文件是常見的操作,可以通過修改網(wǎng)卡ip
西門子博圖GET_NAME讀取IO設(shè)備或DP從站的名稱
“GET_NAME”指令用于讀取 IO 設(shè)備或 DP 從站的名稱。該名稱將顯示在網(wǎng)絡視圖和 IO 設(shè)備或 DP 從站的屬性中。在以下截圖中,
評論