一、 实验效果展示

数码管(7-Segment Display)是电子工程中最经典的显示元件之一。相比于LCD屏幕,它具有高亮度、低成本、控制逻辑底层的优势。

在本实验中,我们将点亮一个一位数码管,并让它循环显示数字 09。这不仅仅是一个显示实验,更是我们深入理解 GPIO控制数字逻辑电平 以及 代码封装技巧 的绝佳机会。

在这里插入图片描述


二、 硬件准备与接线

1. 实验材料

  • Arduino Uno 开发板 x 1
  • 1位 8段数码管 x 1 (共阳极)
  • 220Ω 限流电阻 x 1 (非常重要,防止烧毁数码管LED)
  • 面包板 & 杜邦线 若干

2. 数码管引脚定义解析

数码管本质上是由 8个 LED 组成的(7个笔画 + 1个小数点)。为了控制它们,我们需要知道每个引脚对应的“笔画”位置。

请看下图的引脚定义:

  • 笔画段:a, b, c, d, e, f, g
  • 小数点:dp (Decimal Point)
  • 公共端:COM (共阳极接5V,共阴极接GND)

在这里插入图片描述

(图注:标准的8段数码管引脚分布图)

3. 接线原理

根据实验代码中的定义,我们将引脚一一映射到 Arduino 的数字接口上。

重要接线表:

数码管引脚 Arduino引脚 备注
a D2 顶部横杠
b D3 右上竖杠
c D4 右下竖杠
d D5 底部横杠
e D6 左下竖杠
f D7 左上竖杠
g D8 中间横杠
dp D9 小数点
COM (共阳) 5V 必须要串联电阻!

4. 接线示意图

请严格按照下图连接,注意需要在公共端串联电阻。

在这里插入图片描述

(图注:Arduino与数码管的接线实物图)


三、 代码编写

// 重新定义数码管引脚(按要求映射到2~9)
int a = 2; 
int b = 3; 
int c = 4; 
int d = 5; 
int e = 6; 
int f = 7; 
int g = 8;
int dp = 9;

// 显示数字 0 
void digital_0(void) { 
  unsigned char j; 
  // 修正循环范围:对应a~d(2~5)、e(6)、f(7)、g(8)→ 2~8
  for(j=2;j<=8;j++) {
    digitalWrite(j, LOW); 
  }
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(g, HIGH);   // 熄灭g段(0的g段不亮)
}  

// 显示数字 1 
void digital_1(void) { 
  unsigned char j; 
  digitalWrite(c, LOW);    // 点亮c段(引脚4)
  digitalWrite(b, LOW);    // 点亮b段(引脚3)
  
  // 熄灭其余段:a(2)、d(5)、e(6)、f(7)、g(8)
  digitalWrite(a, HIGH);
  for(j=5;j<=8;j++) {
    digitalWrite(j, HIGH); 
  }
  digitalWrite(dp, HIGH);  // 熄灭小数点
} 

// 显示数字 2 
void digital_2(void) { 
  unsigned char j; 
  digitalWrite(b, LOW);    // 点亮b段
  digitalWrite(a, LOW);    // 点亮a段
  digitalWrite(d, LOW);    // 点亮d段
  digitalWrite(e, LOW);    // 点亮e段
  digitalWrite(g, LOW);    // 点亮g段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(c, HIGH);   // 熄灭c段
  digitalWrite(f, HIGH);   // 熄灭f段
} 

// 显示数字 3 
void digital_3(void) { 
  unsigned char j; 
  digitalWrite(g, LOW);    // 点亮g段
  digitalWrite(d, LOW);    // 点亮d段
  digitalWrite(a, LOW);    // 点亮a段
  digitalWrite(b, LOW);    // 点亮b段
  digitalWrite(c, LOW);    // 点亮c段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(f, HIGH);   // 熄灭f段
  digitalWrite(e, HIGH);   // 熄灭e段
} 

