用FPGA给玩具小车装个大脑:手把手教你用Verilog和Quartus II实现PWM直流电机调速
周末整理房间时,翻出一台尘封多年的玩具小车。看着它呆板的前进后退,突然萌生一个想法:能否用FPGA给它装上"智能大脑",实现更灵活的速度控制和转向?这个看似简单的改造项目,实际上融合了数字电路设计、电机控制和嵌入式系统三大技术领域。本文将带你从零开始,用Verilog语言和Quartus II工具链,为普通玩具小车打造一个可编程的电机驱动核心。
与传统单片机方案相比,FPGA的并行处理特性可以带来更精准的PWM控制,响应延迟可降低到纳秒级。我们使用的Altera Cyclone IV开发板成本不到200元,配合L298N电机驱动模块,就能构建完整的控制系统。下面这个表格对比了三种常见控制方案的特性:
| 特性 | FPGA方案 | Arduino方案 | 专用驱动IC方案 |
|---|---|---|---|
| 响应延迟 | 10-100ns | 10-100μs | 1-10μs |
| 最大PWM频率 | 1-50MHz | 500Hz-20kHz | 5-50kHz |
| 占空比分辨率 | 16位 | 8-10位 | 8-12位 |
| 多电机同步控制能力 | 优秀 | 一般 | 有限 |
1. 硬件准备与系统架构
1.1 所需材料清单
改造项目需要以下硬件组件:
- FPGA开发板(推荐Cyclone IV EP4CE6)
- L298N电机驱动模块
- 直流减速电机(玩具车原装电机即可)
- 锂电池组(7.4V)
- 按键开关(方向控制用)
- 电位器(速度调节用)
注意:FPGA的IO口驱动能力有限,必须通过电机驱动模块连接,直接连接可能损坏芯片。
1.2 系统架构设计
整个控制系统采用分层设计:
- 用户输入层:包括方向按键和速度旋钮
- FPGA控制核心:
- 时钟分频模块
- PWM生成器
- 方向控制逻辑
- 功率驱动层:L298N H桥电路
- 执行层:直流电机
硬件连接示意图如下:
FPGA开发板 ├── GPIO1 → L298N IN1 ├── GPIO2 → L298N IN2 ├── GPIO3 → L298N EN1 ├── 按键1 → 方向控制 └── 电位器 → 速度调节2. Verilog核心模块设计
2.1 时钟分频模块
由于FPGA主时钟通常在50MHz左右,而PWM控制只需要1-20kHz频率,我们需要先设计分频器:
module clk_divider( input clk_50MHz, output reg clk_1kHz ); reg [15:0] counter; always @(posedge clk_50MHz) begin if(counter == 24999) begin // 50MHz/(25000*2)=1kHz clk_1kHz <= ~clk_1kHz; counter <= 0; end else begin counter <= counter + 1; end end endmodule2.2 可调PWM生成器
这是整个系统的核心,通过改变占空比调节电机速度:
module pwm_generator( input clk, input [7:0] duty_cycle, output reg pwm_out ); reg [7:0] counter; always @(posedge clk) begin counter <= counter + 1; pwm_out <= (counter < duty_cycle) ? 1'b1 : 1'b0; end endmodule2.3 方向控制逻辑
通过两个GPIO的高低电平组合控制电机转向:
module direction_ctl( input clk, input dir_btn, output reg motor_in1, output reg motor_in2 ); always @(posedge clk) begin if(dir_btn) begin // 正向 motor_in1 <= 1'b1; motor_in2 <= 1'b0; end else begin // 反向 motor_in1 <= 1'b0; motor_in2 <= 1'b1; end end endmodule3. Quartus II工程实现
3.1 工程创建与配置
- 启动Quartus II,选择File → New Project Wizard
- 选择Cyclone IV EP4CE6器件
- 添加上述Verilog模块文件
- 配置未使用引脚为As inputs, tri-stated
3.2 引脚分配技巧
在Assignment Editor中设置:
- 时钟引脚 → 全局时钟专用引脚
- PWM输出 → 普通IO口
- 方向控制 → 带消抖功能的专用输入引脚
推荐引脚分配方案:
| 信号名称 | FPGA引脚号 | 板载标识 |
|---|---|---|
| clk_50MHz | PIN_23 | CLK0 |
| pwm_out | PIN_45 | GPIO0 |
| motor_in1 | PIN_46 | GPIO1 |
| motor_in2 | PIN_47 | GPIO2 |
| dir_btn | PIN_12 | KEY0 |
4. 系统调试与性能优化
4.1 常见问题排查
- 电机不转:检查L298N使能端是否接高电平
- 方向控制相反:交换IN1和IN2接线
- PWM频率过高:调整分频系数,推荐1-5kHz
- 占空比响应不线性:检查电位器ADC采样代码
4.2 高级优化技巧
- 动态PWM频率调整:
// 根据速度需求自动调整PWM频率 if(duty_cycle < 50) pwm_freq = 1kHz; else if(duty_cycle < 150) pwm_freq = 5kHz; else pwm_freq = 10kHz;- 软启动功能:
// 上电时缓慢增加占空比 always @(posedge clk) begin if(startup_counter < 255) begin duty_cycle <= startup_counter; startup_counter <= startup_counter + 1; end end- 刹车能量回收:
// 快速制动时短接电机两端 if(brake_signal) begin motor_in1 <= 1'b1; motor_in2 <= 1'b1; // 形成能耗回路 end5. 扩展应用:智能小车系统
基础功能实现后,可以进一步扩展:
- 增加超声波模块实现自动避障
- 添加蓝牙模块实现手机遥控
- 集成编码器实现闭环速度控制
一个典型的避障控制逻辑实现:
module obstacle_avoid( input clk, input [7:0] distance, output reg [1:0] action ); parameter SAFE_DIST = 30; // 30cm always @(posedge clk) begin if(distance < SAFE_DIST) begin action <= 2'b01; // 右转 end else begin action <= 2'b10; // 直行 end end endmodule实际测试中发现,当PWM频率设置在3kHz左右时,电机运行最平稳,且啸叫声最小。对于需要精确控制的场景,建议增加编码器反馈,形成闭环控制系统。