一、项目信息
1.1 项目简介
XM Power Kit 是本人过往作品“xuemeng V6” 的续作,进行了极大的强化。它基于STM32F407VET6,在极小的体积下,实现了数控电源、示波器、波形发生器、万用表功能的四合一,简单参数如下:
- 数控电源:基于LM5176,可实现5-32V输入,1V-36V 10A输出,最大300W。
- 示波器:基于STM32的ADC,可实现8bit、2.4Msps、500K带宽、4K存储深度,带触发。
- 信号源:基于STM32的DAC,1Msps,最大50Khz,可实现±5V任意偏置输出。
- 万用表:可实现±36V电压(0.5%)、±3A电流测量(0.5%)、0~30K电阻测量(3%)、二极管档(10%)
下面是设备参数:
- 输入方式:支持 PD 诱骗 + DC 输入,可实现双电源输入。
- 输出方式:香蕉头 + TypeC,其中 TypeC 也可用作拓展接口。
- 显示配置:2.8寸 IPS 屏,ST7789,40pin接口。
- 其它参数:具备丰富的调试/拓展接口,也可以连接上位机进行细节配置。
1.2 项目背景
相比于V6来说,做V7的目的就简单多了,那就是爱好。除此之外,V6有许多地方还显得相当稚嫩,V7则是来补过这些缺陷,顺便学习并实操一些新知识(比如LVGL和RTOS)。
更要说的话,就是我觉得我要用的工具很多,但性能要求不高;宿舍的桌子就那么大点,大功率电源、示波器、万用表….堆得满满的很不方便;此外,每次回家也要带上这一堆工具舒适不方便,所以我就想着造一个小体积 + 性能够用 + 多功能 的工具。于是,XM Power Kit (V7) 诞生了~
除此之外,您也可以前往 雪萌の小窝 进行深度学习,我会在里面更新更详细的技术细节~
或者前往984946310 (答案:雪萌) 进行交流解惑~
群被恶意举报炸了,先进这个:1082694166,答案同上~
1.3 前提提要
- 本项目是6层板,且元件较为密集(基本是0603的),复刻有难度。(见第8章节)
- 本文档建议您详细阅读,有很多注意事项已经写明了,可以避免踩坑。
- 仔细阅读,能避免许多不必要的困惑~
- 求求你了认真看文档!不然我这么多字不白写了😢😢😢
1.4 核心参数
| 类别 | 型号/参数 | 说明 |
|---|---|---|
| 主控MCU | STM32F407VET6 | Cortex-M4,512K Flash,128+64K RAM,主频 144MHz |
| 电源控制芯片 | LM5176 | 同步 4 功率管 Buck-Boost 控制器 |
| 输入/出 检测 | INA226 + TP181 | 输入使用INA226,输出使用电阻分压 + TP181 + ADC |
| PD诱骗 | CH224A | 支持诱骗PD3.2 的 28V 5A 140W |
| 显示屏 | 2.8寸IPS(ST7789) | 请使用40pin版本,购买后面我推荐的 |
| 交互 | SIQ-02FVS3 + 按键 | 使用旋转编码器 + 按键 的组合 |
| 调试接口 | CDC/SWD/蓝牙 | 支持串口下载调试、SWD 调试、蓝牙通信调试 |
| PCB规格 | 87mm * 60mm | 绝大部分采用0603封装,少部分0402,方便焊接 |
BOM表可以使用附件里的,我进行了适当的标注,希望能帮到你!
注意:BOM分了多个sheet,记得都看哦,以免漏东西~
1.5 开源协议
本工程依据CC-BY-NC-SA 4.0知识共享许可协议,请勿用于商用,转载时请标明出处。
开源代码地址:Github仓库
开发软件:CLion + STM32CubeMX
当然,您也可以直接下载附件中的源码~(不建议,附件可能更新不及时)
二、目标 & 日志
- [x] 完成 自校准 功能 (26/4/16)
- [x] 完成 上位机 功能 (26/4/19)
– [ ] 完成 数控电源_曲线显示 功能
- 26/4/23 : 更新了一个示例面板
- 26/4/14 : 完成了示波器APP,完成了示波器的校准编写。
- 26/4/12 : 更改示波器硬件,缓解运放偏置电流Ib问题。
- 26/3/14 : 完成了万用表APP,完成万用表的校准编写。
三、设计架构
3.1 整体简介
PCB采用6层板设计,阻容95%都是0603,方便焊接。板子正面基本都用于 数控电源 功能,右上角用于 示波器 & 波形发生器 功能,下方则用于转换整板的供电;板子背面则是数字&模拟,负责运算、信号处理 等等功能。

3.2 电源架构
电源的稳定性非常重要!本项目又涉及“数字”“模拟”“功率”地的供电,因此较为复杂,下一节有详细讲解:

3.3 硬件架构
硬件的设计更加的复杂,由于空间有限+功能众多,这就意味着香蕉头输出需要复用,下面是架构简图,具体的电路分析会在第四部分进行详细讲解~

3.4 外壳架构
外壳文件与装配方式见本文档附件~


