01-04GPIO-按键控制LED

一、硬件准备

  • 1块STM32F103C8T6最小系统板
  • 1个ST-LINK V2烧录器
  • 两个按键
  • 若干杜邦线

运用:
用按键控制板载PC13LED亮灭

延时函数同上

LED.h

点击查看代码
#ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"  // Device header

// PC13 宏定义LED端口和引脚
#define LED_PIN    GPIO_Pin_13
#define LED_PORT   GPIOC

//函数声明
void LED_Init(void);    //LED GPIO初始化
void LED_ON(void);      //LED点亮
void LED_OFF(void);     //LED熄灭
void LED_Toggle(void);  //LED翻转函数:切换LED的亮/灭状态
void LED_Blink(uint32_t times);  //控制LED闪烁指定次数(times=闪烁次数,1次=亮200ms+灭200ms)

#endif

LED.c

点击查看代码
#include "LED.h"
#include "Delay.h"

// LED GPIO初始化(推挽输出,初始点亮)
void LED_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); 
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;         
    GPIO_InitStruct.GPIO_Pin = LED_PIN;                   
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_PORT, &GPIO_InitStruct);
    
    LED_ON(); // 初始状态:LED点亮
}

// LED点亮
void LED_ON(void)
{
    GPIO_ResetBits(LED_PORT, LED_PIN);
}

// LED熄灭
void LED_OFF(void)
{
    GPIO_SetBits(LED_PORT, LED_PIN);
}

// LED状态翻转(读取当前状态,反置)
void LED_Toggle(void)
{
    if (GPIO_ReadOutputDataBit(LED_PORT, LED_PIN) == Bit_SET)
    {
        LED_ON();
    }
    else
    {
        LED_OFF();
    }
}

// LED阻塞式闪烁:times=闪烁次数(1次=亮200ms+灭200ms)
void LED_Blink(uint32_t times)
{
    for (uint32_t i = 0; i < times; i++)
    {
        LED_ON();
        Delay_ms(200);
        LED_OFF();
        Delay_ms(200);
    }
    // 闪烁结束后恢复点亮状态
    LED_ON();
}

Key.h

点击查看代码
#ifndef __KEY_H
#define __KEY_H

#include "stm32f10x.h"                  // Device header

//宏定义按键端口和引脚
#define KEY1_PORT     GPIOB
#define KEY1_PIN      GPIO_Pin_1
#define KEY2_PORT     GPIOB
#define KEY2_PIN      GPIO_Pin_0

//按键状态定义
#define KEY_NONE      0  //无按键
#define KEY1_SHORT    1  //按键1短按(按下后<500ms释放)
#define KEY1_LONG     2  //按键1长按(按下后>=500ms仍未释放)
#define KEY2_SHORT    3  //按键2短按

//长按判断时间(ms)
#define LONG_PRESS_TIME 500

//函数声明
void Key_Init(void);           //按键初始化
uint8_t Key_Scan(void);        //按键检测
uint8_t Key_CheckPress(void);  //实时检测按键是否按下
#endif

Key.c

点击查看代码
#include "key.h"
#include "Delay.h"

void Key_Init(void)
{
	// 开启GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	// 配置GPIO参数
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;      // PB1引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}


// 阻塞式按键检测:返回按键状态(KEY_NONE/KEY1_SHORT/KEY1_LONG/KEY2_SHORT)
uint8_t Key_Scan(void)
{
    // 1. 检测按键1
    if (GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN) == 0) 
    {
        Delay_ms(20); // 消抖延时(20ms,过滤机械抖动)
        if (GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN) == 0) // 确认按键1真的按下
        {
            uint32_t press_time = 0;
            // 2. 等待按键释放,同时计时
            while (GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN) == 0) 
            {
                Delay_ms(10); // 每10ms计数一次,避免占用过多CPU
                press_time += 10;
                // 3. 若按下时间≥500ms,判定为长按
                if (press_time >= LONG_PRESS_TIME) 
                {
                    // 等待按键释放后返回长按状态
                    while (GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN) == 0);
                    Delay_ms(20); // 释放消抖
                    return KEY1_LONG;
                }
            }
            // 4. 若按下时间<500ms,判定为短按
            Delay_ms(20); // 释放消抖
            return KEY1_SHORT;
        }
    }

    // 5. 检测按键2(仅支持短按,逻辑同按键1简化版)
    if (GPIO_ReadInputDataBit(KEY2_PORT, KEY2_PIN) == 0) 
    {
        Delay_ms(20); // 消抖
        if (GPIO_ReadInputDataBit(KEY2_PORT, KEY2_PIN) == 0) 
        {
            while (GPIO_ReadInputDataBit(KEY2_PORT, KEY2_PIN) == 0); // 等待释放
            Delay_ms(20); // 释放消抖
            return KEY2_SHORT;
        }
    }

    return KEY_NONE; // 无按键
}

