用STM32和PID算法,手把手教你做一个带双闭环的数控电源(附完整代码)
基于STM32的双闭环数控电源实战从PID算法到工程实现在电子设计领域数控电源一直是工程师们热衷探索的项目。不同于传统的线性电源或简单开关电源基于STM32和PID算法的双闭环数控电源能够实现更精确的电压/电流控制、更快的动态响应以及更完善的保护机制。本文将从一个嵌入式开发者的角度分享如何将BUCK电路与STM32相结合通过双闭环PID控制打造一个真正实用的数控电源系统。1. 系统架构设计与核心思想1.1 双闭环控制的基本原理双闭环控制系统由电压环(外环)和电流环(内环)组成这种架构在电源设计中具有显著优势电压环负责维持输出电压稳定响应负载变化电流环快速调节开关管占空比抑制电流波动环间协同两环通过状态机自动切换实现CV/CC模式无缝过渡提示在轻载时优先使用电压环当电流接近设定值时自动切换到电流环这种设计可避免模式切换时的振荡问题。1.2 STM32的硬件资源配置为实现精确控制需要对STM32的硬件资源进行合理分配外设模块功能描述推荐配置ADC电压/电流采样12位分辨率1MHz采样率TIMERPWM波形生成高级定时器(TIM1/TIM8)GPIO状态指示与保护控制推挽输出模式USART参数调试与监控115200bps波特率// PWM初始化示例代码 void PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); TIM_TimeBaseStructure.TIM_Period 1000-1; // PWM周期 TIM_TimeBaseStructure.TIM_Prescaler 72-1; // 72MHz/721MHz TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, TIM_TimeBaseStructure); TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 0; // 初始占空比 TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM1, TIM_OCInitStructure); TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); }2. PID算法的实现与调参技巧2.1 增量式PID的工程优化传统位置式PID在嵌入式系统中存在积分饱和问题我们采用增量式算法并进行多项优化抗积分饱和对输出进行限幅处理微分先行只对测量值微分减少设定值突变的影响变积分系数根据误差大小动态调整积分作用typedef struct { float kp, ki, kd; // PID参数 float ap, ai, ad; // 增量式系数 float error; // 当前误差 float prevError1; // 前一次误差 float prevError2; // 前两次误差 float outLast1; // 上一次输出 float inc; // 增量输出 float iLimitHigh; // 输出上限 float iLimitLow; // 输出下限 } PidObject; float pidUpdate(PidObject* pid, const float desired, const float now_value) { float output 0.0f; pid-error desired - now_value; // 增量计算 pid-inc (pid-ap * pid-error) - (pid-ai * pid-prevError1) (pid-ad * pid-prevError2); output pid-inc pid-outLast1; // 输出限幅 if(output pid-iLimitHigh) output pid-iLimitHigh; if(output pid-iLimitLow) output pid-iLimitLow; // 更新状态 pid-prevError2 pid-prevError1; pid-prevError1 pid-error; pid-outLast1 output; return output; }2.2 参数整定的工程方法PID参数的调试是系统稳定的关键推荐采用以下步骤先调电流环将电压环设为开环专注于电流响应从纯比例开始逐步增加P值直到出现轻微振荡加入微分作用抑制超调最后加入积分消除静差再调电压环闭合电流环调试电压外环采用较电流环更小的比例系数适当增加积分时间常数典型参数参考值控制环KpKiKd适用场景电流环0.5-2.00.05-0.20.01-0.1动态响应要求高电压环0.1-0.50.01-0.050-0.02稳态精度要求高3. 状态机与保护机制实现3.1 工作模式状态转换数控电源需要在恒压(CV)、恒流(CC)模式间智能切换状态机是最佳实现方式graph LR A[启动] -- B{故障检测} B -- 正常 -- C[CV模式] C -- D{电流设定值?} D -- 是 -- E[CC模式] E -- F{电压设定值?} F -- 是 -- C B -- 异常 -- G[保护模式] G -- H{故障解除?} H -- 是 -- B对应的代码实现typedef enum { CV_MODE, // 恒压模式 CC_MODE, // 恒流模式 FAULT_MODE // 故障保护 } ControlMode; void ControlTask(void) { // 读取反馈值 float fb_io getCurrentFeedback(); float fb_vo getVoltageFeedback(); // 故障检测 if(checkFaultConditions()) { enterProtectionMode(); return; } // 模式切换逻辑 switch(device.controlMode) { case CV_MODE: device.controlPWM pidUpdate(vPID, vPID.desired, fb_vo); iPID.outLast1 device.controlPWM; if(fb_io iPID.desired) device.controlMode CC_MODE; break; case CC_MODE: device.controlPWM pidUpdate(iPID, iPID.desired, fb_io); vPID.outLast1 device.controlPWM; if(fb_vo vPID.desired) device.controlMode CV_MODE; break; default: device.controlMode CV_MODE; break; } updatePWM(device.controlPWM); }3.2 多重保护机制设计可靠的保护系统是电源设计的关键我们实现了五重保护机制输入过压/欠压保护监测输入电压范围超出阈值立即关闭PWM输出过流保护实时比较电流采样值与设定阈值触发后进入锁定模式需手动复位过温保护通过NTC检测关键器件温度分级保护先降额后关断短路保护硬件比较器快速响应软件检测作为二级保护软启动机制PWM占空比缓慢增加避免开机冲击电流void checkProtection(void) { // 输入电压保护 device.iovp (power.Vi PW_VOLTAGE_IN_MAX) ? 1 : 0; device.iuvp (power.Vi PW_VOLTAGE_IN_MIN) ? 1 : 0; // 输出保护 device.oocp (power.Io PW_CURRENT_OUT_MAX) ? 1 : 0; device.oovp (power.Vo (PW_VOLTAGE_OUT_MAX 0.5f)) ? 1 : 0; // 温度保护 device.otp (temp.mosfet 85.0f) ? 1 : 0; // 任何保护触发 if(device.iovp || device.iuvp || device.oocp || device.oovp || device.otp) { PWM_Stop(); device.faultFlags (device.iovp 0) | (device.iuvp 1) | (device.oocp 2) | (device.oovp 3) | (device.otp 4); LED_FAULT_ON; } }4. 工程实践中的难点与解决方案4.1 采样噪声抑制技术高精度电源对采样信号质量要求极高我们采用多重滤波手段硬件滤波一级RC低通滤波(cutoff ~10kHz)二级运放构成的Sallen-Key滤波器软件滤波滑动平均滤波(窗口大小8-16)递推中值滤波卡尔曼滤波(对动态响应要求高的场合)#define FILTER_WINDOW_SIZE 16 typedef struct { float buffer[FILTER_WINDOW_SIZE]; uint8_t index; float sum; } MovingAverageFilter; float movingAverageUpdate(MovingAverageFilter* filter, float newValue) { // 减去最旧的值 filter-sum - filter-buffer[filter-index]; // 添加新值 filter-buffer[filter-index] newValue; filter-sum newValue; // 更新索引 filter-index (filter-index 1) % FILTER_WINDOW_SIZE; return filter-sum / FILTER_WINDOW_SIZE; }4.2 动态响应优化策略针对负载突变时的电压跌落问题我们开发了多种补偿技术前馈补偿根据负载电流变化率提前调整PWM非线性PID误差大时增大P值误差小时侧重I值自适应控制根据工作点自动调整PID参数动态响应测试数据对比控制策略恢复时间(10%-90%)超调量稳态误差传统PID2.1ms8%±0.5%前馈补偿1.4ms5%±0.3%自适应非线性PID0.9ms2%±0.1%4.3 校准与量产测试方案为确保每台电源的一致性我们设计了完整的校准流程零点校准短路输出端记录ADC读数作为电流零点关闭PWM记录电压采样零点比例校准施加已知负载电流调整比例系数使用精密电压源校准电压采样通道全量程验证从10%到100%额定负载阶梯测试记录关键点的电压调整率typedef struct { float ioZero; // 电流零点偏移 float ioRatio; // 电流比例系数 float voRatio; // 电压比例系数 } CalibrationParams; void calibrationProcess(void) { CalibrationParams calib; // 电流零点校准(输出短路) calib.ioZero 0; for(int i0; i100; i) { calib.ioZero readADCCurrent(); delay(10); } calib.ioZero / 100; // 电流比例校准(施加1A负载) applyLoad(1.0f); // 1A负载 float realCurrent measureCurrentWithPrecisionMeter(); float adcValue 0; for(int i0; i100; i) { adcValue readADCCurrent() - calib.ioZero; delay(10); } adcValue / 100; calib.ioRatio realCurrent / adcValue; // 保存校准参数到Flash saveCalibrationToFlash(calib); }在完成所有硬件连接和软件编程后第一次上电时建议先将PID参数设为0然后逐步增加比例项观察系统响应。实际调试中发现BUCK电感的饱和电流必须留足余量否则在大电流工作时会导致控制环路异常。