给Arduino做项目,十有八九要显示点什么:温度、分数、倒计时、计数器。裸接四位数码管得占十来个IO,面包板上一堆杜邦线绕成毛线团;换成TM1637模块,两根信号线就全搞定。这篇把两份英文教程的流程理顺合并,从模块介绍、接线、装库一路讲到计数器和时钟冒号显示,新手容易卡的地方也一并标出来。
这个模块便宜、好买,几乎是新手必囤件。它和OLED屏走的路线不一样:OLED什么都能画,但驱动、坐标、字库一堆概念;TM1637只会干一件事,显示四位数字,而大多数项目恰恰只需要这个。学会了它,温度计、倒计时器、计分板都是现成的积木。

TM1637模块是什么:两根线点亮四位数字
先说七段数码管本身。所谓七段,就是七个排成"8"字形的LED,每一段编号a到g,不同段亮灭组合出0到9,下点功夫还能拼出几个简单字母。每个LED照理需要两个引脚,但数码管把其中一头在内部全接在一起,做成公共端COM,这才让引脚数量降了下来。
但裸管还是麻烦:四位数字最多28段,全靠单片机逐段扫描,程序写得又臭又长。TM1637模块就是来解决的——它在0.36英寸四位数码管背后配了一颗Titan MicroElectronics的TM1637驱动芯片,扫描刷新全由芯片代劳:你把要显示的数写进去,它自己保持显示,Arduino转头去干别的,不用一直刷新。
供电3.3V到5V都行,接5V的Uno还是3.3V的ESP32都不挑。全亮加最高亮度时电流大约80mA,调低亮度能省不少电。模块中间带一个冒号,做时钟显示天生合适。有一点要知道:它用的是自家两线协议,长得像I2C但不是标准I2C,不能往I2C扫描器里找地址。官方规格里还有一条挺贴心,模块带反接保护,VCC和GND插反一次不至于烧(别故意试)。主要参数如下表:
| 项目 | 参数 |
|---|---|
| 工作电压 | 3.3 – 5 VDC |
| 最大电流 | 约80mA(全亮最高亮度) |
| 反接保护 | 有 |
| 显示尺寸 | 42 × 24 mm |
| 亮度档位 | 8级(0最暗,7最亮) |
| 通信方式 | 两线(CLK + DIO),非标准I2C |
接线:CLK、DIO两根信号线就够
模块一共四个脚:CLK时钟线、DIO数据线、VCC电源、GND地。四个脚的分工很清楚——CLK负责打节拍,DIO负责传数据,芯片靠这一对线就能把所有段和位都管起来。按下面这份接线表插就行,本文的程序以CLK接3、DIO接4为准:
- CLK → 数字脚3
- DIO → 数字脚4
- VCC → 5V
- GND → GND

给新手标个坎:数码管模块普遍有共阴共阳之分,买的时候页面上写的四位管多半是0.36英寸共阳带冒号款,TM1637芯片对外把这事包圆了,你不用关心共阴共阳,库函数直接给数字就行。真正要留心的是排针顺序,不同厂家的模块引脚顺序可能是GND、VCC在另一侧,插线前对着丝印看一遍,别照着记忆插。
顺带说一句,CLK和DIO其实接哪两个数字脚都行,只要和代码里对得上。接9和10也很常见,两种都合法,关键是接线要与代码里的宏定义一致,别接错线却忘了改宏。
装库与第一个程序:从搜库到数字滚动
驱动这块不用自己啃协议,装现成的TM1637Display库(作者是Avishay Orpaz)就行,这个库把常用功能都封装好了。打开Arduino IDE,点左侧的库管理器图标,搜索框里输入tm1637过滤结果,找到Avishay Orpaz的那个TM1637库,点安装按钮。
装完这一步,环境和接线就都齐了,可以进下一个环节。要是搜索结果里蹦出来好几个同名库,认准作者名,别装错了后面编译一堆报错。

