
注:本系列为个人学习 ESP32 的记录,主要通过再记录的方式学习,参考和搬运转载极客侠 GeeksMan 的文章。
著作权归极客侠 GeeksMan 所有,基于 GPL 3.0 协议。原文链接:https://docs.geeksman.com/esp32/Arduino/01.esp32-arduino-intro.html
数码管显示
按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管:
共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极 COM 接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极 COM 接到地线 GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮。
原理图如下:

引脚图中间的两个 COM,是公共端,共阴数码管要将其接地,共阳数码管将其接电源。a,b,c,d,e,f,g,dp 被称为段选线。
如何判断是共阴还是共阳?
如果你不清楚你的数码管到底是共阴还是共阳,可以使用下面三种方法测试。
- 一般在数码管的侧面会标注该数码管的型号,可以在浏览器中搜索该型号,获取对应的原理图

2.用 ESP32 单片机给面包板通电(3.3V 引脚),公共端通过一个限流电阻接电源, 用跳线连通电源和数码管的 LED 引脚,如果亮了说明是共阳型数码管;反之,说明是共阳型数码管。

3.使用万用表的二极管档,红表笔接公共端,黑表笔接任一引脚,亮了说明是共阳型数码管,反之,则说明是共阴型数码管。

提示
建议把所有引脚测试一遍,也可以检查出是否有坏了的 LED。
硬件电路设计
将材料按照下图相连:

软件程序设计
计这个程序时,我们需要使用二维数组。
如果我们想让这个数码管某一引脚亮起来,那么我们需要给对应的引脚设置一个低电平。如果我们想要显示一个数字时,就需要让多个 LED 同时亮,比如数字 1 需要 b、c 引脚给低电平,其余引脚给高电平。程序可以这样写:
// 定义输出引脚并把所有引脚存到数组中
int pin_a = 4;
int pin_b = 5;
int pin_c = 19;
int pin_d = 21;
int pin_e = 22;
int pin_f = 2;
int pin_g = 15;
int pin_dp = 18;
int pin_array[8] = {pin_a, pin_b, pin_c, pin_d, pin_e, pin_f, pin_g, pin_dp};
// 定义数字显示逻辑的二维数组
int number_array[][8] = {
{0, 0, 0, 0, 0, 0, 1, 1}, // 0
{1, 0, 0, 1, 1, 1, 1, 1}, // 1
{0, 0, 1, 0, 0, 1, 0, 1}, // 2
{0, 0, 0, 0, 1, 1, 0, 1}, // 3
{1, 0, 0, 1, 1, 0, 0, 1}, // 4
{0, 1, 0, 0, 1, 0, 0, 1}, // 5
{0, 1, 0, 0, 0, 0, 0, 1}, // 6
{0, 0, 0, 1, 1, 1, 1, 1}, // 7
{0, 0, 0, 0, 0, 0, 0, 1}, // 8
{0, 0, 0, 0, 1, 0, 0, 1}, // 9
};
void setup() {
// 设置所有引脚为输出模式,初始化所有引脚为高电平
for (int i=0;i<8;i++){
pinMode(pin_array[i], OUTPUT);
digitalWrite(pin_array[i], HIGH);
}
}
void loop() {
// 显示数字
int num = 8;
for (int i=0;i<8;i++){
digitalWrite(pin_array[i], number_array[num][i]);
}
}
我们也可以使用函数来把显示数字的逻辑代码封装起来,方便我们在其他地方使用:
// 定义输出引脚并把所有引脚存到数组中
int pin_a = 4;
int pin_b = 5;
int pin_c = 19;
int pin_d = 21;
int pin_e = 22;
int pin_f = 2;
int pin_g = 15;
int pin_dp = 18;
int pin_array[8] = {pin_a, pin_b, pin_c, pin_d, pin_e, pin_f, pin_g, pin_dp};
// 定义数字显示逻辑的二维数组
int number_array[][8] = {
{0, 0, 0, 0, 0, 0, 1, 1}, // 0
{1, 0, 0, 1, 1, 1, 1, 1}, // 1
{0, 0, 1, 0, 0, 1, 0, 1}, // 2
{0, 0, 0, 0, 1, 1, 0, 1}, // 3
{1, 0, 0, 1, 1, 0, 0, 1}, // 4
{0, 1, 0, 0, 1, 0, 0, 1}, // 5
{0, 1, 0, 0, 0, 0, 0, 1}, // 6
{0, 0, 0, 1, 1, 1, 1, 1}, // 7
{0, 0, 0, 0, 0, 0, 0, 1}, // 8
{0, 0, 0, 0, 1, 0, 0, 1}, // 9
};
void display_number(int num){
// 清屏
for (int i=0;i<8;i++){
digitalWrite(pin_array[i], HIGH);
}
// 改变对应引脚的电平;
for (int i=0;i<8;i++){
digitalWrite(pin_array[i], number_array[num][i]);
}
}
void setup() {
// 设置所有引脚为输出模式,初始化所有引脚为高电平
for (int i=0;i<8;i++){
pinMode(pin_array[i], OUTPUT);
digitalWrite(pin_array[i], HIGH);
}
}
void loop() {
// 调用显示数字函数显示数字并延时500毫秒
for (int i=0;i<10;i++){
display_number(i);
delay(500);
}
}
4 位数码管显示
实验原理
4 位数码管,即 4 个 1 位数码管并列集中在一起形成一体的数码管。

