FPGA的UART传输指定文本

一、前文与本文的区别

之前写了一篇UART的博客记录了这个学期学的一点东西,后面还有点内容在这里补充记录一下。

之前只实现了文本的回显,即向FPGA发送的文本,和FPGA向手机蓝牙发送的文本是一致。
而想要FPGA向手机蓝牙发送指定的文本,或者根据手机发送给FPGA的文本回复对应的文本,有两个新东西。其一是了解该UART通讯使用的是什么编码标准,其二是在FPGA中预先存储文本内容。
本文代码效果如图,输入不同,得到的反馈不同:
在这里插入图片描述
下记录一下上述两个新东西的内容

二、手机蓝牙默认使用的编码标准

一开始我认为写好UART后,用FPGA给手机发消息会是很简单的事,只需要百度“文本自动转二进制”等内容,会有一堆网站。然后在这些网站上面复制二进制或十六进制内容存进FPGA,再让FPGA发送,手机就能收到中文的消息了。然而好像没这么简单,这样子出来的文本是乱码。

先说解决方法,自动转出来的文本,比如“你好”自动转换成16进制是“4f60 597d”,其中“你”字是4f60,对应二进制“0100 1111 0110 0000”需要把这几位数字按顺序填入“1110xxxx 10xxxxxx 10xxxxxx”的“x”中。所以“你”字应该以“11100100 10111101 10100000”的方式存在FPGA中。除了这种拓展格式,还有一些拓展格式,具体的可以看这篇文章
这篇文章介绍了ASCII、Unicode和UTF-8的关系。
我简述下他们三者的关系:16进制下,ASCII码从00到0FFH结束,是一些符号和英文文本;Unicode是4位的,包含汉字信息,但是Unicode只是表示形式,不是存储形式;UTF-8是6位的,由Unicode按照上述方式扩展得到,UTF-8是文本信息真正的存储形式

回到最开始出现的乱码问题,就是在FPGA中存储Unicode导致的,正确做法是存UTF-8。还有一个小细节补充一下,网上是有文本自动转UTF-8的工具的,但和通过Unicode扩展得到的不一致,扩展得到的UTF-8编码才能用。这导致每次想存什么文本很麻烦,需要转成Unicode,再一个字一个字去算他们的扩展。

三、在FPGA中存储文本信息

1、这篇博客是通过IP核方法
2、这篇文章里面的内容可以帮助我们自己写出预先有内容的存储器
我的代码采用了第二种方法

四、源码及效果

对比我的上篇文章中的代码改了一点点

1、顶层模块
module bluetooth(
	input sys_clk,
	input rx,
	output tx
);																				//sys_clk为系统板载时钟(50MHz),rx对应蓝牙模块的tx,tx对应蓝牙模块的rx;led是我设置来判断读入数据错没错的
	wire[7:0]message_in;															//message为读入的数据
	wire[7:0]message_out;
	reg [13:0]AB=0;
	wire r_over;
	wire t_over;
	reg t_run=0;
	wire final;
	reg final_l;
	reg t_over_l;
	reg cs=0;
	
	uart_r uart_r_1(.clk(sys_clk),.rx(rx),.message(message_in),.over(r_over));		//读入
	uart_t uart_t_1(.clk(sys_clk),.tx(tx),.message(message_out),.run(t_run),.over(t_over));			//输出
	
	r_final r_final_1(.sys_clk(sys_clk),.over(r_over),.rx(rx),.final(final));            //判断是否输入完成,final的上升沿就已经可以说明输入完成了
	
	rom_1 rom_1_1(.sys_clk(sys_clk),.CS(cs),.AB(AB),.DB(message_out));
	
	always @(posedge sys_clk)
	begin
		final_l<=final;
		t_over_l<=t_over;
		if (final==1&&final_l==0)
		begin
			cs<=1;
			t_run<=1;
			if (message_in==8'b00110000) AB<=0;
			else if (message_in==8'b00110001) AB<=13'b0000000100010;
			else if (message_in==8'b00110010) AB<=13'b0000000110000;
		end
		else if (final==0&&final_l==1)
		begin
			t_run<=0;
		end
		
		if (t_over==1&&t_over_l==0)
		begin
			AB<=AB+1;
		end
	end
endmodule
2、判断输入模块,我的代码只判断了一次输入信息的最后一个字符
module r_final(
	input sys_clk,
	input over,
	input rx,
	output reg final=0
);
	reg [30:0] cnt_clk;
	reg over_l;
	reg start_judge=0;
	
	always @(posedge sys_clk)
	begin
		over_l<=over;
		if (over==0&&over_l==1) start_judge<=1;
		if (start_judge==1)
		begin
			cnt_clk<=cnt_clk+1;
			if (rx==0)
			begin
				cnt_clk<=0;
				start_judge<=0;
			end
			
			if (cnt_clk==6000)
			begin
				final<=1;
			end
			
			if (cnt_clk==7000)
			begin
				final<=0;
				cnt_clk<=0;
				start_judge<=0;
			end
		end
		else begin
			cnt_clk<=0;
		end
	end

endmodule
3、输入输出
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
module uart_t(
	input wire [7:0]message,
	input clk,
	input run,
	output reg tx=1,
	output reg over=0
);																	//大致思路与输入类似,tx连接蓝牙的rx,注意run信号为开始信号,连接的输入的over信号

	reg [12:0]cnt_clk=0;
	reg [4:0]cnt_message=0;
	reg run_l;
	reg en=0;
	
	always @(posedge clk) begin
		run_l<=run;
		if (run==0&&run_l==1) en<=1;
		if (en==1)
		begin
			if (message!=8'b00100100)
			begin
				if (cnt_clk!=5208) cnt_clk<=cnt_clk+1;
				else cnt_clk<=0;
				if (cnt_message==0&&cnt_clk==0)
				begin
					tx<=0;
				end
				else if (cnt_clk==5208&&cnt_message<=7)
				begin
					tx<=message[cnt_message];
					cnt_message<=cnt_message+1;
				end
				else if (cnt_message==8&&cnt_clk==5208) cnt_message<=cnt_message+1;
				else if (cnt_message>8&&cnt_clk!=5208)
				begin
					if (cnt_clk==3000) over<=1;
					tx<=1;
				end
				else if (cnt_message>8&&cnt_clk==5208)
				begin
					over<=0;
					cnt_message<=0;
				end
			end
			else en<=0;
		end
		else begin
			over<=0;
			cnt_message<=0;
			cnt_clk<=0;
			tx<=1;
		end
	end
endmodule
4、ROM模块
module rom_1(
	input sys_clk,
	input wire CS,
	input wire [13:0] AB,
	output reg [7:0] DB
);

	reg [7:0] ROM [1023:0];

	initial 
	begin 
		$readmemh("F:/Program Data/FPGA/third year for college/uart/ROM.txt",ROM);
	end
	
	always @(posedge sys_clk)
	begin
		if (CS==1) DB<=ROM[AB];
	end
endmodule
5、ROM.txt中的内容

在这里插入图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值