FPGA数字控制Buck电源:从环路架构到Verilog实现PID与DPWM

发布时间:2026/9/20 11:16:01
FPGA数字控制Buck电源:从环路架构到Verilog实现PID与DPWM 简介一份围绕FPGA实现Buck型开关电源数字控制的研究论文重点探讨Buck变换器建模、DPID算法设计及多软件联合仿真方法。文档面向电力电子、数字控制方向的科研人员和学生尤其适合正在学习Verilog HDL与FPGA数字电源控制的读者。压缩包内包含1个docx格式的论文文档容量仅139KB全文结构清晰涵盖开关电源概述、Buck变换器工作模式、关键模块设计及Simulink实现等章节。该资源已有167人学习下载。论文详细介绍了小信号模型的数学建模、Verilog硬件描述语言编程架构、DPID控制算法的限幅防溢出处理以及通过HDL Verifier实现ModelSim与MATLAB/Simulink联合仿真的高效路径并总结了多软件协同开发在数字控制研究中的优势可帮助读者系统理解Buck型数字开关电源从理论建模、参数设计到仿真验证的全流程对跨学科学习具有较高参考价值。1. 用FPGA做Buck数字控制核心是建一条低延迟的闭环通路把这个标题拆开看Buck是功率级拓扑FPGA是控制器载体数字控制方法才是真正要解决的问题。在服务器电源、通信电源和车载电源里模拟PWM控制器正在被数字方案替换FPGA的并行处理能力让它在高开关频率、多路交错和复杂保护逻辑面前比MCU更有优势。这套东西的“研究”属性体现在建模、仿真、验证的闭环上不是把PID公式塞进Verilog就完事而是要打通ADC采样、误差计算、PWM调制、死区控制这条完整的数据通路。适合两类人一类是从模拟电源转数字控制的硬件工程师另一类是接了电源类项目的FPGA工程师。前者需要补FPGA时序思维后者需要补Buck环路知识。2. Buck数字控制的系统架构与控制模式选型2.1 同步Buck和异步Buck的驱动差异数字控制怎么适配Buck电路工作原理本身不复杂一个开关管、一个续流器件、一个LC滤波器。异步Buck用二极管续流压降大效率低但驱动简单同步Buck用低边MOSFET代替二极管效率高但需要一对互补PWM信号而且必须插入死区防止上下管直通。FPGA做数字控制时这两者的适配难度不在算法而在PWM输出端的时序处理。对同步Buck我一般会在FPGA内部生成一路PWM然后通过死区生成模块拆成两路互补信号。死区时间的典型值在50ns到200ns之间具体取决于MOSFET的关断延迟和驱动芯片的传播延迟。这个值不能设得太大太大影响效率也不能设得太小太小有直通风险。死区设置的另一个隐蔽问题是高边关断和低边开通的延迟不对称所以数字控制里最好把高边关断到低边开通、低边关断到高边开通的死区分开配置。module deadtime_generator #( parameter DT_H2L 8d10, // 高边关到低边开的死区 parameter DT_L2H 8d12 // 低边关到高边开的死区 )( input logic clk, input logic rst_n, input logic pwm_in, output logic pwm_h, output logic pwm_l ); logic [7:0] cnt; logic pwm_d1, pwm_d2; always_ff (posedge clk or negedge rst_n) begin if (!rst_n) begin pwm_d1 1b0; pwm_d2 1b0; cnt 8d0; end else begin pwm_d1 pwm_in; pwm_d2 pwm_d1; if (pwm_d2 ! pwm_d1) cnt pwm_in ? DT_H2L : DT_L2H; else if (cnt 0) cnt cnt - 1b1; end end assign pwm_h pwm_in (cnt 0); assign pwm_l ~pwm_in (cnt 0); endmodule这段代码的思路是检测PWM边沿变化后启动倒计时倒计时期间输出保持原状态倒计时结束才切换另一路输出。cnt的是在两个边沿分别加载不同的死区值正对应高边和低边不对称的延迟特性。这里把死区生成放在FPGA内部而不是外部驱动芯片里好处是参数可以直接通过寄存器在线修改调试时不用换元件。2.2 电压模式、峰值电流模式与滞环控制的FPGA适配度数字控制方法选型是整个项目的分水岭。最常见的三种方案是电压模式、峰值电流模式和滞环控制它们的FPGA实现难度和动态响应差别很大。电压模式是单环控制ADC只采样输出电压误差经过补偿器输出占空比。结构最简单适合作为第一版跑通闭环的方案。峰值电流模式是双环控制外环是电压环内环是电流环需要实时检测电感电流每个开关周期都要做一次比较。FPGA实现电流内环的难点在于电感电流的上升沿只有几百纳秒比较器必须在极短时间内判断是否翻转PWM这要求ADC或高速比较器的延迟极低时序约束也紧得多。滞环控制不需要补偿器输出电压低于下限就开高边高于上限就关动态响应最快但开关频率不固定EMI和滤波器设计都更麻烦。控制模式环路数量需要采样的信号FPGA实现成本动态响应典型应用电压模式单环输出电压低一般通用Buck、初次验证峰值电流模式双环输出电压、电感电流高快大电流DC-DC、服务器电源滞环控制无补偿器输出电压或输出纹波低最快LED驱动、对频率不敏感场景如果从零开始做我的建议是先用电压模式把整条链路跑通确认ADC采样、PID计算、DPWM输出没有问题再往内环加电流控制。直接上峰值电流模式出问题时很难定位是功率级的原因还是数字采样延迟的原因。2.3 控制环路的时序预算ADC采样、PID计算、DPWM更新之间的延迟数字控制器和模拟控制器的本质区别在于延迟。模拟PWM比较器可以在纳秒级响应误差变化数字控制器必须经过采样、量化、计算、更新PWM寄存器这一串流水线每一步都消耗时间。这个延迟就是数字控制环路的一个固有极点直接限制了可用的环路带宽。以一个开关频率为100kHz的Buck为例开关周期是10usADCPID计算和PWM更新必须在一个周期内完成否则控制量跟不上开关动作。用FPGA做这件事的优势在于流水线结构ADC数据到达后PID的乘加操作可以在几个时钟周期内完成而不是像MCU那样一条一条指令取指执行。常见做法的时序预算分配如下开关频率开关周期ADC转换预留PID计算预留PWM更新与死区环路总延迟100kHz10us4us1us200ns约5.2us500kHz2us800ns400ns150ns约1.35us1MHz1us300ns150ns50ns约0.5us环路总延迟占开关周期的比例越接近一半相位裕度损失越严重。所以FPGA实现时要用pipeline结构误差计算、比例项、积分项放在不同的时钟节拍完成而不是等所有数据准备好后一步算完。另外注意DPWM的占空比更新时机也很关键更新太早会造成次谐波振荡更新太晚相当于增加了PWM调制延迟。3. FPGA内部模块划分从ADC接口到DPWM的实现路径3.1 顶层模块划分与关键信号定义数字控制器的FPGA内部一般分为五个模块采样接口、参考电压与误差计算、PID补偿器、DPWM调制器、保护与状态机。顶层设计时要把模拟域和数字域的边界划清楚ADC输出的数据和PWM输出的驱动信号各自独立调试时方便用ILA单独抓一路。典型端口定义如下信号名称方向位宽说明clkinput1系统时钟100MHz或更高rst_ninput1异步复位同步释放adc_doutinput16ADC串行或并行数据输出adc_convstoutput1启动ADC转换信号pwm_houtput1高边驱动PWMpwm_loutput1低边驱动PWMfault_ininput1过流、过温保护输入实际项目中ADC接口可能是SPI、并行LVDS或者JESD204B具体取决于所选ADC型号和采样速率。低速场合用SPI接口的ADS7950或并行接口的AD7606就可以采样速率超过5MSPS时就开始考虑LVDS接口。FPGA的LVDS接收端内部有专门的硬件资源可以在几百兆赫兹下稳定接收数据但约束要写好。3.2 ADC数据同步与跨时钟域处理避免亚稳态ADC的采样时钟和FPGA系统时钟往往不是同一个源数据跨时钟域进入FPGA时寄存器的建立时间和保持时间可能不满足触发亚稳态。标题里提到的“FPGA复位信号亚稳态”只是亚稳态问题的一种场景实际上所有跨时钟域信号都有这个风险。我一般会在每个ADC数据位进入FPGA后先打两拍然后才参与计算。这是最简单的防亚稳态手段代价是增加两个时钟周期的延迟。如果ADC吞吐率很高数据是连续流式的就要用异步FIFO做缓冲FIFO的读写指针用格雷码跨时钟域同步。这里有一个常见错误有人认为只有复位信号需要关心亚稳态其实任何输入信号都可能有此问题只不过复位引发的问题更直接常被单独拿出来讲。3.3 定点PID的SystemVerilog实现考虑饱和与积分限幅PID参数在FPGA里用定点数表示不直接写浮点。选择Q格式时需要考虑电压采样值和占空比值的动态范围。比如ADC是16位输出电压反馈值范围0到65535参考电压设定在中间值32768附近误差范围在-32768到32767之间。PID输出要映射到DPWM的占空比寄存器通常也用16位。module pi_controller #( parameter AW 16, // ADC采样值位宽 parameter MW 32, // 乘累加结果位宽 parameter Q 16 // 定点数小数位数 )( input logic clk, input logic rst_n, input logic [AW-1:0] v_ref, input logic [AW-1:0] v_fb, input logic sample_valid, output logic signed [MW-1:0] duty_out ); localparam signed [15:0] KP 16d5243; // Q16格式实际约0.08 localparam signed [15:0] KI 16d3277; // Q16格式实际约0.05 localparam signed [31:0] SAT_MAX 32h00FF_FFFF; localparam signed [31:0] SAT_MIN 32hFF00_0000; logic signed [AW:0] err; logic signed [MW-1:0] p_term; logic signed [MW-1:0] i_term; logic signed [MW-1:0] i_acc; assign err $signed({1b0, v_ref}) - $signed({1b0, v_fb}); always_ff (posedge clk or negedge rst_n) begin if (!rst_n) begin p_term 0; i_term 0; i_acc 0; duty_out 0; end else if (sample_valid) begin p_term err * $signed(KP); i_term err * $signed(KI); if (i_acc i_term SAT_MAX) i_acc SAT_MAX; else if (i_acc i_term SAT_MIN) i_acc SAT_MIN; else i_acc i_acc i_term; duty_out (p_term i_acc) Q; end end endmodule这里p_term和i_term的计算使用并行乘法器在同一个时钟周期内完成。积分累加器i_acc加了上下限饱和防止占空比饱和后积分项继续增大造成恢复时的大超调也就是抗积分饱和。duty_out最终的右移Q位把定点数还原成整数占空比。这个模块的输入输出都是寄存器输出时序上是干净的。带宽整定时可以通过外接寄存器动态修改KP和KI不用重新综合但需要建立一组寄存器映射用来在调试时写参数。3.4 DPWM调制器计数器比较模式与占空比锁存DPWM的实现方式有计数器比较和CDCM(基于延迟链)两种。计数器比较模式的分辨率受系统时钟频率限制假设系统时钟100MHzPWM频率100kHz那么每个开关周期有1000个计数单位占空比分辨率大约是0.1%即10位精度。对大多数Buck应用够用。如果开关频率到1MHz计数单位只剩100个分辨率掉到1%环路会产生极限环振荡就要考虑延迟链方案。计数器比较模式的关键在占空比更新时机。下文代码中duty_reg只在计数器溢出时更新确保一个PWM周期内占空比保持恒定。如果duty随时可写入DPWM组合逻辑载波中途翻转会在开关波形上产生毛刺严重的会引起功率管误导通。module dpwm #( parameter N 16 )( input logic clk, input logic rst_n, input logic [N-1:0] duty, // PID输出的占空比 input logic update_en, // 允许更新标志 output logic pwm_out ); logic [N-1:0] counter; logic [N-1:0] duty_reg; always_ff (posedge clk or negedge rst_n) begin if (!rst_n) begin counter 0; duty_reg 0; end else begin if (counter {N{1b1}}) begin counter 0; if (update_en) duty_reg duty; end else counter counter 1b1; end end assign pwm_out (counter duty_reg); endmoduleupdate_en信号通常由控制状态机产生在ADC采样转换完成后拉高一个时钟周期这样PID输出到DPWM的更新正好落在PWM周期起始处。输出pwm_out再接到前面提到的死区生成模块配合成互补PWM对。整个链路从ADC采样到PWM更新的总延迟是固定的这个固定延迟在环路建模中可以用一个纯延时环节近似。4. 参数设计与时序约束让Bucket环路稳定而不振荡4.1 采样频率与开关频率的关系怎么定数字控制器的采样频率通常等于开关频率即在每个开关周期内采样一次输出电压。采样时刻必须避开功率管开关动作产生的振荡和振铃所以ADC触发信号一般由PWM计数器在特定计数值处产生而不是用固定速率自由运行。以100kHz开关频率为例PWM计数器在中间点触发ADC转换此时高边刚关断续流管导通开关节点电压处于相对稳定的状态。如果在这个点附近还有振铃就是硬开关带来的振铃耦合到采样电路上常见处理是在ADC前端加RC滤波截止频率设置在开关频率的5到10倍用来滤掉振铃而不过度衰减有效信号。Buck电路元件选型在这个过程中联动影响采样输出电容的ESR过高会让采样毛刺变大ESR过低又会让环路稳定所需的零点频率偏移这两者要平衡。开关频率与ADC吞吐率的对应关系单次采样一周期100kHz需要100kSPS500kHz需要500kSPS1MHz需要1MSPS。注意所有数字电源的ADC吞吐率需求都不高难的是保持转换延迟一致性也就是每次触发到数据有效的时间要稳定不能有时长时短否则环路延迟就不是固定的补偿器会失配。4.2 反馈通道与PWM通道的延迟预算分配把整个闭环从ADC到PWM拆成时间轴有以下关键延迟节点延迟链节点产生位置典型时间ADC采样保持与转换延迟ADC芯片内部0.5us - 3usFPGA输入同步打拍跨时钟域处理2-3个系统时钟PID计算流水线FPGA逻辑3-5个系统时钟PWM寄存器锁存等待等待PWM周期边界0 - 1个开关周期死区延迟FPFA内死区生成50ns - 200nsPID计算流水线是固定的时钟周期数不管有多少个乘法器并行总能在几个时钟周期内出结果。真正不可控的是PWM寄存器锁存等待它要等到计数器溢出才能更新占空比这个等待时间平均是半个开关周期。这个等待时间占整个环路延迟的比例很大所以常见做法是把DPWM的更新点对齐到ADC触发点后固定几个时钟周期让整个环路延迟变成确定值补偿器根据这个确定的延迟来设计。4.3 时序约束怎么写输入延迟与跨时钟域约束FPGA工程中时序约束是数字电源和普通逻辑项目最大的区别。普通逻辑只要保证时钟频率能满足即可数字电源还要精确约束ADC数据和PWM输出的时序。create_clock -name sys_clk -period 10.000 [get_ports clk] create_clock -name adc_clk -period 25.000 [get_ports adc_clk] set_input_delay -clock adc_clk -max 8.0 [get_ports {adc_data[*]}] set_input_delay -clock adc_clk -min 2.0 [get_ports {adc_data[*]}] set_false_path -from [get_clocks adc_clk] -to [get_clocks sys_clk]set_input_delay的值由ADC芯片的数据手册给出max和min分别对应数据在时钟沿后多长时间到达。如果不写这两个约束综合工具会认为数据在时钟沿时刻到达优化结果与实际不匹配。set_false_path加在ADC异步时钟域和系统时钟之间告诉工具这两个时钟域之间的路径不需要分析——因为已经在逻辑层做了同步打拍或FIFO处理。此外复位信号也要做约束。异步复位、同步释放是数字电源项目里的标配复位释放时正好落在时钟上升沿附近会触发亚稳态同步化处理后可以避免恢复时间违例。约束上与普通时钟约束相同但代码里要用两级同步器不能把外部复位直接接到所有触发器的异步复位端。4.4 用仿真验证闭环响应开环先过再闭口写testbench验证PID模块时要把系统建模为被控对象模型而不是只检查PID的输出值。简单的Buck系统模型可以用一个状态空间方程描述在testbench里用离散时间步进模拟输出电压对占空比的响应。timescale 1ns/1ps module tb_pi_controller; logic clk; logic rst_n; logic sample_valid; logic [15:0] v_ref; logic [15:0] v_fb; logic signed [31:0] duty_out; pi_controller dut (.*); // 模拟Buck功率级一阶惯性并叠加延迟 // vout (duty_out - vout) * 0.1 real vout; real duty_real; always #50 begin duty_real $itor(duty_out) / 65536.0; vout vout (duty_real * 12.0 - vout) * 0.08; v_fb $rtoi(vout / 12.0 * 65535.0); end initial clk 0; always #5 clk ~clk; initial begin rst_n 0; v_ref 16d32768; v_fb 16d30000; vout 5.5; #100 rst_n 1; repeat (20) begin #100 sample_valid 1; #10 sample_valid 0; end #1000 $finish; end endmoduletestbench里用一个real类型的变量模拟输出电压每50ns更新一次。这个模型是简化的但仿真出来的PID输出趋势与真实环路一致足以用来验证模块功能。真正上板前还要做两个检查第一开环状态下给固定占空比输出电压是否稳定在期望范围内第二闭环后从轻载切重载观察输出电压跌落幅度和恢复时间是否符合预期。4.5 上板调试用ILA抓PID输出和PWM占空比FPGA调试数字电源最常用的工具是Vivado或Quartus里的逻辑分析仪核在设计中插入ILA观察内部信号。抓取要点是把v_fb、err、duty_out、pwm_out同时接进去触发条件设为err的绝对值超过某个阈值这样能捕捉到负载突变瞬间的环路响应。抓到的波形中如果duty_out出现大幅振荡而且频率在1kHz到10kHz之间说明环路增益过大比例项增益要下调。如果负载突变后duty_out长时间饱和在最大值积分项饱和限制可能太紧需要放宽SAT_MAX。如果v_fb上叠加了周期性毛刺而且毛刺频率等于开关频率说明ADC采样时刻没有避开开关动作要调整PWM计数器中间点触发的具体计数值。ILA采样时钟用系统时钟采样深度不低于4096确保能在单次触发中覆盖整个瞬态恢复过程。5. 应用扩展多相交错BucK与数字均流5.1 多相交错如何降低输出纹波单相Buck在输出大电流时电感体积和输出电容需求都会变大。多相交错Buck的基本思路是N个Buck单元并联每个单元的PWM相位错开360/N度各相电流纹波在输出端部分抵消。四相设计可以将有效纹波频率提升到开关频率的4倍输出电容的容值和体积显著下降。FPGA实现交错PWM非常自然。每个DPWM模块独立运行各相计数器的初始值分别设为周期的0/N、1/N、2/N……即可产生错开相位差。5.2 数字均流每相独立电流环还是共电压环加均流支路多相交错的难点在均流。由于各相元器件参数不完全一致各相电感量和导通电阻有差异占空比一样时各相电流并不相等。数字控制的常见做法是电压外环共用一路PID输出一个总占空比基准每相各加一个均流补偿项该补偿项由本相电流与平均电流之差乘以系数生成叠加到本相占空比上。均流系数不宜过大过大容易引起相间振荡。我一般从每千安培误差对应调整5%占空比起步在满载条件下观察各相电流是否趋近。如果发现某相电流持续偏低先检查该相PWM驱动波形是否有死区异常不一定是均流环路的问题——功率级的Layout不对称也可能造成电流偏差。5.3 验证技巧触发条件设在PWM周期中间点最后给一个实用技巧抓取ILA波形验证环路响应时把采样时钟取为系统时钟但触发条件要精确设在PWM周期中间点。配置逻辑大致是当计数器过中间值且err超出阈值时触发采样。这样同时能看到ADC采样点位置和该周期更新后的占空比值判断补偿器是否在一个开关周期内完成了对扰动的首次修正。如果触发采样的数据里占空比更新滞后了两个以上周期说明PID流水线等待事件过长要把计算提前一拍或简化乘法器链路。本文还有配套的精品资源点击获取