FPGA直流电机PWM控制:占空比原理、Verilog实现与闭环设计 简介面向FPGA与电子信息类毕业设计的直流电机PWM控制完整项目包内容贯通硬件描述、逻辑仿真、综合实现到论文答辩全部环节。包内共127个文件压缩包约2.1MB主要包含VHDL硬件描述源码.vhd、原理图与工程文件.bdf/.bsf/.qsf/.qpf、编译下载文件.sof/.pof以及开题报告、毕业论文.doc/.docx和答辩PPT.pptx可完整呈现基于Quartus的FPGA设计工程从代码编写、功能仿真到板级验证的流程。已有1544人学习下载。整套资料围绕脉宽调制PWM生成、占空比调节、电机转速控制和顶层模块互联展开不仅给出定时器、计数器、比较器及滤波器等关键代码片段和顶层原理图还提供配套文档与答辩材料便于理解FPGA并发处理在电机调速中的应用。适合需要参考完整项目结构、快速上手FPGA开发流程并希望独立完成类似毕设课题的本科生与研究生。1. FPGA直流电机PWM控制毕设先拆需求再选方案很多人第一次把 FPGA 和真实硬件连在一起不是点灯而是让一台直流电机按设定速度转起来。“FPGA 直流电机 PWM 控制”这个标题看起来简单接线只有几根Verilog 代码也不过几十行但真正到了毕设验收现场老师追问的往往是那几个“简单”的点PWM 频率为什么选 20kHz占空比和平均电压是什么关系把 FPGA 换成单片机行不行问题答不上来手上的”全套”资料就只是 PPT 复述。这篇文章把一套能落地的 FPGA 直流电机 PWM 控制方案拆开讲清楚。我默认你手上有 FPGA 开发板、一个直流电机和一块常见驱动模块目标是打通“按键或串口设定目标转速 → FPGA 产生 PWM → H 桥驱动电机 → 编码器反馈回 FPGA”的完整链路同时把每个环节的参数含义、计算方法和典型坑点说清楚。新手能按步骤做出来老手可以直接拿走关键参数做移植。2. FPGA直流电机PWM控制的硬件架构与器件选型2.1 电机调速为什么选PWM占空比与平均电压的关系直流电机调速的本质是调节电枢两端的平均电压。线性调压方案简单但损耗大串电阻调速在负载变化时转速不稳用 MOSFET 按固定频率通断电源负载上得到的是脉冲电压电机电枢绕组的电感会让电流近似连续等效平均电压就等于占空比乘以电源电压U_avg D × Vcc这里 D 是占空比即一个 PWM 周期内高电平时间与周期的比值。占空比 50%、电源电压 12V 时平均电压是 6V电机转速大致减半。这个公式是毕设论文里第一个要写清楚的部分也是后面所有参数计算的基础。PWM 频率的选择则要看电机和驱动电路的电气特性。频率太低电流纹波大电机在低频段发出啸叫转速也容易抖动频率太高MOSFET 的开关损耗上升驱动芯片明显发热而且栅极驱动电路需要更强的瞬态电流能力。常见做法是有刷直流电机取 10kHz 到 25kHz我一般直接选 20kHz正好避开人耳敏感频段同时留足开关损耗余量。PWM频率范围电机表现适用场合100Hz ~ 1kHz啸叫明显转速波动大电流纹波大简易小车、低速演示、教学实验10kHz ~ 20kHz噪音低电流连续效率较高绝大多数有刷直流电机、舵机控制50kHz 以上纹波进一步降低但驱动损耗上升高速无刷电机、高性能模型电机这里说的频率是 PWM 开关频率不是电机换向频率。无刷直流电机BLDC的 PWM 频率和换相逻辑是两层概念很多毕设会把“有刷电机调 PWM、无刷电机调换相”当成扩展方向。如果你拿到的是 48V 无刷直流电机驱动项目那么在 FPGA 里要额外实现换相时序本篇文章讲的单路 PWM 生成模块仍然是最底层基础。2.2 FPGA、驱动芯片和H桥各自负责什么毕设电路最常见的架构是“FPGA 最小系统 电源 电机驱动模块 直流电机”驱动模块使用集成 H 桥芯片比如 TB6612FNG、DRV8833、L298N。FPGA 只需要输出 PWM 和方向控制信号驱动芯片负责功率放大和续流。这种分工不是偷懒而是工程上必然的边界划分FPGA 引脚驱动能力只有几毫安直接驱动继电器或电机必然烧毁驱动芯片内部集成了 MOSFET、续流二极管和逻辑电平转换能把 3.3V 逻辑电平扩成电机电流。选择驱动芯片时要看三个参数持续电流、逻辑电平范围、最高 PWM 频率。TB6612FNG 持续电流约 1.2A峰值 3A逻辑电压最低可以到 2.7VPWM 频率上限在 100kHz 以上做 12V 以下有刷电机毕设基本够用。市面上的驱动模块通常已经带好电源滤波电容和逻辑电源稳压直接接线即可。如果题目要求自己设计 H 桥那就需要 4 个 N 沟道 MOSFET并额外处理栅极驱动和死区FPGA 生成的 PWM 信号还要经过光耦或栅极驱动器才能接到功率管。FPGA 和单片机在这套任务里的分工差异是答辩时很容易被问到的问题任务FPGA实现单片机实现备注多路PWM硬件计数器并行输出不占CPU定时器中断更新占空比多路时抖动明显FPGA适合多电机、多舵机场景方向控制方向信号与PWM在时钟沿对齐GPIO翻转即可两边都不难故障响应过流信号直接触发组合逻辑ns级关断中断响应几十微秒FPGA优势明显速度闭环固定时钟周期执行PI运算定时器中断做软件PID高频电流环更适合FPGA这套分工决定了 Verilog 代码里的模块边界。我一般在 FPGA 内把功能拆成独立模块PWM 生成、方向控制、编码器四倍频计数、故障保护、串口或按键交互。每个模块只保留简单接口联调时出了问题能直接定位到具体模块而不是在一个大状态机里从头梳理逻辑。2.3 H桥PWM电路的数学原理与死区取值如果题目要求自己搭 H 桥死区dead time是绕不过去的问题。H 桥对角线上的两个功率管交替导通但如果同一侧上下两只管同时导通电源会直接短路形成直通shoot-through。为了避免直通必须保证一只功率管完全关闭后另一只才开始导通这段等待时间就是死区。死区时间取决于 MOSFET 的栅极电荷和栅极驱动电路能力常见范围是 100ns 到 1μs。假设系统时钟 50MHz一个时钟周期 20ns死区设为 10 个时钟周期就是 200ns适合低速 MOSFET。需要注意的是死区并不是越长越安全它会让有效占空比变小。考虑死区后的有效占空比近似为D_eff D - 2 × t_dead × f_pwm例如 f_pwm 20kHzt_dead 500ns则 2 × 500ns × 20kHz 0.02即占空比损失 2%。在需要精确占空比的场合应当根据这个公式做死区补偿把目标占空比提前调大。联调时先看示波器上的上下管栅极波形慢慢调整死区值直到既看不到同时导通又不过分损失占空比。3. Verilog实现PWM生成、占空比计算与仿真验证3.1 一个可综合的PWM生成模块FPGA 生成 PWM 的核心是一个计数器和一个比较器。计数器在每个时钟沿加一到达周期值后清零比较器判断当前计数值是否小于占空比阈值决定输出电平。下面是最小可综合模块module pwm_gen #( parameter CLK_FREQ 50_000_000, // 系统时钟频率单位Hz parameter PWM_FREQ 20_000, // PWM频率单位Hz parameter PWM_BITS 16 // 占空比寄存器位宽 )( input wire clk, input wire rst_n, input wire [PWM_BITS-1:0] duty, // 期望占空比范围0~CNT_MAX output reg pwm_out ); localparam CNT_MAX CLK_FREQ / PWM_FREQ; // 每个PWM周期的计数值 reg [PWM_BITS-1:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 0; else if (cnt CNT_MAX - 1) cnt 0; else cnt cnt 1; end always (posedge clk or negedge rst_n) begin if (!rst_n) pwm_out 0; else pwm_out (cnt duty) ? 1b1 : 1b0; end endmodule逻辑说明第一个always块让计数器在 0 到CNT_MAX - 1之间循环形成一个周期为CLK_FREQ / PWM_FREQ的锯齿波第二个always块把计数器值和duty比较输出高电平的时间比例就是占空比。占空比等于duty / CNT_MAX例如CNT_MAX 2500时duty 1250对应 50%。参数说明PWM_BITS决定占空比量化精度。16 位在 20kHz 频率下每档调节对应 12V / 2500 ≈ 4.8mV 的平均电压变化足够平滑如果位宽不够低速时转速会明显跳变。CNT_MAX必须小于等于2^PWM_BITS否则计数器会溢出。这里duty最大可以取到CNT_MAX此时cnt永远小于duty输出恒为高正好实现 100% 占空比。3.2 频率、占空比和位宽的计算方法硬件代码里的参数不是随便写的核心公式只有两个计数周期值CNT_MAX CLK_FREQ / PWM_FREQ占空比分辨率1 / CNT_MAX以 50MHz 时钟、20kHz PWM 为例CNT_MAX 2500占空比分辨率为 0.04%。如果改用 5kHz 低频CNT_MAX 10000分辨率提升到 0.01%但电机会开始啸叫。想要同时做到“高频高分辨率”要么提高系统时钟要么引入均匀脉冲调制这类算法后者对毕设来说通常没有必要。实际调试时我习惯先用一段脚本把参数算好再填进代码python3 - EOF clk 50_000_000 f_pwm 20_000 cnt_max clk // f_pwm bits (cnt_max - 1).bit_length() print(fCNT_MAX {cnt_max}) print(fPWM_BITS 至少需要 {bits} 位) print(fduty1250 时占空比 {1250/cnt_max*100:.1f}%) EOF这段脚本的作用是避免人肉口算CNT_MAX一旦超过寄存器位宽计数器会在中间被截断PWM 频率完全错乱。PWM_BITS取cnt_max - 1的位宽再向上留 1 到 2 位余量保证duty能表示到 100%。3.3 仿真测试验证占空比和复位行为写完模块后不要直接上板先跑一遍行为仿真。下面是一个最小 testbenchmodule tb_pwm_gen(); reg clk 0; reg rst_n 0; reg [15:0] duty 1250; wire pwm_out; always #10 clk ~clk; // 周期20ns频率50MHz pwm_gen #( .CLK_FREQ(50_000_000), .PWM_FREQ(20_000), .PWM_BITS(16) ) uut ( .clk(clk), .rst_n(rst_n), .duty(duty), .pwm_out(pwm_out) ); initial begin repeat(5) (posedge clk); rst_n 1; #1_000_000; // 仿真1ms观察20个PWM周期 if (pwm_out) $display(pwm out high); duty 2500; // 100%占空比 #1_000_000; $finish; end endmodule仿真时重点关注两点。第一复位释放后的第一个 PWM 周期里输出高电平时间是否正确很多设计会在复位边沿产生一个异常窄脉冲。第二把duty改成 0 和CNT_MAX确认能输出真正的 0% 和 100%。仿真通过后再用示波器测 FPGA 引脚此时如果波形不对问题往往在引脚约束或探头接地而不是逻辑本身。4. 直流电机PWM控制联调、PWM故障保护与常见坑4.1 联调前的接线与最小系统检查上电之前先做一次系统检查这一步能省掉至少一半的调试时间。核心是确认电源、逻辑电平和地线都正确特别是驱动模块的电机电源地与 FPGA 的逻辑地一定要共地否则 PWM 高电平没有参考地驱动芯片会收到不确定电平。检查项典型参数常见问题FPGA开发板时钟25/50/100MHz代码里的CLK_FREQ与实际晶振不一致直流电机电源额定电压、电流2倍余量电源容量不足加载后电压跌落驱动模块TB6612FNG或DRV8833电机电源与逻辑电源未共地编码器AB相或霍尔信号信号线上缺少上拉FPGA读到乱电平PWM引脚3.3V FPGA输出接驱动芯片输入未查驱动芯片逻辑电平范围我一般按这个顺序上电先不插电机给 FPGA 和驱动模块单独供电用示波器确认 PWM 引脚有正确波形然后空载接电机让占空比从 0 逐步升到 50%不要一次给满最后带载测试听声音、摸芯片温度、看母线电压。如果第二步波形异常不要急着怀疑电机先回头检查时钟参数和约束。4.2 PWM故障保护从组合逻辑直关到故障锁存PWM 故障保护是毕设里最容易被忽略但老师最爱追问的点。直流电机堵转时电流可能达到额定电流的 5 到 10 倍驱动芯片温度快速上升如果 FPGA 继续输出 100% 占空比基本会烧管子。单片机方案通常用 ADC 采样电流软件中断里拉低 GPIOFPGA 方案则要把保护做到硬件路径上过流比较器输出直接进入 FPGA组合逻辑立即关断 PWMmodule pwm_safety ( input wire clk, input wire rst_n, input wire fault_n, // 过流/过温信号低有效 input wire pwm_raw, output wire pwm_safe ); // 故障期间直接关断不使用时钟寄存响应时间只有门级延迟 assign pwm_safe fault_n ? pwm_raw : 1b0; reg fault_flag; always (posedge clk or negedge rst_n) begin if (!rst_n) fault_flag 1b0; else if (!fault_n) fault_flag 1b1; // 故障锁存需要外部复位清除 end endmodule逻辑说明assign pwm_safe这条组合逻辑路径在fault_n变低的瞬间就会把 PWM 输出拉低响应时间只有几十纳秒远比中断可靠。fault_flag的作用是锁存故障状态防止故障源恢复后电机自动重新启动必须由按键或上位机发送清除命令才能复位。这个设计在答辩时可以详细展开因为它是 FPGA 和单片机方案拉开差距的关键点。4.3 频率、占空比和死区带来的五类典型故障联调阶段的故障通常集中在五个方面我把排查经验整理成一张表现象可能原因定位手段电机不转只有嗡嗡声PWM频率太低或占空比低于启动阈值示波器测PWM周期检查duty值驱动芯片发烫死区太短上下管直通示波器双通道测上下管栅极波形转速和占空比不成比例空载电机存在摩擦和电枢压降用测速器记录转速曲线100%占空比时输出异常CNT_MAX超过PWM位宽或duty被截断重新计算CNT_MAX和PWM_BITS编码器计数抖动信号边沿未做同步处理毛刺干扰在FPGA里加两级同步器或输入滤波占空比小于 5% 时电机启动扭矩不足是一个容易被忽略的问题。负载电机在静止状态下需要额外扭矩克服静摩擦如果目标占空比只有 3%电机可能一直不转。常见做法是加入启动补偿检测到目标转速大于 0 时先输出 20% 到 30% 占空比一小段时间电机转起来后再降回目标占空比。这个逻辑在 FPGA 里可以用一个有限状态机实现启动状态和正常运行状态分占两个状态。5. 从开环PWM到闭环测速毕设答辩加分实践5.1 用编码器和PI控制器闭合速度环开环 PWM 控制已经能让电机转起来但一旦负载增加转速会明显下降而占空比并没有变化。如果只在论文里写“电机转速随占空比变化”说服力不够。闭环速度环的最小实现是AB 相编码器接入 FPGA四倍频计数后得到单位时间脉冲数换算成转速然后经过比例积分控制器输出新的占空比。PI 参数计算比代码更关键。假设编码器是 1000 线四倍频后每转 4000 个计数采样周期 10ms那么转速计算公式为RPM cnt_10ms × 60 / (4000 × 0.01)例如 10ms 内计数 400 个转速就是 400 × 60 / 40 600 RPM。积分项需要限幅防止电机堵转时占空比积到 100% 无法恢复。比例项和积分项的系数可以先在仿真里用阶跃响应调一轮再上板细调。5.2 用ILA和awk脚本量化验证占空比上板验证不能只靠“感觉转得快”。在 Vivado 里把 PWM 信号加到 ILA集成逻辑分析仪采下来导出 CSV 后可以直接统计占空比awk -F, NR1 { if ($21) high; total } END { printf 占空比%.2f%%\n, high/total*100 } pwm_capture.csv这个命令读取 ILA 导出的两列数据第二列为 PWM 信号值。注意 ILA 采样时钟要至少高于 PWM 频率 10 倍20kHz 的 PWM 用 50MHz 采样单周期内能采到 2500 个点统计结果才可靠。同样的方法可以验证 PID 效果记录不同占空比下编码器换算出的 RPM画出一条转速-占空比曲线这条曲线就是论文里最有说服力的实验数据。5.3 答辩现场老师会追的三个问题第一个问题是“PWM 频率为什么选这个值”。回答时不要只说“为了避噪音”要给出计算逻辑电机电感 L 和电阻 R 决定电流时间常数PWM 频率应当远高于 1/(L/R)同时开关损耗又要控制在可接受范围20kHz 是两者平衡点。第二个问题是“死区设了多大依据是什么”。回答时直接给出2 × t_dead × f_pwm的占空比损失公式说明死区不是随便定的。第三个问题是“你的系统里 FPGA 比单片机强在哪”。回答时指向组合逻辑故障保护和并行多路 PWM顺便提一句如果继续做速度观测器FPGA 还可以把卡尔曼滤波作为独立模块加速这个延展方向也符合当前 FPGA 控制应用的趋势。最后留一个实用技巧在 FPGA 顶层代码里把 PWM 占空比寄存器通过串口或片上逻辑分析仪实时读出来演示时调大调小目标转速同时显示实际占空比和编码器速度。这一屏数据比任何波形图都能直接回答“你的控制系统到底稳不稳”。本文还有配套的精品资源点击获取