
前阵子同事拿了一块板子来找我继电器一吸合单片机就重启换个三极管上去过两天又烧掉拿示波器看继电器关断瞬间的反峰直接冲到 60V。这种场景我真不是第一次见了很多新手一上来就用三极管驱动继电器或者步进电机结果要么带不动、要么烧管子、要么信号干扰复位最后折腾一圈换成 ULN2003所有问题都消停了。ULN2003 在驱动继电器、步进电机这类电感性负载上几乎是教科书级别的标准答案七路达林顿管、单通道 500mA 驱动能力、内置续流二极管、输入兼容 TTL/CMOS 电平。一个 16 脚的芯片能把三极管方案里的基极电阻、续流二极管、驱动管颗粒全部收编外围电路简化到你只需要拉一根 IO 线进去。这篇文章就手把手带你把 ULN2003 驱动继电器和步进电机的电路过一遍从内部结构到实际接线再到 STM32F103C8T6 上的完整外围电路与调试排错全程按实操来写。1. 三极管驱动方案的三个坑为什么你总在烧管子1.1 三极管开关电路能带继电器但不代表能带好用 NPN 三极管做开关驱动继电器是大多数教科书里教的方案单片机 IO 经基极电阻接到三极管基极集电极接继电器线圈发射极接地。看起来很简单但实际落地时坑一个接一个。首先是饱和条件三极管要可靠导通必须保证IB * β IC比如继电器线圈 5V/70Ω工作电流约 71mA如果管子 β 只有 100基极电流至少要 0.7mA 才能饱和考虑到温度、离散性往往要留 2 到 3 倍裕量基极电阻得按 2mA 甚至更大去算。很多初学者只想着 IO 高电平 3.3V串个 1kΩ 就完事算下来基极电流才 2mA 勉强若换成低电平 0V 就关断不了结果管子工作在放大区发热严重继电器触点抖动最后烧管。再说电平匹配。现在主控大多是 3.3V IO而继电器和步进电机系统常是 5V 或 12V。用三极管当电平转换电路没问题但基极限流电阻、上拉电阻、负载电阻的组合每个都不一样每个通道都要单独算一遍。做一路还行做八路继电器你可以想象一下 PCB 上那个电阻阵有多壮观。ULN2003 的输入侧兼容 TTL/CMOS3.3V 直接可推输出侧却可以开关到 50V等于把电平转换和功率驱动一起打包了。1.2 反电动势与续流保护烧管子的真正原因继电器线圈、步进电机绕组、电磁阀都是电感性负载流过电感的电流不能突变。三极管关断瞬间线圈会感应出一个反向电压方向是试图维持电流继续流动这个反峰电压可以很高。你拿示波器测继电器线圈两端关断瞬间经常能测到 40V 到 80V 的尖峰而 S8050、SS8550 这类小功率贴片三极管的 Vceo 通常只有 25V 左右一下就打穿。解决方式是在线圈两端并一个续流二极管注意方向二极管负极接电源正正极接三极管集电极侧也就是线圈的下端。线圈断电时感应电流通过二极管回流到电源电压被钳在电源电压加一个二极管压降而不是几十伏的高压尖峰。这个二极管就是续流二极管也是继电器驱动电路里绝对不能省的东西。但问题在于很多初学者要么忘记加要么方向接反要么二极管耐压和速度不够所以实际用起来三极管方案的可靠性很低。而 ULN2003 在内部已经给每个输出端都集成了一颗续流二极管你只需要把 COM 引脚接到负载电源上反峰问题自动解决。1.3 ULN2003 与三极管驱动电路的替代关系ULN2003 内部是七组达林顿管等效于“三极管 三极管”的组合封装总电流放大倍数可以做到 1000 倍以上输入只要很小的电流就能驱动很大的输出电流。它不是一个 1:1 简单替代方案它还把三极管方案里零散的偏置电阻、续流二极管全整合进芯片内部。这也是为什么经常能看到“单片机继电器驱动电路”“PWM 驱动电磁阀”这类项目里推荐芯片第一行就写 ULN2003。如果你现在有一个用三极管搭的继电器驱动电路可以直接把三极管、基极电阻、续流二极管全拆掉换成 ULN2003 对应的输入和输出引脚。需要注意的是ULN2003 的输出是高边灌入还是低边灌入它是低边开关灌电流负载一端接电源正另一端接输出引脚引脚导通后电流从负载流入芯片到地所以是“吸入”电流和 NPN 三极管低边开关一模一样。这种接法对 MCU 而言最安全因为负载电源和逻辑电源可以在物理上分开。2. ULN2003 内部长什么样一个芯片塞进七组达林顿管2.1 引脚定义与内部结构ULN2003 常见封装是 DIP-16 或 SOP-16引脚看起来很多其实规律性很强引脚功能说明17输入 17接单片机 IO内部已带 2.7kΩ 基极限流电阻8GND接地所有输出级的公共发射极9COM内置续流二极管的公共阳极接负载电源正1016输出 17低边输出接电感性负载下端内部每一路的结构是输入引脚先经过一个 2.7kΩ 电阻接到第一个三极管基极第一个三极管发射极又接到第二个三极管基极两个管子组成达林顿对最终输出是第二管的集电极。这样做的直接好处是输入阻抗高、驱动电流小、电流放大能力强。注意达林顿管虽然放大倍数很大但饱和压降会偏高ULN2003 在 100mA 输出时 VCE(sat) 约 0.9V 到 1.1V满负载 350mA 时会到 1.3V 左右设计时要考虑这部分功耗。关于网上总出现的“IB 大于 IC”这种奇怪说法我在这里统一解释一下。三极管在饱和区工作时由于 VCE 很小集电极电流会受到外电路限制此时再增大 IBIC 也不会明显变大甚至会出现 IC 不再等于 β×IB 的情况这时候从关系式上看“IB 大于 IC”确实可能出现在极端饱和和大功率管场景下。但对于 ULN2003 这种达林顿阵列你基本不用担心 IB 的大小内部 2.7kΩ 电阻已经把基极电流限制在毫安级输出端轻松可以做到几百毫安是远大于输入电流的。你只需要保证输入电压高于开启阈值即可。2.2 关键参数与选型心得我只挑几个项目里真正会卡住人的参数说。第一个是最大输出电流ULN2003 单路规格是 500mA但这是单个通道的极限值不是七路一起跑 500mA 还能长期工作的意思。芯片封装本身散热能力有限如果多路同时大电流比如步进电机四相同时导通建议把总功耗控制在 1W 以内。第二个是耐压输出端最高可以承受 50V也就是负载电源别超过 50V42 步进电机、24V 继电器、12V 电磁阀都绰绰有余。第三个是输入阈值ULN2003 的输入开启电压约 2V 左右3.3V 单片机和 5V 单片机都能直接驱动不需要额外电平转换。实际选型时还要注意 ULN2003 和 ULN2803 的区别ULN2803 是 8 路DIP-18 封装引脚排列类似只是多了一组输入输出。如果你的项目要驱动 8 路继电器或 8 路指示灯可以直接上 2803。如果你的负载是小功率信号继电器电流只有 30mA用 ULN2003 属于大材小用但胜在省心成本和一颗中功率三极管加二极管差不了多少。2.3 内置续流二极管的接法很多人第一步就错了ULN2003 内置续流二极管每个输出引脚内部并联着一颗二极管阳极接输出引脚阴极统一接到 COM 引脚。理论上你只要把 COM 引脚接到负载电源正续流路径就完整了。但实操中发现很多人栽在 COM 接线上为了图省事把 COM 引脚悬空或者接地结果内置二极管形同虚设线圈关断反峰照样打坏输出晶体管。正确接法以 12V 继电器为例12V 电源正极分成两条一条直接进继电器线圈的正端另一条接 ULN2003 的第 9 脚 COM。线圈负端接 ULN2003 某个输出引脚比如第 10 脚。单片机给第 1 脚高电平输出管导通线圈通电给低电平输出管关断线圈里的电流通过内部二极管回流到 12V 电源。整个回路没有经过去耦、没有经过单片机干净利落。3. 用 ULN2003 驱动继电器接线图与电源规划3.1 一个继电器的最简电路直接给最终能抄作业的电路。准备STM32F103C8T6 最小系统板一块ULN2003 一片5V 继电器一个5V 电源一路再飞几根杜邦线。单片机 PA0 输出高电平接 ULN2003 第 1 脚输入。ULN2003 第 8 脚接地第 9 脚 COM 接 5V继电器线圈一端接 5V另一端接 ULN2003 第 10 脚输出 1。继电器触点部分按你的实际负载去接比如 220V 灯泡或直流电机注意触点容量和负载电源完全独立。这样当 PA0 为高电平时输出管导通继电器吸合PA0 为低电平时输出管截止继电器释放。全程不需要在外部加任何晶体管、二极管和电阻。如果你想让板子更稳妥在 ULN2003 的输入引脚和单片机之间串一个 1kΩ 电阻这个电阻可以吸收走线上的振铃也防止万一单片机引脚配置错误时灌入过大电流实测中建议加上。3.2 多路继电器与电源分配多路继电器接线就是重复第一路的接法。ULN2003 一共 7 路你完全可以一路对应一个继电器。但电源规划不能简单并联完事。单片机供电、逻辑部分和继电器供电、负载供电最好分开比如系统用 12V 给继电器线圈用 AMS1117-3.3 把 12V 降到 3.3V 给单片机或者 5V 给逻辑、单片机再用独立的 12V 给继电器负载。为什么强调这一点继电器吸合瞬间线圈会涌入比稳态工作电流大得多的浪涌电流如果单片机和继电器共用同一个 5V 电源吸合时电源电压会被瞬间拉低STM32F103C8T6 复位阈值通常低于 2.0V 左右就可能复位。这也是很多人“继电器一吸合单片机就重启”的根本原因。我在实际项目中把继电器电源和单片机电源用两个独立 LDO 分开中间用 0Ω 电阻单点连接地问题立刻消失。ULN2003 的地和单片机的地一定要共地否则输入电平参考点不一致会导致输出乱动。共地用一根粗短线别绕远路走细线。如果你要驱动 8 路以上用两片 ULN2003 即可或者是更大封装的 ULN2803。驱动电流特别大的继电器比如 24V 线圈、工作电流超过 300mA可以把 ULN2003 的两个通道并联起来输入引脚并联、输出引脚并联两个输出管一起分担电流。这个并联操作在数据手册里是被允许的实测电流分配基本均匀注意两个输出引脚接的负载电感和续流路径保持一致即可。3.3 两路继电器接电机正反转的控制案例网上经常搜到“两路继电器接电机正反转”的电路这也是 ULN2003 非常典型的应用场景。直流电机要实现正反转本质是让电机两端的电压极性反过来。用两个继电器可以搭一个简易的“H 桥”。这里我用双刀双掷继电器不太好买简单做法是两只单刀双掷继电器加一个公共电源组合。具体思路电机有两根线 M、M-。继电器 K1 的 COM 接 MK1 的常开触点接 12V常闭触点接地继电器 K2 的 COM 接 M-K2 的常开触点接 12V常闭触点接地。当 K1 吸合、K2 释放M 接 12V、M- 接地电机正转当 K1 释放、K2 吸合M 接地、M- 接 12V电机反转。两路继电器都由 ULN2003 驱动输入分别接单片机的两个 IO。这个电路最简单但有一个隐患如果 K1 和 K2 同时吸合12V 直接短路。所以程序里必须加互锁逻辑绝不能同时给两个 IO 高电平。实际项目中我更喜欢用一个双刀双掷继电器或者专用电机驱动芯片来做正反转但如果手头只有两个继电器上面这个电路是最快能跑的。需要注意电机本身也是电感性负载虽然继电器触点是机械开关但切断电机电流时同样会有反峰建议在电机两端并联一个双向 TVS 或 RC 吸收电路。ULN2003 内置的续流二极管只针对继电器线圈本身不针对继电器触点后面带的电机负载。4. 用 ULN2003 驱动 28BYJ-48 步进电机从原理到代码4.1 步进电机工作原理与励磁顺序讲接线之前先把步进电机原理说清楚。步进电机内部有多个绕组相按一定顺序给不同相通电转子会一步步转动。以最常见的小型 28BYJ-48 步进电机为例它是四相五线永磁减速步进电机四根相线分别接内部四个绕组的一端五根线里剩下那根是公共线一般接电源正。只要按照 A-B-C-D 的顺序轮流给绕组通电转子就会按固定角度步进。这是几种常见励磁方式工作模式通电顺序特点单相整步A → B → C → D简单力矩小每步角度大双相整步AB → BC → CD → DA力矩大每步角度与单相相同1-2 相半步A → AB → B → BC → C → CD → D → DA步距角减半转动更平滑推荐使用28BYJ-48 标称“5.625°/64”意思是内部转子每步转 5.625°经过 1:64 减速齿轮后输出轴每一步是 0.0879° 左右。所以半步模式下转一圈需要 4096 个脉冲整步模式是 2048 个脉冲。很多人调程序发现电机转得巨慢以为坏了其实就是没算减速比或者时序给太快电机跟不上堵转。4.2 五线四相电机接线与驱动时序ULN2003 驱动 28BYJ-48 的电路非常标准。我自己写过的接法是28BYJ-48 的红线公共线接 5V橙色、黄色、粉色、蓝色四根相线分别接 ULN2003 的第 10、11、12、13 脚输出。ULN2003 的输入 14 脚接单片机的 PB0PB3。注意ULN2003 第 9 脚 COM 也要接 5V这点和驱动继电器一样用于内部续流二极管回收电机绕组反峰。不要小看这一步步进电机绕组也是电感PWM 切换瞬间反压很凶COM 不接电源芯片输出管很容易烧。实际模块上你经常看到“ULN2003 步进电机驱动板”板上芯片就是 ULN2003模块已经帮你把公共线、COM、电源、指示灯都连好了你只需要把 IN1IN4 接到单片机。如果自己做一个按上面这个接法就可以。驱动时序代码以 STM32 HAL 库为例定义相位表。使用 PB0PB3推挽输出速度 2MHz。半步序列写出来const uint8_t step_phase[8][4] { {1,0,0,0}, // A {1,1,0,0}, // AB {0,1,0,0}, // B {0,1,1,0}, // BC {0,0,1,0}, // C {0,0,1,1}, // CD {0,0,0,1}, // D {1,0,0,1} // DA };每调用一次就按照当前相位给四个 IO 赋值void stepper_set_phase(uint8_t phase) { HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, step_phase[phase][0] ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, step_phase[phase][1] ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, step_phase[phase][2] ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, step_phase[phase][3] ? GPIO_PIN_SET : GPIO_PIN_RESET); }在主循环里正向转就是相位 index 从 0 到 7 循环反向就是 7 到 0 循环。每次换相要加延时28BYJ-48 我实测下来1-2 相半步模式步间延时 1ms 到 3ms 之间比较稳也就是每秒约 300 到 800 步对应输出轴每秒大约 70 到 200 度太快了电机会堵转电流急剧上升芯片发烫。如果你要做速度控制用一个定时器中断每隔 N 个 1ms 就调一次相位比 delay 好得多。方向控制可以用一个全局变量 dir每次相位加减它就行。4.3 STM32F103C8T6 外围电路与代码示例现在把 STM32F103C8T6 的最小系统和 ULN2003 完整组合画一遍。STM32F103C8T6 是 3.3V 主控LQFP48 封装最小外围电路包括8MHz 晶振加两个 20pF 负载电容、复位电路、BOOT0 拉低到 GND、3.3V 电源加 100nF 去耦电容、SWD 调试接口四根线SWDIO、SWCLK、GND、3.3V。这些是外围电路的地基任何一个细节不认真都会导致程序烧写不稳定或者复位异常。ULN2003 侧电路注意三点。输入可以直连 STM32 的 GPIO但建议每路串联 1kΩ 电阻这个电阻不改变逻辑电平但能在硬件出错、引脚误配置时保护 GPIO也能阻尼信号振铃。COM 引脚必须接电机或继电器的电源正比如 5V。ULN2003 的 GND 要和 STM32 的 GND 在靠近 STM32 的地方单点相连不要让电流回流经过模拟信号区。负载电源比如电机 5V、继电器 12V不要和 STM32 的 3.3V 共用一个 LDO前面说过负载浪涌会引起 MCU 复位。代码层面用 STM32CubeMX 配置四个 GPIO 为推挽输出打开定时器 TIM2 做 1ms 中断。中断里计数每 count 到设定值就执行一次 stepper_set_phase并把相位 index 加或减 1。这样你只需要改 step_delay 变量就能平滑调速不会被 HAL_Delay 阻塞主循环。volatile uint8_t step_index 0; volatile uint16_t step_delay 2; // 每2ms一步 uint16_t step_counter 0; void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(htim2, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(htim2, TIM_FLAG_UPDATE); step_counter; if (step_counter step_delay) { step_counter 0; if (step_dir) // 1正转0反转 step_index (step_index 1) % 8; else step_index (step_index 7) % 8; stepper_set_phase(step_index); } } }关于 ENA 引脚再解释一句。很多步进电机驱动器模块比如 A4988、DRV8833上有 ENA 和 ENA- 引脚这个使能脚用来切断电流输出。ENA 通常接单片机或直接接 5VENA- 接地或由单片机控制拉低。ULN2003 模块一般没有 ENA因为它是纯相位驱动不存在使能概念。但你在查“步进电机 ENA”时大概率会用到带驱动的步进电机板子这时 ENA 接高电平、ENA- 接低电平就是使能逻辑反过来会有电机根本不转或者锁不住轴。碰到这种问题先直接短接 ENA 跳线帽排除使能脚电平问题再查时序。5. 调试记录跳动、堵转、发热与复位5.1 使能脚、PWM 频率和共地问题我在调试一个 42 步进电机和 A4988 驱动的项目时遇到过电机完全不动的情况。查了半天A4988 模块上的 ENA 跳线帽默认没有短接ENA 没接电源芯片一直处于禁用状态你给 STEP 脉冲再多也不走。后来把 ENA 脚接高电平才恢复。用 ULN2003 驱动的项目里虽然没有 ENA但如果你把 ULN2003 模块当成“普通继电器板”去接步进电机忘记把公共线接到电源也会出现类似“电机不转但芯片发烫”的问题本质都是供电链路没闭合。共地问题也是重灾区。如果你的单片机用 USB 供电ULN2003 用另外的 5V 电源供电两个电源的地必须连起来。不共地单片机输出的高电平相对于 ULN2003 的地可能只有零点几伏完全打不开达林顿管。实际排查时用万用表量 ULN2003 输入引脚和 GND 之间的电压如果发现逻辑高电平只有 1V 不到基本就是共地出了问题。5.2 电源纹波、去耦电容和接地走线步进电机转动时电流是突变的尤其每相切换瞬间电流变化率非常大会在电源线上感应出很大的纹波。如果你的 ULN2003 电源线和单片机电源线挨得很近电源纹波就会耦合到单片机的复位脚或 ADC 参考上造成莫名其妙的重启或读数乱跳。我在自己画 PCB 时习惯这样做ULN2003 的 VCC 引脚靠近芯片处放一个 100nF 陶瓷电容再并一个 10μF 电解电容两个电容尽量贴近芯片引脚负载电源入口处放一个大一点的电解电容比如 470μF位置靠近电机或继电器连接端子。接地走线也讲究最好做单点接地或星形接地。大电流回流通路和单片机地平面不要重叠太长如果实在没法分开就在 STM32 的 VSS 附近和 ULN2003 的 GND 之间用一根短粗线单点连接避免大电流流过单片机的地回路。飞线调试时我的经验是电源线用 20AWG 左右杜邦线只用来传信号不要用细杜邦线传电机电流否则线损压降足够让电机转不起来。5.3 常见问题速查表现象可能原因排查与解决继电器不吸合输入电平不够COM 没接电源继电器线圈开路量输入电压量线圈电阻确认 COM 接电源正继电器吸合后单片机复位电源共地不良或负载电源与逻辑电源共用电容电源分开负载地单点连回逻辑地负载端加 470μF 电容ULN2003 发热严重输出电流过大、多路同时高负载、PWM 频率过高检查负载电流并联通道或加散热步进电机不转公共线没接电源ENA 使能不对相线顺序接错先独占通电顺序手动测确认公共线接正电源步进电机震动大、丢步时序太快驱动电流不足电源电压跌落降低步频检查电源容量电机并联电容程序烧不进 STM32BOOT0 没拉低SWD 线过长复位电容异常BOOT0 接 GNDSWD 线尽量短示波器查 NRST关断瞬间芯片损坏COM 脚没接负载电源内置续流二极管没起作用把 COM 直接接到负载电源正极5.4 一个容易忽略的小细节通道并联扩容再分享一个实际经验。有一次我需要驱动一个 12V、线圈电阻 40Ω 的继电器工作电流到 300mA单通道 ULN2003 虽标称 500mA 但温度很高长期吸合不放心。后来把 ULN2003 的两个通道并联输入两脚并联、输出两脚并联相当于两个达林顿管共同分担电流芯片温度明显下降。并联时唯一要注意的是两个输出通道对应的输入都要给高电平代码里写两个 IO 同时拉高即可。如果你用的是 2803 做 8 路原理一样。这个并联扩容的方法用在 28BYJ-48 电机上也很实用。28BYJ-48 单相绕组电流在 5V 下会到 100mA 以上四相轮流导通单路其实够用但如果你用双相励磁模式同时导通两相电流会叠加芯片功耗上升并联通道可以多留点余量。我把四路都并联了另一组通道长期堵转测试时芯片只是温热再也没出现“冒烟”的情况。6. 从三极管到 ULN2003我的项目体验做嵌入式的这些年里我早期也用过三极管搭继电器驱动当时觉得自己很懂模电基极电阻算得很精细电路看起来也很专业。但实际项目里三极管离散性、温度漂移、反电动势击穿、 PCB 布局干扰任何一环出问题都要花大量时间去排查。后来全面切换到 ULN2003反而把精力省下来去调控制逻辑。我的体会是能用 ULN2003 的地方尽量别用三极管。它不是那种“高精尖”器件但它把你在实际项目中会遇到的大多数坑提前填平了。等到你真的要驱动几百毫安以上的负载、或者做高频 PWM 控制再回去用三极管、MOS 管和专用驱动芯片不迟。最后提醒一句无论用 ULN2003 还是三极管COM 引脚和续流路径永远不要省。这个细节是无数烧管子的教训换来的。