下面这段测试程序一次演示所有常用动作:全亮点两秒、0到9滚动、负数、前导零、指定位置显示、冒号、摄氏度符号,最后亮出dOnE。原样贴进IDE,选好板子和串口,上传:
// Include the library
#include "TM1637Display.h"
// Define the connections pins
#define CLK 3
#define DIO 4
// Create a display object of type TM1637Display
TM1637Display display = TM1637Display(CLK, DIO);
// Create an array that turns all segments ON
const uint8_t allON[] = { 0xff, 0xff, 0xff, 0xff };
// Create an array that turns all segments OFF
const uint8_t allOFF[] = { 0x00, 0x00, 0x00, 0x00 };
// Create 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
};
// Create degree celsius 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() {
// Set the brightness to 5 (0=dimmest 7=brightest)
display.setBrightness(5);
// Set all segments ON
display.setSegments(allON);
delay(2000);
display.clear();
// Show counter 0-9
int i;
for (i = 0; i < 10; i++) {
display.showNumberDec(i);
delay(50);
}
delay(2000);
display.clear();
display.showNumberDec(-12); // Prints _-12
delay(2000);
display.clear();
display.showNumberDec(-999); // Prints -999
delay(2000);
display.clear();
display.showNumberDec(31, false); // Prints __31
delay(2000);
display.clear();
display.showNumberDec(31, true); // Prints 0031
delay(2000);
display.clear();
display.showNumberDec(14, false, 2, 1); // Prints _14_
delay(2000);
display.clear();
display.showNumberDec(-5, false, 2, 1); // Prints _-5_
delay(2000);
display.clear();
// Prints 12:34
display.showNumberDecEx(1234, 0b01000000, false, 4, 0);
delay(2000);
display.clear();
// Prints 15°C
int temperature = 15;
display.showNumberDec(temperature, false, 2, 0);
display.setSegments(celsius, 2, 2);
delay(2000);
display.clear();
// Prints dOnE
display.setSegments(done);
}
void loop() {
}上传成功后,屏幕会依次跑完这一串动作。代码开头两行是关键:宏定义CLK和DIO是哪两个脚,再用它们创建显示对象。如果你的接线和3、4不一样,改这两个宏就行。
四个数组是段码表,用SEG_A到SEG_G按位或拼出想要的字形。allON和allOFF分别管全亮和全灭;done数组拼出字母dOnE,是这类模块约定俗成的"任务完成"提示;celsius数组拼出摄氏度符号,给后面显示温度用。
setup里先setBrightness(5)把亮度定在中间档,0最暗7最亮,室内用5左右刚好。接着setSegments(allON)全亮两秒,这是检查四个数字位都正常的最快办法。clear()负责清屏,每换一个显示内容前先清,不然上一位的残影会留着。
showNumberDec进阶:计数器、冒号到指定位置显示
日常用得最多的函数是showNumberDec,四个参数依次是:要显示的数、是否补前导零、显示几位、从第几位开始(0是最左边一位)。做个0到9的计数器,一个循环就够:
// Show counter 0-9
int i;
for (i = 0; i < 10; i++) {
display.showNumberDec(i);
delay(50);
}前导零和显示位置的玩法看这两行。showNumberDec(31, false)显示成"__31",把第二个参数改成true就是"0031";第三、四个参数一起用,能控制显示几位、靠哪一侧:
display.showNumberDec(31, false); // Prints __31
display.showNumberDec(31, true); // Prints 0031
display.showNumberDec(14, false, 2, 1); // Prints _14_
display.showNumberDec(-5, false, 2, 1); // Prints _-5_负数也能直接传,-12显示成"_-12",负号占一位,四位管够。做时钟显示就换showNumberDecEx,第二个参数0b01000000是点亮中间冒号的位掩码。把小时乘100加分钟传进去,屏幕上就是12:34这样的效果,接个DS3231时钟模块或者随便什么时间源都能用:
// Prints 12:34
display.showNumberDecEx(1234, 0b01000000, false, 4, 0);把这套函数串起来,常见项目就都有了模板:秒表用showNumberDec每百毫秒刷一次计数值;倒计时接两个按键做加减;电子骰子用random(1,7)配showNumberDec;接上传感器,读数往showNumberDec里一塞就是仪表。四位数字虽少,配合冒号和位置参数,表达力比想象中够用。
主程序里那行15°C的演示是位置显示和段码的组合拳:showNumberDec先把温度写进前两位,setSegments再把摄氏度符号写到后两位,两步合起来就是完整的"15°C"。这套写法可以搬到任何"数字加单位"的场景。
新手容易踩的坑
最常见的翻车是显示不亮或乱闪,九成是VCC和GND接反或没插紧。虽然模块带反接保护,但保护不等于能正常工作,先查电源线再看信号线,杜邦线虚接在这种两线协议上特别常见。
第二是改了接线没改宏。代码里CLK是3,线却插在9上,屏幕自然没反应,而且编译不会报错——编译器不知道你的线插哪了,两处必须一致。第三是亮度写超范围,0到7共八档,写8不报错但效果未知,调试阶段老老实实写5。
第四,别拿I2C地址扫描器找它。TM1637不走标准I2C,扫不到地址不代表模块坏了,这是它和OLED最大的区别。第五,代码里那段0到9的滚动每格只delay(50)毫秒,看着快是正常的,真做计数器时把延时改到你需要就行。
整体下来,这块模块是新手最值得先玩熟的外设之一:接线四根,装一个库,一行函数就出效果。跑通本文的测试程序,计数器、时钟显示、温度计就都是往上叠功能的事了,照着上面的参数表和坑点排查,基本一次点亮。











冒号位掩码0b01000000这个记下来了,正好拿去做个厨房计时器,比OLED省事多了。
这块模块是我入坑数码管的起点。当时裸接过一次四位管,28个段扫得头皮发麻,后来换TM1637才发现人家芯片早把扫描做完了,两根线就出效果。文章里那个dOnE段码表我印象很深,SEG_A按位或拼字母这个思路学会了以后,连倒计时的°C符号都不用查表了。新手真别跳过第一个测试程序,全亮那两秒能帮你把坏管和接线错当场筛出来。
装库认准Avishay Orpaz这条太对了,搜tm1637出来好几个库,装错一个编译直接给你一屏红。
CLK改宏忘了同步这个坑我踩过,屏幕死活不亮还以为是模块坏了,结果是线插9代码写3。