一、变量定义部分(理解程序的 "记忆")
c
运行
/* USER CODE BEGIN PV */
static uint8_t last_button_state = 1; // 初始为高电平(未按下)
static uint8_t device_mode = 0; // 设备模式:0=LD1, 1=LD3, 2=蜂鸣器, 3=全部关闭
static uint8_t device_state[3] = {0, 0, 0}; // 设备状态:0=关, 1=开
/* USER CODE END PV */
这部分定义了程序运行中需要 "记住" 的变量,就像人的短期记忆:
last_button_state
:记录上一次按键的状态(1 = 未按下,0 = 按下),用于检测按键的 "变化"(从按下到松开或反之)。初始值为 1,因为按键未按下时默认是高电平(硬件上拉)。device_mode
:记录当前操作的设备模式(0-3 分别对应不同设备),类似遥控器的 "模式切换"。device_state[3]
:数组,分别记录 3 个设备的开关状态(索引 0=LD1,1=LD3,2 = 蜂鸣器),0 表示关,1 表示开。
二、函数声明(提前 "告知" 程序要用到的功能)
c
运行
/* USER CODE BEGIN PFP */
void beep_short(void);
/* USER CODE END PFP */
这行代码是函数声明,告诉编译器:后面会定义一个叫beep_short
的函数,无参数、无返回值。作用是提前 "报备",避免编译器在遇到函数调用时不认识该函数。
三、初始化代码(程序启动时的 "准备工作")
c
运行
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET); // LD1关闭
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // LD3关闭
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 蜂鸣器关闭// 开机提示音
beep_short();
HAL_Delay(200);
beep_short();
/* USER CODE END 2 */
这部分是程序启动后首先执行的初始化操作:
- 关闭所有外设:通过
HAL_GPIO_WritePin
函数将 3 个设备的引脚设为低电平(GPIO_PIN_RESET
),确保程序启动时所有设备都是关闭状态。- LD1 接在 GPIOC 的 Pin4
- LD3 接在 GPIOC 的 Pin13
- 蜂鸣器接在 GPIOA 的 Pin15
- 开机提示音:调用
beep_short
函数让蜂鸣器短响两次(间隔 200ms),提示程序已正常启动。
四、主循环逻辑(程序的 "核心动作",反复执行)
这部分在while(1)
循环中,是程序运行时一直在重复做的事情,就像人反复 "观察→判断→行动" 的过程。
1. 读取当前按键状态
c
运行
uint8_t current_button_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9);
- 通过
HAL_GPIO_ReadPin
函数读取按键引脚(GPIOC 的 Pin9)的当前状态(1 = 未按下,0 = 按下),并存在current_button_state
变量中。
2. 检测按键 "按下" 动作(下降沿检测)
c
运行
if (last_button_state == 1 && current_button_state == 0) {// 按键从"未按下"变为"按下"(下降沿)HAL_Delay(20); // 防抖延时current_button_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9);if (current_button_state == 0) { // 确认按键确实按下// 按键处理逻辑...}
}
这是按键检测的核心逻辑,作用是准确识别一次有效的按键按下:
- 条件
last_button_state == 1 && current_button_state == 0
:判断按键是否从 "未按下"(1)变为 "按下"(0),这种变化叫 "下降沿"。 HAL_Delay(20)
:延时 20ms,是为了 "消抖"—— 按键机械结构会导致按下瞬间有微小抖动(状态快速变化),延时后再读一次状态,避免误判。- 再次判断
current_button_state == 0
:确认按键确实处于按下状态,排除抖动干扰。
3. 按键按下后的处理
当确认按键按下后,执行以下操作:
(1)按键提示音
c
运行
beep_short(); // 调用蜂鸣器短响函数,反馈按键已被按下
(2)切换设备模式
c
运行
device_mode = (device_mode + 1) % 4;
device_mode
从 0 开始,每次按键加 1,通过%4
(取余 4)实现循环:0→1→2→3→0→...,对应 4 种模式的切换。
(3)根据模式控制外设(核心逻辑)
通过switch-case
语句,根据当前device_mode
执行不同操作:
c
运行
switch(device_mode) {case 0: // 控制LD1device_state[0] = !device_state[0]; // 取反:0→1(开),1→0(关)// 控制LD1引脚状态(开=高电平,关=低电平)HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, device_state[0] ? GPIO_PIN_SET : GPIO_PIN_RESET);// 关闭其他设备,确保每次只控制一个设备HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);device_state[1] = 0; // 同步更新其他设备的状态记录device_state[2] = 0;break;// case 1(控制LD3)、case 2(控制蜂鸣器)逻辑与case 0类似,只是操作的引脚不同case 3: // 全部关闭模式// 关闭所有设备引脚HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);// 同步更新所有设备状态为0(关)device_state[0] = 0;device_state[1] = 0;device_state[2] = 0;break;
}
逻辑要点:
- 每个模式只控制一个设备,同时关闭其他设备(避免多个设备同时工作)。
- 用
device_state
数组记录设备状态,方便下次切换时判断当前状态(开 / 关)。 - 通过三元运算符
device_state[0] ? GPIO_PIN_SET : GPIO_PIN_RESET
快速设置引脚状态(开 = 高电平,关 = 低电平)。
4. 等待按键释放并更新状态
c
运行
// 等待按键释放
while (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9) == 0) {// 空循环,直到按键松开(状态变为1)
}
HAL_Delay(20); // 释放时的防抖延时
- 这部分确保一次按键只触发一次模式切换,避免按键长按导致多次切换。
- 按键松开后再延时 20ms,进一步消除释放时的机械抖动。
5. 更新按键状态记录
c
运行
last_button_state = current_button_state;
- 将当前按键状态保存到
last_button_state
,为下一次循环的 "状态变化检测" 做准备。
五、蜂鸣器短响函数(封装重复功能)
c
运行
/* USER CODE BEGIN 4 */
void beep_short(void)
{HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_SET); // 蜂鸣器开启(高电平)HAL_Delay(100); // 持续100msHAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 蜂鸣器关闭(低电平)
}
/* USER CODE END 4 */
- 这是一个封装好的函数,实现蜂鸣器短响 100ms 的功能。
- 好处:避免重复写相同代码,需要蜂鸣器提示时直接调用
beep_short()
即可,让程序更简洁。
总结:程序的整体流程
- 初始化:关闭所有设备,蜂鸣器响两次提示启动。
- 循环检测:反复读取按键状态,判断是否有有效按下。
- 按键处理:确认按键按下后,切换模式→控制对应设备(开 / 关)→关闭其他设备→等待按键释放。
- 状态记录:通过变量记录按键状态和设备状态,实现 "记忆" 功能。
关键思路提炼(自己写时可复用)
- 按键检测:通过 "上一次状态 + 当前状态" 判断按键变化(下降沿),加延时消抖。
- 状态管理:用变量 / 数组记录设备状态,方便切换时判断当前状态。
- 模式切换:用
switch-case
或数组索引实现多模式循环。 - 功能封装:重复使用的功能(如蜂鸣器提示)写成函数,简化代码。
理解这些逻辑后,你可以尝试修改设备引脚、增加更多模式(如控制 4 个设备),或者改用中断方式检测按键,逐步掌握 STM32 的 GPIO 控制编程。