当多位数码管一体时,它们内部的公共端是独立的,而负责显示什么数字的段线(a-dp)全部是连接在一起的,独立的公共端可以控制多位一体中的哪一位数码管点亮,而连接在一起的段线可以控制这个能点亮数码管亮什么数字,通常我们把公共端叫做 位选线 ,连接在一起的段线叫做 段选线,有了这两个线后,通过单片机及外部驱动电路就可以控制任意的数码管显示任意的数字了。
4 位数码管与 1 位数码管的原理基本一致,除了引脚不同。

相同的地方是,数码管中的 LED 的分段映射相同,如下图:

有区别的地方首先是 1 位数码管有两个相同的公共(COM)端接地或者接电源,而 4 位数码管没有公共端,有四个控制不同位置显示的选通端。

一般一位数码管有 10 个引脚,四位数码管是12 个引脚,关于具体的引脚及段、位标号大家可以查询相关资料,最简单的办法就是用数字万用表测量,若没有数字万用表也可用 5V 直流电源串接1k 电阻后测量,将测量结果记录,通过统计便可绘制出引脚标号。多位数码管有许多是按一定要求设计的,引脚不完全按照一般规则设定,所以需要在使用时查找手册,最直接的办法就是按照数码管上的标示向生产商要
硬件电路设计
将材料按照下图相连:


