1. 项目背景

在嵌入式开发中,按键控制 LED 进行不同的显示效果是一个常见的实验。本项目基于 STM32 单片机,实现四个独立按键控制 LED 进行不同的灯效变化。

2. 硬件设计

2.1 按键电路原理

从按键电路图来看,每个按键(KEY1-KEY4)都与 GPIO 端口(PC0-PC3)相连,同时通过 220Ω 的限流电阻接地。

  • 按键按下时,GPIO 读取为低电平(0)。
  • 按键释放时,GPIO 读取为高电平(1)。

2.2 LED 控制电路

LED 电路由 8 颗 LED(LED1-LED8)组成,每个 LED 通过 510Ω 的限流电阻连接到 VCC,并受 PNP 型三极管 S8550 控制。

  • 三极管 Q1 的基极由 LED_SW 控制,当 LED_SW 置低时,三极管导通,LED 获得驱动电流。
  • 通过 LED1LED8 对应的 GPIO 控制各个 LED 的亮灭状态。

3. 代码实现

3.1 按键扫描函数

uint8_t key_status(void)
{
	if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
	{
		key_flag = 1;
	}
	if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0)
	{
		key_flag = 2;
	}
	if (HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == 0)
	{
		key_flag = 3;
	}
	if (HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) == 0)
	{
		key_flag = 4;
	}
	if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1 &&
		HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1 &&
		HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin) == 1 &&
		HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin) == 1)
	{
		if (key_flag == 3 || key_flag == 4)
		{
			key_flag = 0;
		}
	}
	return key_flag;
}

3.2 LED 控制函数

单个 LED 控制
void led_on(uint8_t led) {
	HAL_GPIO_WritePin(LED_SW_GPIO_Port, LED_SW_Pin, GPIO_PIN_RESET);
	switch (led) {
		case 1: HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); break;
		case 2: HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); break;
		case 3: HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); break;
		case 4: HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, GPIO_PIN_RESET); break;
		case 5: HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, GPIO_PIN_RESET); break;
		case 6: HAL_GPIO_WritePin(LED6_GPIO_Port, LED6_Pin, GPIO_PIN_RESET); break;
		case 7: HAL_GPIO_WritePin(LED7_GPIO_Port, LED7_Pin, GPIO_PIN_RESET); break;
		case 8: HAL_GPIO_WritePin(LED8_GPIO_Port, LED8_Pin, GPIO_PIN_RESET); break;
	}
}
全部 LED 控制
void led_all_on(){
	HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_RESET);
	for (int i = 1; i <= 8; i++) {
		led_on(i);
	}
}

void led_all_off(){
	HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_RESET);
	for (int i = 1; i <= 8; i++) {
		led_off(i);
	}
}

3.3 流水灯功能

左移流水灯
void led_left(void){
	uint8_t j;
	HAL_GPIO_WritePin(LED_SW_GPIO_Port, LED_SW_Pin, GPIO_PIN_RESET);
	for(uint8_t i = 1; i <= 8; i++){
		led_on(i);
		HAL_Delay(DELAY_MS);
		led_off(i-1);
		for (j = 0; j < DELAY_MS / 10; j++) {
			HAL_Delay(10);
			key_status();
			if (key_flag != 1) return;
		}
	}
}
右移流水灯
void led_right(void){
	uint8_t j;
	HAL_GPIO_WritePin(LED_SW_GPIO_Port, LED_SW_Pin, GPIO_PIN_RESET);
	for(uint8_t i = 8; i > 0; i--){
		led_on(i);
		HAL_Delay(DELAY_MS);
		led_off(i+1);
		for (j = 0; j < DELAY_MS / 10; j++) {
			HAL_Delay(10);
			key_status();
			if (key_flag != 2) return;
		}
	}
}

3.4 红绿灯控制

void led_red(){
	HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_RESET);
	led_on(1); led_on(3); led_on(5); led_on(7);
}

void led_green(){
	HAL_GPIO_WritePin(LED_SW_GPIO_Port,LED_SW_Pin,GPIO_PIN_RESET);
	led_on(2); led_on(4); led_on(6); led_on(8);
}

4. 主循环

while(1){
	key_status();
	switch(key_flag){
		case 0: led_all_off(); break;
		case 1: led_left(); break;
		case 2: led_right(); break;
		case 3: led_red(); break;
		case 4: led_green(); break;
	}
}

目前 led_left()led_right() 使用 for 循环逐个点亮 LED,并在 HAL_Delay 过程中不断轮询按键状态。可以优化为:仅当 key_flag 仍为 1 或 2 时继续循环

避免多次调用 HAL_GPIO_WritePin(LED_SW_GPIO_Port, LED_SW_Pin, GPIO_PIN_RESET);

void led_left(void) {
    uint8_t i = 1;
    while (key_flag == 1) {
        led_on(i);
        HAL_Delay(DELAY_MS);
        led_off(i);
        i = (i % 8) + 1; // 让 LED 依次点亮
    }
}

使用数组优化 LED 控制 目前 led_on()led_off() 代码冗余,可用数组优化:

GPIO_TypeDef* LED_PORTS[] = {LED1_GPIO_Port, LED2_GPIO_Port, LED3_GPIO_Port, LED4_GPIO_Port, 
                             LED5_GPIO_Port, LED6_GPIO_Port, LED7_GPIO_Port, LED8_GPIO_Port};
uint16_t LED_PINS[] = {LED1_Pin, LED2_Pin, LED3_Pin, LED4_Pin, 
                       LED5_Pin, LED6_Pin, LED7_Pin, LED8_Pin};

void led_on(uint8_t led) {
    if (led >= 1 && led <= 8) {
        HAL_GPIO_WritePin(LED_PORTS[led - 1], LED_PINS[led - 1], GPIO_PIN_RESET);
    }
}
 

5. 结论

本项目实现了按键控制 LED 的不同显示模式,涵盖流水灯、红绿灯控制等。整个程序逻辑清晰,能够用于 STM32 的按键与 LED 控制实验。希望对大家有所帮助!

6. 优化

增加调试技巧

  1. 按键消抖
    当前代码没有进行按键消抖,可能会导致误触发。可以使用简单的软件消抖,例如:

uint8_t read_key(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) {
    if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) {
        HAL_Delay(10);  // 简单消抖
        if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) {
            return 1;
        }
    }
    return 0;
}
然后在 key_status() 中使用 read_key(KEY1_GPIO_Port, KEY1_Pin) 代替 HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)

添加串口调试信息
在关键位置加入 printf 输出,有助于调试: 

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