// 实时检测按键是否按下(无消抖,用于功能4“按着灭、松开亮”)
uint8_t Key_CheckPress(void)
{
    // 返回0=按键1按下,1=按键1未按下
    return GPIO_ReadInputDataBit(KEY1_PORT, KEY1_PIN);
}

LED_Func1.h

点击查看代码
#ifndef __LED_FUNC1_H
#define __LED_FUNC1_H

void LED_Func1_Init(void);
void LED_Func1_Run(void);

#endif

LED_Func1.c

点击查看代码
//按键 1 控制 LED 亮灭切换(按一下灭 / 再按一下亮)

#include "LED_Func1.h"
#include "LED.h"
#include "Key.h"

void LED_Func1_Init(void)
{
    LED_Init();  // 初始化LED
    Key_Init();  // 初始化按键
}

//按一下按键1切换LED状态
void LED_Func1_Run(void)
{
    uint8_t key = Key_Scan();  // 检测按键
    if (key == KEY1_SHORT)     // 按键1短按
    {
        LED_Toggle();          // 翻转LED(亮→灭/灭→亮)
    }
}

LED_Func2.h

点击查看代码
#ifndef __LED_FUNC2_H
#define __LED_FUNC2_H

void LED_Func2_Init(void);
void LED_Func2_Run(void);

#endif

LED_Func2.c

点击查看代码
#include "LED_Func2.h"
#include "LED.h"
#include "Key.h"

// 状态标记:记录LED是否被按键1熄灭(0=未熄灭/亮,1=已熄灭)
static uint8_t is_led_off_by_key1 = 0;

void LED_Func2_Init(void)
{
    // 初始化硬件:LED默认点亮,按键初始化
    LED_Init();    // LED初始状态为亮(由LED_Init()保证)
    Key_Init();    // 按键初始化(上拉输入)
    
    // 初始状态:LED亮,未被按键1熄灭
    is_led_off_by_key1 = 0;
}

void LED_Func2_Run(void)
{
    uint8_t key_state = Key_Scan();  // 检测按键事件

    // 1. 按键1短按:熄灭LED,并标记“已被按键1熄灭”
    if (key_state == KEY1_SHORT)
    {
        LED_OFF();                  // 熄灭LED
        is_led_off_by_key1 = 1;     // 标记状态:已被按键1熄灭
    }

    // 2. 按键2短按:仅在“LED已被按键1熄灭”的状态下,才点亮LED
    else if (key_state == KEY2_SHORT && is_led_off_by_key1 == 1)
    {
        LED_ON();                   // 点亮LED
        is_led_off_by_key1 = 0;     // 重置状态:已被按键2点亮
    }
}

LED_Func3.h

点击查看代码
#ifndef __LED_FUNC3_H
#define __LED_FUNC3_H

void LED_Func3_Init(void);
void LED_Func3_Run(void);

#endif

LED_Func3.c

点击查看代码
//长按按键 1 灭、短按亮

#include "LED_Func3.h"
#include "LED.h"
#include "Key.h"

// 功能3专用:标记“是否已执行长按灭灯”
static uint8_t long_press_flag = 0;

void LED_Func3_Init(void)
{
    LED_Init();
    Key_Init();
}

void LED_Func3_Run(void)
{
    uint8_t key = Key_Scan();
    // 长按按键1且未灭灯→灭灯
    if (key == KEY1_LONG && long_press_flag == 0)
    {
        LED_OFF();
        long_press_flag = 1;
    }
    // 短按按键1且已灭灯→亮灯
    else if (key == KEY1_SHORT && long_press_flag == 1)
    {
        LED_ON();
        long_press_flag = 0;
    }
}

LED_Func4.h

点击查看代码
#ifndef __LED_FUNC4_H
#define __LED_FUNC4_H

void LED_Func4_Init(void);
void LED_Func4_Run(void);

