欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)

程序员文章站 2024-02-24 23:28:28
...

一、准备及目标

采用的元器件:T型口数据线2;总线1;USB Blaster1;FPGA板子1;HC-05蓝牙模块*1。
使用的软件:手机:蓝牙串口SPP;电脑:Quartus。
实现的目标:手机输入一段信息,通过蓝牙传递信息给FPGA,FPGA收到信息后,再将该信息通过蓝牙传递回手机。
如图:
Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)

二、为什么要写这么一篇文章

太菜了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、实物连接

这个不难:Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)

四、代码

代码很非常丑陋且基础,但毕竟自己写的,读得懂:

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、仿真效果

Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)
一个小Tip,Add Waves之后课以通过这一排按钮重新加载波形:
Verilog实现:HC-05蓝牙与FPGA信息互传(UART协议)