闲来无事整个小车玩玩,设想的小车可以有蓝牙模块来控制模式切换,通过发送指令来更改相对应的功能,当避障的时候可以自动规避障碍物,当处于红外循迹时,可以跟随规划的轨迹前线,当手动遥控时可以控制前进后退左右转向停止等功能。
先介绍一下使用的红外模块,其上有四个管脚,vcc接电源,gnd接地,D0传输红外线是否被吸收,A0传输模拟信号主要是不同距离输出不同的电压,但是此脚一般可以不接.
然后思路就是通过判断D0传过来的电平信号来判断小车有没有接触到黑线,当红外线被吸收,也就是触碰到黑线,D0会持续输出高电平,直到它检测到红外线返回才会回归低电平,基于对管脚传回的电平检测,可以判断是否接触到黑线。
这部分代码很好写,可以参考如下代码:
`timescale 1ns / 1ps
module red_line(
input sysclk ,///系统时钟
input rst_n ,///复位
input [1:0] D0 ,///红外探测模块的电平输入
output reg [3:0] car//输出轮子控制
);
always @(posedge sysclk)
if(!rst_n)
car<=4'b1001;
else if( D0[0]==1 && D0[1]==1 )
car<=4'b0000;
else if(D0[0]==1) //左边的红外探测模块
car<=4'b0101;
else if(D0[1]==1) //右边的红外探测模块
car<=4'b1010;
else
car<=4'b1001; //保持状态
endmodule
输入系统时钟,复位,红外探测模块的电平输入,然后输出小车轮子一个怎么走,就只有一个简单的判断语句。
然后是超声波模块,超声波模块的应用,之前写过了,这里就不再赘述。
超声波模块的距离还要输入到一个避障模块中,这个模块只负责成立输入的距离信息,判断是否达到阈值。
`timescale 1ns / 1ps
module car_c(
input sysclk,
input rst_n,
input [13:0] distance,
output reg [3:0] car
);
always @(posedge sysclk)
if(!rst_n)
car<=4'b1001;
else if(distance>=15)
car<=4'b1001;
else if(distance<15)
car<=4'b1010 ;
else
car<=car;
//assign car=4'b1001; //1010 左转 0101 右转 1001 前进 0110 后退
endmodule
然后是蓝牙模块,蓝牙模块包括两部分,接收的rx,这里是基于蓝牙模块来说的,对蓝牙模块的接收和发送,我们需要通过蓝牙模块来进行指令控制,输入特定的十六进制代码输出特定的控制信息:
这里我们定义了开发板的振动频率也就是一秒震动多少次,然后是波特率,这里没有涉及到其他的模块,如果是不同的蓝牙模块之间通信,波特率要设置的一样。
//串口读取,可调整波特率,运行频率,本次设计不可以修改数据传输的长度//
`timescale 1ns / 1ps
module uart_RX_car
#(
parameter SYSCLK = 50_000_000 ,//系统时钟
parameter Baud = 9600 //波特率
)
(
input sysclk ,
input rst_n ,
input RX ,
output reg vaild ,//数据有效
output reg [7:0] Data
);
localparam CLK_DELAY = SYSCLK/Baud;
localparam CLK_MID = CLK_DELAY/2;
localparam IDLE=2'd0;//空闲状态,等待RX信号被拉低
localparam START=2'd1;//起始状态
localparam DATA=2'd2;//数据接收
localparam STOP=2'd3;//停止状态
reg [1:0] cur_state;
reg [1:0] next_state;
reg [1:0] rx_flag = 2'b11;
reg [4:0] cnt_bit;//传输数据个数
reg [31:0] cnt;//记录波特率,即记录何时让cnt_bit加1
******开始信号寄存*****
always@(posedge sysclk)
if(!rst_n)
rx_flag<=2'b11;
else
rx_flag<={rx_flag[0],RX};
******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
if(rx_flag==2'b10)
next_state = START;
else
next_state = IDLE;
end
START:begin
if(cnt_bit==5'd1)
next_state = DATA;
else
next_state = START;
end
DATA:begin
if(cnt_bit==5'd9)
next_state = STOP;
else
next_state = DATA;
end
STOP:begin
if(cnt_bit==5'd9 && rx_flag == 2'b11)
next_state = IDLE;
else
next_state = STOP;
end
default:begin
next_state = IDLE;
end
endcase
end
******state3*****
always@(posedge sysclk)
if(!rst_n) begin
cnt_bit <= 5'd0;
cnt <= 32'd0;
Data <= 8'd0;
vaild <= 32'd0;
end
else
case(next_state)
IDLE:begin
cnt_bit <= 5'd0;
cnt <= 32'd0;
vaild <= 32'd0;
Data <= Data;//可以为0,也可以保持上一次的数据
end
START:begin
if(cnt >= CLK_DELAY-1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 32'd1;
end
else begin
cnt_bit <= cnt_bit;
cnt <= cnt + 32'd1;
end
Data <= Data;
vaild <= 0;
end
DATA:begin
if(cnt >= CLK_DELAY-1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 32'd1;
end
else begin
cnt_bit <= cnt_bit;
cnt <= cnt + 32'd1;
end
if(cnt == CLK_MID-1) //中点的时候进行数据的采集,这样能保证数据采集比较稳定//
Data <= {RX,Data[7:1]};
else
Data <= Data;
vaild <= 0;
end
STOP:begin
if(cnt >= CLK_DELAY-1)begin
cnt <= 32'd0;
cnt_bit <= cnt_bit + 32'd1;
end
else begin
cnt_bit <= cnt_bit;
cnt <= cnt + 32'd1;
end
Data <= Data;
if(cnt == CLK_MID-1)
vaild <= 1;
else
vaild <= 0;
end
default:begin
cnt_bit <= 5'd0;
cnt <= 32'd0;
Data <= 8'd0;
vaild <= 32'd0;
end
endcase
endmodule
然后是接收控制信息的模块,rx传出来的Data会输入到这个模块里面,根据输入的控制信息输出
对应的小车控制信息。
module rx_ctrl(
input sysclk ,
input rst_n ,
input [7:0] Data ,
output reg [3:0] car_ctrl_2
);
always@(posedge sysclk)
if(!rst_n)
car_ctrl_2 <= 4'b0000;
else
case(Data)
8'h47 : car_ctrl_2 <= 4'b1001;//前进
8'h4b : car_ctrl_2 <= 4'b0110;//后退
8'h4a : car_ctrl_2 <= 4'b1010;//右转
8'h48 : car_ctrl_2 <= 4'b0101;//左转
8'h49 : car_ctrl_2 <= 4'b0000;//停止
default :car_ctrl_2 <= 4'b0000;
endcase
endmodule
然后是TOP模块,我们需要将所有的模块都都连接起来。这里顶层加了一个led的输出,这样方便我们观察我们当前处于什么状态,也可以方便我们切换状态,通过判断led处于什么状态,将小车的控制输出交给相对应的控制输出模块。在这个里面,数码管和led都可以不用加,这里加入了只是因为可以更好的帮助我们观察小车的状态。
`timescale 1ns / 1ps
module TOP8(
input sysclk,
input rst_n,
input echo,//输出的超声波
input [1:0] D0,
input RX,
output trig ,//产生高电平
output reg [3:0] led ,
output [7:0] seg ,
output [3:0] dig ,
output reg [3:0] car
);
//assign car=4'b1010;
wire [13:0] distance;
wire [7:0] Data;
wire [3:0] car_ctrl0;//避障
wire [3:0] car_ctrl1;//循迹
wire [3:0] car_ctrl2;//蓝牙
always @(posedge sysclk)
if(!rst_n)
led<=4'b0000;
else
case(Data)
8'h41:led<=4'b0001;//避障
8'h42:led<=4'b0010;//循迹
//8'h43:led=4'b0100;//蓝牙
default :led<=4'b0100;
endcase
always @(posedge sysclk)
if(!rst_n)
car<=4'b0000;
else
case(led)
4'b0001:car<=car_ctrl0;//避障
4'b0010:car<=car_ctrl1;//循迹
4'b0100:car<=car_ctrl2;//蓝牙
default :car<=4'b0000;
endcase
csb csb1(
.sysclk (sysclk ),
.rst_n (rst_n ),
.trig (trig ),//产生高电平
.echo (echo ),//输出的超声波
.distance(distance)
);
seg seg1(
. sysclk (sysclk) ,
. rst_n (rst_n ) ,
. num (distance ) , //数码管上显示的数字0-9999
. seg (seg ) ,//段选8个灯
. dig (dig ) //位选4个数码管
);
car_c car1(
. sysclk (sysclk) ,
. rst_n (rst_n ) ,
. distance (distance) ,
. car (car_ctrl0 )
);
red_line red1(
.sysclk (sysclk),
.rst_n (rst_n ),
.D0 (D0 ),
.car (car_ctrl1 )
);
uart_RX_car
#(
. SYSCLK(50_000_000 ) ,//系统时钟
. Baud (9600 ) //波特率
)
uart1(
. sysclk ( sysclk ) ,
. rst_n ( rst_n ) ,
. RX ( RX ) ,
. vaild ( vaild ) ,//数据有效
. Data ( Data )
);
rx_ctrl rx1(
. sysclk ( sysclk ) ,
. rst_n ( rst_n ) ,
. Data ( Data ) ,
.car_ctrl_2 (car_ctrl2 )
);
endmodule
文章来源地址https://www.uudwc.com/A/BvPDX/
文章来源:https://www.uudwc.com/A/BvPDX/