自制双通道USB电源控制器,实现独立通断与电流监测
发布时间:2026/8/27 5:30:00 作者:尧图编辑部 阅读量:1,286

双通道USB电源控制器这件事起因特别朴素做嵌入式测试时经常要模拟某个USB外设突然断电、再重新上电的场景。手动拔插不仅效率低USB座子反复插拔还会磨损更没法精确控制断电时长。后来又要测设备在不同输入电压下的表现甚至要记录上电瞬间的电流波形普通USB Hub根本没有这种细粒度控制能力市面上的USB电源管理模块要么只做单路要么控制接口封闭想集成到自动化测试脚本里难度很大。最后实在没得选干脆自己做了个双通道的USB端口电源控制器。这个项目说白了就是两个USB端口的电源VBUS可以独立控制通断支持电流监测、过流保护、软启动同时通过USB链路和上位机通信把每个通道的实时状态上报给测试脚本。整个项目覆盖了功率链路设计、MCU控制逻辑、USB协议栈这几个方面做完之后在测试环境里跑了几个月替我省了大量重复操作。如果你也在做设备测试、电源管理或者USB外网供电相关的开发这篇分享应该能给你一些可以直接落地的参考。1. 双通道USB电源控制器到底解决什么问题1.1 和普通USB Hub供电的差异在哪很多人第一反应是用USB Hub不就行了区别其实很大。普通USB Hub的每个下行端口确实有独立的电源开关但那是为了满足USB规范里的过流保护要求开关粒度很粗大多数Hub根本不提供软件控制接口。而且标准的USB Hub会把上行端口的5V直接转给下行端口不会给你逐路通断的能力更不用说测电流。这个控制器相当于在USB电源链路上加了一个可编程的断路器功率计。它不管USB信号线只管VBUS这一路电源。USB信号线可以直接旁路过去或者在控制器内部做信号中继控制器只负责电源分配。这样做的直接好处是你可以通过上位机脚本随时断掉某一路设备电源再放行完全模拟真实世界的掉电/上电过程。1.2 典型应用场景实际使用中我主要遇到这么几类场景基本覆盖了这个控制器的主要价值自动化测试中的异常掉电模拟很多设备需要在掉电后重新上电确认能否正常恢复工作。用手动插拔做这种测试太累而且每次插拔时间误差很大。用控制器配合脚本可以实现几百毫秒到几秒的精确断电重复性极高。USB外设的功耗测量带电流检测功能后可以直接看到设备待机、运行、休眠时的电流变化。我在调试一个USB摄像头时就是用这个控制器确认了它在待机模式下并没有真正进入低功耗状态。过流/短路保护测试需要确认被测设备在抽取超过设定电流时系统能安全断开。普通USB Hub的过流保护是固定值且没有日志输出而控制器可以通过软件设定阈值并且把过流事件上报给上位机。双设备独立供电控制两个通道可以同时控制两个不同的外设独立设置电压、电流阈值和开关策略这在对比两个同类设备功耗表现时特别方便。1.3 项目功能规格我给自己定的指标是这样的项目参数输入电源USB 5V / DC 5V外接供电通道数量2路独立VBUS输出单通道额定电流最大2.5A过流保护范围100mA - 3000mA软件可配电流采样精度约±1%经校准后开关响应时间从命令下发到VM切换完成约150μs软启动时间可调默认1ms - 10ms状态反馈通道电流、电压、开关状态、过流标志上位机接口USB HID免驱动这些参数对绝大多数USB设备测试都够用了而且整个设计成本不算高核心元器件加起来也在可控范围内。2. 功率链路设计从输入保护到双通道独立开关2.1 电源输入路径与总保险整个功率链路的起点是电源输入。因为控制器本身是USB设备所以最常见的供电方式就是直接从USB上行口取5V电源。但我强烈建议留一个外部DC 5V输入接口原因很简单被测设备的功耗可能会比较大单靠USB上行口的500mA/900mA电流往往不够用而且两台设备同时满载时只靠USB口供电很容易触发主机的过流保护。输入路径上我加了这几样东西SS34肖特基二极管用途是防反接。如果外部电源极性接反它会直接阻断反向电流。虽然会带来大约0.3V的压降但考虑到这是保护电源路径的第一道关卡这点损耗是值得的。TVS管SMBJ5.0A吸收电源上的瞬态尖峰。USB线缆在热插拔瞬间容易产生电压过冲TVS能把这些尖峰钳位在安全范围内。总输入保险丝自恢复保险丝2A或3A防止板子本身短路后引发更大事故。自恢复保险丝的好处是故障排除后它会自己恢复导通不用手动换。输入路径的PCB走线宽度也很关键我算过2.5A电流、1oz铜厚至少需要大约0.8mm线宽才能保证温升不明显。实际我用了1.5mm并把顶层和底层并联铺铜安全余量更足。2.2 通道功率开关选型每个通道需要一路开关电路。我比较过两种主流方案分立MOSFET方案和集成负载开关芯片方案。分立MOSFET方案的思路是一颗P-MOS管串在VBUS上加上栅极驱动电路三极管或专门的栅极驱动器MCU通过GPIO控制开关。好处是成本低、元件少、导通阻抗可以选得极低但坏处也很明显没有内置软启动、没有内置限流、没有过热保护主控侧需要自己做电流控制算法而且一旦软件跑飞MOS没有自我保护能力。集成负载开关方案则完全不同。像TPS22918、MIC94090、RT9742这类芯片内部集成了MOSFET、软启动电路、限流电路和过热保护外部只需要加几个电容就能正常工作。MCU只需要控制一个EN引脚的高低电平芯片自己会处理开关过程中的浪涌问题。虽然单颗芯片比一颗MOS贵几块钱但综合设计成本和可靠性优势非常明显。我实际选了TPS22918这颗芯片做通道开关主要看中它的几个特性导通阻抗极低典型值约20mΩ满载2.5A时的压降才50mV左右对被测设备影响微乎其微。软启动时间可编程可外接电容设置软启动时间能有效抑制上电瞬间的浪涌电流。实测在带1000μF电容负载的情况下软启动时间设为5ms时浪涌电流可以控制在1A以内。内置过流保护有过流保护引脚可以设置限流点。快速关断功能关闭时通过内部放电电阻快速泄放输出电容上的电荷避免设备出现关断后有残压的假死状态。2.3 电流检测方案电流检测是整个控制器里精度要求最高的部分。我需要知道每个通道当前实际消耗了多少电流所以必须在功率链路上插入采样元件。我对比过三种方案方案精度压降复杂度成本低侧采样电阻普通运放中等低低非常低高侧采样电阻差分运放高低中中集成电流检测芯片INA226等很高极低低较高我最终选择了INA226它直接支持高侧采样内置16位ADC能同时读取电流和总线电压I2C接口输出。最关键的是它采样的电压是通过差分输入读取的不受地线干扰影响精度非常稳定。实际测下来在1A范围内误差可以控制在±0.5%以内。采样电阻的选择也有一番讲究。阻值越大信号越容易测准但压降和发热也越大。我用的采样电阻是10mΩ额定功率1W。按照2.5A满载计算压降约25mV功耗约62.5mW完全在安全范围内。这个压降对于5V供电来说几乎可以忽略不计也不至于影响被测设备的正常工作电压。2.4 过流保护逻辑到底在哪一层生效关于过流保护我最开始的思路是MCU通过INA226不断检测电流发现超过设定值后立刻关闭通道开关。这确实能实现限流功能但实测发现有个问题——从检测到电流超限到MCU执行关闭动作至少需要几毫秒的时间。对于真正的短路故障几毫秒足够让电源轨上的电压被拉得很低影响其他通道的工作。所以实际的过流保护做成了两级硬件级快速保护负载开关芯片自带的限流功能响应时间在微秒级能在短路瞬间限制输出电流。这个功能是芯片内置的不需要MCU参与所以无论软件正常还是卡死保护始终生效。软件级慢速保护MCU通过INA226持续监控电流如果发现持续超过设定值超过一定时间比如10ms就主动关闭对应的通道。这样既可以避免瞬时大电流引起的误动作又能实现更精细的过流控制策略。这两级保护配合后我实测过把输出直接短路负载开关会在几微秒内限制电流MCU随后会在几十毫秒内上报故障并关断通道整个过程不会对其他通道造成明显干扰。3. 控制与状态反馈MCU侧怎么处理双通道3.1 MCU选型与最小系统MCU是整个控制器的核心。我最终选了STM32F103C8T6这几乎是DIY项目里的标准答案了。选择它的原因很直白自带USB设备控制器可以直接枚举成HID设备有足够的GPIO控制负载开关的EN引脚有硬件I2C接口驱动INA226主频72MHz足够跑USB协议栈和状态机还有丰富的定时器和中断资源。引脚分配大致如下引脚功能PA11 / PA12USB D-/DPB6 / PB7I2C1接INA226PA0 / PA1GPIO输出控制通道1/2的负载开关ENPA2 / PA3GPIO输入读取过流保护标志可选PA4 / PA5LED指示通道1/2状态最小系统很简单8MHz晶振加两个20pF电容复位电路用一个10kΩ上拉电阻加0.1μF电容就行VBAT接3.3VVDDA引脚记得单独加0.1μF去耦电容。这些细节如果不注意可能会在USB枚举时出现偶发的失败现象。3.2 双通道开关控制与软启动通道开关的控制逻辑其实非常简单MCU把负载开关的EN引脚拉高通道就打开拉低通道就关闭。难点在于时机。我做了一个通道状态机每个通道有四种状态关闭、开启中、开启、关闭中。切换时并不是瞬间跳变而是有过渡过程从关闭到开启拉高EN引脚等待负载开关完成软启动然后读取INA226确认输出电压已经稳定才进入开启状态。从开启到关闭拉低EN引脚等待负载开关内部的放电电阻泄放完输出电容的电荷读取输出电压接近0V后才进入关闭状态。这套状态机看起来麻烦但能保证每次开关动作都被完整执行完毕避免出现命令发了但设备没启动或者关闭后有残压导致设备状态异常的边界情况。软启动时间的选择需要根据负载情况调整。我的默认值是5ms这个值是在多数USB设备上测试过的既能有效抑制浪涌电流又不会让设备因为上电太慢导致枚举失败。如果被测设备输入电容特别大比如有几百μF我会把软启动时间适当调长到10ms甚至20ms。3.3 电流电压采样与标定INA226的驱动代码不算复杂但有几个细节需要特别注意。INA226的I2C地址是通过A0、A1引脚的电平状态配置的。如果只用一个INA226把A0和A1都接地地址就是0x40。如果是双通道就用两个INA226一个地址0x40一个地址0x41分别对应通道1和通道2。寄存器配置有几个关键项平均模式AVG建议设置为16次平均。这样能有效滤除采样噪声又能保持足够的响应速度。转换时间没有特定要求默认值即可。电流校准寄存器这个是最重要的。INA226的电流值是通过校准寄存器配合采样电压算出来的校准寄存器的值需要根据采样电阻的实际阻值来填写。计算方式如下校准值 0.00512 /电流分辨率 × 采样电阻阻值。我用的采样电阻是10mΩ设电流分辨率为1mA那么校准值 0.00512 /0.001 × 0.01 512转换成16进制就是0x0200。标定过程是这样的先用电子负载给通道灌一个已知电流比如1.000A然后读INA226的电流寄存器计算偏差。如果采样电阻有误差这个偏差会以固定比例出现。我通常会在0.5A和2A两个点校准然后用线性拟合修正。修正后的精度实测可以在±1%以内。3.4 状态指示与按键逻辑状态指示我用的是两颗双色LED红/绿。每个通道一颗绿灯亮表示通道正常开启红灯亮表示通道被过流保护切断红绿交替闪烁表示正在执行开关动作。按键逻辑也比较简单每个通道配一个轻触按键短按切换开关状态长按3秒执行强制断电再上电的循环。这个按键功能平时不太用主要是板子在无上位机环境下单独调试时比较方便。调试串口也要预留一个。这个控制器本身走USB HID但固件调试阶段用UART打印日志会方便很多。我在板子上留了一组UART排针PA9/PA10测试时接USB转串口模块所有调试信息都从这里打出来。但要注意USB枚举正常后HID数据和UART日志是并行的不会互相干扰。4. 与上位机通信USB链路选型与协议定义4.1 CDC vs HID选哪个控制器和上位机之间需要一个通信通道。可选方案有USB CDC虚拟串口和USB HID人机交互设备两种。USB CDC的优点在于使用简单上位机把它当成普通串口打开读写数据就像操作COM口一样。缺点也同样明显Windows下需要安装驱动虽然Windows 10/11自带了CDC类驱动Linux下需要modemmanager的干扰处理而且在嵌入式系统里如果被识别成串口调试信息混杂的可能性很大。USB HID的优点在于免驱动Windows/Linux/macOS都内置HID类驱动、传输稳定、延迟可预测非常适合控制类应用。缺点是端点包最大长度只有64字节全速HID不适合大批量数据传输。但对于我这个应用场景——每次通断命令也就几个字节状态上报也就十几个字节——HID的带宽完全够用。最终我选了HID。原因很实际当这个控制器插到一台陌生电脑上时我不用先装驱动系统直接就识别了这在测试现场特别重要。4.2 标准HID描述符怎么改HID设备的关键是一份描述符Report Descriptor它定义了上位机和设备之间传输的数据格式。我的应用使用厂商自定义用途Vendor-defined Usage Page这样就不会触发系统自带的多媒体控制、鼠标键盘等误操作。一个简化版本的HID Report Descriptor如下0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x09, 0x01, // Usage (0x01) 0xA1, 0x01, // Collection (Application) 0x09, 0x02, // Usage (Vendor Usage 0x02) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8 bits) 0x95, 0x40, // Report Count (64) 0x81, 0x02, // Input (Data, Var, Abs) 0x09, 0x03, // Usage (Vendor Usage 0x03) 0x15, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x00, // Logical Maximum (255) 0x75, 0x08, // Report Size (8 bits) 0x95, 0x40, // Report Count (64) 0x91, 0x02, // Output (Data, Var, Abs) 0xC0 // End Collection这段描述符定义了一个64字节的输入报告设备发给主机和64字节的输出报告主机发给设备都是厂商自定义用途。STM32的USB HID工程可以直接基于官方例程改主要替换HID Report Descriptor数组和修改端点大小配置。需要注意端点大小必须设为64字节因为HID规范里全速设备的输入/输出端点最大数据包就是64字节。如果定义小于64字节上位机读取时可能出现数据截断。4.3 命令帧格式定义通信协议设计成固定长度为8字节的数据帧。这个长度有讲究定义为8字节后单次USB HID传输最多可以包含8个命令帧批量操作时也能用64字节满载发送。设备发送的上行帧状态上报字节内容0帧头固定0xAA1状态标志bit0: 通道1开关状态bit1: 通道2开关状态bit2: 通道1过流bit3: 通道2过流bit4: 通道1软启动中bit5: 通道2软启动中2通道1电流高字节3通道1电流低字节4通道2电流高字节5通道2电流低字节6保留7校验和前面所有字节的异或值上位机发送的下行帧控制命令字节内容0帧头固定0xCC1命令码0x01: 开通道0x02: 关通道0x03: 查询状态0x04: 设置过流阈值0x05: 设置软启动时间2通道号0x00: 全部0x01: 通道10x02: 通道23-4参数如过流阈值mA值高字节在前5-6保留7校验和这个协议简单、可靠上位机只要按帧格式组包通过HID接口写/读就行。在实际使用中我配合Python的hidapi库做了个简单的控制脚本几百行代码就实现了完整的测试流程。4.4 枚举与通信可靠性细节HID通信有个容易踩的坑设备在枚举完成后第一次通信往往会有额外延迟。这是因为主机可能在枚举阶段重新读取描述符设备固件需要确保在配置完成后能及时响应主机的IN/OUT请求。我实测后发现如果单片机固件里在处理枚举回调时添加了延时就会导致设备无法被主机正确识别。另外HID通信默认使用控制传输和中断传输。中断传输的间隔要合理设置。我把轮询间隔设为10msbInterval10这样在保持低CPU占用率的同时能有足够的交互实时性。控制命令从发出到设备端接收实测延迟大约在20ms以内完全满足测试需求。通信可靠性方面我用了帧头长度校验和的方式。校验和是前面所有字节的异或值简单但有效。如果中收到校验错误的帧会直接丢弃并置一个通信错误标志上位机可以通过查询指令得知上一次通信是否有错误发生。这种设计在调试时能省很多事。5. 实测数据与可靠性验证5.1 开关时间与电压跌落设备做好后我用示波器做了详细的实测。主要测两个指标开关延迟和负载切换时的电压跌落。开关延迟的测量方法上位机发送命令同时示波器在通道输出端抓VBUS电压上升/下降波形。从命令发出到VBUS开始上升大约150μs到VBUS稳定在4.75V以上USB规范下限大约8ms。这个时间主要受软启动控制。带负载切换时的电压跌落测试更有意思我接了一个1A恒流负载在通道开启的瞬间VBUS电压从0V爬升。由于软启动的作用爬坡曲线是线性上升而不是阶跃跳变。实测从0V到4.8V整个过程没有出现明显的过冲或振铃。关闭时VBUS会在几十微秒内降到0V关断后输出端的残压只有几十毫伏符合预期。5.2 过流保护触发一致性过流保护的一致性测试我用了电子负载做多轮测试分别设定阈值50mA、100mA、500mA、1A、2A然后把负载电流逐渐加大记录保护触发的实际电流点。设置阈值实测触发点1实测触发点2实测触发点3平均值100mA106mA104mA105mA105mA500mA515mA508mA511mA511mA1000mA1022mA1018mA1021mA1020mA2000mA2031mA2028mA2025mA2028mA可见触发点整体偏高但偏移比例稳定大约在2%-5%之间。这与INA226采样电阻的阻值误差以及负载开关限流电路的内部失调有关系。如果对精度要求更高可以在软件里做线性校正。5.3 电流采样精度验证电流采样精度的验证我用了六位半万用表做标定给通道接一个精密电阻负载用万用表测量回路电流同时读INA226的电流值。万用表读数INA226读数误差0.201A0.203A0.99%0.512A0.515A0.59%1.023A1.019A-0.39%1.512A1.504A-0.53%2.051A2.039A-0.59%整体看在0.2A到2A范围内误差控制在±1%以内。这个精度对大多数电源/功耗测试场景都够用了。如果还想更准可以换更高精度的采样电阻0.1%精度或者做多点校准我做了两点线性拟合效果已经不错了。5.4 长时间稳定与温度表现最后是可靠性验证。我把两个通道分别接了1.5A和1.8A的电子负载断电上电循环48小时每1000次记录一次状态。48小时跑完后开关次数超过5万次没有出现一次误动作也没有出现通道状态和上位机显示不一致的情况。温度方面在25℃室温下满载2.5A连续运行30分钟后用热成像仪测了板子表面温度。最高温点出现在负载开关芯片处约48℃完全在合理范围内。采样电阻的温度大约在40℃左右对精度的影响可以忽略。如果把两个通道同时跑满2.5A板子整体温升也就再高5℃散热压力不大。6. 开发过程中踩过的坑和解决思路6.1 USB枚举失败与参考地处理第一个遇到的大坑是USB枚举偶发失败。现象是板子插到电脑上有时候能识别有时候识别不了没有任何规律。排查了很久最后发现是参考地问题——我调试时同时插了USB线和USB转串口模块两个模块连到了同一台电脑但它们在电气上共用了不同的地线回路。当同时插入时USB D/D-信号线上的共模电压被瞬间抬高导致主机无法正常完成枚举握手。解决办法有两个一是调试时尽量只插一种USB连接要么插HID通信线要么插UART调试线减少地环路二是在PCB设计时把USB信号线的参考地平面上多加几颗0.1μF去耦电容并尽量让D/D-差分对走线等长、靠近减少信号完整性风险。6.2 热插拔浪涌损坏负载开关的风险还有一个比较棘手的坑输出端接了电容性负载比如被测设备输入有几百μF电容在通道开启瞬间负载开关要输出一个非常大的浪涌电流给电容充电。如果软启动时间设置太短这个浪涌电流会超过负载开关的限流保护点导致芯片被误触发保护甚至永久损坏。第一次遇到时我调试一个摄像头模组上电瞬间系统直接死机。检查后发现模组输入端有470μF电容而我的软启动时间默认只有1ms。1ms内给470μF充电平均电流接近2.35A远超负载开关的限流点。后来我把软启动时间调整到5ms浪涌电流稳定在700mA左右。所以如果你也遇到类似问题建议先看一下负载侧的等效电容再决定软启动时间不要一上来就追求极快的开关速度。6.3 双通道同时开关时的地弹干扰双通道同时开关时两个负载开关同时导通瞬间抽取大电流PCB上的地线会产生明显的电压波动这个现象俗称地弹。地弹直接影响INA226的采样精度——你会发现通道1的电流值会莫名跳动而自己并没有改变任何负载。我的排查思路是这样的先怀疑是电源纹波于是把示波器探头直接点到INA226的电源脚发现输入电源纹波确实有但幅度不大不会导致这么大的读数波动。后来把INA226的模拟地和数字地分开布线发现问题依旧。最后发现问题是输出端回流路径太靠近采样电阻导致采样信号线上耦合了大量噪声。解决办法是把两个通道的功率回路完全独立走线避免共用地线路径过窄同时给每个通道的输出电容单独加大容量。改版后双通道同时开关时电流读数波动从原来的±20mA降到了±2mA以内问题彻底解决。6.4 采样电阻位置引起的电压采样偏差最后一个是关于采样电阻位置的坑。INA226支持高侧采样采样电阻放在VBUS正线上这个位置决定了采样到的电压是设备端的供电电压而不是输入端的电压。听起来很合理但实际操作中如果输出端电源线走线过长采样电阻到输出端口之间的走线也会有压降导致读数偏低。解决办法是缩短采样电阻到输出端口之间的走线宽度和长度我目前用的是Kelvin四线采样在采样电阻两端各引出两根线分别接INA226的电流采样正负极和电压采样正负极这样采样电压就只反映电阻本身的压降不受其他走线影响。实测在2A负载下电压读数偏差从30mV降到了2mV以内精度提升非常明显。自己在实际做这套设计时的体会是USB电源控制器这种项目看起来简单但真正把精度、可靠性、底层通信协议都做好需要考虑的细节非常多。如果你只是想要一个能用的小工具现成模块拼一拼也够但如果跟我一样需要把它集成到自动化测试环境里自己动手从功率链路到通信协议完整做一遍你学到的会远超预期。这个控制器后来还被扩展到了四通道版本控制逻辑和通信协议基本没怎么改只是加了两个同样的通道电路。以后如果你想做多路电源监控这套架构可以直接复用。