蓝桥杯嵌入式第三课--LED与按键检测
创始人
2025-05-29 02:12:39

前言

纵观多年考题,LED和按键检测作为必考的一个部分同时也作为GPIO的重点考察内容一直都是我们必须要掌握的部分。本节课带着大家,从底层硬件开始,把GPIO的这两个考点学的清清楚楚!

点亮LED!

一、底层硬件

板载的LED如上图所示,一共有八个LED灯,并且由一块锁存器芯片控制单片机引脚和LED的开关,这么做的目的是,LED的部分引脚和LCD的引脚产生了冲突,因此LED的(PC8\PC10\PC12\PC14)和LCD不能同时使用!,另外锁存器芯片的LE脚为高电平时,Q和D连通,LE脚为低电平时,Q端保持之前的状态,见真值表如下:

具体来说就是,PC8-PC16控制八个LED灯,低电平有效;PD2控制锁存器的开关,高电平时连通,低电平时断开。

二、程序设计思路

LED的程序比较固定,市面上流传的都是一次性随意点亮八个灯的版本,因为考试的时候我们需要盲打出来,所以这里我先和大家讨论一下程序设计的思路:

模块大体就两个步骤:先完成LED初始化,保证LED灯都在熄灭的状态,然后在点亮需要的LED灯。可能有人会疑惑:为啥每次LED操作之后都要把PD2从高拉到低呢?

这是因为PD2拉高是为了让Q端的数据和D端的同步,PD2拉低是为了让D端的数据不影响LED端,这是功能需要,也是使用的一种规范。就像上下车要打开和关闭车门一样。

三、实例代码

//函数的输入是八位的16进制数,每一位分别代表一个LED,如0x05=0000_0101,相当于第1个和3个亮
void led_disp(uint8_t led){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,led<<8,GPIO_PIN_RESET);//左移八位的原因是我们控制的引脚是从8-16的高八位HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

按键输入检测

一、底层硬件

从上图看出,板载的按键一共有四个,它们未按下时为高电平,按下后为低电平。

具体地来说,Key1-4分别为 PB0、PB1、PB2、PA0。

二、程序设计思路

按键这事儿吧,其实没有什么固定的套路,核心就是要带消抖功能,老夫遍寻网络世界,发现实现按键检测的方式千千万万,从最基本的轮询式到高级一点的中断,可谓繁多复杂,所以必须要因题制宜,找到最合适的方法。下面我介绍常用的两种方式。

  1. 基础款(带消抖,不精确)

相信很多人都学过51单片机,里头按键的检测最基本的就是采用if嵌套的方式,使用延时函数进行消抖处理:

优点是简单,缺点是复杂场合下使用Bug较多,编程负责,按键按下次数不好判定,长按短按的实现只能通过调整延时时间,容易误判。

b.升级款(使用GPIO外部中断进行按键检测)

这个思路很好理解,按键按下时会产生下降沿,一按按钮就触发一次,长按也没啥影响,毕竟只对边沿敏感,所以这个的重点就在于配置上,下图是配置外部中断的步骤:

三、具体实现

a.基础款

uint8_t keyscan(void) //该函数返回按下的按键号
{if(~HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)|~HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)|~HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)|~HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)){HAL_Delay(100); //消抖时间设置if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0){return 1;}if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==0){return 2;}if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==0){return 3;}if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0){return 4;}}return 0;
}

b.中断款

一、找到对应引脚,设置为GPIO_EXTI模式,并在GPIO栏里设置中断模式以及是否上下拉

二、在NVIC中打开外部中断

三、生成代码,我们在stm32g4xx_it.c中找到中断的句柄函数

点击HAL_GPIO_EXIT_IRQHandler();函数,找到原型实现:

我们可以看到中断发生后,先执行的操作是清除中断标志位,然后进入回调函数,因此我们需要自己编写我们的回调函数:

我们可以看到,回调函数是一个弱函数,因此我们只需要找一个地方重写它即可,不需要对其进行改动。我将其写在main.c中如下:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin==GPIO_PIN_0) //判断是哪个按键按下,因为GPIO的外部中断都会进入这个函数{if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0)//再次确认消抖{按下后执行的语句;}//__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);//清楚中断标志,可省略,因为前一级中有代码执行该操作}}

这样,每按下一次按键,都会执行一遍回调函数,也就实现了精确测量按键按下个数的目的。

HAL库GPIO函数

一、HAL_GPIO_ReadPin

/*** @brief  Read the specified input port pin.* @param  GPIOx where x can be (A..G) to select the GPIO peripheral for STM32G4xx family* @param  GPIO_Pin specifies the port bit to read.*         This parameter can be any combination of GPIO_PIN_x where x can be (0..15).* @retval The input port pin value.*/
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