软件程序设计
我们一步一步的来,先写一个最简单的程序,让任意一位数码管显示任意数字,代码可以这么写:
#include <Arduino.h>
// 定义位选线引脚
int seg_1 = 5;
int seg_2 = 18;
int seg_3 = 19;
int seg_4 = 21;
// 定义位选线数组
int seg_array[4] = {seg_1, seg_2, seg_3, seg_4};
// 定义段选线引脚;
int a = 32;
int b = 25;
int c = 27;
int d = 12;
int e = 13;
int f = 33;
int g = 26;
int dp = 14;
// 定义位选线引脚
int led_array[8] = {a, b, c, d, e, f, g, dp};
// 定义共阴极数码管不同数字对应的逻辑电平的二维数组
int logic_array[10][8] = {
//a, b, c, d, e, f, g, dp
{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
};
// 清屏函数
void clear() {
//将段选线和位选线初始化
for (int i=0;i<4;i++) {
digitalWrite(seg_array[i], HIGH);
}
for (int i=0;i<8;i++) {
digitalWrite(led_array[i], LOW);
}
}
// 显示数字的函数
void display_number(int order, int number) {
// 清屏
clear();
// 把对应位选线的电平拉低
digitalWrite(seg_array[order], LOW);
// 显示数字
for (int i=0;i<8;i++) {
digitalWrite(led_array[i], logic_array[number][i]);
}
}
void setup() {
//因为4为数码管上面是段选线下面是位选线,需要导通得段选线为高电平、位选线为低电平,才会有电势差才能导通,在初始化时不能导通,所以要设置段选线为低电平、位选线为高电平
// 设置所有位选线引脚为输出模式,初始化所有位选线引脚为高电平
for (int i=0;i<4;i++) {
pinMode(seg_array[i], OUTPUT);
digitalWrite(seg_array[i], HIGH);
}
// 设置所有段选线引脚为输出模式,初始化所有段选线引脚为低电平
for (int i=0;i<8;i++) {
pinMode(led_array[i], OUTPUT);
digitalWrite(led_array[i], LOW);
}
}
void loop() {
// 第显示数字1818
// display_number(0, 1);
// display_number(1, 8);
// display_number(2, 1);
// display_number(3, 8);
//按顺序让所有位置显示 0~9
for (int i=0;i<4;i++) {
for (int j=0;j<10;j++) {
display_number(i, j);
delay(200);
}
}
}
我们选择多位数码管,肯定是要在不同位置显示不同数字的,这时候,我们需要用到 动态扫描。
什么是动态扫描?
动态扫描是对位选端扫描,8 个引脚控制每个数码管的段选线,通过刷新位选端和 8 个引脚的状态,来实现显示不同的数字。
我们可以通过运行下面这段代码,更生动形象地理解 动态扫描 的原理:
/*
该程序的作用是演示动态扫描原理。
在线文档:https://docs.geeksman.com/esp32/Arduino/08.esp32-arduino-4-digits-7segment.html
*/
// 定义位选线引脚
int seg_1 = 5;
int seg_2 = 18;
int seg_3 = 19;
int seg_4 = 21;
// 定义位选线数组
int seg_array[4] = {seg_1, seg_2, seg_3, seg_4};
// 定义段选线引脚;
int a = 32;
int b = 25;
int c = 27;
int d = 12;
int e = 13;
int f = 33;
int g = 26;
int dp = 14;
// 定义位选线引脚
int led_array[8] = {a, b, c, d, e, f, g, dp};
// 定义共阴极数码管不同数字对应的逻辑电平的二维数组
int logic_array[10][8] = {
//a, b, c, d, e, f, g, dp
{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
};
// 延时时间
int count = 355;
// 清屏函数
void clear() {
for (int i=0;i<4;i++) {
digitalWrite(seg_array[i], HIGH);
}
for (int i=0;i<8;i++) {
digitalWrite(led_array[i], LOW);
}
}
// 显示数字的函数
void display_number(int order, int number) {
// 清屏
clear();
// 把对应位选线的电平拉低
digitalWrite(seg_array[order], LOW);
// 显示数字
for (int i=0;i<8;i++) {
digitalWrite(led_array[i], logic_array[number][i]);
}
}
void setup() {
// 设置所有位选线引脚为输出模式,初始化所有位选线引脚为高电平
for (int i=0;i<4;i++) {
pinMode(seg_array[i], OUTPUT);
digitalWrite(seg_array[i], HIGH);
}
// 设置所有段选线引脚为输出模式,初始化所有段选线引脚为低电平
for (int i=0;i<8;i++) {
pinMode(led_array[i], OUTPUT);
digitalWrite(led_array[i], LOW);
}
}
void loop() {
display_number(0, 1);
delay(count);
display_number(1, 2);
delay(count);
display_number(2, 3);
delay(count);
display_number(3, 4);
delay(count);
if (count > 10) {
if (count > 110) {
count -= 50;
}else {
count -= 10;
}
}
}
理解了 动态扫描 的原理之后,我们就可以写代码了,先把之前写的这些代码复制过来,然后我们还需要实现通过动态扫描的方法实现 4 位数字显示的功能:
// 4 位数码管显示函数
void display_4_number(int number) {
// 把输入的数字格式化为 4 位数的数组
if (number < 10000) {
// 获取每一位对应的数字
// // 获取个位
// int seg_4_number = number % 10;
// number /= 10;
//
// // 获取十位
// int seg_3_number = number % 10;
// number /= 10;
//
// // 获取百位
// int seg_2_number = number % 10;
// number /= 10;
//
// // 获取千位
// int seg_1_number = number % 10;
// 定义格式化数组
int number_array[4];
// 使用循环获取格式化数组
for (int i=3;i>=0;i--) {
number_array[i] = number % 10;
number /= 10;
}
// 显示数字
for (int i=0;i<4;i++) {
display_number(i, number_array[i]);
delay(5);
}
}
}
系列导航:返回目录索引 | 上一篇:02. GPIO 输出:点亮 LED 与流水灯 | 下一篇:04. GPIO 输入:按键实验

