给机器人或小车做面板,数字显示是最常见的需求:电量、速度、计数、倒计时,都需要一块能亮数字的屏。TM1637 四位数码管模块大概是最省事的答案,四根线接上 ESP32,两个 GPIO 就能驱动四位数字。我翻了一遍 Last Minute Engineers 2026年4月更新的那篇 ESP32 教程,把接线、装库、代码和几个容易踩的坑理成这篇,照着做就能点亮。

认识 TM1637:自带驱动芯片的显示模块
这个模块是两样东西的组合:一块 0.36 英寸的四位七段数码管,加一颗 Titan MicroElectronics 出品的 TM1637 LED 驱动芯片。芯片把刷新显示的脏活全包了——ESP32 不用反复轮询每一位数码管,只要把要显示的内容发过去,剩下的扫描刷新由模块自己完成,主控可以把精力花在别的任务上。

它受欢迎还有两个原因。一是供电灵活,3.3V 和 5V 都能工作,所以接 ESP32 这类 3.3V 主控和接经典 Arduino 这类 5V 主控都行。二是它用的是自家的两线协议,长得像 I2C 但并不是标准 I2C,不过这不用操心,现成的库已经把协议细节封装好了。按底稿给出的参数,全段点亮、最大亮度时整块模块大约吃 80mA 电流,调低亮度能省不少电。模块的关键规格整理如下:
- 工作电压:3.3 至 5V DC
- 最大电流:约 80mA(全段点亮、最高亮度时)
- 反接保护:有
- 显示尺寸:42mm × 24mm(1.65 × 0.95 英寸)
- 显示高度:12mm(0.47 英寸,典型值)
- 字符高度:9.14mm(0.36 英寸)
很多 TM1637 模块中间还带一个冒号,做电子钟显示 12:34 这种格式特别方便。
引脚定义与接线:四根线搞定
模块一共只有四个引脚,定义如下。

- CLK:时钟输入脚
- DIO:数据输入输出脚
- VCC:电源正,接 3.3V 至 5V
- GND:地
接 ESP32 的对应关系是:VCC 接 VIN,GND 接 GND,CLK 接 GPIO 26,DIO 接 GPIO 25。完整的接线见下图,两根电源两根信号,插面包板几分钟的事。

