基于立创地文星开发板的USB-Hub电压电流表DIY全解析:硬件选型、软件配置与外壳设计
基于立创地文星开发板的USB-Hub电压电流表DIY全解析:硬件选型、软件配置与外壳设计
最近在捣鼓一些USB设备,发现手头缺一个能实时监测供电情况的工具。市面上的成品要么功能单一,要么价格不菲。于是,我琢磨着用立创的地文星开发板自己做一个。这个项目不仅是一个USB-Hub,扩展了USB接口,还能实时显示电压、电流、功率,甚至画出电流波形,非常实用。今天,我就把这个项目的完整制作过程,从硬件选型、电路设计、软件编程到外壳设计,一步步分享给大家。无论你是刚入门的嵌入式爱好者,还是想学习如何整合多个模块完成一个完整项目的开发者,相信这篇教程都能给你带来不少启发。
1. 项目概览与硬件选型
咱们先来看看这个USB-Hub电压电流表都能干什么。简单说,它就是一个“智能”的USB分线器。当你把手机、U盘等设备插上去时,它不仅能提供额外的USB接口,还能在屏幕上实时告诉你:
- 电压:当前USB口的供电电压是多少伏。
- 电流:设备消耗了多少安培的电流。
- 功率:根据电压和电流自动计算出的功率(瓦特)。
- 波形:以图形方式显示电流随时间的变化,方便观察设备启动、休眠时的电流波动。
要实现这些功能,我们需要几样核心的“食材”:
- 主控MCU:立创地文星开发板。它是整个项目的大脑,负责读取传感器数据、驱动屏幕、控制USB-Hub芯片。这块板子基于沁恒微电子的MCU,资源丰富,性价比高,非常适合DIY。
- USB-Hub芯片:CH334P。这是一颗USB 2.0的集线器芯片,可以将一路USB输入扩展为多路输出。我们的项目用它来提供额外的USB接口。
- 电流测量模块:TP181A1-CR电流感应放大器+20mΩ分流电阻。这是测量电流的关键。电流流过小电阻会产生微小电压,TP181A1-CR将这个微小电压放大,供MCU的ADC(模数转换器)读取。
- 显示屏:1.14寸SPI屏幕。用来显示所有测量数据和波形图。SPI接口占用MCU引脚少,驱动简单。
- 外壳:使用SolidWorks设计并3D打印,让作品更美观、坚固。
整个系统的测量范围是电压最大6.6V,电流最大3.3A,足够应对大多数USB设备的监测需求。
2. 电路设计详解
电路是整个项目的骨架。虽然原文提供了图片,但咱们得把关键部分拆开揉碎了讲明白。这里我结合图片和原理,给大家梳理一下各个模块是怎么连接和工作的。
2.1 电源与USB输入接口
电源是项目的起点。从原理图看,设计有两个USB母座:
- USB5 (Type-C):用于数据传输和5V供电输入。当你用一根C to C线连接电脑或充电器时,电力和数据都从这里进来。
- USB6 (Type-C):这是一个外接电源输入口。这个设计非常贴心!当你需要给大功率设备(比如移动硬盘)供电时,电脑USB口的5V/0.5A可能不够。这时,你可以通过这个口接入一个独立的、电流更大的5V电源(比如手机充电器),确保设备稳定工作。
提示:在实际焊接时,USB接口的电源引脚(VBUS)和地线(GND)一定要用粗一点的导线,或者敷铜处理,以减少线路压降,让测量更准确。
2.2 电流测量电路
这是项目的核心测量部分,咱们重点讲。
工作原理:测量电流最常用的方法就是“分流器”法。我们在主供电回路(VBUS)中串联一个阻值很小的精密电阻(20mΩ)。当电流流过时,根据欧姆定律(U=I*R),电阻两端会产生一个微小的电压差。比如流过3.3A电流时,电压差只有 3.3A * 0.02Ω = 0.066V,也就是66毫伏。这个信号太小,MCU的ADC很难直接精确测量。
所以我们需要TP181A1-CR这颗电流感应放大器。它的作用就是把这个小电压信号放大到一个MCU方便读取的范围(比如0-3.3V)。这颗芯片的增益是固定的(具体值需查数据手册),当参考电压(Vref)接3.3V时,它配合20mΩ的分流电阻,正好可以实现最大3.3A电流的测量。放大后的电压输出直接连接到地文星开发板的某个ADC输入引脚。
接线要点:
- 分流电阻:一定要选用高精度、低温漂的采样电阻(如毫欧级、1%精度),并且功率要足够(3.3A^2 * 0.02Ω ≈ 0.22W,建议选用0.5W或以上规格)。
- 放大器布线:TP181A1-CR的输入引脚(连接到分流电阻两端)的走线要尽可能对称、等长,并且远离电源等噪声源,以避免引入共模干扰,影响测量精度。
2.3 USB-Hub扩展电路
CH334P是一颗非常常用的USB 2.0 Hub芯片。它有一个上行端口(连接电脑或主设备)和多个下行端口(连接你的U盘、鼠标等)。在电路中:
- 它的上行数据线(D+, D-)连接到地文星开发板的USB接口,或者直接连接到输入USB口的数据线。
- 它的下行端口则引出到我们做好的多个USB-A母座上。
- 电路图中还为一个下行端口设计了一个LED指示灯,用于指示该端口的工作状态,这个细节很实用。
注意:CH334P需要外部提供稳定的3.3V或5V电源(具体看芯片手册),并需要连接一个12MHz的晶振。这些外围电路一定要按照数据手册推荐的值来设计,否则Hub可能无法正常工作。
2.4 SPI显示屏接口
1.14寸的SPI屏幕驱动起来相对简单。SPI通常需要4根线:
- SCK:时钟线。
- MOSI:主设备输出,从设备输入,用于发送显示数据。
- DC:数据/命令选择线。
- CS:片选线。
- 另外还需要复位线(RST)和背光控制线(BLK)。
在原理图中,这些线被连接到了地文星开发板的普通GPIO上。在软件中,我们需要模拟SPI的时序,或者使用MCU的硬件SPI外设来驱动它,后续编程部分会讲到。
3. 软件编程:ADC与DMA配置
硬件搭好了,接下来就是让地文星开发板“活”起来。核心任务就是准确、快速地读取电流放大器输出的电压值。这里用到了两个重要的外设:ADC(模数转换器)和DMA(直接存储器访问)。
为什么要用DMA?ADC转换完成会产生数据。如果让CPU不停地去查询ADC有没有数据,或者用中断让CPU来搬运数据,都会占用大量的CPU时间。而DMA就像一个“数据搬运工”,可以在ADC转换完成后,自动把数据从ADC的结果寄存器搬运到我们定义的内存数组中,全程不需要CPU参与。这样CPU就能腾出手来处理屏幕刷新、计算功率等更重要的任务。
下面我们直接看代码,我逐段加上详细注释。
3.1 DMA配置
首先,我们配置DMA通道。这个项目里,ADC有多个通道(比如测量电流的通道、测量电压的通道),我们需要为每个通道配置一个独立的DMA通道来搬运数据。
void adc_dma_config(void) { DMA_InitTypeDef DMA_InitStruct = {0}; // 定义一个DMA初始化结构体并清零 __RCC_DMA_CLK_ENABLE(); // 首先,使能DMA的时钟 // 配置第一个DMA通道 (例如对应ADC通道4) DMA_InitStruct.DMA_DstAddress = (uint32_t)ADC4_ResultBuff; // 目标地址:数据要搬运到哪里?这里是一个我们定义好的数组ADC4_ResultBuff DMA_InitStruct.DMA_DstInc = DMA_DstAddress_Increase; // 目标地址递增:每搬运一个数据,存储地址自动增加,这样就能连续存储多个采样值 DMA_InitStruct.DMA_Mode = DMA_MODE_BLOCK; // BLOCK传输模式:DMA会一次性搬运完设定的数据量 DMA_InitStruct.DMA_SrcAddress = (uint32_t)&CW_ADC->RESULT0; // 源地址:数据从哪里搬?从ADC的结果寄存器0地址 DMA_InitStruct.DMA_SrcInc = DMA_SrcAddress_Fix; // 源地址固定:因为ADC结果总是放在这个固定寄存器,所以地址不递增 DMA_InitStruct.DMA_TransferCnt = 0x6; // DMA传输次数:这里设置为6,意味着一次触发会连续搬运6个数据 DMA_InitStruct.DMA_TransferWidth = DMA_TRANSFER_WIDTH_16BIT; // 数据位宽:ADC结果是12位,用16位变量存储 DMA_InitStruct.HardTrigSource = DMA_HardTrig_ADC_TRANSCOMPLETE; // 硬触发源:由ADC转换完成事件来触发DMA搬运 DMA_InitStruct.TrigMode = DMA_HardTrig; // 触发模式:硬件触发 // 将以上配置初始化到DMA通道1 DMA_Init(CW_DMACHANNEL1, &DMA_InitStruct); DMA_Cmd(CW_DMACHANNEL1, ENABLE); // 使能DMA通道1 // 以同样的方式,配置DMA通道2、3、4,分别对应ADC的其他结果寄存器(RESULT1, RESULT2, RESULT3) // 它们的目标地址指向不同的数组:ADC3_ResultBuff, ADC2_ResultBuff, ADC1_ResultBuff // 这样,四个ADC通道的数据就能被DMA自动搬运到四个独立的数组里了。 DMA_InitStruct.DMA_DstAddress = (uint32_t)ADC3_ResultBuff; DMA_InitStruct.DMA_SrcAddress = (uint32_t)&CW_ADC->RESULT1; DMA_Init(CW_DMACHANNEL2, &DMA_InitStruct); DMA_Cmd(CW_DMACHANNEL2, ENABLE); DMA_InitStruct.DMA_DstAddress = (uint32_t)ADC2_ResultBuff; DMA_InitStruct.DMA_SrcAddress = (uint32_t)&CW_ADC->RESULT2; DMA_Init(CW_DMACHANNEL3, &DMA_InitStruct); DMA_Cmd(CW_DMACHANNEL3, ENABLE); DMA_InitStruct.DMA_DstAddress = (uint32_t)ADC1_ResultBuff; DMA_InitStruct.DMA_SrcAddress = (uint32_t)&CW_ADC->RESULT3; DMA_Init(CW_DMACHANNEL4, &DMA_InitStruct); DMA_Cmd(CW_DMACHANNEL4, ENABLE); }3.2 ADC配置
配置好DMA这个“搬运工”后,我们来配置ADC这个“测量员”。
void adc_config(void) { ADC_DeInit(); // 复位ADC外设,恢复到默认状态 ADC_InitTypeDef ADC_InitStructure = {0}; ADC_SerialChTypeDef ADC_SerialChStructure = {0}; __RCC_ADC_CLK_ENABLE(); // 使能ADC时钟 // 配置ADC基本参数 ADC_InitStructure.ADC_AccEn = ADC_AccDisable; // 不开启累加功能。累加可以对多次采样求平均,提高精度,这里先不用。 ADC_InitStructure.ADC_Align = ADC_AlignRight; // 采样结果右对齐。ADC是12位的,结果存在一个16位变量的低12位,方便读取。 ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div4; // ADC时钟分频。主时钟(PCLK)先4分频再给ADC用,确保ADC工作在合适的频率(如16MHz)。 ADC_InitStructure.ADC_DMAEn = ADC_DmaEnable; // **关键!** 使能ADC的DMA请求。ADC一转换完,就自动触发DMA搬运。 ADC_InitStructure.ADC_InBufEn = ADC_BufDisable; // 不使能内部缓冲器。高速采样时通常关闭。 ADC_InitStructure.ADC_OpMode = ADC_SingleChOneMode; // 操作模式:单次单通道?注意看后面,实际被序列通道模式覆盖了。 ADC_InitStructure.ADC_SampleTime = ADC_SampTime10Clk; // 采样时间。时间越长,采样越准确,但速度越慢。10个时钟周期是个常用值。 ADC_InitStructure.ADC_TsEn = ADC_TsDisable; // 禁止内部温度传感器,我们只用外部通道。 ADC_InitStructure.ADC_VrefSel = ADC_Vref_VDDA; // 参考电压选择VDDA(即MCU的模拟供电电压,通常3.3V)。这是ADC测量的基准,必须稳定。 // 配置ADC的序列通道扫描模式(这才是实际使用的模式) // 这个模式允许ADC自动按顺序扫描多个通道(比如通道0,1,2,3),非常适合多路信号采集。 ADC_SerialChStructure.ADC_Sqr0Chmux = ADC_SqrCh0; // 序列0扫描通道0 ADC_SerialChStructure.ADC_Sqr1Chmux = ADC_SqrCh1; // 序列1扫描通道1 ADC_SerialChStructure.ADC_Sqr2Chmux = ADC_SqrCh2; // 序列2扫描通道2 ADC_SerialChStructure.ADC_Sqr3Chmux = ADC_SqrCh3; // 序列3扫描通道3 ADC_SerialChStructure.ADC_SqrEns = ADC_SqrEns03; // 使能序列0到序列3,即总共扫描4个通道。 ADC_SerialChStructure.ADC_InitStruct = ADC_InitStructure; // 将基础配置关联进来 /* 配置为序列通道连续采样模式 */ ADC_SerialChContinuousModeCfg(&ADC_SerialChStructure); // 调用库函数,应用序列扫描连续采样配置 // 调用我们刚才写好的DMA配置函数 adc_dma_config(); ADC_Enable(); // 使能ADC模块 ADC_SoftwareStartConvCmd(ENABLE); // 软件启动ADC转换。启动后,ADC就会按照序列模式,连续、自动地扫描4个通道,并通过DMA将数据送到内存。 }配置完成后,ADC就会不知疲倦地工作。我们只需要在主循环里,定期去读取ADC1_ResultBuff到ADC4_ResultBuff这些数组里的值,它们就是各个通道最新的电压ADC值。再根据公式(实际电压 = ADC值 / 4096 * 3.3V)换算成实际电压,进而计算出电流和功率。
经验分享:调试时,可以先不用DMA,用ADC单次转换+查询的方式,读取一个已知电压(比如3.3V或GND),看看ADC值是否正确。确保ADC基础功能正常后,再上DMA,这样排查问题会更容易。
4. 结构设计与成品展示
一个好的电子项目,一个漂亮结实的外壳能给它加分不少。原作者使用了SolidWorks这款专业的三维设计软件来建模。
设计思路:
- 测量与定位:首先用卡尺精确测量所有核心部件(开发板、屏幕、USB母座、Hub芯片板)的尺寸和安装孔位置。
- 外壳设计:设计一个上下盖结构。下盖主要固定主板和屏幕,上盖则留有窗口让屏幕露出来,并开好USB接口的孔位。
- 考虑散热:由于要测量最大3.3A的电流,分流电阻和USB接口会有一定发热。可以在外壳侧面或背面设计一些通风栅格。
- 导出与打印:将设计好的模型导出为STL格式,就可以用3D打印机进行打印了。材料建议选用PLA或者ABS。
从原作者分享的实物图来看,成品非常精致。屏幕被很好地固定在外壳窗口内,四个USB-A口和一个Type-C输入口排列整齐,所有板卡都稳固地安装在内,是一个完成度非常高的DIY作品。
5. 下一步与资源
至此,一个功能完整的USB-Hub电压电流表从硬件到软件的完整路径就清晰了。你可以根据这个框架,在立创EDA上绘制自己的PCB,打样焊接,然后烧录程序。
原作者还提供了一个B站视频链接,动态展示了这个设备的工作情况,包括屏幕显示效果和波形刷新,非常直观,强烈建议观看以获取更感性的认识。
这个项目的魅力在于,它融合了模拟电路(电流采样)、数字电路(MCU、USB Hub)、嵌入式编程(ADC/DMA)和机械设计(外壳)多个领域的知识。当你亲手把它做出来,并看到屏幕上实时跳动的电流数据时,那种成就感是无与伦比的。希望这篇详细的解析能帮你成功复现或启发你做出属于自己的作品。如果在制作过程中遇到问题,不妨多查查芯片数据手册,用万用表量一量关键点电压,调试的乐趣也正在于此。
