别再纠结了!用ULN2003驱动数码管,共阴共阳到底怎么接?附实测电路图
第一次用ULN2003驱动数码管,是在一个周末的项目里折腾到凌晨两点。照着网上搜来的电路图焊好板子,上电,数码管纹丝不动。排查了一个多小时,最后发现罪魁祸首根本不是代码,而是我手里的数码管是共阴,网上的图用的是共阳。就这一个“阴”和“阳”的区别,让无数新手在ULN2003面前栽了跟头。
这篇文章就把这件事彻底讲明白:ULN2003为什么能驱动数码管、共阴共阳分别该怎么接、限流电阻怎么选、动态扫描有哪些坑,以及我在实测中踩过的各种问题。不说废话,直接上干货,最后附上可复现的实测电路说明和排查表,希望能帮你少走几小时弯路。
1. ULN2003和数码管,先搞懂这俩的“脾气”
1.1 ULN2003的实质:7路达林顿管阵列,输出是“灌”进去的
很多朋友把ULN2003当成一个“万能驱动芯片”,拿来就接,亮了就算,不亮就懵。要搞明白它能不能驱动数码管,得先弄清楚它内部到底是什么。
ULN2003是典型的达林顿晶体管阵列,内部集成了7路NPN达林顿对。每一路的逻辑很简单:输入端给高电平,输出端就导通,相当于一根开关把输出接到地;输入端给低电平,输出端就断开。注意,输出端断开的时候,它并不是输出一个高电平,而是处于悬空状态。这是因为ULN2003的输出极是集电极开路(OC门)结构,内部没有任何上拉电阻。
这个特性决定了ULN2003天生适合做“灌电流”驱动。什么叫灌电流?可以想象它像一排放到地面的接地开关:负载的一端接正电源,另一端接ULN2003的输出端。当某一路开关合上时,电流从正电源流过负载,被“灌”进ULN2003再流到地,负载工作。驱动继电器、步进电机、LED灯珠,用的都是这个原理。
所以记住一句话:ULN2003是低压侧驱动器件,它把负载的另一端拉到地,让电流通过负载。它不是推挽输出,不会主动往外面“推”电压。这个本质决定了它在共阳和共阴数码管面前的表现完全不同。
1.2 共阴和共阳,区别就在公共端
数码管是由多段发光二极管组成的,共阴和共阳描述的是这些二极管的连接方式。
共阳数码管:所有段的发光二极管阳极连接在一起,引出一个公共端(COM),工作时COM接电源正极。点亮某一段,只需要把该段的阴极拉低,让电流从COM流入,经该段LED,从段脚流出到地。
共阴数码管:所有段的发光二极管阴极连在一起,公共端(COM)接GND。点亮某一段,需要从段脚流入电流,经过LED到COM,然后到地,所以要靠段脚输出高电平来点亮。
两种数码管点亮条件完全相反:共阳要“低电平点亮段”,共阴要“高电平点亮段”。
ULN2003输出端能做什么?它只能把段脚拉低,或者让段脚悬空。拉低正好满足共阳数码管的点亮条件,而悬空在共阴数码管面前毫无意义。这就是为什么ULN2003驱动共阳数码管顺理成章,而驱动共阴数码管大多数人一接就翻车。
1.3 数码管“静态显示”和“动态扫描”对驱动的要求
数码管的显示方式大致分两种。
静态显示:每个数码管的段脚由独立的信号持续控制,显示内容稳定,不依赖扫描。优点是程序逻辑简单、不闪烁;缺点是占IO口太多,3位数码管就要占用21个IO(3个位选再加8个段选,具体看电路),MCU一般扛不住。实际中常用锁存器、移位寄存器扩展,或者用ULN2003等驱动芯片减少MCU直接驱动的压力。
动态扫描:所有数码管的段选共用一组IO,位选单独控制。程序轮流让每一位导通几百微秒到几毫秒,利用人眼视觉暂留,看起来所有位都在同时亮。这种方式大大减少IO占用,3位数码管只需要3个位选+8个段选=11个IO,再配合ULN2003这类驱动芯片,MCU的IO压力小很多。
ULN2003在两种模式下的角色通常是:负责段选信号的大电流驱动,把MCU的IO逻辑电平转换成能点亮LED段的大电流灌入能力。如果只是静态显示一位或两位数码管,IO口直接串电阻也能点亮,但亮度有限且容易让MCU过热;接到ULN2003上,输入只需很小电流,输出能承受更大电流,稳定可靠得多。
2. 共阳数码管实测,按这个接基本一次成功
2.1 元器件清单和接线逻辑
我这次实测用的是最普通的红色共阳数码管(型号类似3611BS),加上一块ULN2003模块,这几样东西几乎每个电子爱好者手边都有。
清单如下:
- 共阳数码管1个,管脚排列是上四下四那种(具体看引脚图,最好用万用表确认)
- ULN2003芯片或模块,7路用6路就够(a、b、c、d、e、f、g),如果要小数点就占7路
- 220Ω或330Ω限流电阻8个
- 5V电源或开发板(我用的STM32,电源从板子取5V)
- 面包板和杜邦线若干
接线逻辑的一句话版本:MCU的IO口接ULN2003输入端,ULN2003输出端串限流电阻接数码管段脚,数码管公共端COM接VCC,所有GND共地。
下面是实测用的接线示意图,我尽量画清楚:
+5V ─────────────────────────────┐ │ 共阳数码管COM(公共端) │ ┌── a ── R(220Ω) ── OUT1(ULN2003) ├── b ── R(220Ω) ── OUT2 ├── c ── R(220Ω) ── OUT3 ├── d ── R(220Ω) ── OUT4 ├── e ── R(220Ω) ── OUT5 ├── f ── R(220Ω) ── OUT6 ├── g ── R(220Ω) ── OUT7 └── dp── R(220Ω) ── OUT8(ULN2003第8路,若有) │ MCU IO1 ───── IN1(ULN2003) │ MCU IO2 ───── IN2 │ MCU IO3 ───── IN3 │ MCU IO4 ───── IN4 │ MCU IO5 ───── IN5 │ MCU IO6 ───── IN6 │ MCU IO7 ───── IN7 │ │ GND ───────── GND(ULN2003) ───────┘这里有个细节:ULN2003的输出端是集电极开路,外部负载接在哪端呢?正确接法是负载(数码管段LED)一端接限流电阻,限流电阻另一端接ULN2003输出端,数码管段LED另一端其实是在数码管内部汇到COM,而COM接VCC。这样当ULN2003导通时,电流路径是VCC→COM→段LED→段脚→限流电阻→ULN2003输出→地,LED点亮。
2.2 限流电阻怎么选:算一次你就记住了
关于限流电阻,网上说法很乱,有人用100Ω,有人用1kΩ。其实可以用欧姆定律一次算清楚。
5V供电下,红色LED的正向压降通常在1.8V~2.2V,取1.9V计算。ULN2003在输出导通时,饱和压降约为1V左右。于是整个回路上除限流电阻外的压降是:
1.9V(LED)+1V(ULN2003饱和压降)=2.9V
限流电阻需要承受的电压是:
5V-2.9V=2.1V
如果想让数码管工作在10mA左右(普通红色数码管这个电流已经比较亮):
R=2.1V/0.01A=210Ω
所以实测中220Ω就很合适。如果想让亮度更高一点,例如15mA,可以选150Ω;如果只是近距离显示,330Ω也能亮,只是稍暗,而且更省电。
我实测的结论是:5V供电、红色共阳数码管,用220Ω限流电阻,显示效果和寿命都比较好。绿色或蓝色数码管LED压降更高(约3.0V~3.4V),同样的电阻下电流会小一些,可能显得暗,可以适当减小电阻到100Ω~150Ω。
另外,多位数码管动态扫描时,每一位只有1/位数的占空比在工作,平均亮度会下降,可以把限流电阻适当调小一点,比如10mA占空比下用150Ω甚至100Ω,具体根据实测亮度来。但切勿去掉限流电阻直接短接,LED会瞬间烧掉。
2.3 实测案例:点亮一个“8.”和一个倒计时
我用STM32F103的PA0~PA6分别接ULN2003的1~7路输入,PA7接第8路控制小数点。数码管用共阳,COM接5V。测试程序很简单,先把a~g全置高,数码管显示“8.”:
#include "stm32f1xx_hal.h" void GPIO_Init(void) { __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } void Display_8_Dot(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // a HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); // b HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // c HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET); // d HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // e HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // f HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET); // g HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET); // dp }注意,STM32的IO输出3.3V高电平,ULN2003输入高电平门槛不高,实测驱动没有任何问题。如果一个IO口推不动,可以并两个IO口,但通常没必要。51单片机5V系统就更稳了,直接接就行。
如果要用51做倒计时秒表,思路也差不多:用定时器产生1秒中断,秒值减到0时停止;显示部分用动态扫描,3位数码管位选分别接到另外的IO口(注意动态扫描的位选需要额外电路,下面会说)。ULN2003照旧接段选,代码部分无非是多一个位选轮询函数和段码表。
段码表我直接给你参考,共阳数码管、段脚按a-b-c-d-e-f-g-dp排列,低电平点亮时用取反值:
// 共阳数码管段码表,1表示段灭,0表示段亮 const unsigned char code SEG_CODE[] = { 0xC0, // 0 0xF9, // 1 0xA4, // 2 0xB0, // 3 0x99, // 4 0x92, // 5 0x82, // 6 0xF8, // 7 0x80, // 8 0x90, // 9 };如果用ULN2003做低电平驱动,输入高电平让输出拉低点亮段,所以MCU输出的段码要和“低电平点亮”匹配。上面这份是共阳数码管常用取反码,喂给ULN2003输入端前要确认逻辑方向一致:需要点亮的段给高电平,熄灭的段给低电平。所以实际用这份表时,把表里的1/0反过来理解更方便,或者在代码里做一次按位取反。
2.4 用ULN2003做动态扫描的注意点
动态扫描最经典的做法是段选由ULN2003驱动、位选由PNP三极管或锁存器控制。因为共阳数码管的公共端接VCC,选通某一位时,需要让该位的COM保持高电平;不选通时,COM需要断开或者拉低。ULN2003是低位导通,不擅长直接控制高侧位选,所以位选我用的是PNP三极管(比如S8550)反相控制:IO输出低时三极管导通,COM接VCC;IO输出高时三极管截止,该位灭。
如果你手里的ULN2003模块上带了“COM”引脚(9脚),这个引脚是内部续流二极管的公共端,用来接感性负载电源用的。驱动数码管这种纯阻性负载,COM引脚可以悬空,千万别把它和数码管COM搞混。
动态扫描时有一个坑:ULN2003输出的关断有延迟。实测扫描频率超过2kHz后,容易出现“拖影”或亮度不均。建议扫描周期控制在1kHz到2kHz,每位点亮时间1ms左右,并且在切换位选时先关闭段选(消隐),再切换位选,最后打开下一段的段选。否则切换瞬间容易看到上一段余晖。
3. 共阴数码管怎么办?别硬接,换条路走
3.1 直接用ULN2003驱动共阴为什么不行
我见过不少新手把共阴数码管的段脚接到ULN2003输出端,以为输入高电平点亮段。结果上电后整个数码管要么全灭,要么亮度极低,要么部分段伴随微弱发光。
原因不复杂。共阴数码管的COM接GND,段脚需要正电压才能点亮。ULN2003的输出端只有两个状态:导通(接近0V)和截止(悬空)。导通时段脚被拉到0V,LED两端电压几乎为0,不会亮;截止时段脚悬空,电压由漏电流决定,电流极小,基本也点不亮。
更麻烦的是,某些“特殊接法”号称能用ULN2003驱动共阴,实际上是把ULN2003放在位驱动位置,将公共端拉低来选通。比如某一位共阴数码管的COM接ULN2003输出,输入高电平时COM被拉低,同时段脚由IO或其它驱动芯片输出高电平,才能点亮。这种方案绕了一圈,段驱动实际靠的不是ULN2003。
所以,想用ULN2003直接驱动共阴数码管的段,本质上就是接反了方向,逻辑完全错误。别在这个方向上死磕。
3.2 共阴数码管的3种可行驱动方案
如果手里已经有共阴数码管,且一定想用,我有三种方案供你选,按省事程度排序。
方案一:换共阳数码管。这是最省事的办法,成本几毛钱,拆下来换个型号,电路照前面第2章接,立刻搞定。别觉得换管子“丢人”,我后来做项目都是先确认共阴共阳再画板,绝不在这上面浪费时间。
方案二:段驱动用MCU IO口或74HC573/74HC245这类高电平输出缓冲器,ULN2003只做位驱动。这个方案适合动态扫描的共阴多位数码管。位选部分让ULN2003拉低COM,段选部分用IO口直接推高电平点亮段。要注意的是共阴管的段电流全部汇到COM,再由ULN2003流向地,COM要承担所有段的电流总和,ULN2003每路最大500mA,3~5位数码管完全够。但IO直推段的驱动能力有限,一个IO口一般只能稳定输出几mA到20mA,多位同时亮可能出现亮度不够。这时可以在段驱动加74HC573或74HC245缓冲。
方案三:用CD4511、74HC164这类专用译码/移位芯片。CD4511是BCD七段译码器,输入4位BCD码,输出七段码,输出为高电平有效,正好适配共阴数码管。缺点是它只支持0~9数字,不支持小数点,需要小数点得另外加IO控制。74HC164是8位串入并出移位寄存器,串行数据从IO口进来,并行输出给共阴数码管的段脚,优点是可以多片级联驱动多位,缺点是显示数据要自己算好段码。
我实测过CD4511配共阴数码管的方案,显示数字很清晰,但亮度一般,因为CD4511输出驱动能力有限(几mA级别),用在小尺寸高亮数码管上还能接受。如果希望字更亮,最好在CD4511输出后面加缓冲或换方案。
3.3 带小数点、多位数码管的特殊接法
共阴数码管的小数点(dp)段,和普通段一样,段脚需要高电平才能点亮。用方案二时,dp脚直接接IO或缓冲输出即可。多位共阴数码管动态扫描时,位选COM分别接ULN2003的不同输出,段选共同接IO缓冲。程序上先送段码,再选通该位。
举个例子,2位共阴数码管动态扫描,段选共用B0~B7,位选COM1接ULN2003 OUT1,COM2接OUT2。显示第一位数时,先让OUT1输入为高电平(COM1被拉低),同时段脚送高电平点亮需要的段;显示第二位数时,把OUT1关闭,OUT2打开,循环扫描。
这里要特别注意共阴动态扫描和共阳动态扫描的位选逻辑是相反的:共阴位选是“拉低选通”,共阳位选是“拉高选通”。如果套用网上共阳的位选程序去驱动共阴,结果一定是显示错乱、亮度不均甚至不亮。
4. 实测踩坑记录:不亮、乱码、亮度不均
4.1 常见故障现象与排查顺序
我在各种现场调试中总结出一个排查顺序:电源→公共端→段脚→IO逻辑→代码。每次出问题,按这个顺序查能少走很多弯路。
先说电源。ULN2003需要共地,单片机、数码管、ULN2003三者的GND必须连在一起。电源电压不足也会导致数码管亮度低或者闪烁,尤其动态扫描时电流大,劣质电源压降明显。我用过一个5V/500mA的充电头带4位数码管,一上电电压掉到4.3V,字非常暗,换个1A电源立刻正常。
再查公共端。共阳数码管的COM没接VCC,或者共阴的COM没接GND,就是最常见的“不亮”原因。很多人以为COM就像芯片电源一样必须接,没错,但经常在接很多根线时漏接COM,或者用面包板时COM和旁边脚短路。
段脚排查。常见问题是用错引脚顺序,把a段接到OUT2上,显示8的其中一笔由另一段驱动,数字越看越怪。最靠谱的做法是用万用表二极管档,先确认数码管公共端和每个段脚的对应关系,再根据引脚图核对板子走线。推荐买数码管时先找数据手册,找不到就用万用表从头测一遍,别靠猜。
IO逻辑和代码基本是最后查的。ULN2003输入端高电平有效,输入端悬空也可能导致输出导通,所以MCU不驱动的IO口要设为低电平或下拉。动态扫描代码里尤其要检查位选和段选的切换顺序,消隐没做好容易有鬼影。
4.2 用万用表判断共阴共阳,不用猜
不知道手里数码管是共阴还是共阳?不需要猜,万用表二极管档20秒搞定。
做法:万用表拨到二极管档,红表笔接数码管的某个引脚(先假设是公共端),黑表笔依次碰其余引脚。如果数字显示某个正向压降值(约1.5~2.5V),说明红表笔接的是公共端,而且该数码管是共阳(因为阳极接红表笔,电流经LED到黑表笔的阴极段脚,正向导通)。如果红表笔接公共端时没有读数,反过来黑表笔接该引脚,红表笔碰其他引脚有读数,则说明黑表笔接的是公共端,数码管是共阴。
确定公共端后,把公共端固定,用红表笔或黑表笔扫描其他引脚,就能画出每个引脚对应哪一段。这个办法我每次拿到没资料的数码管都用,比看丝印可靠得多。
4.3 Proteus仿真里的几个易错点
很多人在Proteus里仿真,不亮或者乱码,经常不是逻辑问题,而是元件选错。
Proteus库里的数码管常用模型名称:共阳是7SEG-COM-ANODE,共阴是7SEG-COM-CATHODE。别小看这一字之差,选错模型,仿真电路和实物完全对不上。还有一种“MPX”系列是多位数码管,例如7SEG-MPX8-CC-BLUE表示8位共阴蓝色数码管。注意“COM-ANODE”是共阳,“COM-CATHODE”是共阴,顺序别记反。
另外一个常见仿真问题是ULN2003在Proteus里的引脚。部分库版本中ULN2003的引脚和实物不完全一致,或需要给COM引脚接电源才能正确显示续流二极管特性。驱动数码管时COM悬空就行,但如果仿真结果异常,可以把COM接VCC试试,对纯电阻负载不影响。
仿真不能代替实测,尤其在动态扫描时,仿真里看不出刷新率不足、鬼影、驱动能力不够这些硬件问题。我习惯先用Proteus验证代码逻辑,再上真实板子测硬件,两头都要。
4.4 常见接线错误速查表
这里按我踩坑概率从高到低整理了一个速查表,建议收藏。
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 数码管完全不亮 | 共阳COM没接VCC / 共阴COM没接GND | 万用表确认公共端并正确连接 |
| 数码管完全不亮 | ULN2003 GND和MCU GND没共地 | 所有GND连在一起 |
| 数码管完全不亮 | IO口全部输出低电平,ULN2003不导通 | 按段码表给对应IO设高电平 |
| 数字乱码,某几段总是反着亮 | 段脚接错位置 | 用万用表测段位映射,重新接线 |
| 某一段不亮 | 该段限流电阻开路或虚焊 | 检查该段回路电阻和焊点 |
| 整体亮度偏低 | 限流电阻太大/电源电压不足 | 换小电阻,换大功率电源 |
| 显示闪烁,亮度不均 | 动态扫描频率太低或位选延时太长 | 提高扫描频率到1kHz以上,均匀分配位时间 |
| 切换数字时有拖影 | ULN2003关断延迟,扫描切换未消隐 | 位选切换前先关闭全部段选 |
| 数字全亮但有一两位多亮 | 共阳共阴搞反,或代码位选逻辑反了 | 确认数码管极性,调整位选逻辑 |
这个表不能解决所有问题,但覆盖了绝大多数翻车现场。第8条“切换数字时有拖影”,很多人一开始不会注意,等做时钟、秒表这类动态刷新显示时就会遇到。解决方法是段选置全0或全1做消隐,然后改位选,再开新段。稍微加几行代码的事,别偷懒。
还有一个容易忽略的点:ULN2003输入不能悬空。如果MCU还没初始化或者某个IO处于高阻态,ULN2003输入端相当于浮空,可能会随机导通,数码管就出现“乱跳”的段。解决方法是在MCU初始化时把所有控制IO先设为推挽输出低电平,或者硬件上加下拉电阻(10kΩ到GND即可)。
这次实测下来,我的体会是ULN2003驱动数码管这件事,弄懂原理比抄电路重要得多。弄清了“灌电流”和“拉电流”的区别,共阴共阳不会再是玄学,动态扫描的时序也能理清楚。如果你正要搭板子,我的建议是提前花两分钟用万用表确认一下数码管极性,电阻按220Ω起步,电源选大一点余量,先把一位静态显示调亮,再去做多位动态扫描扩大规模。最后再分享一个小经验:手边备两种数码管(共阳、共阴各一),调试时遇到反过来接的板子,也能立刻交叉验证,不会卡在原地。