STM32基础--GPIO输入(按键检测)

硬件设计

按键机械触点断开、闭合时,由于触点的弹性作用,按键开关不会马上稳定接通或一下子断开,使用按键时会产生图中的带波纹信号,可以用软件消抖处理滤波,或者硬件消抖方式。
在这里插入图片描述

下图为硬件消抖的电路连接图

在这里插入图片描述
在这里插入图片描述

从按键的原理图可知,这些按键在没有被按下的时候,GPIO 引脚的输入状态为低电平 (按键所在的电路不通,引脚接地),当按键按下时,GPIO 引脚的输入状态为高电平 (按键所在的电路导通,引脚接到电源)。只要我们检测引脚的输入电平,即可判断按键是否被按下。

软件设计

同 LED 的工程,为了使工程更加有条理,我们把按键相关的代码独立分开存储,方便以后移植。在“工程模板”之上新建“bsp_key.c”及“bsp_key.h”文件,这些文件也可根据您的喜好命名,这些文件不属于 STM32 标准库的内容,是由我们自己根据应用需要编写的。

编程要点

  1. 使能 GPIO 端口时钟;
  2. 初始化 GPIO 目标引脚为输入模式 (浮空输入);
  3. 编写简单测试程序,检测按键的状态,实现按键控制 LED 灯。

代码分析

别忘了的事

添加bsp_key.c文件和bsp_key.h文件
别忘了添加文件的时候,在魔术棒的地方加路径。(加上bsp_key.h存放的路径)

按键引脚宏定义(bsp_key.h中)

同样,在编写按键驱动时,也要考虑更改硬件环境的情况。我们把按键检测引脚相关的宏定义到“bsp_key.h”文件中。

//引脚定义
#define KEY1_GPIO_CLK										RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT									GPIOA
#define KEY1_GPIO_PIN										GPIO_Pin_0

#define KEY2_GPIO_CLK										RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT									GPIOC
#define KEY2_GPIO_PIN										GPIO_Pin_13

以上代码根据按键的硬件连接,把检测按键输入的 GPIO 端口、GPIO 引脚号以及 GPIO 端口时钟封装起来了。

按键 GPIO 初始化函数

利用上面的宏,编写按键的初始化函数。

/**
  * @brief  初始化KEY的IO,浮空输入
  * @param  无
  * @retval 无
  */
void Key_GPIO_Config(void)
{
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef  GPIO_InitStructure;

		/*开启KEY相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK | KEY2_GPIO_CLK ,ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;

		/*设置引脚模式为浮空输入*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = GPIO_Mode_IN_FLOATING;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);	
	
}

同为 GPIO 的初始化函数,初始化的流程与“LED GPIO 初始化函数”章节中的类似,主要区别是引脚的模式。函数执行流程如下:
(1) 使用 GPIO_InitTypeDef 定义 GPIO 初始化结构体变量,以便下面用于存储 GPIO 配置。
(2) 调用库函数 RCC_APB2PeriphClockCmd 来使能按键的 GPIO 端口时钟,调用时我们使用“|”操作同时配置两个按键的时钟。
(3) 向 GPIO 初始化结构体赋值,把引脚初始化成浮空输入模式,其中的 GPIO_Pin 使用宏“KEYx_GPIO_PIN”来赋值,使函数的实现方便移植。由于引脚的默认电平受按键电路影响,所以设置成浮空输入。
(4) 使用以上初始化结构体的配置,调用 GPIO_Init 函数向寄存器写入参数,完成 GPIO 的初始化,这里的 GPIO 端口使用“KEYx_GPIO_PORT”宏来赋值,也是为了程序移植方便。
(5) 使用同样的初始化结构体,只修改控制的引脚和端口,初始化其它按键检测时使用的 GPIO引脚。

检测按键的状态

初始化按键后,就可以通过检测对应引脚的电平来判断按键状态。
在bsp_key.h中

/** 按键按下标置宏
* 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* 若按键按下为低电平,把宏设置成 KEY_ON=0 ,KEY_OFF=1 即可
*/
#define KEY_ON													1
#define KEY_OFF 												0

在bsp_key.c中

/**
	* @brief检测是否有按键按下
	* @paramGPIOx: 具体的端口, x 可以是(A...G)
	* @paramGPIO_PIN: 具体的端口位, 可以是 GPIO_PIN_x(x 可以是 0...15)
	* @retval	按键的状态
	*	@arg KEY_ON: 按键按下
	*	@arg KEY_OFF: 按键没按下
**/

uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
	/* 检测是否有按键按下 */
	if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON){
		//因为有硬件消抖,故不写软件消抖
			/* 等待按键释放 */
		while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
		
		return KEY_ON;
			
	}
	else{
		return KEY_OFF;
	}

}