// 显示数字 4 
void digital_4(void) { 
  digitalWrite(c, LOW);    // 点亮c段
  digitalWrite(b, LOW);    // 点亮b段
  digitalWrite(f, LOW);    // 点亮f段
  digitalWrite(g, LOW);    // 点亮g段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(a, HIGH);   // 熄灭a段
  digitalWrite(e, HIGH);   // 熄灭e段
  digitalWrite(d, HIGH);   // 熄灭d段
} 

// 显示数字 5 
void digital_5(void) { 
  unsigned char j; 
  digitalWrite(a, LOW);    // 点亮a段
  digitalWrite(f, LOW);    // 点亮f段
  digitalWrite(g, LOW);    // 点亮g段
  digitalWrite(c, LOW);    // 点亮c段
  digitalWrite(d, LOW);    // 点亮d段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(b, HIGH);   // 熄灭b段
  digitalWrite(e, HIGH);   // 熄灭e段
} 

// 显示数字 6 
void digital_6(void) { 
  unsigned char j; 
  digitalWrite(a, LOW);    // 点亮a段
  digitalWrite(f, LOW);    // 点亮f段
  digitalWrite(g, LOW);    // 点亮g段
  digitalWrite(e, LOW);    // 点亮e段
  digitalWrite(d, LOW);    // 点亮d段
  digitalWrite(c, LOW);    // 点亮c段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(b, HIGH);   // 熄灭b段
} 

// 显示数字 7 
void digital_7(void) { 
  unsigned char j; 
  digitalWrite(a, LOW);    // 点亮a段
  digitalWrite(b, LOW);    // 点亮b段
  digitalWrite(c, LOW);    // 点亮c段
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  // 熄灭d(5)、e(6)、f(7)、g(8)
  for(j=5;j<=8;j++) {
    digitalWrite(j, HIGH); 
  }
} 

// 显示数字 8 
void digital_8(void) { 
  unsigned char j; 
  // 点亮所有段(a~g:2~8)
  for(j=2;j<=8;j++) {
    digitalWrite(j, LOW); 
  }
  digitalWrite(dp, HIGH);  // 熄灭小数点
} 

// 显示数字 9 
void digital_9(void) { 
  unsigned char j; 
  // 点亮a~d、f~g(熄灭e段)
  for(j=2;j<=5;j++) {
    digitalWrite(j, LOW); 
  }
  digitalWrite(f, LOW);
  digitalWrite(g, LOW);
  
  digitalWrite(dp, HIGH);  // 熄灭小数点
  digitalWrite(e, HIGH);   // 熄灭e段
} 

void setup() { 
  int i;
  // 初始化4~11 → 修正为实际使用的2~9引脚
  for(i=2;i<=9;i++) {
    pinMode(i, OUTPUT);    // 设置2~9引脚为输出模式
  }
} 

void loop() { 
  while(1) {
    digital_0();
    delay(2000);  
    digital_1();
    delay(2000); 
    digital_2(); 
    delay(2000); 
    digital_3(); 
    delay(2000); 
    digital_4(); 
    delay(2000); 
    digital_5(); 
    delay(2000); 
    digital_6(); 
    delay(2000); 
    digital_7(); 
    delay(2000); 
    digital_8(); 
    delay(2000); 
    digital_9(); 
    delay(2000); 
  } 
}

四、 核心知识拓展:共阳极 vs 共阴极

这是初学者最容易混淆的概念,也是点亮数码管的关键。

1. 内部结构的区别

数码管内部其实就是8个发光二极管(LED)。

  • 共阳极 (Common Anode, CA)
    • 所有LED的**正极(阳极)**连接在一起,引出一个公共端 COM。
    • 接法:COM端接 5V
  • 共阴极 (Common Cathode, CC)
    • 所有LED的**负极(阴极)**连接在一起,引出一个公共端 COM。
    • 接法:COM端接 GND

2. 触发逻辑(电平)的不同

由于接法的不同,导致点亮 LED 的逻辑完全相反:

类型 公共端接法 点亮电平 熄灭电平 逻辑口诀
共阳极 接 5V LOW (0) HIGH (1) 低电平点亮 (0亮1灭)
共阴极 接 GND HIGH (1) LOW (0) 高电平点亮 (1亮0灭)

