基于FPGA的蓝牙遥控,超声波避障,红外循迹的智能小车

        闲来无事整个小车玩玩,设想的小车可以有蓝牙模块来控制模式切换,通过发送指令来更改相对应的功能,当避障的时候可以自动规避障碍物,当处于红外循迹时,可以跟随规划的轨迹前线,当手动遥控时可以控制前进后退左右转向停止等功能。

        先介绍一下使用的红外模块,其上有四个管脚,vcc接电源,gnd接地,D0传输红外线是否被吸收,A0传输模拟信号主要是不同距离输出不同的电压,但是此脚一般可以不接.

        然后思路就是通过判断D0传过来的电平信号来判断小车有没有接触到黑线,当红外线被吸收,也就是触碰到黑线,D0会持续输出高电平,直到它检测到红外线返回才会回归低电平,基于对管脚传回的电平检测,可以判断是否接触到黑线。

基于FPGA的蓝牙遥控,超声波避障,红外循迹的智能小车_第1张图片

这部分代码很好写,可以参考如下代码:

`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

你可能感兴趣的:(fpga开发)