HAL庫GPIO輸入模式在cubemx中的配置
上節(jié)課程介紹了GPIO輸出模式的配置,包括修改IO標(biāo)簽,選擇GPIO模式、GPIO上下拉等,本節(jié)輸入模式有很多相同之處,節(jié)省時間,小飛哥就簡單介紹一下
GPIO選擇與配置
依然是先來看看我們的Alios 開發(fā)板上的按鍵硬件連接,總共有3個用戶按鍵,分別掛在GPIOE11、GPIOE14和GPIOE10上,連接方式采用的是低電平有效,也即是,按鍵按下之后,MCU檢測到的電平為低電平,松開之后檢測為高電平,外部上拉電阻4.7K接下來,我們只需要在cubemx對這3個GPIO進行配置即可,工程在上一節(jié)內(nèi)容上繼續(xù)添加,打開上一節(jié)的cubemx工程,選擇PE14引腳,選擇引腳模式為輸入模式然后修改標(biāo)簽為USR_KEY2,其他兩個按鍵一樣的配置方法,3個按鍵配置完成之后如下圖所示:接下來依然是對GPIO的模式、配置進行修改,這里需要注意一點就是GPIO的上下拉配置,不要瞎選,這個一定是跟硬件相關(guān)的,比如本次硬件設(shè)計,空閑時是保持高電平的,那么配置上拉是比較合適的,但是呢,此時外部已經(jīng)有了上拉電阻,我們內(nèi)部上拉就不是很有必要了,也可以配置為不上拉也不下拉,但是配置為下拉一定是不合適的,可能會引起信號的誤動作,功耗的增加等不必要的麻煩,所以說,cubemx配置只是為你減輕了重復(fù)性搬運工作,電路工作原理必須了然于胸,選擇最為合適的配置配置比較簡單,我們此次采用的是輪詢獲取GPIO狀態(tài)的方法來實現(xiàn)按鍵,當(dāng)然也可以采用外部中斷的方式,對于普通按鍵來講,不是很有必要,輪詢GPIO狀態(tài)即可
HAL庫GPIO輸入模式操作詳解與結(jié)構(gòu)介紹
接下來,我們生成代碼即可打開工程之后,我們發(fā)現(xiàn)上一節(jié)配置的輸出IO和這一節(jié)的輸入IO是放在一起的,cubemx生成代碼的規(guī)則是把同一類外設(shè)統(tǒng)一放在相應(yīng)的模塊初始化代碼中,宏定義統(tǒng)一放在main.h中如果能夠接受這個布局的話是沒有問題的,如果不能還是比較麻煩的輸入模式相關(guān)的GPIO API也比較少,只涉及到關(guān)于GPIO操作的API:
/*Initializationandde-initializationfunctions*****************************/ voidHAL_GPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_Init); voidHAL_GPIO_DeInit(GPIO_TypeDef*GPIOx,uint32_tGPIO_Pin); /** *@} */ /**@addtogroupGPIO_Exported_Functions_Group2IOoperationfunctions *@{ */ /*IOoperationfunctions*****************************************************/ GPIO_PinStateHAL_GPIO_ReadPin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin);
如何使用呢?
參數(shù)GPIO_TypeDef *GPIOx可以是GPIO組的地址: #defineGPIOA((GPIO_TypeDef*)GPIOA_BASE) #defineGPIOB((GPIO_TypeDef*)GPIOB_BASE) #defineGPIOC((GPIO_TypeDef*)GPIOC_BASE) #defineGPIOD((GPIO_TypeDef*)GPIOD_BASE) #defineGPIOE((GPIO_TypeDef*)GPIOE_BASE) #defineGPIOF((GPIO_TypeDef*)GPIOF_BASE) #defineGPIOG((GPIO_TypeDef*)GPIOG_BASE) #defineGPIOH((GPIO_TypeDef*)GPIOH_BASE) #defineGPIOI((GPIO_TypeDef*)GPIOI_BASE)
參數(shù)GPIO_Pin可以是GPIO的引腳號: #defineGPIO_PIN_0((uint16_t)0x0001)/*Pin0selected*/ #defineGPIO_PIN_1((uint16_t)0x0002)/*Pin1selected*/ #defineGPIO_PIN_2((uint16_t)0x0004)/*Pin2selected*/ #defineGPIO_PIN_3((uint16_t)0x0008)/*Pin3selected*/ #defineGPIO_PIN_4((uint16_t)0x0010)/*Pin4selected*/ #defineGPIO_PIN_5((uint16_t)0x0020)/*Pin5selected*/ #defineGPIO_PIN_6((uint16_t)0x0040)/*Pin6selected*/ #defineGPIO_PIN_7((uint16_t)0x0080)/*Pin7selected*/ #defineGPIO_PIN_8((uint16_t)0x0100)/*Pin8selected*/ #defineGPIO_PIN_9((uint16_t)0x0200)/*Pin9selected*/ #defineGPIO_PIN_10((uint16_t)0x0400)/*Pin10selected*/ #defineGPIO_PIN_11((uint16_t)0x0800)/*Pin11selected*/ #defineGPIO_PIN_12((uint16_t)0x1000)/*Pin12selected*/ #defineGPIO_PIN_13((uint16_t)0x2000)/*Pin13selected*/ #defineGPIO_PIN_14((uint16_t)0x4000)/*Pin14selected*/ #defineGPIO_PIN_15((uint16_t)0x8000)/*Pin15selected*/ #defineGPIO_PIN_All((uint16_t)0xFFFF)/*Allpinsselected*/
輸入模式返回值為獲取到的GPIO狀態(tài),也即是高低電平狀態(tài),在沒有按鍵按下的時候,返回GPIO_PIN_SET,按鍵按下時候,返回GPIO_PIN_SET
@endverbatim *@{ */ /** *@briefReadthespecifiedinputportpin. *@paramGPIOxwherexcanbe(A..H)toselecttheGPIOperipheralforSTM32L4family *@paramGPIO_Pinspecifiestheportbittoread. *ThisparametercanbeanycombinationofGPIO_Pin_xwherexcanbe(0..15). *@retvalTheinputportpinvalue. */ GPIO_PinStateHAL_GPIO_ReadPin(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin) { GPIO_PinStatebitstatus; /*Checktheparameters*/ assert_param(IS_GPIO_PIN(GPIO_Pin)); if((GPIOx->IDR&GPIO_Pin)!=0x00u) { bitstatus=GPIO_PIN_SET; } else { bitstatus=GPIO_PIN_RESET; } returnbitstatus; }
GPIO輸入模式的簡單測試
接下來在之前led的任務(wù)中,對按鍵狀態(tài)進行測試,這里涉及到按鍵的消抖,所謂“消抖”就是:當(dāng)檢測到按鍵狀態(tài)變化時,不是立即去響應(yīng)動作,而是先等待閉合或斷開穩(wěn)定后再進行處理。即為按鍵消抖按鍵消抖:可分為硬件消抖和軟件消抖。
硬件消抖就是在按鍵上并聯(lián)一個電容,如圖 8-11 所示,利用電容的充放電特性來對抖動過程中產(chǎn)生的電壓毛刺進行平滑處理,從而實現(xiàn)消抖。
但實際應(yīng)用中,這種方式的效果往往不是很好,而且還增加了成本和電路復(fù)雜度,所以實際中使用的并不多。絕大多數(shù)情況下,我們是用軟件即程序來實現(xiàn)消抖的
延時消抖
最簡單的消抖原理,就是當(dāng)檢測到按鍵狀態(tài)變化后,先等待一個 10ms 左右的延時時間,讓抖動消失后再進行一次按鍵狀態(tài)檢測,如果與剛才檢測到的狀態(tài)相同,就可以確認(rèn)按鍵已經(jīng)穩(wěn)定的動作了
staticvoidrt_led1_flash_entry(void*parameter) { for(;;) { if(!HAL_GPIO_ReadPin(USR_KEY1_GPIO_Port,USR_KEY1_Pin)) { rt_thread_mdelay(100); if(!HAL_GPIO_ReadPin(USR_KEY1_GPIO_Port,USR_KEY1_Pin))//消抖 { rt_kprintf("Key1ispresseded! "); } } if(!HAL_GPIO_ReadPin(USR_KEY2_GPIO_Port,USR_KEY2_Pin)) { rt_thread_mdelay(100); if(!HAL_GPIO_ReadPin(USR_KEY2_GPIO_Port,USR_KEY2_Pin))//消抖 { rt_kprintf("Key2ispresseded! "); } } if(!HAL_GPIO_ReadPin(USR_KEY3_GPIO_Port,USR_KEY3_Pin)) { rt_thread_mdelay(100); if(!HAL_GPIO_ReadPin(USR_KEY3_GPIO_Port,USR_KEY3_Pin))//消抖 { rt_kprintf("Key3ispresseded! "); } } } }
測試結(jié)果:
多功能按鍵移植
上面是簡單的按鍵狀態(tài)獲取,實際中,一個項目可能按鍵有限但同時又要實現(xiàn)復(fù)雜的功能切換,那么按鍵的功能如果很單一的話,就不能夠滿足需求,接下來,小飛哥帶大家一起移植一個很不錯的多功能按鍵框架,代碼來源是github的jiejieTop
源碼地址:GitHub - jiejieTop/ButtonDrive: 純C語言實現(xiàn)的一個按鍵驅(qū)動,可移植性強,支持單雙擊、連按、連按釋放、長按;采用回調(diào)處理按鍵事件(自定義消抖時間),使用只需3步,1:創(chuàng)建按鍵,2:按鍵事件與回調(diào)處理函數(shù)鏈接映射。然后周期檢查按鍵。我們下載源碼進行移植,把文件夾里面的這些文件,我們移植到自己的文件目錄,下載文件中包含的main.c中是一個完整的按鍵初始化,實現(xiàn),大家可以看看就知道如何使用了下面小飛哥就帶大家來看看如何移植到自己的系統(tǒng)里面
![e7d83ed083514e07e330211e338b33b7.png](en-resource://datab可以把memset,printff替換為rt-thread的驅(qū)動,當(dāng)然也可以不關(guān)注重新編譯一下,剛才的警告已經(jīng)消失了然后我們看看如何實現(xiàn)這個多功能按鍵:
先來看看一些配置項宏定義,位域、結(jié)構(gòu)體、回調(diào)函數(shù),面向?qū)ο蟮囊惶讝|西都在,是很適合學(xué)習(xí)的,有關(guān)于短按、長按、單擊、雙擊等等的配置,我們根據(jù)自己的需要配置即可
#defineBTN_NAME_MAX32//名字最大為32字節(jié) /*按鍵消抖時間40ms,建議調(diào)用周期為20ms 只有連續(xù)檢測到40ms狀態(tài)不變才認(rèn)為有效,包括彈起和按下兩種事件 */ #defineCONTINUOS_TRIGGER0//是否支持連續(xù)觸發(fā),連發(fā)的話就不要檢測單雙擊與長按了 /*是否支持單擊&雙擊同時存在觸發(fā),如果選擇開啟宏定義的話,單雙擊都回調(diào),只不過單擊會延遲響應(yīng), 因為必須判斷單擊之后是否觸發(fā)了雙擊否則,延遲時間是雙擊間隔時間 BUTTON_DOUBLE_TIME。 而如果不開啟這個宏定義,建議工程中只存在單擊/雙擊中的一個,否則,在雙擊響應(yīng)的時候會觸發(fā)一次單擊, 因為雙擊必須是有一次按下并且釋放之后才產(chǎn)生的*/ #defineSINGLE_AND_DOUBLE_TRIGGER1 /*是否支持長按釋放才觸發(fā),如果打開這個宏定義,那么長按釋放之后才觸發(fā)單次長按, 否則在長按指定時間就一直觸發(fā)長按,觸發(fā)周期由BUTTON_LONG_CYCLE決定*/ #defineLONG_FREE_TRIGGER0 #defineBUTTON_DEBOUNCE_TIME2//消抖時間(n-1)*調(diào)用周期 #defineBUTTON_CONTINUOS_CYCLE1//連按觸發(fā)周期時間(n-1)*調(diào)用周期 #defineBUTTON_LONG_CYCLE1//長按觸發(fā)周期時間(n-1)*調(diào)用周期 #defineBUTTON_DOUBLE_TIME20//雙擊間隔時間(n-1)*調(diào)用周期建議在200-600ms #defineBUTTON_LONG_TIME50/*持續(xù)n秒((n-1)*調(diào)用周期ms),認(rèn)為長按事件*/ #defineTRIGGER_CB(event) if(btn->CallBack_Function[event]) btn->CallBack_Function[event]((Button_t*)btn) typedefvoid(*Button_CallBack)(void*);/*按鍵觸發(fā)回調(diào)函數(shù),需要用戶實現(xiàn)*/ typedefenum{ BUTTON_DOWM=0, BUTTON_UP, BUTTON_DOUBLE, BUTTON_LONG, BUTTON_LONG_FREE, BUTTON_CONTINUOS, BUTTON_CONTINUOS_FREE, BUTTON_ALL_RIGGER, number_of_event,/*觸發(fā)回調(diào)的事件*/ NONE_TRIGGER }Button_Event; /* 每個按鍵對應(yīng)1個全局的結(jié)構(gòu)體變量。 其成員變量是實現(xiàn)濾波和多種按鍵狀態(tài)所必須的 */ typedefstructbutton { /*下面是一個函數(shù)指針,指向判斷按鍵手否按下的函數(shù)*/ uint8_t(*Read_Button_Level)(void);/*讀取按鍵電平函數(shù),需要用戶實現(xiàn)*/ charName[BTN_NAME_MAX]; uint8_tButton_State:4;/*按鍵當(dāng)前狀態(tài)(按下還是彈起)*/ uint8_tButton_Last_State:4;/*上一次的按鍵狀態(tài),用于判斷雙擊*/ uint8_tButton_Trigger_Level:2;/*按鍵觸發(fā)電平*/ uint8_tButton_Last_Level:2;/*按鍵當(dāng)前電平*/ uint8_tButton_Trigger_Event;/*按鍵觸發(fā)事件,單擊,雙擊,長按等*/ Button_CallBackCallBack_Function[number_of_event]; uint8_tButton_Cycle;/*連續(xù)按鍵周期*/ uint8_tTimer_Count;/*計時*/ uint8_tDebounce_Time;/*消抖時間*/ uint8_tLong_Time;/*按鍵按下持續(xù)時間*/ structbutton*Next; }Button_t;
然后看看如何使用API,我們只需要實現(xiàn)GPIO的狀態(tài)獲取、創(chuàng)建按鍵對象、編寫回調(diào)函數(shù)即可,在任務(wù)中輪詢按鍵狀態(tài),移植起來是非常方便的
#defineKEY_ON0 /*Privatemacro-------------------------------------------------------------*/ /*Privatevariables---------------------------------------------------------*/ Button_tButton1; /*Privatefunctionprototypes-----------------------------------------------*/ staticuint8_trt_read_key1(void) { returnHAL_GPIO_ReadPin(USR_KEY1_GPIO_Port,USR_KEY1_Pin); } staticvoidBtn1_Dowm_CallBack(void*btn) { PRINT_INFO("Button1單擊!"); } staticvoidBtn1_Double_CallBack(void*btn) { PRINT_INFO("Button1雙擊!"); } staticvoidBtn1_Long_CallBack(void*btn) { PRINT_INFO("Button1長按!"); } staticvoidBtn1_Continuos_CallBack(void*btn) { PRINT_INFO("Button1連按!"); } staticvoidBtn1_ContinuosFree_CallBack(void*btn) { PRINT_INFO("Button1連按釋放!"); } /*Privateusercode---------------------------------------------------------*/ /** *@functionrt_ledflash_entry *@author:小飛哥玩嵌入式-小飛哥 *@TODO:LED控制線程 *@param: *@return:NULL */ staticvoidrt_led1_flash_entry(void*parameter) { Button_Create("Button1", &Button1, rt_read_key1, KEY_ON); Button_Attach(&Button1,BUTTON_DOWM,Btn1_Dowm_CallBack);//單擊 Button_Attach(&Button1,BUTTON_DOUBLE,Btn1_Double_CallBack);//雙擊 Button_Attach(&Button1,BUTTON_CONTINUOS,Btn1_Continuos_CallBack);//連按 Button_Attach(&Button1,BUTTON_CONTINUOS_FREE,Btn1_ContinuosFree_CallBack);//連按釋放 Button_Attach(&Button1,BUTTON_LONG,Btn1_Long_CallBack); for(;;) { Button_Process();//需要周期調(diào)用按鍵處理函數(shù) rt_thread_mdelay(20); //if(!HAL_GPIO_ReadPin(USR_KEY1_GPIO_Port,USR_KEY1_Pin)) //{ //rt_thread_mdelay(100); //if(!HAL_GPIO_ReadPin(USR_KEY1_GPIO_Port,USR_KEY1_Pin))//消抖 //{ //rt_kprintf("Key1ispresseded! "); //} //} //if(!HAL_GPIO_ReadPin(USR_KEY2_GPIO_Port,USR_KEY2_Pin)) //{ //rt_thread_mdelay(100); //if(!HAL_GPIO_ReadPin(USR_KEY2_GPIO_Port,USR_KEY2_Pin))//消抖 //{ //rt_kprintf("Key2ispresseded! "); //} //} //if(!HAL_GPIO_ReadPin(USR_KEY3_GPIO_Port,USR_KEY3_Pin)) //{ //rt_thread_mdelay(100); //if(!HAL_GPIO_ReadPin(USR_KEY3_GPIO_Port,USR_KEY3_Pin))//消抖 //{ //rt_kprintf("Key3ispresseded! "); //} //} } }
來看一下測試效果:
-
硬件
+關(guān)注
關(guān)注
11文章
3312瀏覽量
66200 -
GPIO
+關(guān)注
關(guān)注
16文章
1204瀏覽量
52051 -
低電平
+關(guān)注
關(guān)注
1文章
115瀏覽量
13269
原文標(biāo)題:03-HAL庫GPIO輸入與多功能按鍵實現(xiàn)
文章出處:【微信號:小飛哥玩嵌入式,微信公眾號:小飛哥玩嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論