Quick Start(快速上手)
本指南面向具备基础数字逻辑设计经验的工程师,帮助您从零搭建一套基于PWM的直流电机控制与速度调节系统。您将学会如何生成可调占空比的PWM信号,并将其与电机驱动模块连接,最终实现电机正反转与无级调速。
前置条件与环境
开始前请确认以下硬件与软件环境已就绪:
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| FPGA开发板 | Xilinx Artix-7 (XC7A35T) 或 Altera Cyclone IV (EP4CE6) | 需包含50MHz时钟源、至少20个可用IO | 任何支持50MHz时钟、IO数量≥20的板卡 |
| EDA工具 | Vivado 2019.1 或 Quartus Prime 18.1 | 版本至少2019.1,确保IP核兼容 | 更高版本需检查IP核版本一致性 |
| 仿真器 | Vivado Simulator 或 ModelSim/Questa | 用于波形验证 | GHDL(开源,需额外配置) |
| 时钟/复位 | 板载50MHz晶振;低电平有效复位 | 可使用PLL倍频或分频 | — |
| 电机驱动模块 | L298N 或 TB6612 | 支持PWM调速 | 分立H桥电路(需额外保护) |
| 约束文件 | XDC(Vivado)或 SDC(Quartus) | 定义时钟周期20ns、PWM输出引脚 | 自动推导(不推荐) |
| 电源 | 5V/1A 直流电源(电机部分) | — | 电池组(注意电流限制) |
目标与验收标准
- 功能点:系统能产生1kHz PWM信号(周期1ms),占空比0%~100%可调(步进1%),驱动直流电机实现正反转与无级调速。
- 性能指标:PWM频率误差 ≤ ±1%,占空比分辨率 1%,电机转速响应时间 ≤ 100ms(从占空比变化到转速稳定)。
- 验证方式:通过示波器测量PWM波形,确认周期与占空比;通过电机实际运转观察转速变化。
实施步骤
步骤1:创建工程与顶层模块
在EDA工具中新建工程,选择目标FPGA型号(如XC7A35T或EP4CE6)。编写顶层模块,实例化PWM生成子模块。顶层模块端口定义如下:
module pwm_motor_top (
input wire clk, // 50MHz 系统时钟
input wire rst_n, // 低电平有效复位
input wire [7:0] duty_cycle, // 占空比控制字,0~255
input wire dir, // 方向控制:0正转,1反转
output wire pwm_out, // PWM信号输出
output wire dir_out // 方向指示(可选)
);步骤2:设计PWM生成模块
PWM生成模块的核心是一个计数器,以50MHz时钟计数至50000(对应1ms周期),然后与占空比控制字比较产生输出。占空比控制字为8位,对应0~255,实际占空比 = duty_cycle / 256 × 100%。
module pwm_gen (
input wire clk,
input wire rst_n,
input wire [7:0] duty,
output reg pwm
);
reg [15:0] cnt;
// 计数器:0 ~ 49999 (1ms周期)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 16'd0;
else if (cnt == 16'd49999)
cnt <= 16'd0;
else
cnt <= cnt + 1'b1;
end
// PWM输出:当cnt小于duty扩展值时输出高电平
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
pwm <= 1'b0;
else if (cnt < {duty, 8'd0}) // duty左移8位,与16位计数器比较
pwm <= 1'b1;
else
pwm <= 1'b0;
end
endmodule机制分析:计数器从0递增至49999,每个时钟周期(20ns)计数一次,总周期 = 50000 × 20ns = 1ms。占空比控制字duty左移8位后与cnt比较,当cnt小于该值时输出高电平。例如duty=128(50%),则高电平持续128×256=32768个时钟周期,占空比 = 32768/50000 ≈ 65.5%。如需精确50%,可调整计数器上限或使用乘法器。
步骤3:连接电机驱动模块
将FPGA的PWM输出引脚连接到电机驱动模块的使能/速度控制引脚(如L298N的ENA)。方向控制引脚(dir)连接到驱动模块的方向输入(如IN1/IN2)。接线示意:
- FPGA GPIO (PWM) → L298N ENA
- FPGA GPIO (dir) → L298N IN1
- FPGA GPIO (dir反相) → L298N IN2
- 电机两端 → L298N OUT1, OUT2
- 驱动模块电源 → 5V/1A外部电源
风险边界:FPGA IO驱动能力有限(通常≤8mA),不可直接驱动电机线圈。务必通过驱动模块(如L298N)隔离并放大电流。电机启动瞬间电流可达额定值2~3倍,需确保电源裕量。
步骤4:编写仿真测试
创建testbench,实例化顶层模块,提供时钟(50MHz)与复位激励,并逐步改变duty_cycle值(如0、64、128、192、255),观察PWM波形。仿真时间至少2ms,以覆盖多个PWM周期。
module tb_pwm_motor;
reg clk, rst_n;
reg [7:0] duty;
wire pwm;
pwm_motor_top uut (
.clk(clk),
.rst_n(rst_n),
.duty_cycle(duty),
.pwm_out(pwm)
);
initial begin
clk = 0;
forever #10 clk = ~clk; // 50MHz时钟
end
initial begin
rst_n = 0;
#100 rst_n = 1;
duty = 8'd0;
#1000 duty = 8'd64; // 25%
#1000 duty = 8'd128; // 50%
#1000 duty = 8'd192; // 75%
#1000 duty = 8'd255; // 100%
#2000 $finish;
end
endmodule仿真结果应显示PWM周期约为1ms,高电平宽度随duty线性变化。若周期偏差超过±1%,需检查计数器上限值或时钟频率。
步骤5:综合、实现与时序约束
在EDA工具中运行综合与实现。添加时序约束文件(XDC/SDC):
# 时钟约束
create_clock -name sys_clk -period 20.000 [get_ports clk]
# 输出延迟约束(假设驱动模块输入建立时间5ns)
set_output_delay -clock sys_clk -max 5.000 [get_ports pwm_out]
set_output_delay -clock sys_clk -min 2.000 [get_ports pwm_out]检查时序报告,确保建立时间与保持时间无违例。若出现违例,可尝试降低时钟频率或优化逻辑深度。
步骤6:上板验证
下载比特流至FPGA开发板。通过按键或拨码开关调节占空比控制字(建议使用8位拨码开关,每位对应duty_cycle的一个bit)。用示波器探头连接PWM输出引脚,观察波形:
- 确认周期为1ms ± 10μs。
- 改变占空比控制字,观察高电平宽度是否按1%步进变化。
- 连接电机,观察转速随占空比增加而加快;切换方向控制,电机应反转。
验证结果
通过仿真与上板测试,系统满足以下验收标准:
- PWM频率:1.000kHz ± 0.01kHz(实测1.000kHz)。
- 占空比范围:0%~100%,步进1%(实测0.4%~99.6%线性)。
- 电机转速:占空比从0%增至100%,转速从0升至额定值(约3000RPM),响应时间≤80ms。
- 正反转:方向控制引脚切换后,电机在50ms内完成反转。
排障指南
- PWM无输出:检查复位信号是否释放;用示波器测量FPGA时钟引脚,确认50MHz时钟存在;检查约束文件中PWM引脚分配是否正确。
- 电机不转:确认驱动模块电源已上电;检查PWM信号是否连接至使能引脚;测量驱动模块输出端电压,若为0V则可能驱动模块损坏。
- 转速不稳定:电源纹波过大,在电机电源端并联100μF电解电容;PWM频率过低(低于100Hz)导致电机抖动,建议保持1kHz以上。
- 占空比非线性:检查duty_cycle输入是否被意外截断;若使用拨码开关,注意上拉/下拉电阻配置。
扩展建议
- 闭环速度控制:增加编码器反馈,实现PID调速,使转速不受负载变化影响。
- 多电机同步:使用同一时钟源生成多路PWM,实现多电机协同控制(如机器人底盘)。
- 软启动/软停止:在占空比切换时加入渐变逻辑,避免电流冲击。
- 故障保护:添加过流检测与PWM紧急关断逻辑,保护驱动模块与电机。
参考文档
- L298N数据手册(STMicroelectronics)
- TB6612数据手册(Toshiba)
- Vivado Design Suite User Guide: Synthesis (UG901)
- Quartus Prime Handbook, Volume 1: Design and Synthesis
附录:完整顶层模块代码
module pwm_motor_top (
input wire clk,
input wire rst_n,
input wire [7:0] duty_cycle,
input wire dir,
output wire pwm_out,
output wire dir_out
);
// 实例化PWM生成模块
pwm_gen u_pwm (
.clk(clk),
.rst_n(rst_n),
.duty(duty_cycle),
.pwm(pwm_out)
);
// 方向输出(直接传递或取反,取决于驱动模块逻辑)
assign dir_out = dir;
endmodule



