- 机器人等级考试
自定义`shiftOut`函数
- 2025-7-17 15:49:20 @
下面是使用自定义shiftOut
函数实现数码管数字显示和按键控制的Arduino代码。这个实现替代了标准库的shiftOut
,并通过位操作直接控制移位寄存器。
/*
按键处理:使用digitalRead函数读取按键状态,按键未按下时为高电平,按下时为低电平。
通过延时消除按键抖动,每次按键松开后将显示数字加1,超过10时回到0。
*/
const byte disNum[11] = {
// 数码管各段状态定义(共阴极:1=亮,0=灭)
// abcdefgdp
0b11111100, //0
0b01100000, //1
0b11011010, //2
0b11110010, //3
0b01100110, //4
0b10110110, //5
0b10111110, //6
0b11100000, //7
0b11111110, //8
0b11110110, //9
0b00000001 //.
};
const int keyPin = 3; // 按键引脚
const int dataPin = 11; // 数据引脚 (DS)
const int clockPin = 10; // 时钟引脚 (SH_CP)
const int latchPin = 9; // 锁存引脚 (ST_CP)
void setup() {
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(keyPin, INPUT);
digitalWrite(clockPin, LOW);
digitalWrite(latchPin, LOW);
displayNumber(0); // 初始显示0
}
int currentNumber = 0;
void loop() {
if (digitalRead(keyPin) == LOW) { // 检测按键按下
delay(20); // 消抖
while (digitalRead(keyPin) == LOW); // 等待按键释放
delay(20); // 消抖
currentNumber = (currentNumber + 1) % 11; // 循环0-10
displayNumber(currentNumber);
}
}
// 自定义shiftOut函数 - 替代标准库函数
void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {
for (int i = 0; i < 8; i++) {
if (bitOrder == LSBFIRST) {
digitalWrite(dataPin, !bitRead(val, i)); // 取反,适配数码管定义
} else {
digitalWrite(dataPin, !bitRead(val, 7 - i)); // 取反,适配数码管定义
}
digitalWrite(clockPin, LOW);
digitalWrite(clockPin, HIGH);
}
}
// 显示数字函数
void displayNumber(int number) {
myShiftOut(dataPin, clockPin, LSBFIRST, disNum[number]); // 发送数据
digitalWrite(latchPin, LOW); // 开始传输
digitalWrite(latchPin, HIGH); // 锁存数据
}
代码说明:
-
自定义shiftOut函数:
myShiftOut
函数模拟了标准库的shiftOut
功能- 通过位操作逐位发送数据到移位寄存器
- 支持低位在前(LSBFIRST)的数据传输方式
- 数据位取反以适配数码管段定义(0=亮,1=灭)
-
数码管控制:
- 使用74HC595移位寄存器控制数码管
- 通过
displayNumber
函数将数字转换为段码并显示 - 段码定义包含0-9和小数点的显示模式
-
按键处理:
- 按键连接到引脚3,使用内部上拉电阻
- 检测到按键按下后进行消抖处理
- 按键释放后更新显示数字
这个实现完全使用自定义的移位输出函数,通过位操作直接控制数码管显示,相比标准库函数提供了更灵活的控制方式。
3 条评论
-
admin SU @ 2025-7-17 16:29:42
-
2025-7-17 16:11:21@
/* 按键处理:使用digitalRead函数读取按键状态,按键未按下时为高电平,按下时为低电平。 通过延时消除按键抖动,每次按键松开后将显示数字加1,超过10时回到0。 */ const byte disNum[11] = { // 数码管各段状态定义(共阴极:1=亮,0=灭) // abcdefgdp 0b11111100, //0 0b01100000, //1 0b11011010, //2 0b11110010, //3 0b01100110, //4 0b10110110, //5 0b10111110, //6 0b11100000, //7 0b11111110, //8 0b11110110, //9 0b00000001 //. }; const int keyPin = 3; // 按键引脚 const int dataPin = 9; // 数据引脚 (DS) const int clockPin = 10; // 时钟引脚 (SH_CP) const int latchPin = 11; // 锁存引脚 (ST_CP) void setup() { pinMode(dataPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(latchPin, OUTPUT); pinMode(keyPin, INPUT); digitalWrite(clockPin, LOW); digitalWrite(latchPin, LOW); displayNumber(0); // 初始显示0 } int currentNumber = 0; void loop() { if (digitalRead(keyPin) == LOW) { // 检测按键按下 delay(20); // 消抖 while (digitalRead(keyPin) == LOW); // 等待按键释放 delay(20); // 消抖 currentNumber = (currentNumber + 1) % 11; // 循环0-10 displayNumber(currentNumber); } } // 自定义shiftOut函数 - 替代标准库函数 //myShiftOut每次发送8位数据 void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) {//byte类型就是无符号的8位整数 for (int i = 0; i < 8; i++) { if (bitOrder == LSBFIRST) {// 如果是低位优先 数据val的顺序和数码管的段的顺序一致 //LSBFIRST相当于0 digitalWrite(dataPin, !bitRead(val, i)); // 取反,适配数码管定义 } else {// 如果是高位优先 数据val的顺序和数码管的段的顺序相反 MSBFIRST相当于1 digitalWrite(dataPin, !bitRead(val, 7 - i)); // 取反,适配数码管定义 } digitalWrite(clockPin, LOW); digitalWrite(clockPin, HIGH);//发送上升沿脉冲信号,数据移位 } } // 显示数字函数 void displayNumber(int number) { //myShiftOut(dataPin, clockPin, LSBFIRST, disNum[number]); // 发送数据 shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]);// 发送数据,取反适配数码管定义 digitalWrite(latchPin, LOW); // 开始传输 digitalWrite(latchPin, HIGH); // 锁存数据 }
程序功能解释
这是一个基于Arduino的数码管显示控制系统,通过按键控制数字显示。主要功能包括:
- 实时检测按键状态,通过消抖处理确保信号稳定
- 每次按下按键后,数码管显示的数字会递增(0-10循环)
- 使用自定义的移位输出函数控制数码管显示
硬件连接说明
- 按键连接到引脚3,采用外部上拉(需外部上拉电阻)
- 数码管通过74HC595移位寄存器控制:
- 数据引脚(DS)连接到Arduino引脚9
- 时钟引脚(SH_CP)连接到Arduino引脚10
- 锁存引脚(ST_CP)连接到Arduino引脚11
关键代码解析
1. 数码管段码定义
const byte disNum[11] = { 0b11111100, //0 0b01100000, //1 ... 0b00000001 //. };
- 定义了0-9和小数点的段码(共阴极数码管)
- 每个字节的8位分别对应数码管的a-g和dp段(低位在前)
2. 自定义移位输出函数
void myShiftOut(byte dataPin, byte clockPin, byte bitOrder, byte val) { for (int i = 0; i < 8; i++) { if (bitOrder == LSBFIRST) { digitalWrite(dataPin, !bitRead(val, i)); } else { digitalWrite(dataPin, !bitRead(val, 7 - i)); } digitalWrite(clockPin, LOW); digitalWrite(clockPin, HIGH); } }
- 模拟标准库的
shiftOut
函数,将8位数据逐位移出 - 通过
bitRead
函数读取数据位,并取反后输出(适配数码管定义) - 时钟信号采用上升沿触发(LOW->HIGH)
3. 显示控制函数
void displayNumber(int number) { shiftOut(dataPin, clockPin, LSBFIRST, ~disNum[number]); digitalWrite(latchPin, LOW); digitalWrite(latchPin, HIGH); }
- 实际使用了标准库的
shiftOut
函数(与自定义函数功能类似) - 通过取反操作(
~disNum[number]
)适配数码管段码定义 - 锁存信号控制数据何时更新到输出端
4. 按键处理逻辑
if (digitalRead(keyPin) == LOW) { delay(20); // 消抖 while (digitalRead(keyPin) == LOW); // 等待释放 delay(20); // 消抖 currentNumber = (currentNumber + 1) % 11; displayNumber(currentNumber); }
- 检测到按键按下(低电平)后进行消抖处理
- 按键释放后更新显示数字(0-10循环)
代码中的注意事项
-
引脚配置与取反操作:
- 数码管段码定义中使用的是共阴极逻辑(1=亮,0=灭)
- 但在
displayNumber
函数中使用了~disNum[number]
取反操作 - 这意味着代码中可能存在逻辑冲突,需要根据实际硬件连接调整
-
自定义函数与标准函数混用:
- 代码中定义了
myShiftOut
函数,但在displayNumber
中使用了标准库的shiftOut
- 建议保持一致,避免混淆
- 代码中定义了
-
按键配置:
- 代码中使用
INPUT
模式但未启用内部上拉 - 需要确保外部有上拉电阻,否则可能导致按键检测不稳定
- 代码中使用
这个程序通过移位寄存器实现了数码管的控制,并通过按键实现了数字的循环切换,是一个基础的Arduino输入输出控制示例。
-
2025-7-17 15:50:30@
四、按键控制数码管
1. 电路连接
在之前的电路基础上,增加一个按键:
- 按键一端连接ESP32的3号引脚(keyPin)
- 按键另一端接地
- 可以在按键两端并联一个10kΩ的上拉电阻,或者使用ESP32内部的上拉电阻(在代码中设置)
2. 代码实现
const int keyPin = 3; // 按键引脚 const int DS = 9; // 数码管DS引脚 const int SH_CP = 10; // 数码管SH_CP引脚 const int ST_CP = 11; // 数码管ST_CP引脚 // 定义二维数组:存储10个数字+1个小数点的各段状态(共阴极:1=亮,0=灭) const byte disNum[11][8] = { {1,1,1,1,1,1,0,0}, //0 {0,1,1,0,0,0,0,0}, //1 {1,1,0,1,1,0,1,0}, //2 {1,1,1,1,0,0,1,0}, //3 {0,1,1,0,0,1,1,0}, //4 {1,0,1,1,0,1,1,0}, //5 {1,0,1,1,1,1,1,0}, //6 {1,1,1,0,0,0,0,0}, //7 {1,1,1,1,1,1,1,0}, //8 {1,1,1,1,0,1,1,0}, //9 {0,0,0,0,0,0,0,1} //. }; int currentNum = 0; // 当前显示的数字 void setup() { pinMode(keyPin, INPUT); // 设置按键引脚为输入模式,并启用上拉电阻 Serial.begin(9600); // 初始化串口通信,波特率为9600 pinMode(DS, OUTPUT); // 数码管的DS引脚 pinMode(SH_CP, OUTPUT); // 数码管的SH_CP引脚 pinMode(ST_CP, OUTPUT); // 数码管的ST_CP引脚 // 为了避免上电干扰 digitalWrite(SH_CP, LOW); digitalWrite(ST_CP, LOW); displayNumber(currentNum); // 初始显示0 } void displayNumber(int num) { // 数码管显示数字函数 for (int i = 7; i >= 0; i--) { digitalWrite(DS, !disNum[num][i]); // 共阳数码管需要取反 digitalWrite(SH_CP, LOW); // 拉低移位时钟 digitalWrite(SH_CP, HIGH); // 拉高移位时钟,产生上升沿,数据移位 } digitalWrite(ST_CP, LOW); // 拉低存储时钟 digitalWrite(ST_CP, HIGH); // 拉高存储时钟,产生上升沿,数据从移位寄存器传输到存储寄存器,数码管显示 } int buttonValue; void loop() { buttonValue = digitalRead(keyPin); if (buttonValue == 0) { // 按键按下(由于启用了上拉电阻,按下时为低电平) Serial.println("按键按下"); delay(20); // 跳过按键前沿抖动区域 // 按键一直按着就会在这里一直执行死循环,等待按键松开 while (digitalRead(keyPin) == 0); delay(20); // 跳过后沿抖动 Serial.println("按键松开"); // 每次按键后显示下一个数字,循环显示0-10 currentNum++; if (currentNum > 10) { currentNum = 0; } displayNumber(currentNum); // 显示当前数字 } delay(20); }
3. 代码解析
- 按键处理:使用
digitalRead
函数读取按键状态,由于启用了上拉电阻,按键未按下时为高电平,按下时为低电平。通过延时来消除按键的抖动。 - 数字切换:每次按键松开后,将当前显示的数字加1,当超过10时回到0,然后调用
displayNumber
函数显示新的数字。 - 这种按键控制方式可以实现按一次按键切换一个数字的功能,你也可以根据需要修改代码,实现其他功能,比如长按切换模式等。
- 1