我使用的FPGA是Xilinx的PYNQ-7020(ZYNQ-Z2),在Vivado2018.3平台使用Verilog进行编程。
目录
1.电机驱动介绍:
2.模块编写介绍:
2.1 Verilog代码
2.2 方向逻辑分析
2.4PWM_Divider模块
3. 调用方法
使用TB6612FNG电机驱动,驱动一个电机需要FPGA对芯片有3个输入:
IN1 | 控制正反转 |
IN2 | 控制正反转 |
PWM | 控制转速 |
IN1 IN2控制转向的逻辑如下:
IN1 | IN2 | 转向 |
1 | 0 | 正转 |
0 | 1 | 反转 |
0 | 0 | 不转 |
对于电机控制模块,我们需要输入的有:
①运动方向(mode)——0停止,1前进,2后退,3左转,4右转
②行驶速度(pwm)——0-255,越大速度越快
③系统时钟(clk)和复位按钮(reset)
module Four_Wheel_Control (
input wire clk, // 系统时钟输入
input wire reset, // 复位按键输入
input wire [4:0] mode, // 运动模式输入(0-停止,1-前进,2-后退,3-左转,4-右转)
input wire [7:0] pwm, // PWM值输入(0-255范围)
output wire motor_A_PWM, // 电机A的PWM控制信号输出
output wire motor_A_IN1, // 电机A的IN1控制信号输出
output wire motor_A_IN2, // 电机A的IN2控制信号输出
output wire motor_B_PWM, // 电机B的PWM控制信号输出
output wire motor_B_IN1, // 电机B的IN1控制信号输出
output wire motor_B_IN2, // 电机B的IN2控制信号输出
output wire motor_C_PWM, // 电机C的PWM控制信号输出
output wire motor_C_IN1, // 电机C的IN1控制信号输出
output wire motor_C_IN2, // 电机C的IN2控制信号输出
output wire motor_D_PWM, // 电机D的PWM控制信号输出
output wire motor_D_IN1, // 电机D的IN1控制信号输出
output wire motor_D_IN2 // 电机D的IN2控制信号输出
);
wire direction_A, direction_B, direction_C, direction_D;
// 方向控制逻辑
// 方向控制逻辑
assign direction_A = (mode == 1 || mode == 3) ? 0 : 1; // 前进和左转为正向,其他为反向
assign direction_B = (mode == 1 || mode == 3) ? 0 : 1;
assign direction_C = (mode == 1 || mode == 4) ? 0 : 1; // 前进和右转为正向,其他为反向
assign direction_D = (mode == 1 || mode == 4) ? 0 : 1;
// 实例化PWM分频模块
PWM_Divider pwm_A_divider (
.clk(clk),
.reset(reset),
.duty(pwm),
.pwm(motor_A_PWM)
);
PWM_Divider pwm_B_divider (
.clk(clk),
.reset(reset),
.duty(pwm),
.pwm(motor_B_PWM)
);
PWM_Divider pwm_C_divider (
.clk(clk),
.reset(reset),
.duty(pwm),
.pwm(motor_C_PWM)
);
PWM_Divider pwm_D_divider (
.clk(clk),
.reset(reset),
.duty(pwm),
.pwm(motor_D_PWM)
);
// 输出电机方向和转向信号
assign motor_A_IN1 = direction_A;
assign motor_A_IN2 = ~direction_A;
assign motor_B_IN1 = direction_B;
assign motor_B_IN2 = ~direction_B;
assign motor_C_IN1 = direction_C;
assign motor_C_IN2 = ~direction_C;
assign motor_D_IN1 = direction_D;
assign motor_D_IN2 = ~direction_D;
endmodule
其中比较难理解的是方向逻辑,其中AC一组,BD一组
如果mode等于1或3,direction_A和direction_B被设置为0,表示正向(例如前进或左转)。
如果mode等于1或4,direction_C和direction_D被设置为0,表示正向(例如前进或右转)。
对于其他mode值,direction_A、direction_B、direction_C和direction_D被设置为1,表示反向。
模式(mode) | direction_A | direction_B | direction_C | direction_D | 实际功能 |
0 | 1 | 1 | 1 | 1 | 后退 |
1 | 0 | 0 | 0 | 0 | 前进 |
2 | 1 | 1 | 1 | 1 | 后退 |
3 | 0 | 0 | 1 | 1 | 左转 |
4 | 1 | 1 | 0 | 0 | 右转 |
逻辑真值表
其中我们可以发现一个问题:mode=0时是后退而不是停止。因此,在主模块中,如果mode=0,我们可以将pwm≤0,这就实现了停止。
counter在0-255之间不断累加重置,当counter
module PWM_Divider (
input wire clk, // 系统时钟输入
input wire reset, // 复位按键输入
input wire [7:0] duty, // 占空比输入(0-255范围)
output reg pwm // PWM输出
);
reg [7:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= 0;
else if (counter >= 255)
counter <= 0;
else
counter <= counter + 1;
end
always @(posedge clk) begin
if (reset)
pwm <= 0;
else if (counter < duty)
pwm <= 1;
else
pwm <= 0;
end
endmodule
第一个always块是一个组合逻辑块,它根据时钟上升沿和复位信号的状态更新计数器的值。如果复位信号为高电平,计数器被重置为0。如果计数器的值大于等于255,它会被重置为0。否则,计数器的值递增1。
第二个always块是一个时序逻辑块,它在每个时钟上升沿时根据计数器和占空比的比较更新PWM输出的值。如果复位信号为高电平,PWM输出被强制为低电平(0)。如果计数器的值小于占空比(duty),PWM输出被设置为高电平(1)。否则,PWM输出被设置为低电平(0)。
在主模块中实例化Four_Wheel_Control模块,在always块中通过调整模式与占空比,就能够实现电机的实时控制。
具体结构如下: