0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

jf_L18yujSQ 来源:小飞哥玩嵌入式 作者:小飞哥玩嵌入式 2022-11-02 09:33 次阅读

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

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

GPIO选择与配置

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

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

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

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

测试结果:2f9441fa-5a43-11ed-a3b6-dac502259ad0.png

324fdfbc-5a43-11ed-a3b6-dac502259ad0.png

多功能按键移植

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

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

![e7d83ed083514e07e330211e338b33b7.png](en-resource://datab331bf5d4-5a43-11ed-a3b6-dac502259ad0.png可以把memset,printff替换为rt-thread的驱动,当然也可以不关注341080c2-5a43-11ed-a3b6-dac502259ad0.png3426be6e-5a43-11ed-a3b6-dac502259ad0.png重新编译一下,刚才的警告已经消失了345549e6-5a43-11ed-a3b6-dac502259ad0.png然后我们看看如何实现这个多功能按键:

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

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

}

来看一下测试效果:348f5564-5a43-11ed-a3b6-dac502259ad0.png

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 硬件
    +关注

    关注

    11

    文章

    2922

    浏览量

    64822
  • GPIO
    +关注

    关注

    16

    文章

    1136

    浏览量

    50582
  • 低电平
    +关注

    关注

    1

    文章

    79

    浏览量

    13091

原文标题:03-HAL库GPIO输入与多功能按键实现

文章出处:【微信号:小飞哥玩嵌入式,微信公众号:小飞哥玩嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    STM32 HAL CUBEMX配置 ADC采集 精选资料分享

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录STM32 HAL CUBEMX配置 ADC采集软硬件型号1.单通道不定时任意时刻采集信号
    发表于 08-11 07:46

    HAL配合CUBEMX配置

    本次教程主要介绍 :HAL配合CUBEMX配置一些常用外设的初始化,直观感受STM32编程,用最短时间入门STM32。
    发表于 08-11 07:07

    STM32 HAL CubeMX教程

    STM32 HAL CubeMX教程(七)ADC电压采集ADC简介CubeMX配置ADC初始化代码分析电压采集、串口打印ADC简介STM3
    发表于 08-12 06:36

    如何解决CubeMX配置HAL不进串口中断的问题?

    如何解决CubeMX配置HAL不进串口中断的问题?
    发表于 11-16 09:04

    如何对STM32cubeMXHAL串口进行配置

    如何对STM32cubeMXHAL串口进行配置呢?STM32cubeMXHAL
    发表于 12-08 06:39

    使用CUBEMX配置hal输入捕获

    之前有使用标准配置的,具体可以看我之前的博客这个项目是使用CUBEMX配置hal来写,相
    发表于 01-06 07:23

    如何对基于HALGPIO进行配置

    GPIO是什么?GPIO有哪几种工作模式?如何对基于HALGPIO进行
    发表于 01-19 06:47

    STM32CubeMXHAL对F103和F411的GPIO配置和使用方法

    文章目录STM32CubeMX第一章GPIO前言一、GPIO的工作模式二、使用步骤1.引入库2.读入数据总结前言  本文使用STM32CubeMX
    发表于 01-26 06:28

    基于HALGPIO点灯

    【三】零基础上手HAL之—GPIO点灯3.1前言我们已经大致的了解了Cubemx软件界面的一般操作,懂得如何新建工程和使用步骤了,接下来我们正式进入使用
    发表于 01-26 07:08

    STM32CubeMX使用LL输入GPIO怎么解决?

    我不确定这是否是一个错误,但 STM32CubeMX 假设在使用 LL 输入GPIO?并且没有将其配置为 Input ? 我发现我必
    发表于 12-27 07:24

    STM32CubeMX应用教程 第一章 GPIO

       Nucleo-F411RE文章目录STM32CubeMX第一章 GPIO前言一、GPIO的工作模式二、使用步骤1.引入库2.读入数据总结前言  本文使用STM32
    发表于 12-02 13:51 9次下载
    STM32<b class='flag-5'>CubeMX</b>应用教程 第一章 <b class='flag-5'>GPIO</b>

    HAL库的GPIO八种工作模式

    众所周知,STM32的GPIO的工作模式有八种,但是打开HAL库发现,有12中可以供自己设置。先不要懵,下面解释一下这12种工作模式的区别。实际上这12种是包含在通用的8种工作
    发表于 12-05 13:06 8次下载
    <b class='flag-5'>HAL</b>库的<b class='flag-5'>GPIO</b>八种工作<b class='flag-5'>模式</b>

    【STM32】标准库与HAL库对照学习教程三--使用库函数配置GPIO点亮LED灯

    【STM32】标准库与HAL库对照学习教程三--使用库函数配置GPIO点亮LED灯一、前言二、准备工作三、LED硬件电路四、使用标准库工程点亮LED灯1、GPIO初始化一般步骤2、
    发表于 12-05 14:06 15次下载
    【STM32】标准库与<b class='flag-5'>HAL</b>库对照学习教程三--使用库函数<b class='flag-5'>配置</b><b class='flag-5'>GPIO</b>点亮LED灯

    00_STM32F4学习_HAL库_GPIO函数

    HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);//初始化要用引脚的工作
    发表于 12-05 14:21 12次下载
    00_STM32F4学习_<b class='flag-5'>HAL</b>库_<b class='flag-5'>GPIO</b>函数

    【STM32】标准库与HAL库对照学习教程特别篇--GPIO详讲

    【STM32】SMT32标准库与HAL库对照学习教程特别篇--GPIO详讲一、前言二、GPIO简介1、定义2、分类3、复用三、GPIO工作模式
    发表于 01-13 16:12 12次下载
    【STM32】标准库与<b class='flag-5'>HAL</b>库对照学习教程特别篇--<b class='flag-5'>GPIO</b>详讲