#第七届立创电赛# 基于N32G430与CH224K的桌面PD电压电流表设计与防误触实现
手把手教你做一个带PD诱骗的桌面电压电流表最近在捣鼓USB PD充电头发现市面上的PD诱骗器功能都比较单一要么只能诱骗电压要么只能测量两者结合的成品很少。正好手头有国民技术的N32G430开发板和CH224K PD诱骗芯片就想着自己做一个集电压电流测量和PD诱骗于一体的桌面小工具。这个项目特别适合想学习USB PD协议、ADC测量和嵌入式系统开发的初学者我会把整个设计过程掰开揉碎了讲清楚。这个工具能干什么呢简单说就是两件事一是实时监测USB PD电源的输出电压和电流二是可以主动让PD电源输出你想要的电压档位9V、12V、15V、20V。为了防止不小心按错键导致设备烧毁我还加了个3秒防误触确认机制——必须按住按键3秒才会真正切换电压。下面咱们就从硬件到软件一步步把这个小工具做出来。1. 硬件设计核心芯片选型与电路做硬件项目第一步永远是选对芯片。这个项目的核心就两个主控MCU和PD诱骗芯片。1.1 主控MCU为什么选N32G430C8L主控我选了国民技术的N32G430C8L这是一颗基于ARM Cortex-M4F内核的32位单片机。对于咱们这个项目来说它有几个特别合适的优点128MHz主频处理ADC采样、OLED显示刷新绰绰有余响应速度快内置浮点单元(FPU)计算电压、电流、功率这些带小数的值直接硬件计算不用软件模拟效率高12位ADC最高4.7Msps采样率测量电压电流精度够用速度也快64KB Flash 16KB RAM存程序、做数据缓冲完全够用丰富的外设有多个UART、I2C、SPI接口方便接OLED屏和其他模块最重要的是这颗芯片性价比很高对于学生项目和DIY来说很友好。1.2 PD诱骗芯片CH224K如何工作PD诱骗的核心是CH224K芯片。你可能要问什么是PD诱骗简单说USB PDPower Delivery是一种智能的电源协议充电头和设备之间会“协商”该用多少电压电流。普通的USB-A口默认是5V但PD充电头Type-C口默认输出也是5V只有当你告诉它“我需要更高电压”时它才会升压。CH224K干的就是这个“告诉”的活儿——它模拟了一个需要快充的设备跟PD充电头说“嘿给我个20V或其他档位”充电头收到请求后就会切换到对应的电压输出。CH224K用起来特别简单它通过三个引脚CFG1、CFG2、CFG3的电平组合来选择诱骗的电压档位CFG1CFG2CFG3输出电压低低低5V高低低9V低高低12V高高低15V低低高20V注意具体哪个引脚对应哪个电平一定要看你买的CH224K芯片的数据手册不同批次的芯片可能有细微差别。咱们的MCU就用三个普通的GPIO引脚输出对应的高低电平组合就能控制CH224K诱骗出想要的电压了。1.3 电源设计如何给整个系统供电整个板子需要3.3V供电给MCU、OLED屏、指示灯等用。但PD充电头输出的电压可能是5V-20V怎么变成稳定的3.3V呢这里用了一颗LP2950-3.3稳压芯片。它的特点是输入电压最高能到30V完全覆盖PD的所有档位输出固定3.3V最大能提供100mA电流压差小效率相对较高电路很简单输入接个滤波电容输出再接个滤波电容基本上就能稳定工作了。1.4 电压电流测量分压电阻和采样电阻测量电压很简单就是用电阻分压。因为PD电压最高20V而MCU的ADC只能测量0-3.3V所以需要用两个电阻把20V分压到3.3V以内。比如用100k和20k电阻串联20V电压在20k电阻上分到的电压就是20V × (20k/(100k20k)) ≈ 3.33V刚好在ADC量程内。测量电流要用到采样电阻。在输出回路上串联一个小的精密电阻比如0.01Ω电流流过时会产生微小电压。用MCU的ADC测量这个电压根据欧姆定律 I U/R 就能算出电流。这里要注意这个电压很小比如1A电流只有10mV所以可能需要加一个运放放大后再给ADC测量。1.5 防误触设计硬件上的考虑防误触主要在软件里实现但硬件上也要配合按键选用带明显手感的微动开关按下去有“咔哒”声蜂鸣器按键按下时发出提示音LED指示灯每个电压档位对应一个LED当前输出电压的LED常亮2. 软件设计程序流程与关键代码硬件搭好了接下来就是写程序。我用的是Keil MDK开发环境代码结构比较清晰。2.1 工程搭建与基础配置首先得安装N32G430的芯片支持包然后在Keil里新建工程。工程里需要这几个关键文件main.c主程序文件bsp_delay.h/.c延时函数用于防误触的3秒计时bsp_key.h/.c按键检测和处理bsp_led.h/.c控制LED和CH224K的CFG引脚MonoScreen.h/.cOLED屏幕驱动提示这些文件在立创EDA的工程附件里都能找到下载后直接添加到你的Keil工程里就行。2.2 主程序逻辑状态机思想整个程序我用状态机的思路来写这样逻辑清晰不容易出bug。主要有以下几个状态测量状态正常显示当前电压、电流、功率设置状态按键按下进入电压选择模式确认状态按住3秒后等待最终确认切换状态执行电压切换更新CH224K控制引脚主循环大概长这样int main(void) { // 初始化所有外设 System_Init(); // 系统时钟初始化 ADC_Init(); // ADC初始化用于测量电压电流 KEY_Init(); // 按键初始化 LED_Init(); // LED和CH224K控制引脚初始化 OLED_Init(); // OLED屏幕初始化 Buzzer_Init(); // 蜂鸣器初始化 uint8_t current_state STATE_MEASURING; uint8_t target_voltage VOLTAGE_5V; // 默认5V uint32_t button_press_time 0; while(1) { switch(current_state) { case STATE_MEASURING: // 正常测量并显示 Measure_Voltage_Current(); Display_Measurement(); if(KEY_Pressed()) // 检测到按键按下 { current_state STATE_SETTING; button_press_time Get_Tick_Count(); // 记录按下时刻 Buzzer_Beep(); // 蜂鸣器提示 } break; case STATE_SETTING: // 显示将要切换的电压 Display_Target_Voltage(target_voltage); if(!KEY_Pressed()) // 按键松开了 { current_state STATE_MEASURING; Display_Cancel(); // 显示取消 } else if(Get_Tick_Count() - button_press_time 3000) // 按住超过3秒 { current_state STATE_CONFIRM; Display_Confirm(); // 显示确认 } break; case STATE_CONFIRM: if(!KEY_Pressed()) // 松开按键执行切换 { Set_PD_Voltage(target_voltage); // 设置CH224K引脚 current_state STATE_MEASURING; } break; } Delay_ms(10); // 延时10ms降低CPU占用 } }2.3 ADC采样与计算电压电流的测量精度很重要这里有几个技巧电压测量float Read_Voltage(void) { uint32_t adc_value 0; float voltage 0; // 采样多次取平均减少噪声影响 for(int i 0; i 16; i) { adc_value ADC_Read(ADC_CHANNEL_VOLTAGE); Delay_ms(1); } adc_value adc_value / 16; // 16次平均 // ADC值转电压ADC值 × 3.3V / 4096 voltage adc_value * 3.3 / 4096.0; // 考虑分压电阻比例还原真实电压 // 假设分压比例是 20k:(100k20k) 1:6 voltage voltage * 6.0; return voltage; }电流测量类似但要考虑采样电阻和可能的放大倍数。2.4 OLED显示驱动我用的是128x64的OLED屏通过I2C接口驱动。显示内容要清晰易读void Display_Measurement(void) { OLED_Clear(); // 第一行电压 OLED_ShowString(0, 0, Voltage:, 8); OLED_ShowFloat(60, 0, voltage, 2, 8); // 显示2位小数 OLED_ShowString(100, 0, V, 8); // 第二行电流 OLED_ShowString(0, 16, Current:, 8); OLED_ShowFloat(60, 16, current, 3, 8); // 显示3位小数 OLED_ShowString(100, 16, A, 8); // 第三行功率 OLED_ShowString(0, 32, Power:, 8); OLED_ShowFloat(60, 32, voltage * current, 2, 8); // 功率电压×电流 OLED_ShowString(100, 32, W, 8); // 第四行当前PD档位 OLED_ShowString(0, 48, PD Mode:, 8); switch(current_voltage_mode) { case VOLTAGE_5V: OLED_ShowString(64, 48, 5V, 8); break; case VOLTAGE_9V: OLED_ShowString(64, 48, 9V, 8); break; case VOLTAGE_12V: OLED_ShowString(64, 48, 12V, 8); break; case VOLTAGE_15V: OLED_ShowString(64, 48, 15V, 8); break; case VOLTAGE_20V: OLED_ShowString(64, 48, 20V, 8); break; } }2.5 防误触实现细节防误触是这个项目的安全核心实现时要注意// 按键检测函数 uint8_t KEY_Pressed(void) { // 读取按键对应GPIO引脚的电平 // 注意要加去抖动处理 static uint8_t last_state 0; static uint32_t last_time 0; uint8_t current_state GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN); if(current_state ! last_state) { last_time Get_Tick_Count(); last_state current_state; } // 只有稳定按下超过50ms才认为是有效按下 if((Get_Tick_Count() - last_time 50) (current_state 0)) // 0表示按下 { return 1; } return 0; } // 在设置状态下实时显示倒计时 void Display_Countdown(uint32_t press_time) { uint32_t elapsed Get_Tick_Count() - press_time; uint32_t remaining 3000 - elapsed; // 3000ms 3秒 if(remaining 0) { uint8_t seconds remaining / 1000 1; // 显示剩余秒数 OLED_ShowNum(90, 48, seconds, 1, 8); OLED_ShowString(100, 48, s, 8); } }3. 调试与烧录让板子跑起来程序写好了怎么烧到芯片里呢3.1 烧录器连接N32G430支持SWD调试接口只需要4根线3.3V给目标板供电如果板子自己供电了这根线可以不接GND地线必须接SWCLK时钟线SWDIO数据线我用的是创芯工坊的PWLINK烧录器在Keil里配置一下就行打开Keil的Options for Target对话框选择Debug选项卡选择PWLINK调试器在Settings里确认SWD模式速度可以设到4MHz重要电平要设置为3.3V否则可能烧坏OLED屏或其他3.3V器件3.2 调试技巧调试这种项目我一般会分几步第一步先调通基础功能让LED能亮灭按键检测正常OLED能显示文字第二步单独测试PD诱骗写个简单程序固定输出某个CFG组合用万用表测量CH224K输出的电压是否正确测试所有档位5V、9V、12V、15V、20V第三步测试ADC测量用可调电源给测量输入端加已知电压看OLED显示的值是否准确调整分压电阻比例或软件校准系数第四步整合所有功能测试正常测量模式测试按键切换流程测试防误触是否正常工作3.3 常见问题与解决在实际制作中你可能会遇到这些问题问题1CH224K诱骗不出电压检查CFG1/CFG2/CFG3引脚电平组合是否正确确认PD充电头支持对应的电压档位检查VBUS连接是否正常问题2ADC测量值跳动大在ADC输入引脚加一个0.1uF的滤波电容软件上做多次采样取平均检查地线是否连接良好问题3OLED显示乱码检查I2C地址是否正确一般是0x78或0x7A确认初始化序列正确检查电源电压是否稳定问题4按键反应不灵敏调整按键去抖动时间检查上拉电阻是否接好确认GPIO配置为输入模式且使能了上拉4. 实际使用与改进建议这个板子做出来后我平时用它来测试各种PD充电头、移动电源特别方便。比如想知道某个充电头是否支持20V输出插上这个表切换到20V档一看便知。如果你也想做一个这里有些建议给初学者的建议可以先在立创EDA上查看原工程理解原理图布局焊接时先焊电源部分测试3.3V正常后再焊其他程序调试分模块进行不要一下子全写上可能的改进方向增加Type-C输入接口兼容更多充电器加入电池供电做成便携式增加数据记录功能通过串口上传到电脑加入温度测量监控充电头温度安全注意事项切换电压前一定要确认接的设备支持该电压不要长时间超过PD充电头的最大功率测量高压时注意绝缘避免短路这个项目虽然不大但涵盖了嵌入式开发的很多基础内容GPIO控制、ADC采样、I2C通信、状态机编程、人机交互设计。通过动手做一遍你对这些概念的理解会比单纯看书深刻得多。最重要的是做出了一个真正有用的工具那种成就感是买现成产品比不了的。