有一点值得强调:VCC 走 VIN 是借 USB 的 5V,模块吃 80mA 毫无压力;但如果你把模块接在 3.3V 脚上也完全能亮,只是亮度略低。CLK 和 DIO 这两个 GPIO 原则上可以换任意数字脚,改代码里的两个宏就行,教程选 26 和 25 是为了避开 ESP32 的启动 strapping 脚,省得烧录时添乱。
装库与烧录:Arduino IDE 三步走
先确保 Arduino IDE 里已经装好 ESP32 板卡支持包,这一步卡住的人最多,驱动、串口、bootloader 报错基本都是板卡包没装对导致的,这里不展开。板卡包就位后装显示库:
- 打开 Arduino IDE,点左侧边栏的库管理器图标;
- 搜索框输入 tm1637 过滤结果;
- 找到 Avishay Orpaz 写的 TM1637 库,点安装。
截至2026年11月6日,库管理器里搜 tm1637 第一屏就能看到这个库,作者名对得上就可以装。装完就能直接编译下面的程序。
测试程序:一次跑全常用玩法
下面这段测试程序把常用操作全走一遍:全段点亮、0 到 9 计数、负数、前导零、居中显示、冒号、摄氏度符号,最后显示 dOnE。照着烧一遍,显示正常就说明接线没问题。
#include "TM1637Display.h"
// TM1637 connections to ESP32
#define CLK 26
#define DIO 25
// Create display object
TM1637Display display(CLK, DIO);
// An array that turns every segments ON
const uint8_t allON[] = { 0xff, 0xff, 0xff, 0xff };
// An array that sets individual segments per digit to display the word "dOnE"
const uint8_t done[] = {
SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, // d
SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
SEG_C | SEG_E | SEG_G, // n
SEG_A | SEG_D | SEG_E | SEG_F | SEG_G // E
};
// An array that displays the °C symbol
const uint8_t celsius[] = {
SEG_A | SEG_B | SEG_F | SEG_G, // degree symbol
SEG_A | SEG_D | SEG_E | SEG_F // C
};
void setup() {
// Start with a medium brightness
// Brightness range is 0 (dimmest) to 7 (brightest)
display.setBrightness(5);
// Set every segments ON
display.setSegments(allON);
delay(2000);
// Clear the display
display.clear();
// Count from 0 to 9
for (int i = 0; i < 10; i++) {
display.showNumberDec(i);
delay(200);
}
delay(1000);
display.clear();
// Show negative numbers
display.showNumberDec(-12); // shows " -12"
delay(2000);
display.clear();
display.showNumberDec(-999); // shows "-999"
delay(2000);
display.clear();
// Show number without leading zeros
display.showNumberDec(31, false); // shows " 31"
delay(2000);
display.clear();
// Show number with leading zeros
display.showNumberDec(31, true); // shows "0031"
delay(2000);
display.clear();
// Show number in the middle
display.showNumberDec(14, false, 2, 1); // shows " 14 "
delay(2000);
display.clear();
// Show a negative number in the middle
display.showNumberDec(-5, false, 2, 1); // shows " -5 "
delay(2000);
display.clear();
// Show 12:34 using the colon
display.showNumberDecEx(1234, 0b01000000, false, 4, 0);
delay(2000);
display.clear();
// Show 15°C
int temperature = 15;
display.showNumberDec(temperature, false, 2, 0);
display.setSegments(celsius, 2, 2);
delay(2000);
display.clear();
// Show dOnE
display.setSegments(done);
}
void loop() {
}
一个先说清楚的细节:按本站编辑器的兼容要求,文中尖括号形式的头文件一律写成了双引号形式,即 #include "TM1637Display.h"。在 Arduino 工具链下两种写法对库搜索路径等价,直接编译不受影响。
程序骨架:引脚与显示对象
程序开头先声明 ESP32 的两个引脚,再用这两个引脚构造显示对象:
// TM1637 connections to ESP32
#define CLK 26
#define DIO 25
// Create display object
TM1637Display display(CLK, DIO);
换引脚只改这两个宏,其他代码不动。
逐段点亮:SEG 宏与两种写法
库允许你控制每一位数码管上的每一段 LED。控制方式有两种:第一种按位顺序拼十六进制数,段顺序是(小数点)G F E D C B A,例如 0x39 二进制是 00111001,点亮 A、D、E、F 四段,正好显示字母 C;第二种更直观,直接用 SEG_A 到 SEG_G 这些宏按位或拼出来,上面程序里的 done 数组和 celsius 数组就是这么写的。
三个核心函数
setBrightness(brightness, on) 控制亮度,取值 0 到 7,0 最暗 7 最亮;第二个参数可省,传 false 会直接关显示,传 true 或不传才是正常点亮——display.setBrightness(5, false) 这种写法看着设了亮度,实际是把屏关了,这是个容易懵的点。
setSegments(segments, length, position) 逐段控制:第一个参数是段码数组,第二个是更新几位,第三个是从哪位开始(0 是最左,3 是最右),后两个参数可省。显示 dOnE 是四位全刷:
// Prints dOnE
display.setSegments(done);
只刷最后两位显示摄氏度符号,就要把位数和起始位都给上:
// Prints __°C
display.setSegments(celsius, 2, 2);
showNumberDec(number, leading_zeros, length, position) 是显示十进制数的主力,负号会自动占位。计数循环配合 delay 就能滚动显示:
// Show counter 0-9
int i;
for (i = 0; i < 10; i++) {
display.showNumberDec(i);
delay(50);
}
第二个参数控制前导零:false 原样显示,true 补零凑满四位。
display.showNumberDec(31, false); // Prints __31
display.showNumberDec(31, true); // Prints 0031
第三、四个参数和前面一样管位数和起始位置,想让数字居中就指定从第 1 位开始刷两位:
display.showNumberDec(14, false, 2, 1); // Prints _14_
display.showNumberDec(-5, false, 2, 1); // Prints _-5_
冒号怎么点:showNumberDecEx
带冒号的电子钟格式要用扩展版 showNumberDecEx,它的第二个参数专门管小数点和冒号,用一个二进制数逐位指定。带冒号的模块上,0b01000000 就是点亮中间那个冒号,显示 12:34 一行搞定:
// Prints 12:34
display.showNumberDecEx(1234, 0b01000000, false, 4, 0);
如果你的模块是每位之间带小数点的款式,0b10000000 点第 1 位后的小数点、0b11100000 三个点全亮,对照模块实物选就行。测试程序末尾没有循环任务,loop 留空即可:
void loop() {
}
写在最后
整套下来,硬件四根线、软件一个库一段码,是性价比最高的显示方案之一。烧录时如果碰上连不上串口,先查板卡包和驱动,再看是不是占用了 strapping 脚——这套排查思路和我平时折腾固件的路数是一样的。把这篇跑通之后,顺理成章的下一步是接 NTP 对时,做一台断电也不用对时间的 Wi-Fi 电子钟,Last Minute Engineers 站上就有现成的后续教程可以接着抄。数字显示这个坎迈过去,给你的机器人加个状态面板就是顺手的事了。











补充一个点,这种模块有的带中间冒号有的不带,做电子钟前先看清楚商品图,不然四位数字中间插不进冒号。
按这个接法搭了一块,VIN 供电确实稳,全段点亮的时候模块摸上去只是温温的。之前在 Uno 上接过一次,当时不懂 setBrightness 第二个参数的坑,写成 (5,false) 还以为买到了坏屏,差点退回去。这篇把 0 到 7 亮度和关显的逻辑讲清楚了,新手照抄就行。冒号那个 0b01000000 提醒得也对,我第一次做钟就是忘了这一位,四位数干瞪眼。
四根线点亮,比点个灯还简单,收藏了。
CLK 和 DIO 换脚那条实用,我板子上 25、26 被外设占了,换个脚改两个宏就完事。