基于ESP32-S2与HUSB238的USB PD电源监控与电压选择器制作
1. 项目概述与核心价值如果你和我一样工作台上堆满了各种需要不同电压供电的小玩意儿——从3.3V的传感器、5V的Arduino到12V的风扇、15V的电机驱动板——那么你一定也受够了在成堆的“墙插式”电源适配器里翻找的麻烦。USB-C Power DeliveryPD协议的出现本应是个福音它允许一个电源输出多种电压。但现实是大多数PD充电器都是“被动响应”模式只有设备主动请求它才会输出对应的电压。我们能不能反过来让我们来主动命令PD电源让它输出我们想要的任意电压呢这就是今天这个项目的核心打造一个完全由你掌控的“PD电源指挥官”。这个“PD电源监控与电压选择器”不仅仅是一个简单的转接头。它的核心是一个基于ESP32-S2微控制器的智能中继站通过I2C总线连接了两块关键芯片HUSB238 PD协议芯片负责与你的PD充电器“谈判”索取指定的电压5V、9V、12V、15V、20VINA219高侧电流传感器则实时监测流向后端设备的电流让你对功耗一目了然。所有状态和操作都通过一块集成的TFT屏幕和三个物理按键完成交互直观得像一台微型仪器。我选择ESP32-S2和CircuitPython来构建它是经过深思熟虑的。ESP32-S2提供了USB-OTG功能和充足的GPIO是连接USB主机电脑和设备PD芯片的桥梁。而CircuitPython则将开发门槛降到了极致——你不需要复杂的IDE、编译工具链只需像管理U盘文件一样编辑代码极大地加速了原型迭代和调试过程。整个项目采用“免焊接”设计所有模块通过STEMMA QT/Qwiic接口的4芯电缆连接再固定在一块“Swirly Grid”铝制网格板上最后可以塞进一个3D打印的外壳里。从零到整机运行最快半小时就能搞定。这个工具的价值远不止于整洁工作台。对于嵌入式开发者它是一个极佳的电源测试平台可以快速验证设备在不同电压下的稳定性对于硬件创客它能驱动那些没有内置PD协议、但又需要高电压的电机、灯带或放大器对于学习者它则是一个活生生的、可触摸的PD协议教学模型。接下来我将拆解从硬件选型、组装、编程到实际使用的每一个细节并分享我在搭建和调试过程中踩过的坑和总结的技巧。2. 硬件深度解析与选型思路一个项目的成败硬件选型是基石。这个项目用到的核心部件不多但每一个的选择都暗含了针对特定需求的考量。理解“为什么是它”能帮助你在未来自己的项目中做出更明智的决策。2.1 核心大脑为什么是ESP32-S2 Reverse TFT Feather主控板的选择决定了项目的功能上限和开发体验。这里用的是Adafruit的ESP32-S2 Reverse TFT Feather。这个名字有点长我们拆开看ESP32-S2这是乐鑫ESP32家族中专注于USB功能的一款芯片。它原生支持USB-OTG这意味着它既可以作为USB设备比如被电脑识别为一个串口也可以作为USB主机去控制其他USB设备。在我们的项目里虽然我们是通过I2C控制HUSB238但ESP32-S2强大的处理能力和丰富的外设为未来扩展比如直接解析PD协议报文留足了空间。相比之下普通的Arduino Uno或ESP8266缺乏这种灵活性和性能储备。Reverse TFT这块板子最妙的设计在于它将一块240x135像素的彩色TFT屏幕直接集成在了板子背面即“Reverse”并通过一个FPC排线连接。这带来了两个巨大优势第一它节省了额外的屏幕模块、杜邦线和宝贵的I/O口第二Adafruit为其提供了成熟的displayio图形库支持在CircuitPython中只需几行代码就能驱动显示极大简化了开发。Feather生态Feather是Adafruit定义的一种板型标准包含了标准的引脚排列和STEMMA QT接口。这意味着它与成千上万的Feather扩展板、STEMMA QT传感器即插即用扩展性极强。板载锂电充电电路和JST PH电池接口也意味着这个项目可以很容易地改造成一个便携设备。实操心得关于“已知良好”的数据线项目文档里轻描淡写地提了一句“使用已知良好的数据同步线”这绝对是血泪教训。我最初用了一根只能充电的USB-C线给Feather刷固件电脑死活识别不出FTHRS2BOOT盘符折腾了半小时才怀疑到线上。务必准备一根全功能的USB-C数据线。一个简单的判断方法是这根线之前必须成功用于手机数据传输或高速外接硬盘。2.2 PD协议谈判官HUSB238芯片的角色与局限HUSB238是这个项目的“魔法”来源。它是一颗USB PD Sink受电端协议芯片。它的工作逻辑是模拟一个符合PD协议的设备与电源Source进行通信并请求电源输出其能力范围内的某个特定电压。工作原理HUSB238通过USB-C接口的CC配置通道引脚与PD电源通信。它内部集成了PD协议物理层和部分链路层我们只需要通过I2C告诉它“我要9V”它就会自动完成复杂的报文交互过程远比我们自己用微控制器模拟PD协议要可靠和简单。I2C与电阻模式这颗芯片支持两种配置模式I2C动态控制和电阻分压固定设置。我们这个项目充分利用了I2C模式实现了软件动态切换电压。如果你想要一个更简单的、固定电压的版本也可以使用其电阻模式通过焊接不同阻值的电阻来设定电压但这失去了灵活性。重要限制HUSB238是一个Sink芯片它只能请求电力不能提供电力。因此你必须为整个系统ESP32-S2和外围电路提供独立的供电。这就是为什么项目中Feather需要另一根USB-C线单独供电。HUSB238输出的PD电压仅供给其VOUT引脚连接的后端负载通过INA219。千万不要试图从HUSB238取电给微控制器供电这会导致整个系统不稳定甚至损坏。2.3 电力哨兵INA219电流传感器的精度与接线INA219是一个基于I2C的高侧电流/功率监测芯片。所谓“高侧”意味着它串联在电源正极V和负载之间进行测量而不是在接地回路中。这种方式的好处是负载的地线仍然是干净的“系统地”不会引入测量电阻的压降。测量原理芯片内部有一个精密的毫欧级采样电阻通常为0.1Ω。电流流过时会产生一个微小压降INA219内部的放大器将这个压差放大再由ADC转换为数字值通过I2C读出。其库函数已经帮我们完成了从原始读数到毫安、毫瓦的换算。量程选择我们使用的Adafruit INA219 breakout默认配置支持±3.2A的测量范围对于大多数桌面电子设备如开发板、小型电机、灯带来说绰绰有余。如果测量更大电流需要注意其分流电阻的功率损耗PI²R避免过热。接线顺序原理图上的连接顺序HUSB238.V - INA219.V- - INA219.V - 负载.V至关重要。这确保了电流先从PD输出端流出流经INA219的采样电阻再到达负载。接地则是HUSB238.GND直接连到负载.GND。务必对照图示仔细连接接反了可能无法测量或损坏传感器。2.4 其他关键部件网格板、线缆与外壳Swirly Grid铝制网格板这不仅仅是一个好看的底板。它的标准化孔距0.1英寸完美匹配所有Feather和Breakout板上的安装孔。使用M2.5的铜柱和螺丝可以构建一个坚固、整齐且利于散热的机械结构。在组装时务必先连接STEMMA QT线再固定板子因为板子装上后侧面的接口会被铜柱挡住极难操作。防水DC电源线组选用5.5/2.1mm桶形插头的公母对线。这种接口在桌面电源设备中极为通用。将母头固定在网格板或外壳上作为系统的输出端口然后用配套的公头线连接你的负载设备非常方便。3D打印外壳外壳文件提供了良好的保护和美观性。打印时建议使用PETG或ABS材料强度更好。外壳上的开孔精准对应了USB-C口、DC输出口和屏幕。四个按钮柱需要对准Feather板上的复位键和两个用户按键D1 D2可能需要根据打印机的精度进行微调。3. 硬件组装与布线实战有了对硬件的深刻理解组装过程就是按图索骥。但“知道”和“做对”之间往往隔着一些容易忽略的细节。下面是我在组装过程中记录的详细步骤和关键检查点。3.1 分步组装流程与技巧准备所有部件将ESP32-S2 Feather、INA219 breakout、HUSB238 breakout、Swirly Grid板、M2.5铜柱6mm和12mm、螺丝、螺母、STEMMA QT线、DC电源线组、开关等全部摆放在面前。建议准备一个小磁铁盘来盛放螺丝防止丢失。预连接线缆关键步骤取一根STEMMA QT线一端插入Feather板上的I2C接口通常标有SCL/SDA另一端插入INA219 breakout板上的QT接口。再取一根STEMMA QT线连接INA219 breakout板上的另一个QT接口与HUSB238 breakout板上的QT接口。至此三块板子通过I2C总线红-3.3V 黑-GND 蓝-SDA 黄-SCL串联在了一起。先接线后固定的原则在这里第一次体现。固定核心板卡到网格板在ESP32-S2 Feather的四个安装孔上穿上4个**M2.5 12mm F-F母对母**的六角铜柱从网格板背面用短螺丝固定。12mm的高度为屏幕和板子底部的元件留出了充足空间。将INA219和HUSB238 breakout板分别用M2.5 6mm F-F铜柱固定在网格板上。位置参考文档中的图示主要考虑线缆走线的顺畅和避开外壳内部结构。通常HUSB238放在靠近预期DC输出口的位置。连接功率线路这是整个硬件连接中最需要细心的一步关系到测量准确性和设备安全。第一步将DC输出母座的正极中心孔用导线连接到INA219 breakout板的V端子。第二步将INA219 breakout板的V-端子用导线连接到HUSB238 breakout板的V端子。第三步将DC输出母座的负极外侧和HUSB238 breakout板的GND端子用导线共同连接到网格板上的一个接地桩如果有或者直接用导线连接在一起。检查逻辑电力流向必须是PD电源 - HUSB238.V - INA219.V- - INA219.V - DC输出正极 - 负载 - DC输出负极 - HUSB238.GND形成一个完整回路。安装DC输出插座与开关将DC母座固定在外壳预留孔或网格板边缘。将串接好的电源开关接入DC输出公头线中。这样你可以在不断开PD电源的情况下直接开关后端负载非常方便调试。总装与合盖将所有线缆用扎带或线卡整理避免杂乱和干涉。将组装好的网格板组件对准外壳底座的卡槽轻轻按压使其“咔哒”一声卡入位。确保两个USB-C口和DC输出口从对应的开孔中露出屏幕正面朝上。盖上顶盖上紧螺丝如果设计有螺丝孔。检查三个按键Reset D1 D2是否都能被外壳的按钮柱顺畅按压。3.2 组装过程中的常见陷阱与排查陷阱一I2C通信失败。上电后屏幕无显示或代码报错找不到设备。排查首先确认STEMMA QT线是否插反虽然防呆但用力过猛也可能插坏。用万用表检查I2C总线的电压SCL和SDA线对GND应有约3.3V电压。可以在CircuitPython的REPL中运行import board; import busio; i2c busio.I2C(board.SCL board.SDA); i2c.scan()来扫描I2C地址。正常情况下应扫描到两个设备地址INA219和HUSB238。地址冲突Adafruit的模块通常地址不同但如果你混用了其他厂家的模块需确认地址。INA219的地址可通过板载跳线改变HUSB238的地址是固定的。陷阱二电流读数始终为0或异常。排查99%的问题出在功率线接续顺序上。请严格按照HUSB238.V - INA219.V- - INA219.V - 负载的顺序检查。用万用表通断档沿着这个路径一点一点测量确保连接无误。负载未接或过轻INA219的精度很高如果负载电流小于1mA可能显示为0。接上一个LED灯或一个小电机再试试。INA219溢出如果代码中打印出“Internal Math Overflow Detected!”说明电流超过了芯片的量程或计算范围。检查负载是否短路或尝试更换量程更大的分流器需要修改库文件配置不推荐新手操作。陷阱三PD电源无输出或电压不对。排查确认给HUSB238供电的USB-C PD电源确实支持PD协议并且功率足够建议65W及以上以支持20V输出。一些老旧的或低质量的USB-C充电器可能只支持5V固定输出。检查连接确保连接HUSB238的USB-C线是全功能线支持数据传输和PD通信。纯充电线无法进行CC通信。查看代码打印信息在串口监视器中代码启动时会打印PD电源提供的可用电压列表。如果列表为空或只有5V基本可以断定是电源或线缆的问题。4. CircuitPython环境部署与项目代码精讲硬件是躯体软件是灵魂。CircuitPython让为这个项目编写灵魂变得异常简单。我们不仅要把代码跑起来更要理解每一行代码背后的意图。4.1 固件烧录与开发环境搭建下载固件访问 circuitpython.org 在搜索框输入“ESP32-S2 Reverse TFT Feather”找到对应的板子页面下载最新的.uf2格式CircuitPython固件文件。进入引导加载模式用数据线连接Feather和电脑。双击复位按钮Reset。这是关键操作第一次按下后板载RGB LED会亮起紫色在它还是紫色的时候迅速再按一次复位键。成功后LED会变为绿色电脑上会出现一个名为FTHRS2BOOT的U盘。这个“双击”节奏需要练习几次。如果只出现紫色然后变回其他颜色说明第二次按晚了。多试几次找到感觉。拖入固件将下载好的.uf2文件拖入FTHRS2BOOT盘符。盘符会自动消失稍等片刻会出现一个新的名为CIRCUITPY的盘符。这表明CircuitPython系统已经成功运行。选择代码编辑器你可以使用任何纯文本编辑器如VS Code Sublime Text Notepad。Adafruit官方推荐Mu Editor因为它内置了串口监视器和代码检查对初学者非常友好。在Mu中你可以直接打开CIRCUITPY盘符下的code.py进行编辑保存后代码会自动重启运行。4.2 项目代码逐层解析项目的核心代码并不长但结构清晰充分体现了CircuitPython面向对象的简洁性。我们来分段解读第一部分导入与初始化import time import board import keypad import displayio import terminalio from adafruit_display_text import label from adafruit_display_shapes.rect import Rect import adafruit_husb238 from adafruit_ina219 import INA219这里导入了所有必需的库。adafruit_husb238和adafruit_ina219是Adafruit为对应硬件提供的专用驱动库封装了所有复杂的I2C通信细节让我们可以用简单的属性如pd.voltage和命令进行操作。displayio和adafruit_display_text是管理TFT屏幕显示的核心它采用“显示组Group”的概念将文字、图形等元素作为对象添加到组中再统一刷新到屏幕。第二部分硬件对象实例化i2c board.I2C() tft_d0_button keypad.Keys((board.D0) value_when_pressedFalse pullTrue) tft_buttons keypad.Keys((board.D1 board.D2) value_when_pressedTrue pullTrue) ina219 INA219(i2c)board.I2C()自动使用ESP32-S2上默认的I2C引脚通常是GPIO8-SDA GPIO9-SCL。keypad.Keys是CircuitPython 7.x之后推荐的新按键库比老的digitalio更高效。这里创建了两个按键对象tft_d0_button对应板载的复位键也被复用为用户按键按下时值为Falsetft_buttons对应另外两个用户按键D1和D2按下时值为True。pullTrue启用了内部上拉电阻。第三部分PD控制器初始化与错误处理try: pd adafruit_husb238.Adafruit_HUSB238(i2c) RUNNING True PLUGGED True except ValueError: print(plug in a USB C PD cable then press reset) RUNNING False PLUGGED False # ... 显示警告文字 ...这是代码中非常健壮的一处设计。尝试初始化HUSB238对象时如果I2C通信失败通常是因为PD电源未连接或线缆有问题会抛出ValueError异常。代码捕获这个异常将运行状态RUNNING和连接状态PLUGGED设为False并在屏幕上显示警告信息而不是让程序崩溃。同时程序会卡在while not RUNNING:的循环里直到你插上PD电源并按下复位键重新运行程序。第四部分主显示界面构建这部分代码定义了颜色创建了背景、文字标签电压值、电流值、按钮提示等所有显示元素并将它们添加到一个显示组中。anchor_point和anchored_position属性用于精确定位文本。第五部分主循环逻辑——事件驱动与状态更新while True: # 1. 检查“设置”键顶部按键 tft_d0_button_event tft_d0_button.events.get() if tft_d0_button_event and tft_d0_button_event.pressed: try: pd.voltage voltages[v] # 核心命令请求PD电源切换电压 voltage_label.text str(voltages[v]) V PLUGGED True except OSError: # 如果设置失败如PD电源断开显示错误 PLUGGED False # 2. 如果PD连接正常检查“升压/降压”键 if PLUGGED: tft_buttons_event tft_buttons.events.get() if tft_buttons_event and tft_buttons_event.pressed: if tft_buttons_event.key_number 0: # D1键电压 v (v 1) % len(voltages) # 循环递增索引 voltage_label.color TXTCOL_DIM # 电压值变暗表示待确认 voltage_label.text [ str(voltages[v]) V] # 加括号提示 # ... 处理D2键电压-逻辑类似 ... # 3. 更新电流读数 current ina219.current current_label.text str(abs(int(current))) mA time.sleep(0.2)事件驱动使用keypad库的events.get()方法非阻塞地检查按键事件避免了低效的轮询和延时。状态机设计通过PLUGGED变量区分系统是否已成功连接PD电源。只有在连接正常时电压调节按钮才生效。这防止了在无PD电源时误操作导致程序出错。两步确认设计按下D1/D2键电压值会变暗并加上方括号如[12V]提示这是一个“待选”状态。只有按下顶部的“设置”键才会真正向HUSB238发出pd.voltage voltages[v]命令完成电压切换。这是一个很好的防误触设计。电流读取ina219.current直接返回以**安培(A)**为单位的浮点数代码中取其绝对值并转换为整数毫安显示。ina219.overflow用于检测传感器内部计算是否溢出电流过大。4.3 代码的优化与扩展思路原版代码已经非常实用但我们可以基于它进行一些增强增加功率显示INA219也能测量总线电压和计算功率。可以在屏幕上增加一行显示power ina219.power单位瓦特。增加电压锁定/循环模式切换当前电压选择是循环的从20V再按“”会回到5V。可以增加一个长按功能切换为“锁定”模式到达最高或最低电压后不再循环。记录能耗利用ESP32-S2的RTC和文件系统可以定时将电压、电流、功率数据写入CSV文件用于分析设备的功耗曲线。Web界面控制利用ESP32-S2的Wi-Fi功能可以创建一个简单的Web服务器通过手机或电脑的浏览器远程查看读数、切换电压将其升级为网络化智能电源。5. 使用指南、故障排查与进阶技巧设备组装完成代码也运行起来了现在让它开始为你工作。这里有一些标准操作流程和可能遇到的问题的解决方案。5.1 标准操作流程供电准备两个USB-C电源。一个建议5V2A或以上连接到ESP32-S2 Feather的USB-C口为整个控制系统供电。另一个必须支持PD协议建议65W多口充电器连接到HUSB238 breakout板的USB-C输入口。连接负载将你想要供电的设备通过DC 5.5/2.1mm桶形插头线连接到本设备的DC输出母座上。确保负载的电压需求在PD电源支持的范围内例如不要试图用一个最大12V的负载去请求20V电压。开机与观察给两个USB-C口上电。屏幕会先初始化然后尝试与PD电源握手。如果成功屏幕顶部会显示“Power Deliverer”中间显示当前设定的电压初始为PD电源提供的第一个可用电压通常是5V下方显示电流初始为0mA。选择电压按下设备左侧靠下的两个按键对应D1和D2来浏览可用的电压。屏幕上的电压值会变暗并带括号表示已选中但未应用。应用电压按下顶部按键复位键来确认并应用选中的电压。此时电压值会高亮显示HUSB238会向PD电源发送请求。如果成功你的负载设备将获得新的电压。同时屏幕下方的电流读数会开始变化反映负载的实际功耗。开关负载你可以使用串联在DC输出线上的物理开关随时断开或接通负载而无需重新协商PD电压。5.2 常见问题速查表问题现象可能原因排查步骤与解决方案屏幕不亮无任何反应1. Feather主板未供电。2. CircuitPython固件未正确刷入。3. 屏幕排线松动。1. 检查连接Feather的USB线缆和电源。2. 尝试双击复位键进入引导模式重新拖入UF2文件。3. 小心地重新插拔屏幕排线。屏幕显示“plug in USB C PD cable”警告1. HUSB238的PD电源未连接或电源不支持PD。2. 连接HUSB238的USB-C线是纯充电线。3. I2C线缆接触不良。1. 更换一个已知支持PD协议的充电器和全功能数据线。2. 检查STEMMA QT线是否插紧在REPL中运行i2c.scan()查看能否发现HUSB238的地址通常是0x08。电压无法切换按下设置键无反应1. PD电源不支持所选的电压档位。2. 代码中voltages数组为空或异常。3. 按键接触不良或接线错误。1. 查看串口输出确认PD电源返回的可用电压列表是否包含你想要的电压。2. 检查D0 D1 D2按键的定义和接线用万用表测量按键按下时引脚电平变化。电流读数始终为01. 功率线连接顺序错误。2. 负载未工作或电流极小。3. INA219模块损坏或地址错误。1.重点检查HUSB238.V - INA219.V- - INA219.V - 负载的连接链。2. 接上一个有明确功耗的负载如LED加电阻。3. 在REPL中检查INA219对象能否创建尝试读取ina219.shunt_voltage分流电压正常应有微小读数。电流读数飘忽不定或为负值1. 负载电流非常小处于传感器噪声区。2. 接线接触电阻过大或松动。3. 负载是感性或容性设备电流波动大。1. 这是高精度测量中的正常现象可考虑在代码中对电流读数做滑动平均滤波。2. 拧紧所有接线端子确保接触良好。3. 对于电机等设备读数波动是正常的。设置高电压如20V时PD电源指示灯闪烁或断开1. PD电源功率不足。2. 负载瞬间电流过大触发电源过流保护。3. 线缆质量差压降过大或电阻过高。1. 使用功率更高的PD电源如100W。2. 确保负载在目标电压下的工作电流在电源和线缆的额定范围内。对于电机等有启动电流的负载需留足余量。3. 使用更粗、质量更好的USB-C和DC输出线缆。5.3 安全注意事项与进阶技巧安全第一本项目涉及市电转换的直流电源。虽然PD电源本身是隔离的但输出最高可达20V足以对人体造成不适或损坏敏感电路。操作时务必谨慎避免输出端短路。在切换电压前务必确认你的负载能够承受该电压。散热考虑当输出大电流如12V3A时HUSB238和INA219芯片会有一定发热。如果将其封闭在3D打印外壳内长期高负荷运行建议在外壳上增加通风孔或考虑在芯片上粘贴小型散热片。校准电流传感器可选INA219出厂已校准但如果你对精度有极致要求可以通过测量一个已知的精确电流如用万用表测量100mA恒流负载然后对比INA219读数在代码中乘以一个校准系数进行微调。利用串口调试在Mu Editor或任何串口终端如PuTTY 波特率115200中打开连接到Feather的串口你可以看到所有的调试信息检测到的可用电压、当前设置、电流读数以及任何错误信息。这是排查问题最强大的工具。代码版本管理CIRCUITPY盘符下的code.py是你的主程序。建议在电脑上建立一个项目文件夹将code.py和使用的库文件位于CIRCUITPY/lib/下备份起来。这样在实验新代码时可以快速回退到稳定版本。这个“PD电源监控与电压选择器”就像给你的工作台增加了一个智能、可视化的电源管理器官。它把隐藏在USB-C接口背后的数字谈判过程变成了可视、可控的物理交互。从简单的为单片机供电到测试电机在不同电压下的转速再到监测某个电路的静态功耗它的应用场景只受你的想象力限制。更重要的是通过亲手搭建它你不仅得到了一个工具更深入理解了USB PD协议、I2C通信、高侧电流测量以及事件驱动的嵌入式编程这些核心概念。这正是创客项目的魅力所在——在解决实际问题的过程中把知识变成实实在在的能力。