第四节:数字控制环路设计

好,咱们今天聊聊数字控制环路。说实话,这是整个电源管理单元里最考验“软硬协同”功底的地方。你光懂模拟环路不行,光会写数字代码也不行,得把两边打通。

数字控制环路,说白了就是三个核心模块的配合:ADC(模数转换器)负责采样输出电压,数字补偿器(PID)负责算误差,DPWM(数字脉宽调制)负责把计算结果变成开关信号。中间还得有个状态机来调度它们。嗯,咱们一个一个拆开讲。

4.1 PID补偿器:数字域里的“比例-积分-微分”

模拟域里我们用运放搭PID,数字域里呢?其实就是用差分方程。我个人习惯用位置式PID,因为直观,调试起来方便。

先看公式:

u[k] = Kp * e[k] + Ki * sum(e[i]) + Kd * (e[k] - e[k-1])

这里:

  • e[k]:当前误差(参考电压 - 采样电压)
  • sum(e[i]):误差累积项,消除静差
  • e[k] - e[k-1]:误差变化率,抑制过冲

我在项目中遇到过一个问题:积分项饱和。说白了就是误差一直存在,积分项越积越大,最后输出饱和,系统失去调节能力。怎么解决?我建议加一个抗积分饱和(Anti-Windup)机制:

// 抗积分饱和实现
if (u[k] > U_MAX) {
    u[k] = U_MAX;
    // 停止积分累加
} else if (u[k] < U_MIN) {
    u[k] = U_MIN;
    // 停止积分累加
} else {
    // 正常累加积分
    integral += e[k];
}
小技巧:系数Kp、Ki、Kd怎么调?我一般先调Kp,让系统响应速度差不多;再加Ki消除静差;最后加Kd抑制过冲。别一上来三个一起调,容易崩。

4.2 数字脉宽调制(DPWM):从数字值到开关信号

DPWM的任务很简单:把PID算出来的占空比数字值,变成真实的PWM波形。但这里有个坑——分辨率

你想想看,ADC采样精度是10位,PID计算也是10位,但DPWM如果只有8位分辨率,那你的控制精度就被砍了一截。我建议DPWM的分辨率至少要比ADC高1~2位。

常见的DPWM实现方式有两种:

方式 原理 优缺点
计数器比较法 计数器从0计到周期值,与占空比寄存器比较 简单,但分辨率受时钟频率限制
延迟线法 用延迟链产生更精细的时间分辨率 分辨率高,但面积大,功耗高

我个人偏好混合架构:粗调用计数器,细调用延迟线。这样既保证了分辨率,又不会太耗资源。

// 简单的计数器比较法DPWM(Verilog风格)
always @(posedge clk) begin
    if (counter == PERIOD) begin
        counter <= 0;
        pwm_out <= 1'b1;
    end else if (counter == duty_cycle) begin
        pwm_out <= 1'b0;
        counter <= counter + 1;
    end else begin
        counter <= counter + 1;
    end
end
注意:DPWM的更新时机很关键。我曾经遇到过PID算完了,但DPWM还在用旧值,导致控制滞后。解决办法是加一个“更新使能”信号,让PID和DPWM同步。

4.3 ADC接口:别让数据“掉地上”

ADC接口设计,说白了就是怎么把ADC采到的数据安全地传给PID。这里有两个要点:

  • 同步采样:ADC的采样时钟要和DPWM的开关频率同步。我习惯在PWM的载波顶点触发ADC采样,这样采到的电压最稳定。
  • 数据缓冲:ADC转换需要时间,PID计算也需要时间。中间加一个FIFO或者寄存器缓冲,防止数据丢失。

我曾经踩过一个坑:ADC采样频率和PID计算频率不匹配。ADC每1us采一次,但PID每10us才算一次,中间9个数据全丢了。后来我改成“采样触发计算”模式——ADC采完一个数据,直接触发PID开始算,这样就不会丢数据了。

// ADC中断服务例程(伪代码)
void ADC_IRQHandler(void) {
    adc_data = ADC_GetValue();
    pid_input = adc_data;  // 直接传给PID
    pid_compute_flag = 1;  // 触发PID计算
}

4.4 状态机设计:环路的“大脑”

状态机负责调度整个控制环路。我见过很多新手把状态机设计得特别复杂,其实没必要。一个电源控制环路的状态机,通常只需要这几个状态:

  1. IDLE:等待启动
  2. SOFT_START:软启动,逐步增加占空比
  3. RUN:正常运行,PID闭环调节
  4. FAULT:故障处理(过压、过流等)

嗯,这里要注意:状态切换的条件一定要清晰。比如从SOFT_START切换到RUN,条件是输出电压达到参考值的90%以上。别用模糊的条件,否则状态机容易“卡死”。

// 状态机示例(C语言风格)
typedef enum {
    IDLE,
    SOFT_START,
    RUN,
    FAULT
} State_t;

State_t current_state = IDLE;

void StateMachine_Run(void) {
    switch (current_state) {
        case IDLE:
            if (start_flag) current_state = SOFT_START;
            break;
        case SOFT_START:
            if (vout >= 0.9 * vref) current_state = RUN;
            if (fault_flag) current_state = FAULT;
            break;
        case RUN:
            PID_Compute();  // 正常PID计算
            if (fault_flag) current_state = FAULT;
            break;
        case FAULT:
            // 执行保护动作
            if (reset_flag) current_state = IDLE;
            break;
    }
}
核心要点:状态机设计要“防抖”。比如检测到过流,别立刻进FAULT状态,先连续检测3次都过流再进。否则一个噪声脉冲就把系统搞复位了。

4.5 实战经验:环路调试的“三板斧”

最后分享一点调试经验。数字控制环路调不通,90%是这三个问题:

  • 时序不对:ADC采样、PID计算、DPWM更新,三个动作的时间关系没对齐。用示波器抓一下关键信号,看看是不是同步的。
  • 系数不对:Kp、Ki、Kd的数值范围没算对。我建议先在仿真里调好系数,再烧到芯片里。
  • 状态机跑飞:状态切换条件没覆盖全,或者有未定义的状态。加一个default分支,把异常状态引回IDLE。

我曾经有一个项目,环路怎么调都不稳定,最后发现是ADC的参考电压纹波太大。换了低噪声的参考源,问题立刻解决。所以啊,别光盯着数字部分,模拟部分的噪声也会影响环路性能

好了,数字控制环路设计就讲到这里。下一节咱们聊聊系统集成与验证,到时候把ADC、PID、DPWM、状态机全部串起来,做一个完整的电源管理单元。