在这里我们定义了一个 Key_Scan 函数用于扫描按键状态。GPIO 引脚的输入电平可通过读取 IDR寄存器对应的数据位来感知,而 STM32 标准库提供了库函数 GPIO_ReadInputDataBit 来获取位状态,该函数输入 GPIO 端口及引脚号,函数返回该引脚的电平状态,高电平返回 1,低电平返回0。Key_Scan 函数中以 GPIO_ReadInputDataBit 的返回值与自定义的宏“KEY_ON”对比,若检测到按键按下,则使用 while 循环持续检测按键状态,直到按键释放,按键释放后 Key_Scan 函数返回一个“KEY_ON”值;若没有检测到按键按下,则函数直接返回“KEY_OFF”。若按键的硬件没有做消抖处理,需要在这个Key_Scan 函数中做软件滤波,防止波纹抖动引起误触发。

接下来我们使用编写按键检测测试流程。

在bsp_key.c中

/**
  * @brief  KEY测试函数,按动按键则小灯变换一个颜色
  * @param  无
  * @retval 无
  */
void Key_GPIO_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 初始化按键 */
	Key_GPIO_Config();
	
	LED1_ON;
	/* 轮询按键状态,若按键按下则反转 LED(还没学到DMA或者中断呢) */
	while(1){
		if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON){
					/*LED2 关闭 */
					LED2_OFF;
					/*LED1 反转 */
					LED1_TOGGLE;
		}
		
		if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON){
				  /*LED1 关闭 */
					LED1_OFF;
					/*LED2 反转 */
					LED2_TOGGLE;
		}	
	}	
}

代码中初始化 LED 灯及按键后,在 while 函数里不断调用 Key_Scan 函数,并判断其返回值,若返回值表示按键按下,则反转 LED 灯的状态。

代码

main.c

/**
 * *****************************************************************************
 * @file        main.c
 * @brief       主函数
 * @author       (六千里)
 * @date        2024-03-09
 * @copyright   无
 * *****************************************************************************
 */
#include "stm32f10x.h"  

int main(void)	
{
	// 来到这里的时候,系统的时钟已经被配置成72M。
	Key_GPIO_Test();

}

bsp_key.c

/**
 * *****************************************************************************
 * @file        bsp_key.c
 * @brief      	按键检测文件
 * @author       (六千里)
 * @date        2024-03-09
 * @copyright   无
 * *****************************************************************************
 */

#include "bsp_key.h"
#include "bsp_led.h"

/**
  * @brief  初始化KEY的IO,浮空输入
  * @param  无
  * @retval 无
  */
void Key_GPIO_Config(void)
{
		/*定义一个GPIO_InitTypeDef类型的结构体*/
		GPIO_InitTypeDef  GPIO_InitStructure;

		/*开启KEY相关的GPIO外设时钟*/
		RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK|KEY2_GPIO_CLK ,ENABLE);
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;

		/*设置引脚模式为浮空输入*/
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);	
		
		/*选择要控制的GPIO引脚*/
		GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;

		/*调用库函数,初始化GPIO*/
		GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);	
	
}

/**
	* @brief检测是否有按键按下
	* @paramGPIOx: 具体的端口, x 可以是(A...G)
	* @paramGPIO_PIN: 具体的端口位, 可以是 GPIO_PIN_x(x 可以是 0...15)
	* @retval	按键的状态
	*	@arg KEY_ON: 按键按下
	*	@arg KEY_OFF: 按键没按下
**/

uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)
{
	/* 检测是否有按键按下 */
	if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON){
		//因为有硬件消抖,故不写软件消抖
			/* 等待按键释放 */
		while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == KEY_ON);
		
		return KEY_ON;
			
	}
	else{
		return KEY_OFF;
	}

}
/**
  * @brief  KEY测试函数,按动按键则小灯变换一个颜色
  * @param  无
  * @retval 无
  */
void Key_GPIO_Test(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();
	
	/* 初始化按键 */
	Key_GPIO_Config();
	
	LED1_ON;
	/* 轮询按键状态,若按键按下则反转 LED(还没学到DMA或者中断呢) */
	while(1){
		if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON){
					/*LED2 关闭 */
					LED2_OFF;
					/*LED1 反转 */
					LED1_TOGGLE;
		}
		
		if(Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON){
				  /*LED1 关闭 */
					LED1_OFF;
					/*LED2 反转 */
					LED2_TOGGLE;
		}	
	}	
}

bsp_key.h

#ifndef __BSP_KEY_H
#define __BSP_KEY_H


/**
 * *****************************************************************************
 * 包含的头文件
 * *****************************************************************************
 */

#include "stm32f10x.h"

