- 机器人等级考试
ESP32 for Arduino 课程学习笔记:按键消抖与四位数码管控制
- 2025-7-21 21:33:49 @
ESP32 for Arduino 课程学习笔记:按键消抖与数码管控制
一、按键抖动原理及消抖方法
1.1 按键抖动的产生原因
按键内部由金属簧片构成,当按下或松开按键时,金属簧片会产生物理振动,导致电路在10-20ms内出现多次通断现象,这种现象称为"按键抖动"。
- 理想状态:按键按下时信号瞬间从HIGH(1)变为LOW(0),松开时瞬间从LOW(0)变为HIGH(1)
- 现实状态:按下和松开时都会产生10-20ms的抖动,之后才进入稳定状态
1.2 按键抖动的波形对比
状态 | 特征 |
---|---|
理想波形 | 信号突变,无中间状态 |
现实波形 | 包含前沿抖动(10-20ms)、稳定状态、后沿抖动(10-20ms) |
1.3 按键消抖的必要性
如果不进行消抖处理,控制器会误判按键被多次按下,导致程序执行错误(如LED频繁闪烁、计数错误等)。
二、按键消抖实现方法
2.1 基础检测代码(无消抖)
void setup() {
pinMode(3, INPUT); // 按键连接引脚3
pinMode(4, OUTPUT); // LED连接引脚4
digitalWrite(4, 0); // 初始关闭LED
Serial.begin(115200); // 初始化串口通信
}
boolean buttonState = false;
void loop() {
// 直接读取按键状态,无消抖处理
Serial.println(digitalRead(3)); // 串口输出按键状态
delay(20); // 短暂延时
}
2.2 简单消抖代码(延时法)
void setup() {
pinMode(3, INPUT); // 按键连接引脚3
pinMode(4, OUTPUT); // LED连接引脚4
digitalWrite(4, 0); // 初始关闭LED
Serial.begin(115200); // 初始化串口通信
}
boolean buttonState = false;
void loop() {
// 当按键被按下(读取到LOW)
if(digitalRead(3) == LOW) {
buttonState = !buttonState; // 切换状态(取反)
Serial.println(buttonState); // 输出当前状态
digitalWrite(4, buttonState); // 控制LED
delay(1000); // 延时跳过抖动区域(关键消抖步骤)
}
delay(20);
}
2.3 消抖原理说明
延时法消抖的核心思想是:当检测到按键状态变化后,延时一段时间(通常大于20ms),跳过抖动阶段,确保读取的是稳定后的状态。
// 关键消抖代码解析
if(digitalRead(3) == LOW) { // 检测到按键按下
buttonState = !buttonState; // 切换状态
delay(1000); // 延时1秒,覆盖抖动时间(实际应用中20-50ms即可)
}
三、四位数码管控制
3.1 数码管控制原理
四位数码管通过"位选"和"段选"信号控制显示:
- 位选:选择哪一位数码管点亮
- 段选:控制数码管显示的数字
通常使用74HC595移位寄存器扩展IO口,通过shiftOut()
函数输出控制信号。
3.2 shiftOut()函数详解
// 函数原型
shiftOut(dataPin, clockPin, bitOrder, value);
参数说明:
dataPin
:数据输出引脚(连接74HC595的DS引脚)clockPin
:时钟引脚(连接74HC595的SH_CP引脚)bitOrder
:位输出顺序(MSBFIRST高位优先,LSBFIRST低位优先)value
:要输出的字节数据
3.3 数码管显示单个数字
// 定义引脚
#define dataPin 2 // 数据引脚
#define clockPin 3 // 时钟引脚
#define latchPin 4 // 锁存引脚
// 数字段码表(共阴极为例)
byte disNum[10] = {
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111 // 9
};
void setup() {
// 初始化引脚
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
}
// 显示单个数字到指定位置
void disPlayNumber(int digit, int number) {
byte v = 0b0;
// 位选信号(选择第digit位)
shiftOut(dataPin, clockPin, MSBFIRST, bitSet(v, digit-1));
// 段选信号(显示number数字)
shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]);
digitalWrite(latchPin, LOW); // 开始传输
digitalWrite(latchPin, HIGH); // 锁存数据(关键步骤)
}
void loop() {
disPlayNumber(1, 8); // 在第1位显示8
delay(5);
}
3.4 多位数字显示
通过循环快速切换不同位的显示,利用人眼视觉暂留效果,实现多位同时显示的视觉效果:
// 显示多位数(如1234)
void disPlayNumbers(int n) {
int arr[5] = {};
// 拆分数字到数组
arr[1] = n % 10; // 个位
arr[2] = n / 10 % 10; // 十位
arr[3] = n / 100 % 10; // 百位
arr[4] = n / 1000 % 10; // 千位
// 循环显示每一位
for(int i = 1; i <= 4; i++) {
disPlayNumber(4 - i + 1, arr[i]);
delay(1); // 短暂延时,保证显示稳定
}
}
void loop() {
disPlayNumbers(721); // 显示721
}
四、位操作函数详解
4.1 bitSet() 函数
设置变量的某一位为1:
// 语法:bitSet(x, n)
// x: 操作的变量
// n: 位位置(从0开始计数)
byte v = 0b0;
bitSet(v, 0); // 设置第0位为1,v变为0b00000001
bitSet(v, 2); // 设置第2位为1,v变为0b00000101
4.2 bitClear() 函数
清除变量的某一位为0:
// 语法:bitClear(x, n)
byte v = 0b11111111;
bitClear(v, 1); // 清除第1位,v变为0b11111101
4.3 bitWrite() 函数
向变量的某一位写入指定值(0或1):
// 语法:bitWrite(x, n, b)
byte v = 0b0;
bitWrite(v, 3, 1); // 向第3位写入1,v变为0b00001000
bitWrite(v, 3, 0); // 向第3位写入0,v变为0b00000000
4.4 位运算符
运算符 | 含义 | 示例 |
---|---|---|
& | 按位与 | 0b1010 & 0b1100 = 0b1000 |
| | 按位或 | 0b1010 | 0b1100 = 0b1110 |
^ | 按位异或 | 0b1010 ^ 0b1100 = 0b0110 |
~ | 按位非 | ~0b1010 = 0b0101 |
<< | 左移 | 0b0001 << 2 = 0b0100 |
>> | 右移 | 0b1000 >> 2 = 0b0010 |
五、综合应用示例
5.1 按键控制数码管计数
#define dataPin 2
#define clockPin 3
#define latchPin 4
#define buttonPin 5
byte disNum[10] = {0b00111111,0b00000110,0b01011011,0b01001111,0b01100110,
0b01101101,0b01111101,0b00000111,0b01111111,0b01101111};
int count = 0;
boolean buttonState = false;
void setup() {
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(buttonPin, INPUT);
Serial.begin(115200);
}
void disPlayNumber(int digit, int number) {
byte v = 0b0;
shiftOut(dataPin, clockPin, MSBFIRST, bitSet(v, digit-1));
shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]);
digitalWrite(latchPin, LOW);
digitalWrite(latchPin, HIGH);
}
void disPlayNumbers(int n) {
int arr[5] = {};
arr[1] = n % 10;
arr[2] = n / 10 % 10;
arr[3] = n / 100 % 10;
arr[4] = n / 1000 % 10;
for(int i = 1; i <= 4; i++) {
disPlayNumber(4 - i + 1, arr[i]);
delay(1);
}
}
void loop() {
// 按键消抖处理
if(digitalRead(buttonPin) == LOW) {
count++; // 计数加1
if(count > 9999) count = 0; // 溢出归零
Serial.println(count);
delay(200); // 消抖延时
}
disPlayNumbers(count); // 显示计数
}
5.2 代码说明
- 初始化引脚和段码表
disPlayNumber()
函数控制单个数码管显示disPlayNumbers()
函数处理多位数显示- 按键检测部分加入消抖延时
- 主循环中不断刷新显示并检测按键状态
通过本教程,你应该掌握了按键消抖的原理与实现方法,以及四位数码管的控制方式。这些知识是ESP32开发中非常基础且重要的内容,可应用于各种交互式电子项目中。
0 条评论
目前还没有评论...