这篇具有很好参考价值的文章主要介绍了基于FPGA的蓝牙循迹小车项目。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。
超声波模块
超声波测距简介
超声波是由机械振动产生的, 可在不同介质中以不同的速度传播, 具有定向性好、能量集中、传输过程中衰减较小、反射能力较强等优点。超声波传感器可广泛应用于非接触式检测方法,它不受光线、被测物颜色等影响, 对恶劣的工作环境具有一定的适应能力, 因此在水文液位测量、车辆自动导航、物体识别等领域有着广泛的应用。
超声波测距是通过不断检测超声波发射后遇到障碍物所反射的回波, 从而测出发射和接收回波的时间差Δt , 然后求出距离S 。在速度v 已知的情况下,距离S 的计算,公式如下:S = vΔt/ 2在空气中,常温下超声波的传播速度是334 米/秒,但其传播速度V 易受空气中温度、湿度、压强等因素的影响,其中受温度的影响较大,如温度每升高1 ℃, 声速增加约0. 6 米/ 秒。因此在测距精度要求很高的情况下, 应通过温度补偿的方法对传播速度加以校正。已知现场环境温度T 时, 超声波传播速度V 的计算公式如下:
V = 331. 5+0.607T
这样, 只要测得超声波发射和接收回波的时间差Δt 以及现场环境温度T,就可以精确计算出发射点到障碍物之间的距离。
HC-SR04超声波测距模块简介
HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。
HC-SR04超声波测距模块特点
1、典型工作用电压:5V
2、超小静态工作电流:小于5mA
3、感应角度(R3 电阻越大,增益越高,探测角度越大):
R3 电阻为392,不大于15 度
R3 电阻为472, 不大于30 度
4、探测距离(R3 电阻可调节增益,即调节探测距离):
R3 电阻为392 2cm-450cm
R3 电阻为472 2cm-700cm
5、高精度:可达0.3cm
6、盲区(2cm)超近
HC-SR04超声波测距模块管脚
VCC(5V)、 Trig(控制端)、 Echo(接收端)、地(GND)
使用方法:控制口发一个10US 以上的高电平,就可以在接收口等待高电平输出。一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离。如此不断的周期测,就可以达到你移动测量的值了。
1、采用IO 触发测距,给至少10us 的高电平信号;
2、模块自动发送8 个40khz 的方波,自动检测是否有信号返回;
3、有信号返回,通过IO 输出高电平,高电平持续时间就是超声波从发射到返回时间.测试距离=(高电平时间*声速(340M/S))/2。
模块时序图
以上时序图表明只需要提供一个10US以上的脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。距离=高电平时间*声速。
代码编写
Verilog `timescale 1ns / 1ps module hc_sr( input sys_clk, input sys_rst_n, input echo, output trig, output [15:0] distance ); parameter delay = 35000750;//15*50 + 70*50_000一个超声波触发周期 localparam S0 = 3'b001; localparam S1 = 3'b010; localparam S2 = 3'b100; reg [31:0] cnt_70ms; //超声波发射信号的周期计数器 reg [2:0] state; reg [2:0] next_state; reg [31:0] cnt_echo; //回响信号寄存器 reg [31:0] cnt_echo_d0;//回响信号打拍 //超声波发射信号的周期计数器赋值 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n)begin cnt_70ms <= 32'b0; end else if(cnt_70ms == delay - 32'b1) cnt_70ms <= 32'b0; else cnt_70ms <= cnt_70ms + 32'b1; end //给trig赋值 assign trig = (cnt_70ms < 15*50) ? 1'b1 : 1'b0; //状态转换 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) state <= S0; else state <= next_state; end //次态逻辑 always@(*)begin if(!sys_rst_n) next_state = S0; else case(state) S0: if(echo) next_state = S1; else next_state = S0; S1: if(~echo) next_state <= S2; else next_state <= S1; S2: next_state <= S0; default: next_state <= S0; endcase end //逻辑输出,各个状态的动作 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) begin cnt_echo <= 1'b0; end else case(state) S0: begin cnt_echo <= 0; cnt_echo_d0 <= cnt_echo_d0; end S1: begin cnt_echo <= cnt_echo + 1; cnt_echo_d0 <= cnt_echo_d0; end S2: begin cnt_echo <= 0; cnt_echo_d0 <= cnt_echo; end default : begin cnt_echo <= 0; cnt_echo_d0 <= cnt_echo_d0; end endcase end //将回响的值赋值给distance assign distance = (cnt_echo_d0*20)/1000/58;
endmodule |
系统框图
数码管显示距离
Verilog `timescale 1ns / 1ps module seg( input sys_clk, input sys_rst_n, input [15:0] number, output reg [7:0] seg, //段选 output reg [3:0] sel //位选 ); parameter CNT_US_MAX = 50; //数据处理,将每一位数字提取出来 wire [3:0] ge; wire [3:0] shi; wire [3:0] bai; wire [3:0] qian; reg [3:0] data; assign ge = number%10; assign shi = number/10%10; assign bai = number/100%10; assign qian = number/1000%10; //状态机 localparam IDLE = 3'd0; localparam GE = 3'd1; localparam SHI = 3'd2; localparam BAI = 3'd3; localparam QIAN = 3'd4; //定义现态和次态 reg [2:0] state; reg [2:0] next_state; reg [15:0] cnt_us; //状态转换 always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) state <= IDLE; else state <= next_state; end //次态逻辑 always @(*)begin case(state) IDLE: next_state = GE; GE: if(cnt_us == CNT_US_MAX - 16'b1) next_state = SHI; else next_state = state; SHI:if(cnt_us == CNT_US_MAX - 16'b1) next_state =BAI; else next_state = state; BAI:if(cnt_us == CNT_US_MAX - 16'b1) next_state = QIAN; else next_state = state; QIAN:if(cnt_us == CNT_US_MAX - 16'b1) next_state = IDLE; else next_state = state; default:next_state = state; endcase end //给cnt_us赋值 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) cnt_us = 15'b0; else if(cnt_us == CNT_US_MAX - 6'b1) cnt_us = 15'b0; else cnt_us = cnt_us + 16'b1; end //逻辑输出,控制段选的数据索引和位选 always@(*)begin case(state) IDLE: begin data = 4'd0; sel = 4'b0000; end GE : begin data = ge; sel = 4'b0001; end SHI : begin data = shi; sel = 4'b0010; end BAI : begin data = bai; sel = 4'b0100; end QIAN : begin data = qian; sel = 4'b1000; end default:begin data = 4'd0; sel = 4'b0000; end endcase end //给段选赋值 always@(*)begin case (data) 4'd0:seg =~8'h3f; 4'd1:seg =~8'h06; 4'd2:seg =~8'h5b; 4'd3:seg =~8'h4f; 4'd4:seg =~8'h66; 4'd5:seg =~8'h6d; 4'd6:seg =~8'h7d; 4'd7:seg =~8'h07; 4'd8:seg =~8'h7f; 4'd9:seg =~8'h6f; default: seg = ~8'h3f; endcase end endmodule |
驱动模块
使用L298驱动芯片驱动四个直流电机。
像这样的连接方式,淘宝卖家一般都会有教程。
使能A和使能B默认用跳线帽连接5V,此时是不调速模式。拔掉跳线帽,即可使用PWM控制速度。
蓝牙模块
控制方法
蓝牙交互信号使用UART通信,通过手机连接蓝牙,发送控制信号给板子,来控制直流电机的工作方式。
UART通信
Verilog module uart_rx( input clk , //系统时钟 input rst_n , //系统复位,低有效
input uart_rxd , //UART接收端口 output reg uart_rx_done, //UART接收完成信号 output reg [7:0] uart_rx_data //UART接收到的数据 );
//parameter define parameter CLK_FREQ = 50000000; //系统时钟频率 parameter UART_BPS = 115200 ; //串口波特率 localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率,对系统时钟计数BPS_CNT次
//reg define reg uart_rxd_d0; reg uart_rxd_d1; reg uart_rxd_d2; reg rx_flag ; //接收过程标志信号 reg [3:0 ] rx_cnt ; //接收数据计数器 reg [15:0] baud_cnt ; //波特率计数器 reg [7:0 ] rx_data_t ; //接收数据寄存器
//wire define wire start_en;
//***************************************************** //** main code //***************************************************** //捕获接收端口下降沿(起始位),得到一个时钟周期的脉冲信号 assign start_en = uart_rxd_d2 & (~uart_rxd_d1) & (~rx_flag);
//针对异步信号的同步处理 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rxd_d0 <= 1'b0; uart_rxd_d1 <= 1'b0; uart_rxd_d2 <= 1'b0; end else begin uart_rxd_d0 <= uart_rxd; uart_rxd_d1 <= uart_rxd_d0; uart_rxd_d2 <= uart_rxd_d1; end end
//给接收标志赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_flag <= 1'b0; else if(start_en) //检测到起始位 rx_flag <= 1'b1; //接收过程中,标志信号rx_flag拉高 //在停止位一半的时候,即接收过程结束,标志信号rx_flag拉低 else if((rx_cnt == 4'd9) && (baud_cnt == BAUD_CNT_MAX/2 - 1'b1)) rx_flag <= 1'b0; else rx_flag <= rx_flag; end
//波特率的计数器赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) baud_cnt <= 16'd0; else if(rx_flag) begin //处于接收过程时,波特率计数器(baud_cnt)进行循环计数 if(baud_cnt < BAUD_CNT_MAX - 1'b1) baud_cnt <= baud_cnt + 16'b1; else baud_cnt <= 16'd0; //计数达到一个波特率周期后清零 end else baud_cnt <= 16'd0; //接收过程结束时计数器清零 end
//对接收数据计数器(rx_cnt)进行赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_cnt <= 4'd0; else if(rx_flag) begin //处于接收过程时rx_cnt才进行计数 if(baud_cnt == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时 rx_cnt <= rx_cnt + 1'b1; //接收数据计数器加1 else rx_cnt <= rx_cnt; end else rx_cnt <= 4'd0; //接收过程结束时计数器清零 end
//根据rx_cnt来寄存rxd端口的数据 always @(posedge clk or negedge rst_n) begin if(!rst_n) rx_data_t <= 8'b0; else if(rx_flag) begin //系统处于接收过程时 if(baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin //判断baud_cnt是否计数到数据位的中间 case(rx_cnt) 4'd1 : rx_data_t[0] <= uart_rxd_d2; //寄存数据的最低位 4'd2 : rx_data_t[1] <= uart_rxd_d2; 4'd3 : rx_data_t[2] <= uart_rxd_d2; 4'd4 : rx_data_t[3] <= uart_rxd_d2; 4'd5 : rx_data_t[4] <= uart_rxd_d2; 4'd6 : rx_data_t[5] <= uart_rxd_d2; 4'd7 : rx_data_t[6] <= uart_rxd_d2; 4'd8 : rx_data_t[7] <= uart_rxd_d2; //寄存数据的高低位 default : ; endcase end else rx_data_t <= rx_data_t; end else rx_data_t <= 8'b0; end
//给接收完成信号和接收到的数据赋值 always @(posedge clk or negedge rst_n) begin if(!rst_n) begin uart_rx_done <= 1'b0; uart_rx_data <= 8'b0; end //当接收数据计数器计数到停止位,且baud_cnt计数到停止位的中间时 else if(rx_cnt == 4'd9 && baud_cnt == BAUD_CNT_MAX/2 - 1'b1) begin uart_rx_done <= 1'b1 ; //拉高接收完成信号 uart_rx_data <= rx_data_t; //并对UART接收到的数据进行赋值 end else begin uart_rx_done <= 1'b0; uart_rx_data <= uart_rx_data; end end endmodule |
循迹模块
使用两个红外传感器放在小车最前处的左右端,红外传感器使用DO口,根据两个DO口的信号驱动小车。
Verilog module xunji( input sys_clk, input sys_rst_n, input [1:0] ddoo, output reg [3:0] car1 //car1是循迹模式 ); //循迹 always @(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) car1 <= 4'b000; else if(ddoo == 2'b00) car1 <= 4'b1010; //前进 else if(ddoo == 2'b10) car1 <= 4'b0110; //左转 else if(ddoo == 2'b01) car1 <= 4'b1001; //右转 else if(ddoo == 2'b11) car1 <= 4'b0000; //停止 end endmodule |
避障模块
Verilog module avoid( input sys_clk, input sys_rst_n, input [15:0] number, output reg [3:0] car_a );
always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) car_a <= 4'b0000; else if(number < 25) car_a <= 4'b0110; else car_a <= 4'b1010; end endmodule |
顶层模块
Verilog module little_car( input sys_clk, input sys_rst_n, input [1:0] ddoo, input echo, input uart_rxd, output trig, output [7:0] seg, output [3:0] sel, output reg [3:0] car ); parameter CLK_FREQ = 50000000; //系统时钟频率 parameter UART_BPS = 9600 ; //串口波特率 parameter CNT_US_MAX = 50; parameter delay = 35000750;//15*50 + 70*50_000一个超声波触发周期 wire [15:0] number; wire [3:0] car1; wire [3:0] car_a; wire [7:0] data; //控制小车 always@(posedge sys_clk or negedge sys_rst_n)begin if(!sys_rst_n) car <= 4'b0000; else if(data == 8'h02 ) car <= 4'b1010; //前进 else if(data == 8'h08) car <= 4'b0101; //后退 else if(data == 8'h04) car <= 4'b0110; //左转 else if(data == 8'h06) car <= 4'b1001; // 右转 else if(data == 8'h05) car <= 4'b0000; else if(data == 8'h0f) car <= car1; //循迹 else if(data == 8'hff) car <= car_a; //避障 else car <= car; end //循迹例化 xunji u_xunji( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .ddoo (ddoo), .car1 (car1) ); //超声波例化 hc_sr #( .delay (delay) )u_hc_sr( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .echo (echo), .trig (trig), .distance (number) ); //避障例化 avoid u_avoid( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .number (number), .car_a (car_a) ); //数码管例化 seg #( .CNT_US_MAX (CNT_US_MAX) )u_seg( .sys_clk (sys_clk ), .sys_rst_n (sys_rst_n), .number (number ), .seg (seg ), .sel (sel ) ); //uart通信 uart_rx #( .CLK_FREQ (CLK_FREQ), .UART_BPS (UART_BPS) )u_uart_rx( .clk (sys_clk ), .rst_n (sys_rst_n ), .uart_rxd (uart_rxd ), .uart_rx_done (uart_rx_done), .uart_rx_data (data) ); endmodule |
仿真文件
模仿蓝UART通信给小车发送控制信号,看连接直流电机的信号是否符合预期。
Verilog `timescale 1ns/1ns //仿真的单位/仿真的精度 module tb_little_car();
//parameter define parameter CLK_PERIOD = 20;//时钟周期为20ns
//reg define reg sys_clk ; //时钟信号 reg sys_rst_n; //复位信号 reg uart_rxd ; //UART接收端口 reg [1:0] ddoo; reg echo; reg uart_rxd; //wire define wire trig; wire [3:0] sel; wire [7:0] seg; wire [3:0] car;
initial begin sys_clk <= 1'b0; sys_rst_n <= 1'b0; uart_rxd <= 1'b1; ddoo <= 2'b00; echo <= 1'b0; #400 sys_rst_n <= 1'b1; //发送8'h55 8'b000_0010 #2000 uart_rxd <= 1'b0; //起始位 #104160 uart_rxd <= 1'b0; //D0 #104160 uart_rxd <= 1'b1; //D1 #104160 uart_rxd <= 1'b0; //D2 #104160 uart_rxd <= 1'b0; //D3 #104160 uart_rxd <= 1'b0; //D4 #104160 uart_rxd <= 1'b0; //D5 #104160 uart_rxd <= 1'b0; //D6 #104160 uart_rxd <= 1'b0; //D7 #104160 uart_rxd <= 1'b1; //停止位 #104160 uart_rxd <= 1'b1; //空闲状态 //发送8'h55 8'b000_1000 #2000 uart_rxd <= 1'b0; //起始位 #104160 uart_rxd <= 1'b0; //D0 #104160 uart_rxd <= 1'b0; //D1 #104160 uart_rxd <= 1'b0; //D2 #104160 uart_rxd <= 1'b1; //D3 #104160 uart_rxd <= 1'b0; //D4 #104160 uart_rxd <= 1'b0; //D5 #104160 uart_rxd <= 1'b0; //D6 #104160 uart_rxd <= 1'b0; //D7 #104160 uart_rxd <= 1'b1; //停止位 #104160 uart_rxd <= 1'b1; //空闲状态 //发送8'h55 8'b000_1000 #2000 uart_rxd <= 1'b0; //起始位 #104160 uart_rxd <= 1'b0; //D0 #104160 uart_rxd <= 1'b0; //D1 #104160 uart_rxd <= 1'b0; //D2 #104160 uart_rxd <= 1'b1; //D3 #104160 uart_rxd <= 1'b0; //D4 #104160 uart_rxd <= 1'b0; //D5 #104160 uart_rxd <= 1'b0; //D6 #104160 uart_rxd <= 1'b0; //D7 #104160 uart_rxd <= 1'b1; //停止位 #104160 uart_rxd <= 1'b1; //空闲状态 //发送8'h55 8'b000_0100 #2000 uart_rxd <= 1'b0; //起始位 #104160 uart_rxd <= 1'b0; //D0 #104160 uart_rxd <= 1'b0; //D1 #104160 uart_rxd <= 1'b1; //D2 #104160 uart_rxd <= 1'b0; //D3 #104160 uart_rxd <= 1'b0; //D4 #104160 uart_rxd <= 1'b0; //D5 #104160 uart_rxd <= 1'b0; //D6 #104160 uart_rxd <= 1'b0; //D7 #104160 uart_rxd <= 1'b1; //停止位 #104160 uart_rxd <= 1'b1; //空闲状态 end
//50Mhz的时钟,周期则为1/50Mhz=20ns,所以每10ns,电平取反一次 always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
//例化顶层模块 little_car u_little_car( .sys_clk (sys_clk ), .sys_rst_n (sys_rst_n), .uart_rxd (uart_rxd ), .ddoo (ddoo ), .trig (trig), .echo (echo), .sel (sel), .seg (seg), .car (car) );
endmodule文章来源:https://www.toymoban.com/news/detail-781509.html |
文章来源地址https://www.toymoban.com/news/detail-781509.html
到了这里,关于基于FPGA的蓝牙循迹小车项目的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!