#endif

LED_Func4.c

点击查看代码
//按着按键 1 灭、松开亮

#include "LED_Func4.h"
#include "LED.h"
#include "key.h"
#include "Delay.h"

void LED_Func4_Init(void)
{
    LED_Init();
    Key_Init();
}

void LED_Func4_Run(void)
{
    // 实时检测按键:按下(0)→灭,松开(1)→亮
    if (Key_CheckPress() == 0)
    {
        LED_OFF();
    }
    else
    {
        LED_ON();
    }
    Delay_ms(10);  // 降低CPU占用
}

LED_Func5.h

点击查看代码
#ifndef __LED_FUNC5_H
#define __LED_FUNC5_H

void LED_Func5_Init(void);
void LED_Func5_Run(void);

#endif

LED_Func5.c

点击查看代码
#include "LED_Func5.h"
#include "LED.h"
#include "Key.h"
#include "Delay.h"

static uint8_t is_blinking = 0;  // 0=不闪烁,1=闪烁

void LED_Func5_Init(void)
{
    LED_Init();    // 初始化LED(默认点亮)
    Key_Init();    // 初始化按键
    is_blinking = 0;  // 强制初始为不闪烁
}

void LED_Func5_Run(void)
{
    // 1. 非闪烁状态:检测按键
    if (is_blinking == 0)
    {
        uint8_t key = Key_Scan();
        
        // 短按:翻转LED(保持不变)
        if (key == KEY1_SHORT)
        {
            LED_Toggle();
        }
        // 长按:强制进入闪烁(用最简单的逻辑)
        else if (key == KEY1_LONG)
        {
            is_blinking = 1;  // 标记为闪烁
            // 调试:长按后立即翻转一次,直观看到进入闪烁
            LED_Toggle();
        }
    }
    // 2. 闪烁状态:循环翻转,直到按键释放
    else
    {
        // 固定200ms翻转一次(简化逻辑)
        Delay_ms(200);
        LED_Toggle();
        
        // 检测按键是否释放(释放则退出闪烁)
        if (Key_CheckPress() == 1)  // 1=释放
        {
            is_blinking = 0;
        }
    }
}

LED_Func6.h

点击查看代码
#ifndef __LED_FUNC6_H
#define __LED_FUNC6_H

void LED_Func6_Init(void);
void LED_Func6_Run(void);

#endif

LED_Func6.c

点击查看代码
//按一下闪烁 5 次

#include "LED_Func6.h"
#include "LED.h"
#include "Key.h"

void LED_Func6_Init(void)
{
    LED_Init();
    Key_Init();
}

void LED_Func6_Run(void)
{
    uint8_t key = Key_Scan();
    if (key == KEY1_SHORT)  // 按一下按键1→闪烁5次
    {
        LED_Blink(5);  // 阻塞式闪烁(结束后自动亮)
    }
}

主函数main.c

点击查看代码
#include "stm32f10x.h"

// 1. 选择要启用的功能:包含对应功能的头文件(注释其他功能)
#include "LED_Func6.h"  // 启用功能1(切换功能时,改这里的头文件名)
// #include "LED_Func2.h"  // 启用功能2
// #include "LED_Func3.h"  // 启用功能3
// #include "LED_Func4.h"  // 启用功能4
// #include "LED_Func5.h"  // 启用功能5
// #include "LED_Func6.h"  // 启用功能6

int main(void)
{
    // 2. 初始化选择的功能(对应头文件的Init函数)
    LED_Func6_Init();  // 启用功能1(切换功能时,改这里的函数名)
    // LED_Func2_Init();  // 启用功能2
    // LED_Func3_Init();  // 启用功能3
    // LED_Func4_Init();  // 启用功能4
    // LED_Func5_Init();  // 启用功能5
    // LED_Func6_Init();  // 启用功能6

    while (1)
    {
        // 3. 循环执行功能逻辑(对应头文件的Run函数)
        LED_Func6_Run();  // 启用功能1(切换功能时,改这里的函数名)
        // LED_Func2_Run();  // 启用功能2
        // LED_Func3_Run();  // 启用功能3
        // LED_Func4_Run();  // 启用功能4
        // LED_Func5_Run();  // 启用功能5
        // LED_Func6_Run();  // 启用功能6
    }
}
posted @ 2025-10-20 10:42  有点闲  阅读(14)  评论(0)    收藏  举报