首页 > 技术知识 > 正文

HAL库GPIO输入模式在cubemx中的配置

上节课程介绍了GPIO输出模式的配置,包括修改IO标签,选择GPIO模式、GPIO上下拉等,本节输入模式有很多相同之处,节省时间,小飞哥就简单介绍一下

GPIO选择与配置

依然是先来看看我们的Alios 开发板上的按键硬件连接,总共有3个用户按键,分别挂在GPIOE11、GPIOE14和GPIOE10上,连接方式采用的是低电平有效,也即是,按键按下之后,MCU检测到的电平为低电平,松开之后检测为高电平,外部上拉电阻4.7KHAL库GPIO输入模式在cubemx中的配置-gpio常用库函数HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数1接下来,我们只需要在cubemx对这3个GPIO进行配置即可,工程在上一节内容上继续添加,打开上一节的cubemx工程,选择PE14引脚,选择引脚模式为输入模式HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数2然后修改标签为USR_KEY2,其他两个按键一样的配置方法,3个按键配置完成之后如下图所示:HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数3接下来依然是对GPIO的模式、配置进行修改,这里需要注意一点就是GPIO的上下拉配置,不要瞎选,这个一定是跟硬件相关的,比如本次硬件设计,空闲时是保持高电平的,那么配置上拉是比较合适的,但是呢,此时外部已经有了上拉电阻,我们内部上拉就不是很有必要了,也可以配置为不上拉也不下拉,但是配置为下拉一定是不合适的,可能会引起信号的误动作,功耗的增加等不必要的麻烦,所以说,cubemx配置只是为你减轻了重复性搬运工作,电路工作原理必须了然于胸,选择最为合适的配置HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数4配置比较简单,我们此次采用的是轮询获取GPIO状态的方法来实现按键,当然也可以采用外部中断的方式,对于普通按键来讲,不是很有必要,轮询GPIO状态即可

HAL库GPIO输入模式操作详解与结构介绍

接下来,我们生成代码即可HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数5打开工程之后,我们发现上一节配置的输出IO和这一节的输入IO是放在一起的,cubemx生成代码的规则是把同一类外设统一放在相应的模块初始化代码中,宏定义统一放在main.h中如果能够接受这个布局的话是没有问题的,如果不能还是比较麻烦的HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数6HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数7输入模式相关的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的任务中,对按键状态进行测试,这里涉及到按键的消抖,所谓“消抖”就是:HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数8当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。即为按键消抖按键消抖:可分为硬件消抖和软件消抖。

硬件消抖就是在按键上并联一个电容,如图 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! “); } } } }

测试结果:HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数9

HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数10

多功能按键移植

上面是简单的按键状态获取,实际中,一个项目可能按键有限但同时又要实现复杂的功能切换,那么按键的功能如果很单一的话,就不能够满足需求,接下来,小飞哥带大家一起移植一个很不错的多功能按键框架,代码来源是github的jiejieTop

源码地址:GitHub – jiejieTop/ButtonDrive: 纯C语言实现的一个按键驱动,可移植性强,支持单双击、连按、连按释放、长按;采用回调处理按键事件(自定义消抖时间),使用只需3步,1:创建按键,2:按键事件与回调处理函数链接映射。然后周期检查按键。HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数11我们下载源码进行移植,把文件夹里面的这些文件,我们移植到自己的文件目录,下载文件中包含的main.c中是一个完整的按键初始化,实现,大家可以看看就知道如何使用了HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数12HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数13下面小飞哥就带大家来看看如何移植到自己的系统里面

![e7d83ed083514e07e330211e338b33b7.png](en-resource://databHAL库GPIO输入模式在cubemx中的配置-gpio常用库函数14可以把memset,printff替换为rt-thread的驱动,当然也可以不关注HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数15HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数16重新编译一下,刚才的警告已经消失了HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数17然后我们看看如何实现这个多功能按键:

先来看看一些配置项宏定义,位域、结构体、回调函数,面向对象的一套东西都在,是很适合学习的,有关于短按、长按、单击、双击等等的配置,我们根据自己的需要配置即可

#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! “); //} //} } }

来看一下测试效果:HAL库GPIO输入模式在cubemx中的配置-gpio常用库函数18

审核编辑:彭静

猜你喜欢