该函数主要功能是用来读取GPIO引脚的电平状态;输入的参数,第一个为GPIO的类型,例如GPIOA、GPIOB等等;输入的第二个参数为GPIO引脚,例如GPIO_PIN_0、GPIO_PIN_1等。

二、HAL_GPIO_WritePin

/*** @brief  Set or clear the selected data port bit.** @note   This function uses GPIOx_BSRR and GPIOx_BRR registers to allow atomic read/modify*         accesses. In this way, there is no risk of an IRQ occurring between*         the read and the modify access.** @param  GPIOx where x can be (A..G) to select the GPIO peripheral for STM32G4xx family* @param  GPIO_Pin specifies the port bit to be written.*         This parameter can be any combination of GPIO_PIN_x where x can be (0..15).* @param  PinState specifies the value to be written to the selected bit.*         This parameter can be one of the GPIO_PinState enum values:*            @arg GPIO_PIN_RESET: to clear the port pin*            @arg GPIO_PIN_SET: to set the port pin* @retval None*/
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

该函数的用于将GPIO引脚设置为高电平(SET)或者是低电平(RESET),输入的参数为:

  1. GPIOx(GPIOA、GPIOB、等等)

  1. GPIO_PIN_x(GPIO_PIN_0、GPIO_PIN_1、等等)

  1. GPIO_PIN_SET / GPIO_PIN_RESET (置位或者复位)

三、HAL_GPIO_TogglePin

/*** @brief  Toggle the specified GPIO pin.* @param  GPIOx where x can be (A..G) to select the GPIO peripheral for STM32G4xx family* @param  GPIO_Pin specifies the pin to be toggled.*         This parameter can be any combination of GPIO_PIN_x where x can be (0..15).* @retval None*/
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

该函数的功能是将GPIO输出的电平状态反转(0-1、1-0),参数为:

  1. GPIOx(GPIOA、GPIOB、等等)

  1. GPIO_PIN_x(GPIO_PIN_0、GPIO_PIN_1、等等)

四、HAL_GPIO_EXTI_IRQHandler

/*** @brief  Handle EXTI interrupt request.* @param  GPIO_Pin Specifies the port pin connected to corresponding EXTI line.* @retval None*/
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)

外部中断服务函数,这个我们在前面的介绍中看到过,其分为两部分:

  1. 中断标志位清楚

  1. 回调函数

不需要我们编写和调用。

五、HAL_GPIO_EXTI_Callback

/*** @brief  EXTI line detection callback.* @param  GPIO_Pin: Specifies the port pin connected to corresponding EXTI line.* @retval None*/
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)

中断回调函数,老朋友了;中断发生后,在中断服务函数里会调用该函数,执行自定义代码。

六、HAL_GPIO_LockPin

/*** @brief  Lock GPIO Pins configuration registers.* @note   The locked registers are GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR,*         GPIOx_PUPDR, GPIOx_AFRL and GPIOx_AFRH.* @note   The configuration of the locked GPIO pins can no longer be modified*         until the next reset.* @param  GPIOx where x can be (A..G) to select the GPIO peripheral for STM32G4xx family* @param  GPIO_Pin specifies the port bits to be locked.*         This parameter can be any combination of GPIO_Pin_x where x can be (0..15).* @retval None*/
HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

该函数的作用是锁住引脚所对应的寄存器某些位,一旦锁住就不能再进行修改,只有复位后才可以重新配置。极少使用。

总结

GPIO虽然简单,但是如果认真说道的话内容其实也很繁杂,更高级的用法是在寄存器的级别,通过操控ODR寄存器等对GPIO进行操作,这个层面会比HAL库更加高效。下一节我们将重点学习定时计数器的相关内容。

相关内容

热门资讯

今年我省粮食产量达515.56... (来源:辽宁日报)转自:辽宁日报 图为在中储粮(盘锦)储运有限公司,装运粮食的重型卡车排起长队...
国家发展改革委部署促进投资止跌... (来源:辽宁日报)转自:辽宁日报 新华社北京12月13日电 (记者魏玉坤) 记者13日从全国发展和改...
江苏省实施《中华人民共和国森林... (来源:新华日报) 目 录 第一章 总则 第二章 森林、林木和林地权属管理...
姜堰数字化产品讲“活”理论 (来源:新华日报) □ 本报记者 卢佳乐 通讯员 姜宣 “王教授,您约我‘喝茶论道’,...
联合国维和部队在苏丹遇袭 6人... 转自:财联社【联合国维和部队在苏丹遇袭 6人死亡】财联社12月14日电,当地时间13日,苏丹武装部队...