共计 1713 个字符,预计需要花费 5 分钟才能阅读完成。
背景痛点
刚开始玩 51 单片机做交互项目时,踩过不少坑。最头疼的就是按键——明明只按了一下,单片机却识别成多次触发。后来才知道这是机械按键的 触点抖动 问题,通常持续 5 -10ms。用示波器看波形时,会发现按下瞬间信号线像抽风一样高频跳变。
另一个痛点是 LCD1602 显示。直接刷新整个屏幕会导致肉眼可见的 字符残影,尤其在切换菜单时,上一屏的内容像幽灵一样短暂残留。后来发现这是由更新速度不匹配造成的。
当项目需要 多级菜单 时,新手常会用一堆 if-else 硬编码。我就干过这种事:第二周再读代码时,自己都理不清逻辑了。更糟的是,添加新功能时牵一发而动全身。
硬件设计
按键消抖电路

- RC 参数计算:要过滤 10ms 的抖动,时间常数 τ =RC 应大于抖动时长。取典型值 R =10kΩ,C=1μF,实际延迟约 10ms(τ=10×10³×1×10⁻⁶=10ms)
- 注意事项:
- 电容建议选用陶瓷电容(如 104)
- 上拉电阻值需与 GPIO 输入阻抗匹配
LCD 驱动扩展
使用 74HC595 芯片扩展 IO,仅用 3 个引脚就能驱动 LCD:
- 595 的 SER 接 P3.4(数据线)
- SRCLK 接 P3.5(时钟线)
- RCLK 接 P3.6(锁存线)
核心代码实现
状态机按键扫描
// 状态枚举(MISRA- C 兼容写法)typedef enum {
KEY_IDLE,
KEY_DEBOUNCE,
KEY_CONFIRMED
} KeyState;
KeyState keyScan(uint8_t pin) {
static KeyState state = KEY_IDLE;
static uint16_t timer = 0;
switch(state) {
case KEY_IDLE:
if(!PIN_READ(pin)) { // 检测到按下
state = KEY_DEBOUNCE;
timer = 10; // 10ms 计时
}
break;
case KEY_DEBOUNCE:
if(--timer == 0) {state = PIN_READ(pin) ? KEY_IDLE : KEY_CONFIRMED;
}
break;
case KEY_CONFIRMED:
if(PIN_READ(pin)) { // 检测释放
state = KEY_IDLE;
return KEY_PRESSED; // 返回有效按键事件
}
break;
}
return KEY_NONE;
}
菜单系统架构
typedef struct {
const __code char *title; // 菜单标题存 ROM
void (*action)(void); // 回调函数指针
struct MenuItem *parent; // 父级菜单
struct MenuItem *children[5]; // 子菜单数组
} MenuItem;
// 示例菜单定义
const __code MenuItem mainMenu = {"主菜单", NULL, NULL, {&subMenu1, &subMenu2}
};
性能优化
定时器中断方案
替换常见的 delay()函数:
- 配置定时器 0 为 1ms 中断
- 在中断服务程序 (ISR) 中维护全局计时变量
- 主循环通过检查计时变量实现非阻塞延时
volatile uint32_t systemTicks = 0;
void Timer0_ISR() interrupt 1 {
TH0 = 0xFC; // 重装初值(1ms@11.0592MHz)systemTicks++;
}
void delayNonBlock(uint16_t ms) {
uint32_t start = systemTicks;
while((systemTicks - start) < ms);
}
避坑指南
长按处理技巧
- 设置 两级阈值:
- 按下持续 500ms 触发长按事件
- 之后每 200ms 触发一次重复事件
- 在状态机中添加
KEY_REPEAT状态
显示残影消除
- 先清除旧内容:
LCD_Clear() - 短暂延时 2ms
- 再写入新内容
- 使用局部刷新(只更新变化的部分)
中断共享变量
- 对跨中断 / 主程序共享的变量:
- 添加
volatile关键字 - 操作时临时关闭中断:
EA = 0; criticalVar++; EA = 1;
思考题
如何用 PWM 驱动蜂鸣器实现以下交互反馈?
1. 短按发出 ” 滴 ” 声(100ms 4kHz)
2. 长按发出 ” 滴滴 ” 声(双脉冲)
3. 菜单切换时播放上行 / 下行音调
仿真工程下载:Proteus 项目包
正文完
