HAL庫GPIO輸入模式在cubemx中的配置
上節課程介紹了GPIO輸出模式的配置,包括修改IO標簽,選擇GPIO模式、GPIO上下拉等,本節輸入模式有很多相同之處,節省時間,小飛哥就簡單介紹一下
GPIO選擇與配置
依然是先來看看我們的Alios 開發板上的按鍵硬件連接,總共有3個用戶按鍵,分別掛在GPIOE11、GPIOE14和GPIOE10上,連接方式采用的是低電平有效,也即是,按鍵按下之后,MCU檢測到的電平為低電平,松開之后檢測為高電平,外部上拉電阻4.7K接下來,我們只需要在cubemx對這3個GPIO進行配置即可,工程在上一節內容上繼續添加,打開上一節的cubemx工程,選擇PE14引腳,選擇引腳模式為輸入模式然后修改標簽為USR_KEY2,其他兩個按鍵一樣的配置方法,3個按鍵配置完成之后如下圖所示:接下來依然是對GPIO的模式、配置進行修改,這里需要注意一點就是GPIO的上下拉配置,不要瞎選,這個一定是跟硬件相關的,比如本次硬件設計,空閑時是保持高電平的,那么配置上拉是比較合適的,但是呢,此時外部已經有了上拉電阻,我們內部上拉就不是很有必要了,也可以配置為不上拉也不下拉,但是配置為下拉一定是不合適的,可能會引起信號的誤動作,功耗的增加等不必要的麻煩,所以說,cubemx配置只是為你減輕了重復性搬運工作,電路工作原理必須了然于胸,選擇最為合適的配置配置比較簡單,我們此次采用的是輪詢獲取GPIO狀態的方法來實現按鍵,當然也可以采用外部中斷的方式,對于普通按鍵來講,不是很有必要,輪詢GPIO狀態即可
HAL庫GPIO輸入模式操作詳解與結構介紹
接下來,我們生成代碼即可打開工程之后,我們發現上一節配置的輸出IO和這一節的輸入IO是放在一起的,cubemx生成代碼的規則是把同一類外設統一放在相應的模塊初始化代碼中,宏定義統一放在main.h中如果能夠接受這個布局的話是沒有問題的,如果不能還是比較麻煩的輸入模式相關的GPIO API也比較少,只涉及到關于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);
如何使用呢?
參數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)
參數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狀態,也即是高低電平狀態,在沒有按鍵按下的時候,返回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的任務中,對按鍵狀態進行測試,這里涉及到按鍵的消抖,所謂“消抖”就是:當檢測到按鍵狀態變化時,不是立即去響應動作,而是先等待閉合或斷開穩定后再進行處理。即為按鍵消抖按鍵消抖:可分為硬件消抖和軟件消抖。
硬件消抖就是在按鍵上并聯一個電容,如圖 8-11 所示,利用電容的充放電特性來對抖動過程中產生的電壓毛刺進行平滑處理,從而實現消抖。
但實際應用中,這種方式的效果往往不是很好,而且還增加了成本和電路復雜度,所以實際中使用的并不多。絕大多數情況下,我們是用軟件即程序來實現消抖的
延時消抖
最簡單的消抖原理,就是當檢測到按鍵狀態變化后,先等待一個 10ms 左右的延時時間,讓抖動消失后再進行一次按鍵狀態檢測,如果與剛才檢測到的狀態相同,就可以確認按鍵已經穩定的動作了
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! "); } } } }
測試結果:
多功能按鍵移植
上面是簡單的按鍵狀態獲取,實際中,一個項目可能按鍵有限但同時又要實現復雜的功能切換,那么按鍵的功能如果很單一的話,就不能夠滿足需求,接下來,小飛哥帶大家一起移植一個很不錯的多功能按鍵框架,代碼來源是github的jiejieTop
源碼地址:GitHub - jiejieTop/ButtonDrive: 純C語言實現的一個按鍵驅動,可移植性強,支持單雙擊、連按、連按釋放、長按;采用回調處理按鍵事件(自定義消抖時間),使用只需3步,1:創建按鍵,2:按鍵事件與回調處理函數鏈接映射。然后周期檢查按鍵。我們下載源碼進行移植,把文件夾里面的這些文件,我們移植到自己的文件目錄,下載文件中包含的main.c中是一個完整的按鍵初始化,實現,大家可以看看就知道如何使用了下面小飛哥就帶大家來看看如何移植到自己的系統里面
![e7d83ed083514e07e330211e338b33b7.png](en-resource://datab可以把memset,printff替換為rt-thread的驅動,當然也可以不關注重新編譯一下,剛才的警告已經消失了然后我們看看如何實現這個多功能按鍵:
先來看看一些配置項宏定義,位域、結構體、回調函數,面向對象的一套東西都在,是很適合學習的,有關于短按、長按、單擊、雙擊等等的配置,我們根據自己的需要配置即可
#defineBTN_NAME_MAX32//名字最大為32字節 /*按鍵消抖時間40ms,建議調用周期為20ms 只有連續檢測到40ms狀態不變才認為有效,包括彈起和按下兩種事件 */ #defineCONTINUOS_TRIGGER0//是否支持連續觸發,連發的話就不要檢測單雙擊與長按了 /*是否支持單擊&雙擊同時存在觸發,如果選擇開啟宏定義的話,單雙擊都回調,只不過單擊會延遲響應, 因為必須判斷單擊之后是否觸發了雙擊否則,延遲時間是雙擊間隔時間 BUTTON_DOUBLE_TIME。 而如果不開啟這個宏定義,建議工程中只存在單擊/雙擊中的一個,否則,在雙擊響應的時候會觸發一次單擊, 因為雙擊必須是有一次按下并且釋放之后才產生的*/ #defineSINGLE_AND_DOUBLE_TRIGGER1 /*是否支持長按釋放才觸發,如果打開這個宏定義,那么長按釋放之后才觸發單次長按, 否則在長按指定時間就一直觸發長按,觸發周期由BUTTON_LONG_CYCLE決定*/ #defineLONG_FREE_TRIGGER0 #defineBUTTON_DEBOUNCE_TIME2//消抖時間(n-1)*調用周期 #defineBUTTON_CONTINUOS_CYCLE1//連按觸發周期時間(n-1)*調用周期 #defineBUTTON_LONG_CYCLE1//長按觸發周期時間(n-1)*調用周期 #defineBUTTON_DOUBLE_TIME20//雙擊間隔時間(n-1)*調用周期建議在200-600ms #defineBUTTON_LONG_TIME50/*持續n秒((n-1)*調用周期ms),認為長按事件*/ #defineTRIGGER_CB(event) if(btn->CallBack_Function[event]) btn->CallBack_Function[event]((Button_t*)btn) typedefvoid(*Button_CallBack)(void*);/*按鍵觸發回調函數,需要用戶實現*/ typedefenum{ BUTTON_DOWM=0, BUTTON_UP, BUTTON_DOUBLE, BUTTON_LONG, BUTTON_LONG_FREE, BUTTON_CONTINUOS, BUTTON_CONTINUOS_FREE, BUTTON_ALL_RIGGER, number_of_event,/*觸發回調的事件*/ NONE_TRIGGER }Button_Event; /* 每個按鍵對應1個全局的結構體變量。 其成員變量是實現濾波和多種按鍵狀態所必須的 */ typedefstructbutton { /*下面是一個函數指針,指向判斷按鍵手否按下的函數*/ uint8_t(*Read_Button_Level)(void);/*讀取按鍵電平函數,需要用戶實現*/ charName[BTN_NAME_MAX]; uint8_tButton_State:4;/*按鍵當前狀態(按下還是彈起)*/ uint8_tButton_Last_State:4;/*上一次的按鍵狀態,用于判斷雙擊*/ uint8_tButton_Trigger_Level:2;/*按鍵觸發電平*/ uint8_tButton_Last_Level:2;/*按鍵當前電平*/ uint8_tButton_Trigger_Event;/*按鍵觸發事件,單擊,雙擊,長按等*/ Button_CallBackCallBack_Function[number_of_event]; uint8_tButton_Cycle;/*連續按鍵周期*/ uint8_tTimer_Count;/*計時*/ uint8_tDebounce_Time;/*消抖時間*/ uint8_tLong_Time;/*按鍵按下持續時間*/ structbutton*Next; }Button_t;
然后看看如何使用API,我們只需要實現GPIO的狀態獲取、創建按鍵對象、編寫回調函數即可,在任務中輪詢按鍵狀態,移植起來是非常方便的
#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();//需要周期調用按鍵處理函數 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! "); //} //} } }
來看一下測試效果:
-
硬件
+關注
關注
11文章
3312瀏覽量
66200 -
GPIO
+關注
關注
16文章
1204瀏覽量
52052 -
低電平
+關注
關注
1文章
115瀏覽量
13269
原文標題:03-HAL庫GPIO輸入與多功能按鍵實現
文章出處:【微信號:小飛哥玩嵌入式,微信公眾號:小飛哥玩嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論