一、按键原理图

其中一种按键按下低电平,不按高电平



按下高电平

二、代码实操
1、单个按键控制
C
/**
* @brief 按键检测函数
* @note 检测PA0引脚的电平变化,实现按键消抖
* @retval 返回按键状态:1-按键按下,0-无按键按下
*/
uint8_t key(void)
{
// 第一次检测PA0引脚是否为高电平(按键可能被按下)
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1)
{
delay_ms(20); // 延时20ms进行消抖,消除机械抖动
// 再次检测确认按键是否真的被按下
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1)
{
// 等待按键释放(保持高电平直到变为低电平)
while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == 1);
return 1; // 返回按键按下的状态
}
}
return 0; // 无按键按下
}
/**
* @brief 主函数
*/
int main(void)
{
// 系统初始化
HAL_Init(); // 初始化HAL库
sys_stm32_clock_init(RCC_PLL_MUL9); // 设置系统时钟为72MHz
delay_init(72); // 初始化延时函数(基于72MHz时钟)
usart_init(115200); // 初始化串口,波特率115200
// 使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStruct;
// 配置PA8为推挽输出模式(用于控制LED等外设)
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式
GPIO_InitStruct.Pin = GPIO_PIN_8; // PA8引脚
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA8
// 配置PA0为输入模式(用于按键检测)
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉电阻(按键按下时为高电平)
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式
GPIO_InitStruct.Pin = GPIO_PIN_0; // PA0引脚
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化PA0
// 主循环
while(1)
{
// 检测按键是否按下
if(key() == 1)
{
// 按键按下时,翻转PA8引脚电平(LED状态取反)
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
}
}
}
代码注释由AI编写

注释:这里记得加上分号,否则按键控制不稳定
while循环后面的分号非常重要,它表示一个空语句,也就是循环体本身为空。
2、多个按键控制
2.1代码解析

1.这里使用 do { ... } while(0) 可以:
-
确保宏在语法上是一个完整的语句
-
在需要分号的地方可以正常使用分号
2. 避免if-else语句的悬挂else问题
如果没有do while包裹:
2. 避免if-else语句的悬挂else问题
如果没有do while包裹:
c
#define MACRO() func1(); func2()
if (condition)
MACRO();
else
// 其他代码
展开后:
c
if (condition)
func1(); func2(); // func2()不在if作用域内!
else
// 编译错误:else没有对应的if
2. 避免if-else语句的悬挂else问题
如果没有do while包裹:
c
#define MACRO() func1(); func2()
if (condition)
MACRO();
else
// 其他代码
展开后:
c
if (condition)
func1(); func2(); // func2()不在if作用域内!
else
// 编译错误:else没有对应的if
do { ... } while(0) 是C语言宏定义的最佳实践之一,它提供了语法上的安全性,同时保持了代码的灵活性。
程序部分代码

#include "./stm32f1xx_it.h"
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
int main(void)
{
// 系统初始化
uint8_t key;
HAL_Init(); // 初始化HAL库
sys_stm32_clock_init(RCC_PLL_MUL9); // 设置系统时钟为72MHz
delay_init(72); // 初始化延时函数(基于72MHz时钟)
usart_init(115200); // 初始化串口,波特率115200
led_init(); //LED引脚初始化
key_init(); // 按键引脚初始化
while(1)
{
key = key_scan(0); //调用key_scan函数,将返回值给key
if(key)
{
switch(key)
{
case KEY0_PRES: //KEY0按下
HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); // LED0翻转
break; //切记一定要加
case KEY1_PRES:
HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN);
break;
}
}
}
}
uint8_t key_scan(uint8_t mode)
{
static uint8_t key_up = 1; /* 按键按松开标志 */
uint8_t keyval = 0;
if (mode) key_up = 1; /* 支持连按 */
if (key_up && (KEY0 == 0 || KEY1 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
delay_ms(10); /* 去抖动 */
key_up = 0;
if (KEY0 == 0) keyval = KEY0_PRES;
if (KEY1 == 0) keyval = KEY1_PRES;
if (WK_UP == 1) keyval = WKUP_PRES;
}
else if (KEY0 == 1 && KEY1 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}
return keyval; /* 返回键值 */
}
2.2长按亮灯
if(key_scan(1)) // 检测按键1是否被按下(假设返回1表示按下)
{
count++; // 如果按键被按下,计数器加1
}
else // 重点:如果按键没有被按下
{
count = 0; // 计数器清零
}
if(count == 200) // 当计数器达到200时
{
HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); // 翻转LED1的状态
}
}
}
STM32按键控制详解
8万+

被折叠的 条评论
为什么被折叠?



