4. 按键切换LED状态
任务说明
本案例基于中微CMS32系列MCU,实现按键切换LED状态的功能:每次按键按下时,LED的亮灭状态翻转一次。核心学习点为按键消抖与边沿检测(仅在按键从按下到释放的下降沿触发一次),以及基于状态机的编程思路。
硬件连接
- LED正极经限流电阻接PB0引脚(输出高电平点亮)
- 按键一端接PA0引脚,另一端接地(按下为低电平)
- PA0内部使能上拉电阻
代码实现
#include "CMS32L051.h"
// 引脚定义
#define LED_PIN GPIO_PB0
#define KEY_PIN GPIO_PA0
// 状态定义
typedef enum {
LED_OFF = 0,
LED_ON
} LED_State_t;
// 按键状态机
typedef enum {
KEY_IDLE = 0, // 空闲(未按下)
KEY_PRESSED, // 已按下(等待释放)
KEY_RELEASE // 已释放(等待下一次按下)
} Key_State_t;
volatile LED_State_t led_state = LED_OFF;
volatile Key_State_t key_state = KEY_IDLE;
// 简单延时函数(非阻塞场景下可替换为定时器)
void delay_ms(uint32_t ms) {
uint32_t i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 8000; j++); // 根据系统时钟调整
}
// GPIO初始化
void GPIO_Init(void) {
// 使能GPIO时钟
SYS_Enable_Clock(CLOCK_GPIOA);
SYS_Enable_Clock(CLOCK_GPIOB);
// PB0配置为推挽输出
GPIO_SetMode(PB, BIT0, GPIO_MODE_OUTPUT);
// PA0配置为输入,使能内部上拉
GPIO_SetMode(PA, BIT0, GPIO_MODE_INPUT);
GPIO_PullUp_Enable(PA, BIT0);
// 初始LED熄灭
GPIO_WritePin(PB, BIT0, 0);
}
// 按键扫描(状态机+边沿检测)
void Key_Scan(void) {
uint8_t key_val = GPIO_ReadPin(PA, BIT0); // 读取当前电平
switch (key_state) {
case KEY_IDLE:
if (key_val == 0) { // 检测到按下
delay_ms(10); // 消抖
if (GPIO_ReadPin(PA, BIT0) == 0) {
key_state = KEY_PRESSED;
}
}
break;
case KEY_PRESSED:
if (key_val == 1) { // 检测到释放(下降沿完成)
delay_ms(10); // 消抖
if (GPIO_ReadPin(PA, BIT0) == 1) {
key_state = KEY_RELEASE;
// 边沿触发:翻转LED状态
if (led_state == LED_OFF) {
led_state = LED_ON;
GPIO_WritePin(PB, BIT0, 1);
} else {
led_state = LED_OFF;
GPIO_WritePin(PB, BIT0, 0);
}
}
}
break;
case KEY_RELEASE:
if (key_val == 0) { // 等待下一次按下
key_state = KEY_IDLE;
}
break;
default:
key_state = KEY_IDLE;
break;
}
}
int main(void) {
GPIO_Init();
while (1) {
Key_Scan();
}
}
状态机说明
按键状态机包含三个状态:KEY_IDLE(等待按下)、KEY_PRESSED(已按下,等待释放)、KEY_RELEASE(已释放,等待下一次按下)。LED翻转动作只在KEY_PRESSED状态检测到按键释放(即电平从低变高的边沿)时执行一次,从而避免按键持续按住时LED反复翻转。
验证方法
- 编译并烧录程序至CMS32L051开发板;
- 上电后观察LED初始状态为熄灭;
- 每次按下并释放按键,LED亮灭状态应翻转一次;
- 快速连续按键时,每次释放只翻转一次,不会出现多次翻转;
- 按住按键不放,LED状态保持不变,松开后才翻转。
判定标准:按键每完成一次“按下-释放”过程,LED状态恰好翻转一次,且无抖动导致的误触发,即判定通过。
关键要点
- 边沿检测:本设计在按键释放(上升沿)时触发翻转,而非按下时触发,可避免按键按下瞬间的抖动干扰;
- 消抖处理:状态切换前后各加入10ms延时确认,确保电平稳定;
- 状态机优势:通过显式状态管理,代码逻辑清晰,便于扩展(如长按、双击等);
- 实时性:主循环持续调用
Key_Scan(),无阻塞等待,可在此基础上添加其他任务。