基于状态机实现超声波避障小车。最近生产实习的FPGA培训课程内容,还是挺简单的。具体原理其他文章应该都烂大街了,重点是状态机的写法,还是很少博主写,没怎么看到,基本上都是时序机写的模块功能。
电机驱动用的L298N,默认全速拉满,没用PWM调制速度。
整改TOP逻辑涉及电机控制,超声波测距,数码管显示超声波读数。后面两个模块之前的文章都写了代码的。这里我就直接引用吧,不做过多篇幅了。
数码管显示seg.v和超声波测距trasonic.v:
FPGA入门实验-基于状态机实现4位共阴极数码管显示超声波模块读数_星羽空间的博客-CSDN博客FPGA基于状态机实现4位共阴极数码管显示超声波模块读数https://blog.csdn.net/qq_25662827/article/details/125213391这里增添了L298N电机驱动代码:
module MOTOR(
input sysclk,
input rst_n,
input ENA,
input ENB,
input [1:0] ATRUN,
input [1:0] BTRUN,
output reg [1:0] EN,
output reg [1:0] AIN,
output reg [1:0] BIN
);
always@(posedge sysclk) begin
if(!rst_n) begin
EN <= 2'b00;
AIN <= 2'b00;
BIN <= 2'b00;
end
else
begin
EN <= {ENB ,ENA};
AIN <= ATRUN;
BIN <= BTRUN;
end
end
endmodule
最后就剩顶层逻辑了。也是状态机实现的。并且做了二阶均值滤波,让超声波读数平滑一点,减少噪声,TOP.v:
module top(
input sysclk ,
input rst_n ,
input Echo ,
output Trig ,
output [7:0] SEG ,
output [3:0] DIG ,
output [1:0] AIN ,
output [1:0] BIN
);
wire [15:0] numtemp ;
reg [15:0] num ;
reg [1:0] ATRUN ;
reg [1:0] BTRUN ;
wire [1:0] MOTOREN ;
reg [31:0] cnt ;
reg [1:0] cur_state, next_state;
parameter IDLE = 2'b00;
parameter GO = 2'b01;
parameter STOP = 2'b10;
parameter TRUN = 2'b11;
parameter DELAY = 32'd62_500_000;
parameter THRESHHOLD = 15'd25;
reg goflag;
seg
#(
.SYSCLK (125_000_000) ,
.TIME (1000) ,
.MODE (0)
)a(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.set (10) ,
.number (num) ,
.DIG (DIG) ,
.SEG (SEG)
);
trasonic
#(
.SYSCLK (125_000_000) ,
.TIME1 (5000 ) ,
.TIME2 (20_000_000 )
)b(
.sysclk (sysclk) ,
.rst_n (rst_n) ,
.Echo (Echo) ,
.Trig (Trig) ,
.distence(numtemp)
);
MOTOR c(
.sysclk(sysclk),
.rst_n(rst_n),
.ENA(1'b1),
.ENB(1'b1),
.ATRUN(ATRUN),
.BTRUN(BTRUN),
.EN(MOTOREN),
.AIN(AIN),
.BIN(BIN)
);
//____________________state1______________________//
always@(posedge sysclk)
if(!rst_n)
cur_state <= IDLE;
else
cur_state <= next_state;
//____________________state2______________________//
always@(*)begin
next_state = IDLE;
case(cur_state)
IDLE:begin
next_state = GO;
end
GO:begin
if(num < THRESHHOLD)
next_state = STOP;
else
next_state = GO;
end
STOP:begin
if(cnt >= DELAY - 1)
if(goflag)
next_state = TRUN;
else if(num >= THRESHHOLD)
next_state = GO;
else
next_state = TRUN;
else
next_state = STOP;
end
TRUN:begin
if(num>= THRESHHOLD)
next_state = STOP;
else
next_state = TRUN;
end
endcase
end
//________________________state3______________________//
always@(posedge sysclk)
if(!rst_n)begin
cnt <= 32'd0;
ATRUN <= 2'b00;
BTRUN <= 2'b00;
goflag <= 1'b0;
num <= numtemp;
end
else begin
case(cur_state)
IDLE:begin
cnt <= 32'd0;
ATRUN <= 2'b00;
BTRUN <= 2'b00;
goflag <= 1'b0;
num <= numtemp;
end
GO:begin
cnt <= 32'd0;
ATRUN <= 2'b01;
BTRUN <= 2'b01;
goflag <= 1'b1;
num <= (num + numtemp)/2;
end
STOP:begin
cnt <= cnt + 1;
ATRUN <= 2'b00;
BTRUN <= 2'b00;
goflag <= goflag;
num <= (num + numtemp)/2;
end
TRUN:begin
goflag <= 1'b0;
ATRUN <= 2'b01;
BTRUN <= 2'b10;
cnt <= 32'd0;
num <= (num + numtemp)/2;
end
endcase
end
endmodule
这个代码量对于新手来说还是有点吃不消的吧。不过反反复复踏踏实实地去理解每个模块的实现方法,和代码逻辑,也就能通透许多了。
在装车过程中,尽量装得标准一点。单侧车轮正反转方向一致,方向前后设定明确(以超声波一侧为车头,除非将此模块作为倒车检测),布线尽量简约一点。