- 机器人等级考试
ESP32 for Arduino 共阳数码管学习笔记
- 2025-7-16 19:03:36 @
ESP32 for Arduino 共阳数码管学习笔记
一、基础知识
1. 共阳数码管原理
共阳数码管是一种常见的显示器件,其内部所有LED的阳极(正极)连接在一起。当需要某一段LED点亮时,需要给该段的阴极(负极)施加低电平;当需要某一段LED熄灭时,需要给该段的阴极施加高电平。
数码管通常有8个段,分别是a、b、c、d、e、f、g(7个段用于显示数字)和dp(小数点)。
2. 74HC595移位寄存器
74HC595是一种常用的移位寄存器芯片,它可以将串行输入的数据转换为并行输出,从而减少微控制器的引脚占用。在数码管控制中,我们通常使用它来控制数码管的各个段。
74HC595主要引脚功能:
- DS(Data Serial):串行数据输入
- SH_CP(Shift Clock):移位时钟,上升沿时数据移位
- ST_CP(Storage Clock):存储时钟,上升沿时数据从移位寄存器传输到存储寄存器
- Q0-Q7:并行数据输出
二、一维数组控制数码管
1. 电路连接
将74HC595的输出端与数码管的对应段连接,具体连接如下:
- 74HC595 Q0 -> 数码管 a
- 74HC595 Q1 -> 数码管 b
- 74HC595 Q2 -> 数码管 c
- 74HC595 Q3 -> 数码管 d
- 74HC595 Q4 -> 数码管 e
- 74HC595 Q5 -> 数码管 f
- 74HC595 Q6 -> 数码管 g
- 74HC595 Q7 -> 数码管 dp
74HC595的控制引脚与ESP32连接:
- DS -> DPin(9)
- SH_CP -> CPin(10)
- ST_CP -> LPin(11)
2. 代码实现(含bitRead函数)
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 LPin = 11, CPin = 10, DPin = 9; // 数码管引脚定义,LPin对应ST_CP,CPin对应SH_CP
void setup() { // 初始化函数
pinMode(LPin, OUTPUT);
pinMode(CPin, OUTPUT);
pinMode(DPin, OUTPUT);
digitalWrite(LPin, LOW);
digitalWrite(CPin, LOW);
}
void loop() {
for (int i = 0; i <= 10; i++) {
disPlayNumber(i); // 显示数字
delay(300);
}
}
void disPlayNumber(int number) { // 数码管显示数字函数
for (int i = 0; i <= 7; i++) { // 遍历每一位,共8位对应数码管的8个段
// bitRead(disNum[number], i)读取disNum[number]的第i位
// 由于是共阳数码管,需要取反,所以用!bitRead(...)
digitalWrite(DPin, !bitRead(disNum[number], i)); // 设置DPin引脚状态(DS引脚)
digitalWrite(CPin, LOW); // 拉低移位时钟
digitalWrite(CPin, HIGH); // 拉高移位时钟,产生上升沿,数据移位
}
digitalWrite(LPin, LOW); // 拉低存储时钟
digitalWrite(LPin, HIGH); // 拉高存储时钟,产生上升沿,数据从移位寄存器传输到存储寄存器,数码管显示
}
3. 代码解析
disNum
数组:一维数组,每个元素是一个字节(8位),分别对应数码管的a、b、c、d、e、f、g、dp段的状态。这里定义的是共阴极的状态,所以在共阳数码管上使用时需要取反。bitRead
函数:Arduino内置函数,用于读取一个字节中指定位置的位值。例如bitRead(disNum[0], 0)
读取数字0对应的字节的第0位。disPlayNumber
函数:将数字对应的段码通过74HC595移位寄存器发送到数码管。通过循环依次发送8位数据,每发送一位就产生一个移位时钟上升沿,最后产生一个存储时钟上升沿,让数码管显示数据。
三、二维数组控制数码管
1. 电路连接
与一维数组控制时的电路连接相同。
2. 代码实现
// 定义二维数组:存储10个数字+1个小数点的各段状态(共阴极:1=亮,0=灭)
const byte disNum[11][8] = {
{1,1,1,1,1,1,0,0}, //0: a,b,c,d,e,f亮;g,dp灭
{0,1,1,0,0,0,0,0}, //1: b,c亮;其他灭
{1,1,0,1,1,0,1,0}, //2: a,b,g,e,d亮
{1,1,1,1,0,0,1,0}, //3: a,b,g,c,d亮
{0,1,1,0,0,1,1,0}, //4: f,g,b,c亮
{1,0,1,1,0,1,1,0}, //5: a,f,g,c,d亮
{1,0,1,1,1,1,1,0}, //6: a,f,g,c,d,e亮
{1,1,1,0,0,0,0,0}, //7: a,b,c亮
{1,1,1,1,1,1,1,0}, //8: 全亮,dp灭
{1,1,1,1,0,1,1,0}, //9: a,b,c,d,f,g亮
{0,0,0,0,0,0,0,1} //小数点:仅dp亮
};
const int DS = 9; //数码管DS引脚
const int SH_CP = 10; //数码管SH_CP引脚(移位时钟)
const int ST_CP = 11; //数码管ST_CP引脚(存储时钟)
void setup() {
Serial.begin(9600); //初始化串口通信,波特率为9600
pinMode(DS, OUTPUT); //数码管的DS引脚
pinMode(SH_CP, OUTPUT); //数码管的SH_CP引脚
pinMode(ST_CP, OUTPUT); //数码管的ST_CP引脚
//为了避免上电干扰
digitalWrite(SH_CP, LOW); //数码管的SH_CP引脚输出低电平
digitalWrite(ST_CP, LOW); //数码管的ST_CP引脚输出低电平
}
void displayNumber(int num) { //数码管显示数字函数
for (int i = 7; i >= 0; i--) { // 从7开始是因为移位寄存器先接收二维数组最右边的数据,才逻辑正常
digitalWrite(DS, !disNum[num][i]); // 共阳数码管需要取反
digitalWrite(SH_CP, LOW); // 拉低移位时钟
digitalWrite(SH_CP, HIGH); // 拉高移位时钟,产生上升沿,数据移位
}
digitalWrite(ST_CP, LOW); // 拉低存储时钟
digitalWrite(ST_CP, HIGH); // 拉高存储时钟,产生上升沿,数据从移位寄存器传输到存储寄存器,数码管显示
}
void loop() {
for (int i = 0; i <= 10; i++) {
displayNumber(i);
delay(500);
}
}
3. 代码解析
disNum
二维数组:第一维表示数字(0-9和小数点),第二维表示数码管的8个段(a、b、c、d、e、f、g、dp)的状态。这里定义的是共阴极的状态,所以在共阳数码管上使用时需要取反。displayNumber
函数:与一维数组控制时的函数类似,不同之处在于这里通过二维数组获取段码。循环变量i
从7开始是因为74HC595移位寄存器是从高位到低位接收数据的,而数组中第一个元素对应a段(高位),最后一个元素对应dp段(低位)。- 为什么变量i要从7开始?因为74HC595移位寄存器的移位顺序是先接收的位会被移到高位,而后接收的位会被移到低位。在这个二维数组中,
disNum[num][0]
对应a段(高位),disNum[num][7]
对应dp段(低位)。所以从7开始循环,先发送dp段(低位),最后发送a段(高位),这样才能保证数码管的各段显示正确。
四、按键控制数码管
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
函数显示新的数字。 - 这种按键控制方式可以实现按一次按键切换一个数字的功能,你也可以根据需要修改代码,实现其他功能,比如长按切换模式等。
五、总结
- 共阳数码管与共阴数码管的区别在于LED的连接方式,共阳数码管需要低电平点亮,共阴数码管需要高电平点亮。
- 可以使用一维数组或二维数组来存储数码管的段码,一维数组更节省内存,二维数组更直观。
bitRead
函数可以方便地读取一个字节中指定位置的位值,在使用一维数组存储段码时非常有用。- 74HC595移位寄存器可以减少微控制器的引脚占用,通过串行通信控制并行输出。
- 按键控制时需要注意消除抖动,以保证按键状态的准确读取。
通过本教程的学习,你应该能够掌握使用ESP32控制共阳数码管的基本方法,并能够根据需要扩展功能,比如控制多位数码管、实现更复杂的显示效果等。
0 条评论
目前还没有评论...