一、前文与本文的区别
之前写了一篇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