本实验使用【共阳极】数码管
这意味着:如果你想让 ‘a’ 段亮起来,你需要在 Arduino 的 D2 引脚输出 LOW。这就是为什么你在原始代码中看到大量的 digitalWrite(x, LOW) 是为了点亮。


五、 代码优化:从“流水账”到“工程化”

1. 原始代码分析(为什么要优化?)

在初学阶段,我们可能会为每个数字写一个函数,例如 digital_0(), digital_1()… 就像你提供的原始代码那样。
虽然这样逻辑简单,但缺点很明显:

  • 代码冗余:显示0-9需要写10个函数,几百行代码。
  • 维护困难:如果引脚换了,你需要去10个函数里挨个修改。
  • 扩展性差:如果还要显示字母 A-F,代码量会爆炸。

2. 优化思路:二维数组查表法

我们可以引入**“真值表”**的概念。
我们将 0-9 每个数字对应的 adp 的亮灭状态(0或1)提前算好,存入一个 二维数组 中。
需要显示数字 X 时,只需要去数组的第 X 行读取数据,然后用 for 循环写入引脚即可。

3. 字模编码(共阳极)

  • 逻辑0 为亮,1 为灭。
  • 顺序:a, b, c, d, e, f, g, dp

例如数字 ‘0’

  • 需要亮:a, b, c, d, e, f -> 对应值 0
  • 需要灭:g, dp -> 对应值 1
  • 数组行{0, 0, 0, 0, 0, 0, 1, 1}

4. 终极优化代码

以下是使用 C++ 数组封装后的专业代码,代码量减少了70%,且功能更强大。

// --- 1. 硬件引脚定义 ---
// 将引脚放入数组,方便循环操作
// 顺序对应:a, b, c, d, e, f, g, dp
const int segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; 
const int pinCount = 8; // 总引脚数

// --- 2. 字模表 (二维数组) ---
// 共阳极数码管真值表:0表示亮(LOW),1表示灭(HIGH)
// 行代表数字 0-9,列代表段 a-dp
const byte digits[10][8] = {
  // a, b, c, d, e, f, g, dp
  {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 < pinCount; i++) {
    pinMode(segmentPins[i], OUTPUT);
    digitalWrite(segmentPins[i], HIGH); // 初始全部熄灭(共阳极HIGH为灭)
  }
}

void loop() {
  // 循环显示 0 到 9
  for (int num = 0; num < 10; num++) {
    displayNumber(num); // 调用封装好的函数
    delay(1000);        // 停留1秒
  }
}

// --- 3. 封装函数:显示任意数字 ---
// 参数:n 是要显示的数字 (0-9)
void displayNumber(int n) {
  // 边界检查:防止索引越界
  if (n < 0 || n > 9) return; 

  // 遍历8个段,根据字模表设置电平
  for (int i = 0; i < pinCount; i++) {
    // digits[n][i] 取出第n行第i列的状态
    digitalWrite(segmentPins[i], digits[n][i]);
  }
}

六、 实验总结与注意事项

1. 为什么必须加电阻?

数码管本质是 LED,如果不加限流电阻直接接 5V,电流会过大(可能超过 LED 承受的 20mA),导致数码管瞬间烧毁或者 Arduino 引脚损坏。220Ω 或 330Ω 是常用的安全阻值。

2. 代码中的 byte 为什么要用?

在定义字模表 digits 时,我们使用了 byte 类型而不是 int

  • int 占用 2 字节。
  • byte 占用 1 字节。
    虽然在本实验中区别不大,但在大型项目中,使用合适的数据类型可以节省宝贵的 SRAM 内存空间。

3. 如何修改为共阴极?

如果你买到了共阴极数码管,只需要改动两处:

  1. 硬件:公共端 (COM) 改接 GND
  2. 代码:字模表里的 01 互换(或者在 digitalWrite 时加一个 ! 取反)。
  3. 硬件连接:在每个段都需要串联电阻(非常重要,防止烧毁数码管LED)。

通过本次实验,我们不仅点亮了数字,更重要的是学会了**“数据驱动逻辑”**的编程思想——用数组来存储状态,用循环来执行动作,这才是嵌入式编程的精髓!

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