Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)
一、准备及目标
采用的元器件:T型口数据线2;总线1;USB Blaster1;FPGA板子1;HC-05蓝牙模块*1。
使用的软件:手机:蓝牙串口SPP;电脑:Quartus。
实现的目标:手机输入一段信息,通过蓝牙传递信息给FPGA,FPGA收到信息后,再将该信息通过蓝牙传递回手机。
如图:
二、为什么要写这么一篇文章
太菜了QAQ,好多网上的东西看不懂,理解了UART,但是找不到一个基于UART且读得懂的代码。所有我把我的东西放在这里,恳请广大网友批评指正的同时,也帮助一些像我一样菜的同学理解一下蓝牙的串口协议。
三、原理
1、UART(异步收发传输器)串口协议
(1)为什么需要UART串口协议?
信息的传递有串行(异步)和并行(同步)两种。同步传输快但需要的线多,异步需要的线少但传输慢。以异步进行信息传递的叫UART,把异步信息和同步信息进行转换的标准叫串口协议。这个标准保证传递的信息时正确的。
(2)UART串口协议的原理:
这个不赘述,找到两个很棒的资料:
1、这个是文字的,细的一批—— FPGA——UART Verilog程序设计
2、这个是bilibili的视频,很直观——【Electronoobs】UART-I2C-SPI 基本串行通信协议 1080P (中英字幕)
2、手机、蓝牙、FPGA
当我给FPGA装上蓝牙模块后,我会很自然的认为信息的传递发生在手机和FPGA之间,这么想会给编程带来一定阻力。这种想法需要纠正为信息的传递发生在蓝牙模块和FPGA之间:手机和蓝牙连接上后相当于一个整体,里面流通的信息是一致的,而对他们而言,FPGA才是外部。所以我需要把蓝牙模块的输出(tx)所连接的FPGA上的引脚定义为(rx),蓝牙模块的输出(rx)所连接的FPGA上的引脚定义为(tx),即蓝牙模块的输入是FPGA的输出,蓝牙模块的输出是FPGA的输入。
3、实物连接
这个不难:
四、代码
代码很非常丑陋且基础,但毕竟自己写的,读得懂:
1、主模块:
module bluetooth(
input sys_clk,
input rx,
output tx,
output wire[1:0]led
); //sys_clk为系统板载时钟(50MHz),rx对应蓝牙模块的tx,tx对应蓝牙模块的rx;led是我设置来判断读入数据错没错的
wire[7:0]message; //message为读入的数据
wire sig; //sig是读入的完成信号,下降沿表示读入完成,同时作为输出的开始信号
uart_r uart_r_1(.clk(sys_clk),.rx(rx),.message(message),.over(sig)); //读入
uart_t uart_t_1(.clk(sys_clk),.tx(tx),.message(message),.run(sig)); //输出
assign led[0]=message[0]; //这两个看数据最后两位存的对不对的
assign led[1]=message[1];
endmodule
2、输入模块:
module uart_r(
input clk,
input wire rx,
output reg [7:0]message,
output reg over=0
); //clk为FPGA板载时钟(50MHz),rx为读入的串行信号,message为对应的并行信号,over的下降沿将表示读入转换完成
reg [12:0]cnt_clk=0; //需要一个量来数clk的个数,每5208个clk,对应0.104us,即波特率9600对应的1bit占用的时常
reg [4:0]cnt_message=0; //计数message的位数,表征传递进行到了第几位
reg [7:0]message_mid=0; //message的前体,在over的下降沿传递给message,避免传递没结束,message就有输出值了
reg r_start=1; //判断第一个0位,表示传递开始
always @(posedge clk)
begin
if (rx==0&&r_start==1) begin
cnt_clk<=cnt_clk+1;
if (cnt_clk==2604&&rx==0) begin
r_start<=0;
cnt_clk<=0;
cnt_message<=0;
message_mid<=0;
end
end //判断是否为开始位,是时开始计算clk,数2604下(0.5bit)即在开始位中间,开始读数
else if (r_start==0) begin
cnt_clk<=cnt_clk+1;
if (cnt_clk==5208) begin //每5208个clk读一次
message_mid[cnt_message]<=rx;
cnt_message<=cnt_message+1;
cnt_clk<=0;
end
else if (cnt_message==8) begin //读完第8位不读了
if (cnt_clk==3000) begin //在数据位第8位的中间往右走2604个clk进入终止位(默认无奇偶校验位),在终止位中(往右走3000个clk和5000个clk之间)输出一个over信号
over<=1;
end
if (cnt_clk==5000) begin //over下降沿,传递完成,message_mid赋值给message,所有信号还原
over<=0;
cnt_clk<=0;
cnt_message<=0;
r_start<=1;
message<=message_mid;
message_mid<=0;
end
end
end //开始读数,每5208个clk读一次
else begin
r_start<=1;
over<=0;
end
end
endmodule
3、输出模块:
module uart_t(
input wire [7:0]message,
input clk,
output reg tx=1,
input wire run
); //大致思路与输入类似,tx连接蓝牙的rx,注意run信号为开始信号,连接的输入的over信号
reg [12:0]cnt_clk=0;
reg [4:0]cnt_message=0;
reg t_start=1;
always @(posedge clk) begin
if (run==1&&t_start==1) begin
t_start<=0;
cnt_clk<=0;
end
else if (run==0&&t_start==0&&cnt_message==0) begin //在run的下降沿开始输出
tx<=0;
cnt_clk<=cnt_clk+1;
if (cnt_clk==5208) begin
tx<=message[cnt_message];
cnt_clk<=0;
cnt_message<=1;
t_start<=0;
end
end
else if (cnt_message>=1) begin
cnt_clk<=cnt_clk+1;
if (cnt_clk==5208) begin
cnt_clk<=0;
if (cnt_message==8) begin
tx<=1;
t_start<=1;
cnt_message<=0;
end
else begin
tx<=message[cnt_message];
cnt_message<=cnt_message+1;
end
end
end
else begin
tx=1;
end
end
endmodule
4、测试模块
`timescale 1ps/1ps
module testbench (
output reg clk,
output reg tx,
input rx,
input [1:0] led
);
reg [23:0]i;
initial
begin
clk=0;
for(i=0;i<668900;i=i+1)#10 clk=~clk;
end
initial begin
tx=1; #200000
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=1; #104200
tx=0; #104200
tx=0; #104200
tx=1; #104200
tx=1;
end
bluetooth blue1(.sys_clk(clk),.tx(rx),.rx(tx),.led(led));
endmodule
5、仿真效果
一个小Tip,Add Waves之后课以通过这一排按钮重新加载波形: