在一個(gè)嵌入式系統(tǒng)中,可能存在許多輸入或輸出的IO口,輸入有霍爾傳感器、紅外對(duì)管等,輸出有LED、電源控制開關(guān)等。
如果說(shuō)硬件可以一次成型,那么隨便一份代碼都可以完成IO的配置工作,但研發(fā)階段的產(chǎn)品,硬件各種修改是難免的,每一次 IO 的修改,對(duì)于底層開發(fā)人員來(lái)說(shuō),可能都是一次挑戰(zhàn)。
因?yàn)橐坏┯心骋粋€(gè) IO 配置錯(cuò)誤,或者原來(lái)的配置沒有修改正確(比如一個(gè) IO 在原來(lái)的硬件適配中是輸入,之后的硬件需要修改成輸出),那么你很難查出來(lái)這是什么問題,因?yàn)檫@個(gè)時(shí)候不僅硬件修改了,軟件也修改了,你需要先定位到底是軟件問題還是硬件問題,所以一個(gè)好用的 IO 的配置框架就顯得很有必要了。
1、自動(dòng)完成 GPIO 的時(shí)鐘初始化工作,也就是說(shuō)你只需要修改引腳即可,不必關(guān)心時(shí)鐘配置,但對(duì)于特殊引腳(比如PB3),還是得另外配置才行。2、應(yīng)用和底層具體 IO 分離,這樣一旦修改了 IO,應(yīng)用代碼不需要進(jìn)行任何修改。3、增加或刪減 IO 變得很簡(jiǎn)單,增加 IO時(shí),首先加入對(duì)應(yīng)枚舉,然后就可以添加對(duì)應(yīng)的 IO 了。刪除 IO時(shí),只要屏蔽對(duì)應(yīng)枚舉值和引腳即可。4、參數(shù)檢查功能, IO 刪除時(shí),因?yàn)槠帘瘟藢?duì)應(yīng)的枚舉,所以編譯時(shí)可以幫你發(fā)現(xiàn)問題,而增加 IO 時(shí),它可以幫你在運(yùn)行時(shí)檢查該 IO是否進(jìn)行配置了,可以防止因?yàn)槭д`導(dǎo)致的問題。5、更改庫(kù)時(shí)可以很方便,只需要修改對(duì)應(yīng)的宏即可,目前可以順利在 GD32 和 STM32 庫(kù)進(jìn)行快速更換。6、對(duì)于輸入 IO 而言,可以方便的修改有效和無(wú)效狀態(tài),防止硬件修改有效電平。對(duì)于輸出 IO 而言,可以設(shè)定初始 IO 電平狀態(tài)。7、代碼簡(jiǎn)單高效,盡可能的復(fù)用代碼,增加一個(gè) IO 只需要很少的空間。8、缺點(diǎn)就是,只對(duì)同種配置的 IO 可以這樣用。 好好看看,或許能學(xué)到不少技巧哦。 審核編輯 :李倩
有道友會(huì)說(shuō),不如使用 CubeMx 軟件進(jìn)行開發(fā)吧。
1、這個(gè)軟件適用于 ST 單片機(jī),以前還能用,現(xiàn)在,除非你家里有礦,不然誰(shuí)用的起STM32?基本上都國(guó)產(chǎn)化了(雖然有些單片機(jī)號(hào)稱兼容,但到底還是有些差異的)。2、公司原本的代碼就是使用標(biāo)準(zhǔn)庫(kù),只是因?yàn)镮O 的變化,你就需要把整個(gè)庫(kù)換掉嗎?時(shí)間上允許嗎?你確定修改后不會(huì)出現(xiàn)大問題?3、國(guó)產(chǎn)化的芯片可沒有所謂的標(biāo)準(zhǔn)庫(kù)和HAL庫(kù)供你選擇,每一家都有各自的庫(kù),如果你的產(chǎn)品臨時(shí)換方案怎么辦?4、HAL 效率問題。今天魚鷹介紹一個(gè)簡(jiǎn)單實(shí)用的框架,可用于快速增加或修改IO配置,甚至修改底層庫(kù)。假設(shè)有3個(gè) LED 作為輸出、3 個(gè)霍爾傳感器作為輸入:輸入配置代碼:調(diào)試的時(shí)候,我們可以很方便的查看每個(gè) 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)
#define BSP_GPIO_PUPD_NONE GPIO_Mode_IN_FLOATING
#define BSP_GPIO_PUPD_PULLUP GPIO_Mode_IPU
#define BSP_GPIO_PUPD_PULLDOWN GPIO_Mode_IPD
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),
};
// 單個(gè) 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 個(gè) 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;
}
// 讀取單個(gè) 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,另一個(gè)枚舉值自動(dòng)修改為相反值
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);//必須,進(jìn)入調(diào)試模式后點(diǎn)擊全速運(yùn)行
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);
}
}
這個(gè)框架有啥好處呢?#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);//必須,進(jìn)入調(diào)試模式后點(diǎn)擊全速運(yùn)行
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);
}
}
1、自動(dòng)完成 GPIO 的時(shí)鐘初始化工作,也就是說(shuō)你只需要修改引腳即可,不必關(guān)心時(shí)鐘配置,但對(duì)于特殊引腳(比如PB3),還是得另外配置才行。2、應(yīng)用和底層具體 IO 分離,這樣一旦修改了 IO,應(yīng)用代碼不需要進(jìn)行任何修改。3、增加或刪減 IO 變得很簡(jiǎn)單,增加 IO時(shí),首先加入對(duì)應(yīng)枚舉,然后就可以添加對(duì)應(yīng)的 IO 了。刪除 IO時(shí),只要屏蔽對(duì)應(yīng)枚舉值和引腳即可。4、參數(shù)檢查功能, IO 刪除時(shí),因?yàn)槠帘瘟藢?duì)應(yīng)的枚舉,所以編譯時(shí)可以幫你發(fā)現(xiàn)問題,而增加 IO 時(shí),它可以幫你在運(yùn)行時(shí)檢查該 IO是否進(jìn)行配置了,可以防止因?yàn)槭д`導(dǎo)致的問題。5、更改庫(kù)時(shí)可以很方便,只需要修改對(duì)應(yīng)的宏即可,目前可以順利在 GD32 和 STM32 庫(kù)進(jìn)行快速更換。6、對(duì)于輸入 IO 而言,可以方便的修改有效和無(wú)效狀態(tài),防止硬件修改有效電平。對(duì)于輸出 IO 而言,可以設(shè)定初始 IO 電平狀態(tài)。7、代碼簡(jiǎn)單高效,盡可能的復(fù)用代碼,增加一個(gè) IO 只需要很少的空間。8、缺點(diǎn)就是,只對(duì)同種配置的 IO 可以這樣用。 好好看看,或許能學(xué)到不少技巧哦。 審核編輯 :李倩
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。
舉報(bào)投訴
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3535瀏覽量
129004 -
霍爾傳感器
+關(guān)注
關(guān)注
26文章
697瀏覽量
62957 -
STM32
+關(guān)注
關(guān)注
2263文章
10849瀏覽量
353908
原文標(biāo)題:簡(jiǎn)單實(shí)用IO輸入輸出框架
文章出處:【微信號(hào):mcu168,微信公眾號(hào):硬件攻城獅】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
io口和串口的區(qū)別 單片機(jī)有多少個(gè)io口
等。而串口,即串行通信接口(Serial Communication Interface),是一種數(shù)據(jù)通信方式,通過(guò)一條數(shù)據(jù)線按照順序傳送數(shù)據(jù)。IO口和串口在功能和用途上存在顯著區(qū)別: 通信方式 : IO口 :實(shí)現(xiàn)簡(jiǎn)單的
誤差放大器的輸入輸出關(guān)系
誤差放大器(Error Amplifier)在電子測(cè)量和控制系統(tǒng)中扮演著至關(guān)重要的角色,其輸入輸出關(guān)系對(duì)于理解和設(shè)計(jì)這些系統(tǒng)至關(guān)重要。以下是對(duì)誤差放大器輸入輸出關(guān)系的詳細(xì)解析,包括其工作原理、輸入輸出特性、影響因素以及實(shí)際應(yīng)用等
隔離變壓器輸入輸出可以隨便接嗎
隔離變壓器的輸入輸出接線并非可以隨便接,而是需要遵循一定的原則、步驟和注意事項(xiàng),以確保其正常、安全、高效地運(yùn)行。 一、接線原則 電壓匹配 :在接線之前,必須確保輸入電源和輸出電器的電壓與隔離變壓器
寄存器的輸入輸出方式
寄存器的輸入輸出方式是數(shù)字電路設(shè)計(jì)中至關(guān)重要的部分,它決定了數(shù)據(jù)如何在寄存器中進(jìn)出以及處理的效率。下面將詳細(xì)探討寄存器的幾種主要輸入輸出方式,包括并行輸入輸出、串行輸入輸出以及雙向
PLC運(yùn)動(dòng)控制中的輸入輸出設(shè)備
在PLC(可編程邏輯控制器)運(yùn)動(dòng)控制系統(tǒng)中,輸入輸出設(shè)備扮演著至關(guān)重要的角色。這些設(shè)備不僅負(fù)責(zé)將外部信號(hào)傳遞給PLC,還負(fù)責(zé)將PLC的處理結(jié)果輸出到外部執(zhí)行機(jī)構(gòu),從而實(shí)現(xiàn)對(duì)機(jī)械設(shè)備運(yùn)動(dòng)的精確控制。以下是對(duì)PLC運(yùn)動(dòng)控制中輸入輸出
PLC輸入輸出信號(hào)異常的原因分析
在工業(yè)自動(dòng)化領(lǐng)域中,PLC(可編程邏輯控制器)作為控制系統(tǒng)的核心部件,其性能的穩(wěn)定性和可靠性對(duì)于整個(gè)生產(chǎn)線的正常運(yùn)行具有至關(guān)重要的影響。然而,在實(shí)際應(yīng)用中,PLC的輸入輸出信號(hào)異常問題時(shí)有發(fā)生,這不
PLC的輸入輸出接口是否需要進(jìn)行隔離保護(hù)?
PLC(可編程邏輯控制器)的輸入輸出接口是否需要進(jìn)行隔離保護(hù),取決于具體的應(yīng)用場(chǎng)景和需求。
鎖相環(huán)的輸入輸出相位一致嗎?
鎖相環(huán)是保證相位一致,還是相位差一致?鎖相環(huán)的輸入輸出相位一致嗎? 鎖相環(huán)(PLL)是一種回路控制系統(tǒng),用于保持輸出信號(hào)的相位與參考信號(hào)的相位之間的恒定關(guān)系。簡(jiǎn)單來(lái)說(shuō),鎖相環(huán)的目的是保證相位一致
輸入輸出電壓差與效率的關(guān)系
在開關(guān)穩(wěn)壓電源中,輸入電壓的范圍是預(yù)知的,輸出電壓也是知道的,但是輸入輸出的電壓差和轉(zhuǎn)換效率的關(guān)系很多人 不清楚,有經(jīng)驗(yàn)的工程師就會(huì)根據(jù)公式去推導(dǎo)出來(lái)輸入輸出電壓差越小,轉(zhuǎn)換效率越高。
發(fā)表于 01-05 15:12
?760次閱讀
環(huán)形變壓器的輸入輸出端接反了會(huì)怎么樣?
環(huán)形變壓器的輸入輸出端接反了會(huì)怎么樣? 環(huán)形變壓器是一種特殊的變壓器,其特點(diǎn)是具有環(huán)形磁芯。環(huán)形變壓器的輸入和輸出端的接反,也就是輸入端連接到輸出
plc輸入輸出的運(yùn)行原理
plc輸入端24v. 說(shuō)明plc輸入是PNP輸入,要知道怎么接線必須知道plc輸入輸出的運(yùn)行原理。
發(fā)表于 12-17 09:27
?1325次閱讀
降壓DCDC有輸入輸出電壓差嗎
在LDO specfication里面,有Dropout Voltage(輸入輸出電壓差)的參數(shù)。如下圖,輸出電流100mA時(shí),典型壓差是100mV;輸出電流是300mA時(shí),MAX壓差是300mV(線路阻抗不變下,流過(guò)電流越大,
工業(yè)自動(dòng)化:開關(guān)量輸入輸出采集控制終端
開關(guān)量是工業(yè)自動(dòng)化場(chǎng)景中最常見的信號(hào)類型之一,如電機(jī)開關(guān)、閥門開關(guān)等。而開關(guān)量輸入輸出采集控制終端(IO數(shù)據(jù)終端)作為連接傳感器、控制器、執(zhí)行器的橋梁,發(fā)揮著重要的作用。 物通博聯(lián)推出的開關(guān)量
評(píng)論