用STM32和PID算法,我给自己做了个可调压调流的桌面数控电源(附完整代码)
从零打造桌面级数控电源STM32与PID算法的实战指南1. 项目背景与设计思路去年工作室搬迁时我发现自己需要频繁切换不同电压的电源适配器来测试各种开发板。市面上的数控电源要么价格昂贵要么功能单一于是萌生了自己动手打造一台的想法。经过三个月的迭代最终完成了一台基于STM32的数控电源支持0-30V/0-5A可调范围精度达到±0.05V和±10mA。这个项目的核心挑战在于实现电压电流双闭环控制。与普通线性电源不同我们采用BUCK降压拓扑通过PWM控制MOSFET开关配合PID算法实现快速响应和稳定输出。整个系统包含以下几个关键部分功率转换模块IR2104驱动IRL3803 MOSFET组合采样电路INA240电流检测放大器差分电压采样控制核心STM32F103C8T6最小系统人机交互0.96寸OLED旋转编码器保护电路输入过压/欠压、输出过流/过压四重保护提示选择STM32F103主要考虑其PWM分辨率72MHz主频下可达1440级和12位ADC的性价比优势2. 硬件设计关键点2.1 功率级设计Buck电路的核心参数计算需要平衡效率、体积和成本。我的最终方案如下表所示参数计算值实际选用备注开关频率100kHz98.4kHz基于STM32定时器配置电感值68μH82μH考虑20%余量输入电容47μF2×22μF并联低ESR电解电容输出电容100μF47μF10μF陶瓷电容滤波高频噪声电感饱和电流的选择尤为重要我通过以下公式验证I_sat ≥ I_out_max ΔI_pp/2 ΔI_pp (V_in - V_out) × V_out / (V_in × L × f_sw)实际测试中发现使用铁硅铝磁环绕制的电感温升比现成功率电感低15℃左右。2.2 采样电路设计电流采样采用0.05Ω/3W的锰铜电阻配合INA240放大器电路设计要点布局时采样电阻尽量靠近MOSFET源极差分走线长度保持一致在放大器输出端添加RC滤波1kΩ100nF电压采样则采用电阻分压电压跟随器结构特别注意分压电阻精度选0.1%在ADC输入端添加TVS二极管防护软件中实现数字滤波移动平均中值3. 软件架构与PID实现3.1 控制逻辑框架整个系统采用状态机模式运行核心代码如下typedef enum { STATE_INIT, STATE_STANDBY, STATE_CV_MODE, STATE_CC_MODE, STATE_FAULT } PSU_State; void PSU_StateMachine(void) { static uint32_t last_update 0; if(HAL_GetTick() - last_update 10) return; switch(current_state) { case STATE_INIT: Hardware_Init(); current_state STATE_STANDBY; break; case STATE_STANDBY: if(user_setting.enable) { PID_Reset(vPID); current_state STATE_CV_MODE; } break; case STATE_CV_MODE: CV_Control_Loop(); if(current setting.current) { current_state STATE_CC_MODE; } break; // 其他状态处理... } last_update HAL_GetTick(); }3.2 PID算法优化传统位置式PID在电源控制中存在积分饱和问题我改用了增量式PID算法typedef struct { float kp, ki, kd; float error, last_error, prev_error; float output, last_output; float max_limit, min_limit; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float feedback) { pid-error setpoint - feedback; float delta pid-kp * (pid-error - pid-last_error) pid-ki * pid-error pid-kd * (pid-error - 2*pid-last_error pid-prev_error); pid-output pid-last_output delta; // 抗积分饱和处理 if(pid-output pid-max_limit) { pid-output pid-max_limit; } else if(pid-output pid-min_limit) { pid-output pid-min_limit; } pid-prev_error pid-last_error; pid-last_error pid-error; pid-last_output pid-output; return pid-output; }参数整定经验先调P直到出现轻微振荡加入D抑制振荡最后加入I消除静差不同电压段可能需要不同的参数组4. 保护机制与故障处理完善的保护电路是电源设计的重中之重我的实现方案包含硬件和软件双重保护硬件保护输入过压TL431比较器直接关断MOSFET驱动输出过流ACS712霍尔传感器快速比较器MOSFET过热NTC贴片电阻迟滞比较软件保护优先级从高到低输入电压异常32V或8V输出短路检测dI/dt1A/ms温度监控MOSFET85℃功率器件失效检测PWM占空比与输出电压不符故障恢复流程采用渐进式策略首次故障立即关断需手动复位短时重复故障自动延迟5秒后尝试恢复持续故障锁定并记录错误代码5. 人机交互优化旋转编码器的处理需要消抖和加速算法void Encoder_Handler(void) { static int32_t counter 0; static uint8_t last_state 0; static uint32_t last_time 0; uint8_t current_state HAL_GPIO_ReadPin(ENC_A_GPIO_Port, ENC_A_Pin) | (HAL_GPIO_ReadPin(ENC_B_GPIO_Port, ENC_B_Pin) 1); // 状态机解码 if(last_state ! current_state) { uint32_t now HAL_GetTick(); uint32_t interval now - last_time; // 加速算法转速越快步进越大 int32_t step (interval 50) ? 5 : (interval 100) ? 2 : 1; if((last_state 0x00 current_state 0x02) || (last_state 0x03 current_state 0x01)) { counter step; } else { counter - step; } last_state current_state; last_time now; } // 应用数值变化 if(abs(counter) ENC_THRESHOLD) { int8_t dir (counter 0) ? 1 : -1; Adjust_Setting(dir * (abs(counter)/ENC_THRESHOLD)); counter 0; } }OLED显示采用自定义字体和分级菜单关键信息包括实时电压/电流大号字体设定值反色显示工作模式CV/CC温度及保护状态图标6. 实测性能与改进方向经过一周的连续老化测试主要性能指标如下测试项目条件结果电压精度5V输出±0.03V负载调整率0-5A跳变50mV跌落纹波噪声20V/3A输出12mVpp转换效率24V输入,12V/5A输出89%发现的几个待改进问题轻载时100mA效率骤降至65%快速切换设定值时会有约200ms的过冲编码器在潮湿环境下偶尔误触发可能的解决方案增加PFM模式改善轻载效率实现PID参数的自适应调整改用光学编码器提升环境适应性