/**
 * *****************************************************************************
 * 宏定义
 * *****************************************************************************
 */

//引脚定义
#define KEY1_GPIO_CLK										RCC_APB2Periph_GPIOA
#define KEY1_GPIO_PORT									GPIOA
#define KEY1_GPIO_PIN										GPIO_Pin_0

#define KEY2_GPIO_CLK										RCC_APB2Periph_GPIOC
#define KEY2_GPIO_PORT									GPIOC
#define KEY2_GPIO_PIN										GPIO_Pin_13

/** 按键按下标置宏
* 按键按下为高电平,设置 KEY_ON=1, KEY_OFF=0
* 若按键按下为低电平,把宏设置成 KEY_ON=0 ,KEY_OFF=1 即可
*/
#define KEY_ON													1
#define KEY_OFF 												0





/**
 * *****************************************************************************
 * .c文件中包含的函数
 * *****************************************************************************
 */
 void Key_GPIO_Config(void);
 uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin);
 void Key_GPIO_Test(void);
 
 #endif  /*__BSP_KEY_H*/

stm32f10x_conf.h

/**
  ******************************************************************************
  * @file    Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h 
  * @author  MCD Application Team
  * @version V3.5.0
  * @date    08-April-2011
  * @brief   Library configuration file.
  ******************************************************************************
  * @attention
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_CONF_H
#define __STM32F10x_CONF_H

/* Includes ------------------------------------------------------------------*/
/* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */
#include "stm32f10x_adc.h"
#include "stm32f10x_bkp.h"
#include "stm32f10x_can.h"
#include "stm32f10x_cec.h"
#include "stm32f10x_crc.h"
#include "stm32f10x_dac.h"
#include "stm32f10x_dbgmcu.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_flash.h"
#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stm32f10x_iwdg.h"
#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_rtc.h"
#include "stm32f10x_sdio.h"
#include "stm32f10x_spi.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_wwdg.h"
#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */
/**
 *自己书写文件的头文件
 **/
#include "bsp_led.h"
#include "bsp_key.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Uncomment the line below to expanse the "assert_param" macro in the 
   Standard Peripheral Library drivers code */
/* #define USE_FULL_ASSERT    1 */

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

#endif /* __STM32F10x_CONF_H */

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

参考https://doc.embedfire.com/products/link/zh/latest/index.html

  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用STM32输入捕获功能来检测按键,可以按照以下步骤进行操作: 1. 配置GPIO口为输入模式,用于连接按键的引脚。 2. 配置TIM(定时器)的一个通道为输入捕获模式,以便捕获按键引脚的状态变化。 3. 在硬件初始化函数中配置GPIO和TIM。 4. 在主循环中检测输入捕获事件并处理按键状态。 下面是一个简单的示例代码,演示如何使用STM32 HAL库来实现按键检测: ```c #include "stm32f4xx_hal.h" // 定义按键引脚 #define BUTTON_PIN GPIO_PIN_0 #define BUTTON_PORT GPIOA // 定义定时器和通道 #define TIM_HANDLE htim2 #define TIM_CHANNEL TIM_CHANNEL_1 // 按键状态变量 GPIO_PinState buttonState = GPIO_PIN_RESET; // 输入捕获回调函数 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM_HANDLE.Instance && htim->Channel == TIM_CHANNEL) { // 获取捕获值 uint32_t captureValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL); // 处理按键状态 if (captureValue > 1000) { buttonState = GPIO_PIN_SET; // 按下 } else { buttonState = GPIO_PIN_RESET; // 松开 } } } int main(void) { // STM32初始化代码 // 配置GPIO口为输入模式 GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = BUTTON_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; // 或者使用GPIO_PULLDOWN HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct); // 配置TIM为输入捕获模式 TIM_IC_InitTypeDef sConfigIC; sConfigIC.ICPolarity = TIM_ICPOLARITY_BOTHEDGE; // 检测上升沿和下降沿 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; sConfigIC.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&TIM_HANDLE, &sConfigIC, TIM_CHANNEL); // 启动输入捕获 HAL_TIM_IC_Start_IT(&TIM_HANDLE, TIM_CHANNEL); while (1) { // 处理按键状态 if (buttonState == GPIO_PIN_SET) { // 按键按下后的操作 } else { // 按键松开后的操作 } } } ``` 在上述示例代码中,我们首先定义了按键引脚、定时器和通道以及按键状态变量。然后在`main`函数中,我们配置了GPIO口为输入模式,并将定时器配置为输入捕获模式。在主循环中,我们通过检查按键状态来执行相应的操作。 请注意,示例代码中使用了STM32 HAL库,你需要根据自己使用的具体型号和开发环境进行相应的配置和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值