STM32独立按键控制LED流水灯实现与解析指南

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 输出,有助于调试: 

    作者:学习使我快乐!!!

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32独立按键控制LED流水灯实现与解析指南

    发表回复