第3章 按键输入实验
第三章 按键输入实验
1. STM32F4 IO口简介
STM32F4 的 IO 口在上两章已经有了比较详细的介绍,这里我们不再多说。 STM32F4 的 IO口做输入使用的时候,是通过调用函数 GPIO_ReadInputDataBit() 来读取 IO 口的状态的。
2. 硬件设计
本实验用的的硬件资源有:
-
指示灯DS0、DS1
-
蜂鸣器
-
4个按键:KEY0、KEY1、KEY2和KEY_UP
DS0、 DS1 以及蜂鸣器和 STM32F4 的连接在上两章都已经分别介绍了,在探索者 STM32F4开发板上的按键 KEY0 连接在 PE4 上、 KEY1 连接在 PE3 上、 KEY2 连接在 PE2 上、 KEY_UP连接在 PA0 上。
这里需要注意的是: KEY0、 KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的, 并且外部都没有上下拉电阻,所以,需要在 STM32F4 内部设置上下拉。
3. 软件设计
3.1 key宏定义
// 各按键引脚宏定义
#define KEY0_GPIO GPIOE
#define KEY0_PIN GPIO_PIN_4
#define KEY1_GPIO GPIOE
#define KEY1_PIN GPIO_PIN_3
#define KEY2_GPIO GPIOE
#define KEY2_PIN GPIO_PIN_2
#define KEY_UP_GPIO GPIOA
#define KEY_UP_PIN GPIO_PIN_0
// 读取IO电平状态
#define KEY0_GET HAL_GPIO_ReadPin(KEY0_GPIO, KEY0_PIN)
#define KEY1_GET HAL_GPIO_ReadPin(KEY1_GPIO, KEY1_PIN)
#define KEY2_GET HAL_GPIO_ReadPin(KEY2_GPIO, KEY2_PIN)
#define KEY_UP_GET HAL_GPIO_ReadPin(KEY_UP_GPIO, KEY_UP_PIN)
// 宏定义按键值
#define KEY0_PRES 1
#define KEY1_PRES 2
#define KEY2_PRES 3
#define KEY_UP_PRES 4
3.2 key初始化函数
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 首先就是开启时钟啦
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
// 接下来配置GPIO
GPIO_InitStructure.Pin = KEY_UP_PIN; // key_up引脚
GPIO_InitStructure.Mode = GPIO_MODE_INPUT; // s输入模式
GPIO_InitStructure.Pull = GPIO_PULLDOWN; // 下拉
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH; // 高速
HAL_GPIO_Init(KEY_UP_GPIO, &GPIO_InitStructure);// 初始化GPIO
GPIO_InitStructure.Pin = KEY0_PIN|KEY1_PIN|KEY2_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);
}
3.3 按键扫描函数
// 按键扫描函数说明
/*
功能:扫描按键,返回按键值
函数参数mode:0表示只扫描一次,1表示一直扫描
返回值:0,没有任何按键按下;1,KEY_UP按下
响应优先级:KEY0 > KEY1 > KEY2 > KEY_UP
*/
uint8_t KEY_Scan(uint8_t mode)
{
static uint8_t key_flag = 1; // 按键松开标志位
if(mode == 1)
key_flag = 1;
// 判断是否有按键按下
if(key_flag && (KEY0_GET == 0 || KEY1_GET == 0 || KEY2_GET == 0 || KEY_UP_GET == 1)) // 不要忘记了KEY_UP是高电平有效
{
delay_ms(10);
key_flag = 0; // 按键按下标志位
if(KEY0_GET == 0) return KEY0_PRES;
else if(KEY1_GET == 0) return KEY1_PRES;
else if(KEY2_GET == 0) return KEY2_PRES;
else if(KEY_UP_GET == 1) return KEY_UP_PRES;
}
else if(KEY0_GET == 1 && KEY1_GET == 1 && KEY2_GET == 1 && KEY_UP_GET == 0)
{
key_flag = 1; // 按键松开
return 0;
}
return 0;
}
- 如果按键按下,防抖延时后会检查是哪一个按键被按下,并返回相应的按键值(
KEY0_PRES
、KEY1_PRES
等)。 - 如果没有按键按下,返回
0
。
3.4 主函数
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
int main(void)
{
uint8_t key_value;
HAL_Init();
Stm32_Clock_Init(336,8,2,7);
delay_init(168);
LED_Init();
KEY_Init();
while(1)
{
key_value = KEY_Scan(0);
switch(key_value)
{
case KEY_UP_PRES: // LED0,LED1互斥
LED1 = !LED1;
LED0 = !LED1;
break;
case KEY0_PRES: // LED0,LED1同时翻转
LED0 = !LED0;
LED1 = !LED1;
break;
case KEY1_PRES:
LED1 = !LED1;
break;
case KEY2_PRES:
LED0 = !LED0;
break;
}
}
}
根据不同的按键值产生不同的效果
4. 小结
前面我们都是将GPIO的基本输出,这一章本章其实就是检测GPIO输入,怎么输入的?通过按键,不同的按键值产生不同的效果,一个新的hal库函数我们需要掌握:
4.1 函数原型
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
4.2 参数
- GPIOx: 指向 GPIO 外设的基地址。例如,
GPIOA
、GPIOB
、GPIOC
等。 - GPIO_Pin: 需要读取状态的 GPIO 引脚的编号。例如,
GPIO_PIN_0
、GPIO_PIN_1
等。
4.3 返回值
- GPIO_PIN_RESET: 引脚的状态为低电平(逻辑 0)。
- GPIO_PIN_SET: 引脚的状态为高电平(逻辑 1)。
4.4 示例代码
下面是一个使用 HAL_GPIO_ReadPin()
函数读取 GPIO 引脚状态的示例。假设我们要读取 GPIOA
端口上的 GPIO_PIN_0
引脚的状态。
#include "stm32f4xx_hal.h"
// 假设已经初始化了 HAL 库和 GPIO
int main(void)
{
// HAL 库初始化
HAL_Init();
// GPIO 初始化配置
__HAL_RCC_GPIOA_CLK_ENABLE(); // 启用 GPIOA 时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置 GPIOA 的引脚 0 作为输入模式
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用上拉或下拉电阻
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
while (1)
{
// 读取 GPIOA 的引脚 0 的状态
GPIO_PinState pinState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
if (pinState == GPIO_PIN_SET)
{
// 引脚状态为高电平
// 执行相应的操作
}
else
{
// 引脚状态为低电平
// 执行相应的操作
}
// 延时
HAL_Delay(100); // 延时 100ms
}
}
2024.9.27 第一次修订,后期不再维护
2024.12.20 丰富内容,优化代码