在此感谢Q群 @打工人,外壳完全由他一人设计,手感非常棒~❤
四、模块设计
这一大节用于细分每一个功能模块,包括输入/输出、数控电源、示波器、波形发生器、万用表,以及其他,包含软硬件都在内。希望我的设计思路能帮到你(´▽`ʃ♡ƪ )。
4.1 输入 & 电源
4.1.1 输入部分
V7的输入逻辑基本复用了已经较为成熟的V6部分:Type-C PD + DC 输入,C口通过CH224A来实现快充协议诱骗,双输入再使用理想二极管芯片来进行防倒灌保护,顺便也支持双输入以获得更大功率。
4.1.1.1 PD诱骗设计
XM Power Kit与V6的区别不大,只不过是从CH224K换成了CH224A:

可以看到,跟CH224K相比,CH224A终于抛弃了224K那种神经的取电方式,外围电路大大的简便了:
- 224K用一个1K电阻做限流,20V诱骗下这个电阻能常态250mw的耗散,最重要的是,他不能直接接3.3V(就是PD没电,VDD上也不能有电)
- 此外,224K似乎容易坏,导致只能诱骗5V,我已经焊坏好几个了。
同时,CH224A也支持PD3.2的28V 5A@140W的诱骗,虽说PD3.2的插头现在还不多,但谁叫他跟224K差不多的价格呢,干嘛不用更好的对吧?
再者,CH224A也支持在电阻配置下,通过IIC来控制诱骗的电压,因此我也可以拿这个功能来做一个简单的PD诱骗支持检测功能(阉了)。
4.1.1.2 双输入保护
对于双输入保护,我使用了两套mx5050t搭配Nmos管构成的理想二极管组成,说简单点,就是相当于两套电源各通过一个Nmos构成“二极管”再会和,只不过理想二极管方案几乎没有能量损耗。

相比于V6来说,这里改用mx5050t,价格会更便宜一些,同时也杜绝了像MX16171买成MX16171D100这种情况。(神经厂商,相近的型号名但引脚定义完全不一样,坑爹呢哈哈)
对于双输入,理论上是支持的,前提是两个电源的压差不能太大,大概0.5V吧,否则mx5050t就判定为反向电压自动关闭mos管了。
4.1.2 电源部分设计
对于我这种,模拟+数字+功率 大杂烩的项目,一个稳定、优质的电源供应方案就显得尤为重要。对于V7的电源,我的要求是:
- 5~32V输入内,无论负载多大,都不能出现掉压重启的情况。
- 对于数字供电,纹波在40mv以内,对于模拟供电,需要保证纹波在10mv以内。

下面我来讲一下设计思路:
- 输入先降压到4V,确保5~32V输入都能够稳定降压
- 随后升压到7V,为后续LDO/DCDC做准备
- 根据不同的负载,选择合适的降压方式
先降压再升压,是为了确保在4.7~5V这个节点上,后级也能得到7V的主供电。先降压到4V也是因为能实现100%占空比的便宜dcdc芯片不多,大多数buck芯片在输入低于设定电压的时候,负载能力会下降的很严重;此外,这样既方便隔离数控电源对主电源造成的巨大波动,又方便后级做滤波处理。
整个系统的空载功耗1.5W左右(包括屏幕),因此不必担心功率不够的问题。为了确保纹波达标,你要做的就是“怂”!像优化dcdc布局、使用ldo隔离负载等都是很有效的方法,但最重要的还是地处理,这跨地供电也是一门学问,你可以参考一下我的处理方式,效果还行。
下面是一些电源的注意事项:
- 降压4V的buck芯片,最好选择最低电压在4.5V及以下的,否则5V输入时很容易掉压。
- ±5VA的二级滤波电路可以缩减为1级,SE8550这个LDO还是挺厉害的。
4.2 数控电源
XM Power Kit,一看这名字就知道本项目主打的就是数控电源功能。V7的电源部分相比V6有较大的改动,但本质操控逻辑上没有太大的变化,但性能可谓是强了不止一点,下面我来讲解:
4.2.1 硬件讲解

4.2.1.1 芯片选型
这一次换用了一颗挺经典的芯片:TI的LM5176 。这芯片的性能可比南芯的sc8701强多了:
- 这是一颗同步 4 功率管 Buck-Boost 控制器;
- 4.2 – 55V输入,0.8 – 55V输出。 (本项目为了安全只支持1 – 36的输出)。
- 以电流模式控制,350Khz频率,以及最大94%的高效率
最重要的是,这颗芯片的驱动能力比8701强得多,这意味着我们能使用更低Rdson的MOS管(这往往意味着更高的输入电容)也不太需要担心驱动不够导致mos半开启而大量发热炸管了;其次,它就不是8701那种qfn封装了,tssop封装要好焊接的多,能大大降低焊接难度。
注意事项:SS、comp引脚的配置跟输入输出电容的容值都有关,做了更改的话记得重新计算参数,TI专门做了各网页用于设计,你可以去看看:TI电源设计工具
4.2.1.2 电压调节功能
于V6相比,LM5176就没有sc8701那种通过PWM来调节电压电流的方式了。因此V7还是用的V6那种调节方案:通过DAC来控制FB引脚的电阻分压网络,进而调节输出电压,而环路稳定什么的就交给5176自己完成。 大致示意图如下:

我们来计算一下,根据基尔霍夫电流定律,我们有:
- $i_1 = i_2 + i_3$
- $i_2 = V_{ref} / R_{d1}$
- $i_3 = (V_{ref} – V_{dac}) / R_{d2}$
- $i_1 \cdot R_{up} = V_{out} – V_{ref}$
经过代换得到:
$$
V_{out} = V_{ref} \cdot \left(1+ \frac{R_{up}}{R_{d1}}\right) + (V_{ref} – V_{dac}) \cdot \frac{R_{up}}{R_{d2}}
$$
带入已知值,式子化为:
$$
V_{out} = 39.491 – 12 \cdot V_{dac}
$$
因此我们就可以知道,当$V_{dac}$ 输出 $0.29\ \mathrm{V}$ 时,$V_{out}$ 理论输出 $36\ \mathrm{V}$ 左右;$V_{dac}$ 输出 $3.22\ \mathrm{V}$ 时,$V_{out}$ 理论输出 $0.8\ \mathrm{V}$。当然根据设计,输出范围被限制在 $1\ \mathrm{V} \sim 36\ \mathrm{V}$。
于是我们就实现了电压调节的设计。对于一个12bit的ADC来说,有效刻度4096格,理论能做到每一个刻度调节:
$$
\frac{36 – 1}{4096 \times \frac{3.22 – 0.29}{3.3}} = 0.0096\ \mathrm{V}
$$
$10\ \mathrm{mV}$的调节步进足够进行较为精细的电压调节了。
当然了,这只是理论上的,它的作用就是告诉我们输出电压与DAC值是线性关系,因此在校准中,我们可以很方便地构建实际的关系函数,只需要采集9个点然后进行线性拟合即可,这一部分会在稍后讲到~
4.2.1.3 输出检测
相比于V6使用INA226来获取电压电流信息,V7进行了很大的改变:使用tp181 + 电阻分压+LMV321的方案,这套方案有如下优点:
- 检测速度更快:226只能做到100次/s的有效数据,新方案1K次/s不是问题。
- 自由度更高:226最大共模电压是36V,新方案只需要调电阻分压,就能支持更高的电压检测。
- 成本很低:226大概三四块钱一颗,新方案1块左右能拿下。
能更快的获取有效数据,意味着软件上PID调节的速率和稳定性能大大提高,性能可以看后面的章节。

可以看到,对于电压检查测,通过 $91\ \mathrm{k}\Omega$ + $8.2\ \mathrm{k}\Omega$ 电阻分压后,经过一个电压跟随器送到ADC,电容电阻什么的都是用来滤除微小干扰的, $C_{73}$ 可以滤掉一些抖动。理论上电压检测范围是 $0 \sim 39.92\ \mathrm{V}$,理论 $\mathrm{LSB} = 0.0097\ \mathrm{V}$。
而对于电流检测,就很简单了,TP181将检流电阻 $R_{52}$ 两端的压降放大50倍,滤波后送入ADC,理论检测范围是 $0 \sim 13.2\ \mathrm{A}$,理论精度 $0.0032\ \mathrm{A}$。
注意事项:
众所周知,DCDC电源会有输出纹波,本项目经过测试大约在 $80\ \mathrm{mV}$ 左右,频率 $1\ \mathrm{kHz}$ (反正实际测的是 $1\ \mathrm{kHz}$) 证实是干扰,已经显著缓解,现在是15mv左右。对于电压检测,经过电阻分压后纹波只有大约 $8\ \mathrm{mV}$,基本不用处理;而对于电流检测,这些波动可以导致 $10\ \mathrm{mA}$ 以上的波动,因此需要进行针对处理。硬件上如图, $R_{54}+C_{71}$ 以及 $R_{57}+C_{74}$ 构成一个截至频率16hz滤波器,可以较好的过滤此波动。
4.2.1.4 输出接口
输出方面,由于跟 万用表 复用的香蕉头接口,以及需要做防倒灌保护,因此跟V6一样,最终输出也选用了一颗MX5050t+ NMOS构成的理想二极管方案,刚好5050t也可以通过高低电平来控制是否开启mos。
此外,由于黑色香蕉头也需要跟 万用表 复用,因此新增了一颗由UCC27517DBVR驱动的Nmos,由此控制黑色香蕉头与功率地的联通。
4.2.2 软件讲解
4.2.2.1 任务架构

软件部分底层使用了Freertos,不过还用了CMSIS OS V2的封装(自动帮我们判断某些函数是否在中断里之类的)。有了rtos的帮助,我得以很方便的实现高效的调度与软件逻辑。
下面是图中四个任务的简单信息:
| 任务名 | 优先级 | 简介 |
|---|---|---|
| PageSelectTask | osPriorityHigh | 负责各个APP程序之间的切换。包括初始化,反初始化等操作 |
| IndevDetectTask | osPriorityNormal4 | 每10ms扫描一次按键/编码器,若有变化,则将事件源、事件类型添加到消息队列中 |
| DpsCoreTask | osPriorityNormal | 负责读取按键事件并分发,同时定期刷新某些界面元素 |
| PIDTask | osPriorityNormal1 | 子任务,负责每1ms进行一次PID运算与调节,其使能/挂起有DpsCoreTask控制 |
下面我们简单讲DpsCoreTask,以及DMA回调函数~
4.2.2.2 DpsCoreTask
这个是数控电源(DPS)的主函数,负责包括数据采集、UI显示控制、按键事件处理,电源参数管理,以及PID算法控制这些功能。
我们先来看看任务主循环:
void Start_DpsCoreTask(void *argument) {
// 初始挂起任务(等待PageSelectTask激活)
osThreadSuspend(DpsCoreTaskHandle);
for (;;) {
// 读取按键事件并分发到当前页面的处理函数
if (osMessageQueueGet(KeyEventQueueHandle, &Keymsg, NULL, 0) == osOK)
page_handlers[current_page](Keymsg);
// 每100ms更新一次界面数值(15×10ms)
// 每1秒更新一次运行时间(100×10ms)
// 每s更新一次风扇状态(100×10ms)
// 任务延时(10ms)
osDelay(10);
}
}
主函数的作用很简单,就是每10ms扫描一次按键,若有事件就通过 page_handlerscurrent_page;对事件进行分发,交给当前页面的响应函数进行处理。此外,每隔固定的时间,就对部分的界面元素进行更新。
下面我们来看看按键事件分发:
/**
* @brief APP页面枚举
* @note 定义DPS系统的所有功能页面
*/
typedef enum {
PAGE_MAIN = 0, // 主界面(默认页面)
PAGE_VOLT_DIGIT, // 电压数位编辑页面
PAGE_CURR_DIGIT, // 电流数位编辑页面
PAGE_QUICK_MENU, // 快速设置菜单页面
PAGE_NUM // 页面总数
} AWG_AppPage_t;
/**
* @brief 页面处理函数表驱动
* @note 索引对应AppPage_t枚举,快速映射页面与处理函数
*/
static void (*page_handlers[PAGE_NUM])(KeyEventMsg_t) = {
[PAGE_MAIN] = dps_main_page_handler,
[PAGE_VOLT_DIGIT] = volt_digit_handler,
[PAGE_CURR_DIGIT] = curr_digit_handler,
[PAGE_QUICK_MENU] = quick_menu_handler,
};
可以看到,我们先创建了一个枚举,用于表示当前所处的页面。那么按键分发就可以根据当前所处的页面,让对应的函数来响应这个按键事件。
那么在对应的按键响应函数中,我们就可以根据事件的触发者(是哪一个按键?还是编码器?),事件的类型(单击?长按?等等),来进行对应的响应,比如切换焦点框、更新某些值的显示等等~ 下面举个例子,就拿digit_page_handler这个函数来说吧:
/**
* @brief 电压/电流数位编辑通用处理函数
* @param msg: 按键事件消息
* @param ctx: 数位编辑上下文指针(volt_ctx/curr_ctx)
* @retval 无
* @note 统一处理电压/电流的数位选择、数值调整、返回主界面等逻辑
*/
static void digit_page_handler(KeyEventMsg_t msg, const DigitEditContext_t *ctx) {
// 映射当前/上一次编辑数位(区分电压/电流)
DigitPosition_e *pCurrent = (ctx == &volt_ctx) ? &VoltagePosition_Current : &CurrentPosition_Current;
DigitPosition_e *pPast = (ctx == &volt_ctx) ? &VoltagePosition_Past : &CurrentPosition_Past;
// 上移/左按:切换到高位数位
if (msg.key == KEY_UP || msg.event == ENCODER_EVENT_PRESS_LEFT) {}
// 下移/右按:切换到低位数位
else if (msg.key == KEY_DOWN || msg.event == ENCODER_EVENT_PRESS_RIGHT) {}
// 右旋转编码器:增加对应数位的数值
else if (msg.event == ENCODER_EVENT_RIGHT) {}
// 左旋转编码器:减少对应数位的数值
else if (msg.event == ENCODER_EVENT_LEFT) {}
// 短按SET/编码器:返回主界面
else if ((msg.key == KEY_SET || msg.key == KEY_ENCODER) && msg.event == KEY_EVENT_CLICK){}
// 无匹配按键事件:直接返回
else return;
// 按键有效时蜂鸣提示
StartBeezer(0);
// 数位选择下划线更新(切换选中数位的指示)
}
可以看到,非常的简单粗暴,判断msg结构体的key和event,就能唯一指定一种事件,只需要补充这个事件会进行何种操作就可以了,其他函数也是同理,这里就不多赘述了。
不过有一点需要注意的是,这种方案终究是把事件跟操作糅合在了一起,这并不方便管理(毕竟你得记得某个操作在哪一个事件类型分支里),但不得不说,目前这种方式是逻辑很清楚的,倒是方便学习理解。
4.2.2.3 中断回调函数
这里说的是ADC+DMA的中断回调函数,更本质的说,是DMA传输完成中断。我们先来看一看ADC以及DMA的部分重要配置:
// ========== ADC部分 ========== //
hadc1.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率(4096级,满足电压/电流采集精度要求)
hadc1.Init.ScanConvMode = ENABLE; // 使能扫描模式(支持多通道采集)
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T8_TRGO; // 触发源:定时器8 TRGO事件
hadc1.Init.DMAContinuousRequests = ENABLE; // 使能DMA连续请求(环形模式必需)
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; // 序列转换完成后触发EOC中断
sConfig.SamplingTime = ADC_SAMPLETIME_144CYCLES; // 采样时间144周期
// ========== DMA部分 ========== //
hdma_adc1.Init.Mode = DMA_CIRCULAR; // 环形模式(循环采集,无需重复启动)
volatile uint16_t dps_adc_raw_buf[128] = {0}; // ADC原始采样缓冲区(128个值,偶数列-电压,奇数列-电流)
HAL_ADC_Start_DMA(&hadc1, (uint32_t *) dps_adc_raw_buf, 128); // 启动ADC DMA采集(缓冲区长度128,环形模式)
// ========== TIM8定时器部分 ========== //
__HAL_TIM_SET_PRESCALER(&htim8, 149); // 设定更新频率64Khz
__HAL_TIM_SET_AUTORELOAD(&htim8, 14);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // 重装载时触发更新事件
配置的目标很简单,就是让ADC 每1秒对两个通道采集64K次,其中每采集到128个数据(俩通道各64个)就触发一次DMA中断,进行数据处理,随后重新开启采集,覆写数据缓冲区,如此循环。
下面我们看看DMA回调函数都做了什么:
void CB_DPS_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
if (hadc->Instance == ADC1) {
AdcTim_OFF(); // 关闭定时器(ADC触发源)
uint32_t sum_ch1 = 0, sum_ch2 = 0;
// 累加64次ADC采样值(偶数列-通道1(电压),奇数列-通道2(电流))
for (uint16_t i = 0; i < 64; i++) {
sum_ch1 += dps_adc_raw_buf[2 * i];
sum_ch2 += dps_adc_raw_buf[2 * i + 1];
}
// 计算均值并叠加校准偏移
int32_t temp1 = (int32_t)(sum_ch1 >> 6) + UserParam.DPS_Voltage_Original;
int32_t temp2 = (int32_t)(sum_ch2 >> 6) + UserParam.DPS_Current_Original;
// 转换为实际电压值(3.3V参考,12位ADC,乘以校准系数)
float raw_voltage = (float) (temp1 > 4096 ? 0 : temp1) * 3.3f / 4096.0f * UserParam.DPS_Voltage_Factor;
// 转换为实际电流值(扣除电压耦合偏移和固定偏移)
float raw_current = (float) (temp2 > 4096 ? 0 : temp2) * 3.3f / 4096.0f * UserParam.DPS_Current_Factor;
// ==========================================
// 软件 IIR 低通滤波
// ==========================================
// 滤波系数 (0.0 ~ 1.0),越小滤波越强,反应越慢
const float filter_display_alpha = 0.02f; // 显示用小系数,保证平滑
const float filter_PID_alpha = 0.2f; // PID用大系数,保证快速响应
// 静态变量保存上一次的滤波结果
static float filtered_voltage = 0.0f; // 滤波后的电压值_显示
static float filtered_current = 0.0f; // 滤波后的电流值_显示
static float filtered_current_pid = 0.0f; // 滤波后的电流值_PID
static float filtered_voltage_pid = 0.0f; // 滤波后的电压值_PID
// 一阶 IIR 滤波公式:Output = alpha * Input + (1 - alpha) * Output_Last
filtered_voltage = filter_display_alpha * raw_voltage + (1.0f - filter_display_alpha) * filtered_voltage;
filtered_current = filter_display_alpha * raw_current + (1.0f - filter_display_alpha) * filtered_current;
filtered_current_pid = filter_PID_alpha * raw_current + (1.0f - filter_PID_alpha) * filtered_current_pid;
filtered_voltage_pid = filter_PID_alpha * raw_voltage + (1.0f - filter_PID_alpha) * filtered_voltage_pid;
// 更新全局变量
Voltage = filtered_voltage;
filtered_current < 0.01f ? (Current = 0.0f) : (Current = filtered_current); // 如果电流小于0.01A,就归零,避免静止时跑量
Current_PID = filtered_current_pid;
Voltage_PID = filtered_voltage_pid;
// 计算实时功率
Power = Voltage * Current;
}
// 电源开启时累计能量(功率×时间,时间单位:小时)
if (IsPowerOn) energy += Power * MS_TO_HOUR;
AdcTim_ON(); // 开启定时器(ADC触发源)
}
还记得我们前文提到的吗?由于 $80\ \mathrm{mV}$ 纹波带来的电压、电流波动,需要对采集数据做滤波处理。
从上面的代码可以看到,我们先对每个通道采集到的 64 个数据做均值处理,再经过校准偏移转换为实际物理量。这一步对抑制 $80\ \mathrm{mV}$ 纹波的效果一般,因为干扰频率只有 $1\ \mathrm{kHz}$,而系统采样率为 $64\ \mathrm{kHz}$,且每次只取 64 个点,只能采样到 $1\ \mathrm{kHz}$ 波形的一部分周期。因此,这里的平均操作更多是用来消除尖峰毛刺。
真正起到关键滤波作用的是后面的一阶 IIR 低通滤波,它的原理非常简单:设定一个在 $0 \sim 1$ 之间的系数 $a$,每次更新时满足:
$$
新数据 = 本次值 \times a + 上一次值 \times (1 – a)
$$
简单来说:当 $a$ 较大时,当前值对结果影响更大,数据响应更快,但滤波效果较弱;当 $a$ 较小时,历史值权重更高,滤波效果更好,但响应速度会变慢。
这也解释了为什么我在代码中区分了filtered_voltage 与 filtered_voltage_pid的原因:因为刷新显示只有8次/s,要求要尽量的平稳,所以a可以小一些;但PID计算要求快速响应,故使用较大的a来保证实时性
4.2.2.4 数控电源校准
下面讲一讲要怎么校准数控电源功能,其实也不用讲,我已经做好操作指引了,这里就讲讲注意事项:
- 请务必使用20V以上的电源校准(或者大功率的电源),否则可能校准时死机。
- 校准过程不可终止,进行完所有步骤才会保存数据。
- 您需要准备:万用表 + 10Ω左右电阻(1W左右)
下面讲讲校准设计的思路:
首先是原点校准,它的作用就是标定0V点。有的设备短接正负极后,ADC还能读到1 – 3个刻度的值,导致显示0.0xxV,我们需要调整原点校准,将这个值抵消掉。
其次是显示校准,看过原理图的就知道,我的方案是91K + 8.2K 电阻分压,将0 – 36V的电压降低到0~3.3V给到ADC。此时的理论降压倍率系数就是 $\frac{91\,\mathrm{k}\Omega + 8.2\,\mathrm{k}\Omega}{8.2\,\mathrm{k}\Omega} = 12.09$,我们需要拿着这个值才能还原真实电压。实际上电阻是有误差的,我们需要手动校准此倍率系数,让电压显示正确。
最后是输出校准,我们是通过DAC来控制实际电压输出的,前文提到,DAC值跟实际电压值成线性关系,我们要做的就是在DAC范围里均匀取9个点进行线性拟合,得到 $V_{\mathrm{out}} = A \cdot V_{\mathrm{dac}} + B$ 中的未知系数A、B的值。
4.2.3 电源性能
先简单测试一下空载性能,20V输入:

可以看到,降压模式下,纹波表现更优;当输出电压接近输入电压( $\pm 3\ \mathrm{V}$ )时,LM5176 会启用混合模式,纹波会略有升高;升压模式下,纹波随输出电压升高的增长速度会更快。但无论哪种工况,空载状态下纹波均未超过 $10\ \mathrm{mV}$ ,属于非常优秀的水平~
不过需要注意的是:风扇开启时,由于风扇与DAC 共用 $7\ \mathrm{V}$ 供电,尽管为DAC 供电的LDO 能很好地滤除 $1\ \mathrm{kHz}$ 的低频干扰,但仍会有少量干扰串入DAC 输出链路,此时整体纹波会抬升 $20 \sim 25\ \mathrm{mV}$ ,最终呈现的效果就是 $1\ \mathrm{kHz}$ 、 $35 \sim 40\ \mathrm{mV}$ 的纹波。
接下来测试一下24.5V输入,恒定20Ω负载的情况:

显而易见的是,降压模式的纹波表现明显要好于混合模式以及升压模式,因此为了您有较好的带载纹波表现,请使用电压较高的电源,让设备工作在降压模式~
4.3 其他模块设计
由于篇幅原因,全部写在这里就太长了,所以我打算将剩下的模块讲解移至我的博客雪萌の小窝,你可以点击下面的连接快速跳转。由于我比较忙,这部分的更新会慢慢补上的。
网站上也有其他更为细致的教程,欢迎你来访问~
五、程序下载与调试
这部分请务必仔细阅读!下面我们先来简单介绍以下:
XM PPWER KIT的程序分为两个部分:
- Pre-check.elf : 前置包,用于创建FATFS、资源拷贝、硬件检查等
- Main_Rel.xxx.elf : 主程序包
我们先下载附件中的 程序资源包.zip,完整解压后得到如下文件(夹):
| 名称 | 类型 | 说明 |
|---|---|---|
| images | 文件夹 | 存放着主程序会用到的所有图片资源 |
| font | 文件夹 | 存放在主程序会用到的所有字体资源 |
| Main_Rel.xxxx.elf | 文件 | 主程序 |
| Pre-check.elf | 文件 | 前置程序 |
| DiskGenius.exe | 程序 | 用于创建FATFS系统 |
同时你需要准备 两条 USBA->C的线,尽量不要使用cc线,避免连不上板子。
注意! 由于我们使用DFU进行程序下载,因此我们还需要下载安装 STM32CubeProgrammer !
你可以使用如下的地址进行下载:
曲线救国方案由B站 @keysking 提供,在此表示诚挚的感谢!
下载好之后,安装,一直同意+下一步 即可,安装路径建议全英文。
5.1 前置程序下载
我们打开CubeProgrammer,按下图这样配置好连接模式:

接下来我们将注意力放到板子上,务必按下面的指令来操作:
- 1. 先按住左侧的BOOT0按键,不要松开
- 2. 再插入USB线到板子上部的USB口,连接电脑
- 3. 插上两三秒后再松开

接下来回到电脑上,按下面的步骤操作:
- 1. 点击刷新图标,正常的话会出现一个USB1的设备 (没出现就重试上一大步的内容)
- 2. 点击Connect进行连接
- 3. 点击Start Programming 刷入程序
- 4. 注意:刷写可能会报错!实际上已经刷进去了,不用怕!(跑到68%然后报错)
- 5. 断开电源,进行下一步

至此,我们前置程序就下载好了,下面请跟随我来操作,完成调试部分,建议您先整体阅读一遍再上手实操!
5.2 前置程序调试
现在我们已经下载好了前置程序,在下一步上电之前,先看看整体的流程:
注意,此时请接入稳定的电源,并使用板子左侧而不是上侧的USB口!
- 1. 上电后,程序首先会检查FLASH是否正常,若不正常,则会直接红屏。
- 2. 若FLASH正常,屏幕会开始在“白”“黄”“蓝”之间不断切换,此时正在格式化FLASH,需要30s左右,请勿断电。
- 3. 格式化完成后,屏幕会变成绿色,此时连接板子上侧的usb到电脑,使用工具创建FAT文件系统,模拟U盘。
- 4. 直接将font和images文件夹拷贝进U盘根目录。
- 5. 断开所有线材,重新上电(板子左侧USB),此时板子会显示所有必须模块的自检情况,若都OK,则可以刷入主程序。
很简单对吧?现在我们仔细讲一讲第三和第四步,请务必按我的操作来:
屏幕变绿之后,将设备连接电脑,打开DiskGenius.exe,加载完成后应当可以看到一个名叫 XMPowerKit 的设备如图,我们单击它:

接下来,我们右键容量条 -> 建立新分区 -> 文件类型选择FAT16 -> 确定 -> 左上角保存更改即可

等待一段时间后(30s,不要急着关软件),电脑上会多出来一个U盘如图:

接下来,我们将程序资源包里的images和font文件夹直接拖进U盘根目录如图,一定要跟图片一样!不然程序读不到资源!!!!

随后先拔出连接电脑的USB线,再断开左侧电源。此时重新上电,正常的话就会显示必要组件的初始化状态如图:

如果有哪一项 FAIL 了,请你检查一下硬件焊接。
至此,前置程序的刷写与调试就算完成啦!应该还算是简单的。为了方便你们调节硬件,专门做了这个前置程序,希望能简便你们的调试工作~
5.3 主程序刷写
这一部分跟前置程序的刷写一模一样,直接复用 5.1 的步骤就行了,只不过是将刷写的程序换成Main_Vx.x.elf罢了。
不过也可能出现 进度条跑到 90% 然后报错的情况,直接忽略,程序已经下进去了,断开跟电脑的连接,重新上电应该就能进入主界面啦~

至此,程序下载与调试部分就算做好了~
六、外壳安装
这部分详见附件的外壳与组装说明.zip,里面有配图进行安装说明。
在此感谢 @打工人 大佬,此外壳完全由他进行设计与改良~❤
七、各APP的操作映射
下面对各个APP的按键操作映射进行讲解,让你快速上手本项目!
一些APP的操作很简单,我就不写在这里了,你上手就会的~
7.1 数控电源APP
此界面为手搓,很漂亮对吧?我下了不少心思,希望你喜欢~
| 按键类型 | 操作类型 | 界面 | 说明 |
|---|---|---|---|
| 编码器 | 长按 | 主界面 | 回到APP选择界面 |
| 编码器/下键 | 右转 / (单击/长按) | 主界面 | 选择下一个APP |
| 编码器/上键 | 左转 / (单击/长按) | 主界面 | 选择上一个APP |
| 编码器/SET键 | 单击 | 主界面 | 进入选中的调节项目 |
| 按键 | SET键长按 | 主界面 | 开启/关闭电源输出 |
| 编码器/上键 | 按下+左转 / (单击/长按) | 参数编辑 | 将光标移动到上一位 |
| 编码器/下键 | 按下+右转 / (单击/长按) | 参数编辑 | 将光标移动到上一位 |
| 编码器 | 右转 | 参数编辑 | 增加选中该位数的值 |
| 编码器 | 左转 | 参数编辑 | 减少选中该位数的值 |
| 编码器/SET键 | 单击 | 参数编辑 | 回到主界面 |
7.2 示波器APP
此界面为手搓,也很漂亮对吧?逻辑+界面大概搓了5000多行代码~
| 按键类型 | 操作类型 | 界面 | 说明 |
|---|---|---|---|
| 编码器 | 长按 | 主界面 | 回到APP选择界面 |
| MODE键 | 单击 | 主界面 | 在“左右平移”,“上下平移”,“触发调节”直接切换 |
| 编码器/上键 | 右转 / (单击/长按) | 主界面 | 右移/上移/增加 对应选项值 |
| 编码器/下键 | 左转 / (单击/长按) | 主界面 | 左移/下移/减少 对应选项值 |
| MODE键 | 长按 | 主界面 | 进入参数选择&编辑界面 |
| 编码器 | 右转 | 参数编辑 | 光标移动到下一个选项 |
| 编码器 | 左转 | 参数编辑 | 光标移动到上一个选项 |
| 上键/下键 | 按下/长按 | 参数编辑 | 调节当前选项的值 |
| MODE键 | 长按 | 参数编辑 | 返回主界面 |
7.3 万用表APP
此界面为手搓,依旧很漂亮对吧?希望你喜欢~
| 按键类型 | 操作类型 | 界面 | 说明 |
|---|---|---|---|
| 编码器 | 长按 | 主界面 | 回到APP选择界面 |
| 编码器 | 按下+右转 | 主界面 | 切换到下一个测量模式 |
| 编码器 | 按下+左转 | 主界面 | 切换到上一个测量模式 |
| 上键 | 按下 | 电阻档 | 切换到30K挡位 |
| 下键 | 按下 | 电阻档 | 切换到2K挡位 |
以上就是我认为稍微复杂一些的APP的操作说明,希望能帮助你快速上手!
八、打板与焊接
XM POWER KIT的焊接难度跟V6相比完全不是一个量级的,同时也是尝试使用6层板,所以请务必认真仔细!下面是我个人的焊接方法,以及焊接中容易出错的点,希望能帮到你!
8.1 打板事项
- 1. 关于6层免费券:首次领取需要考试、打板后需要焊接并转发朋友圈,才能获得下一次免费资格;
- 2. 如果打板的时候,显示文件已经享受过优惠,你可以加几个过孔,改改丝印;如果还不行,可以等待两三天;
- 3. 打板参数:6层板、1.6板厚、3313层压、±20%,不用复查什么的,其余全部默认即可;
8.2 板子焊接
为了高质量的焊接本项目,你必须有以下的工具,否则无法完成焊接!
| 工具 | 作用 | 备注 |
|---|---|---|
| 加热台 | 焊接板子正面(大电感面) | 咸鱼上买就行,20~30元 |
| 热风枪 | 焊接板子背面(MCU面) | 随便整,能到350℃就行 |
| 电烙铁 | 焊接座子、按键 | 功率要大一些,否则焊接不上座子 |
| 焊锡、锡膏、助焊剂 | 镊子、水口钳、吸锡带等 | 这些常用的你得有吧 |
| 洗板水 | 刷洗板子,清爽! | 不用我多说,怎么便宜怎么来! |
本人强烈建议下单钢网!!!
本人的焊接步骤:
- 1. 先焊接正面,钢网上锡膏,先贴电阻电容芯片mos,再贴大电容、电感、继电器,最后再贴导流铜条。
- 2. 使用加热台铁板烧,归为元器件。(注意:此时可以往电感旁的过孔上面挤锡膏,灌满过孔!)
- 3.使用电烙铁+焊油,修正TYPE-C口等容易连锡的元件。
- 4.使用万用表二极管档,测量如下部分是否短路:
- USB输入到地
- 3.7V、7V、3.3V、+5V、-5V的输出电容到地
- 若没有短路可以typeC上电5V,测一测上面提到的电压是否正确
- 5.背面钢网上锡膏,贴除了大按键外的所有元件。(特别注意TMP102的方向!建议对着数据手册来!)
- 6.使用热风枪焊接好背面,随后使用烙铁+焊油修复连锡的地方。
- 7.万用表二极管档,测量3.3V,5V,-5V到地有没有短路!
- 8.焊接风扇座子+香蕉头 + 竖插USB + MCX座子。
- 9.最后用烙铁焊接大按键!
- 10.没有短路就可以试着上电刷程序了~
8.3 焊接注意事项
1.请不要使用热风枪焊接大按键!否则会吹化按键的。
2.尽量不要用热风枪吹香蕉头座子/洗板水洗香蕉头座子,否则会融化/掉色
3.焊接完一面测试一下没问题,就可以用洗板水清洗一面。
4.测试的时候,不要让屏幕背面碰到引脚! 我已经踩过这个坑,24V窜进3.3V给板子一套全带走🤡
5.载流铜片最好焊上,没有的话也必须堆锡!示意图:

九、软件校准
每一个APP都需要校准,为了方便你们,我已经将校准过程简化了很多!你只需要进入校准APP,按照操作指引进行,你就能获得一个非常好用的设备啦!
举个例子,所有APP的校准,都可以在设备上完成,不需要上位机!

感谢华为的神经相机算法,给我非常漂亮的屏幕拍成这样….😒
同时呢,我也会抽时间拍一个校准视频出来,发在B站上。
下面是我建议的校准顺序:(必须先信号源再示波器!)
- 电源校准 -> 信号源校准 -> 示波器校准 -> 万用表校准
下面呢是每一个系统的校准所需要准备的工具,都是很容易获得的:
| 名称 | 工具 | 备注 |
|---|---|---|
| 电源校准 | 万用表 + 10Ω电阻(1W) + 20V电源 | 电流校准用到电阻;务必用20V电源(或者大功率电源),否则会重启 |
| 信号源校准 | 万用表 | 用到万用表200mv档 |
| 示波器校准 | 一字螺丝刀(1.5)+尖头镊子(或者细导线) | 螺丝刀来调前级电容;镊子来短接示波器与信号源 |
| 万用表-电压 | 万用表 | 用万用表2V档最好,精度高 |
| 万用表-电流 | 万用表+电源+阻性负载 | 不要用负载仪,干扰太大了 |
| 万用表-电阻 | 200Ω+2KΩ(0.1%) | 这俩电阻bom表里都有的 |
十、关于BOM表
东西比较多也比较杂乱,请使用附件中的Bom表,我对bom进行了整理,根据元件类型分了多个sheet:
- 表1:立创EDA直接导出的BOM,你可以直接导入立创商城下单(你是富哥的话😁)
- 表2~6:根据元件类型分类的细分表,里面有大量备注,请务必仔细查看!!
- (3):如果你要组装外壳,请前往附件的“外壳与组装说明.zip”,里面有外壳的bom表以及链接。
实话实话,我还是非常建议花些时间在元件购买上的,精打细算下来能省很多钱,不过也要注意不要贪图便宜整到假元件!
下面是一些购买必坑,不定时更新:
- 1. TLV2374IPWR —- 淘宝 —– 深圳市宏隆电子科技商行 (以前可以的,最近买了5个全不能用)
- 2. 关于大/小屏幕定义,我个人是推荐小屏幕的,连接在附件BOM里;此外,大小屏幕只跟外壳有关,都可以买。但是我只建议使用IPS屏幕!普通TN/TFT屏的观感很差!
- 3. 买屏幕的时候,一定要注意引脚定义顺序!!!

十一、二次开发
嘛,二次开发还是有点难的哈哈,我会公开如下信息帮助你开发属于你的新功能:
- 1. 项目源码,见前面github链接或附件
- 2. GUI-Guider工程,主界面以及设置、关于界面使用LVGL构建
同时呢我也用python配合AI制作了三个小工具,在附件的“小工具.zip”中
- 1. 字体取模:你可以导入任意字体并生成.bin文件,将其拷贝到U盘,在font.c/h中声明就能使用新字体~
- 2. 图片取模:同理,将图片转为.bin文件,拷贝到U盘并在image.c/h中声明即可使用~
- 3. 16进制查看器:做着玩的,方便我调试.bin文件
此外,对于设备输出端的TypeC口,不止可以用来供电(跟数控电源相连),我也留了ADC+TIM+SPI接口,你可以自行摸索,拓展独属于你的功能~
开发工具用的是CLion +STM32CubeMX
十二、一些注意事项
这里是我的一些提醒,不定期更新:
- 1. 香蕉头输出口是电源+万用表复用的,你要插表笔的话,要买公头是突出来的,而不是被塑料包起来的。
- 2. 显示屏排线与屏幕的连接处很脆弱,你弯折排线的时候要注意。
十三、其他
1.还请不用私信我,大二太忙了,没有太多精力搞开源设计,所以一般私信不会回复,在此表示抱歉!
有事情可以进群984946310交流!(答案:雪萌)一般群里的消息我才有时间看并回复,谢谢!群被恶意举报炸了,先进这个:1082694166,答案同上~
2.关于程序,我应该是会更新的极慢,如果你有好的想法/改进,可以pull-request到我的github仓库!
3.求你了多多看看我的源码吧!随然可能写的不咋地,但是绝对有很多你可以学习的东西!我写了大量的注释!不懂得话也可以发群里问,我看到了会解答的!
4.嘛,如果你愿意的话!可以整点打赏,我好回回本顺便支持我的个人博客网站!(QQ私聊咱~❤)
十四、演示小动图







群主,群怎么被封了,只好跑这来了
大佬太强了!
大佬牛批,请收下膝盖(๑•̀ㅁ•́ฅ)
推送一下俺的MakerWorld 开源模型链接上去(ฅ´ω`ฅ)
https://makerworld.com.cn/zh/models/2297395-3d_xm_power_kit-pei-tao-ke-ti#profileId-2592822