今天完成了对串口通信的实现,发送端代码,接收端代码的书写,同时还完成了仿真文件的书写。
收获:
值得注意的就是,rx,tx在实际操作的时候要分清楚,芯片的rx引脚接的是其他设备的tx,而芯片的tx引脚就是接的其他设备的rx。这点一定要分清楚,否则很容易错。
其他的就是个人简单体会。
在这个实训中,我学习了利用FPGA通过串口通信与外部设备进行数据交互的基础知识和技能,其中串口通信代码的实现任务是其中的一个主要部分。
首先,我了解了串口通信的基本原理。通过串口通信,数据被以比特为单位传输,其中传输速率由波特率确定。UART是通用异步收发传输协议,它是在无需时钟或时序信号的情况下实现通信的一种串行通信协议。为此,我需要了解如何设置FPGA串口通信的参数,包括波特率、数据位和校验位等等。
其次,我学习了如何使用FPGA编写串口通信的控制代码。在该实训中,我使用了VHDL和Verilog编写了控制FPGA从外部设备接收数据的代码,并且了解了数据如何被解析和存储到FPGA的存储器中,以及如何发送FPGA生成的数据给外部设备。
最后,我还了解了调试工具的使用方法,以便调试我的代码。通过使用调试器,我可以检查代码的执行情况,解决程序中可能存在的错误,提高代码的效率和正确性。
总的来说,这个实训的串口通信任务对我的FPGA编程和硬件设计技能提高有很大的帮助。我熟练地掌握了如何设置FPGA串口通信的参数和编写串口通信的控制代码,并且从中获得了实践经验,使我能够更加深入地理解数字电路和FPGA技术。
最后附上今天的代码:
共用波特率参数模块
`define SYS_FRQ 50_000_000
`define BAUD_115200
`ifdef BAUD_9600
`define BAUD_MAX 9600
`elsif BAUD_19200
`define BAUD_MAX 19200
`elsif BAUD_38400
`define BAUD_MAX 38400
`elsif BAUD_57600
`define BAUD_MAX 57600
`elsif BAUD_115200
`define BAUD_MAX 115200
`else
`define BAUD_MAX 9600
`endif
串口发端:
`include "param.v"
module uart_tx(
input wire clk ,
input wire rst_n ,
input wire tx_req,//发送请求
input wire [7:0] tx_din,//并行数据输入
output reg tx_dout,//串行数据输出
output wire dout_vld//并转串完成标志
);
reg [12:0] cnt_bps ;//波特率计数寄存器
wire add_cnt_bps ;//波特率计数开始
wire end_cnt_bps ;//波特率计数结束
reg [3:0] cnt_bit ;//比特计数寄存器
wire add_cnt_bit ;//比特计数开始
wire end_cnt_bit ;//比特计数结束
reg tx_flag ;//发送标志
reg [9:0] tx_data ;
//波特率计数器设计
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bps <= 13'd0;
end
else if(add_cnt_bps)begin
if(end_cnt_bps)begin
cnt_bps <= 13'd0;
end
else begin
cnt_bps <= cnt_bps + 1'd1;
end
end
else begin
cnt_bps <= cnt_bps;
end
end
assign add_cnt_bps = tx_flag;
assign end_cnt_bps = add_cnt_bps && cnt_bps == (`SYS_FRQ/`BAUD_MAX) - 1'd1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 4'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 4'd0;
end
else begin
cnt_bit <= cnt_bit + 1'd1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = end_cnt_bps;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 9;
//发送标志tx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_flag <= 1'b0;
end
else if(tx_req)begin
tx_flag <= 1'b1;
end
else if(end_cnt_bit)begin
tx_flag <= 1'b0;
end
else begin
tx_flag <= tx_flag;
end
end
//数据缓存,tx_data
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_data <= 10'b0;
end
else if(tx_req)begin
tx_data <= {1'b1, tx_din, 1'b0};
end
else begin
tx_data <= tx_data;
end
end
//tx_dout数据串行输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_dout <= 1'b1;
end
else if(tx_flag && cnt_bps == 1)begin
tx_dout <= tx_data[cnt_bit];
end
else begin
tx_dout <= tx_dout;
end
end
//dout_vld约束
assign dout_vld = ~tx_flag;
endmodule
串口发端仿真代码:
`timescale 1ns/1ns
module uart_tx_tb();
reg clk;
reg rst_n;
reg tx_req;
reg [7:0] tx_din;
wire tx_dout;
wire dout_vld;
always #(10) clk = ~clk;//每隔10ns翻转一次
initial begin//初始化信号
clk = 1'b0 ;//时钟信号初始化为0
rst_n = 1'b0 ;//复位信号初始化为0
#(10) ;//等待10ns
rst_n = 1'b1 ;//复位信号拉高电平
tx_req = 1'b1;
tx_din = 8'b00001111;
#(100_000);
tx_req = 1'b0;
$stop;
end
uart_tx u_uart_tx_tb(
.clk(clk) ,
.rst_n(rst_n) ,
.tx_req(tx_req),//发送请求
.tx_din(tx_din),//并行数据输入
.tx_dout(tx_dout),//串行数据输出
.dout_vld(dout_vld)//并转串完成标志
);
endmodule
串口收端:
`include "param.v"
module uart_rx(
input wire clk ,
input wire rst_n ,
input wire rx_din ,//串行数据输入
output wire [7:0] rx_dout ,//并行数据输出
output reg dout_vld//串转并完成标志
);
reg [12:0] cnt_bps ;//波特率计数寄存器
wire add_cnt_bps ;//波特率计数开始
wire end_cnt_bps ;//波特率计数结束
reg [3:0] cnt_bit ;//比特计数寄存器
wire add_cnt_bit ;//比特计数开始
wire end_cnt_bit ;//比特计数结束
reg rx_din_r0;//同步
reg rx_din_r1;//打拍
wire nedge ;//下降沿
reg rx_flag;//接受标志
reg [9:0] rx_data;//寄存数据包
//波特率计数器设计
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bps <= 13'd0;
end
else if(add_cnt_bps)begin
if(end_cnt_bps)begin
cnt_bps <= 13'd0;
end
else begin
cnt_bps <= cnt_bps + 1'd1;
end
end
else begin
cnt_bps <= cnt_bps;
end
end
assign add_cnt_bps = rx_flag;
assign end_cnt_bps = add_cnt_bps && cnt_bps == (`SYS_FRQ / `BAUD_MAX) - 1'd1;
//比特计数器设计
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 4'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 4'd0;
end
else begin
cnt_bit <= cnt_bit + 1'd1;
end
end
else begin
cnt_bit <= cnt_bit;
end
end
assign add_cnt_bit = end_cnt_bps;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 4'd10 - 1'd1;
//起始位位置检测
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_din_r0 <= 1'b1;
rx_din_r1 <= 1'b1;
end
else begin
rx_din_r0 <= rx_din;
rx_din_r1 <= rx_din_r0;
end
end
assign nedge = ~rx_din_r0 && rx_din_r1;
//接受标志rx_flag约束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_flag <= 1'b0;
end
else if(nedge)begin
rx_flag <= 1'b1;
end
else if(end_cnt_bit)begin
rx_flag <= 1'b0;
end
else begin
rx_flag <= rx_flag;
end
end
//缓存接受到的数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rx_data <= 10'b0;
end
else if(rx_flag && cnt_bps == (`SYS_FRQ/`BAUD_MAX) >> 1)begin
rx_data[cnt_bit] = rx_din_r0;
end
else begin
rx_data <= rx_data;
end
end
assign rx_dout = rx_data[8:1];
//串转并完成标志dout_vld约束
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_vld <= 1'b0;
end
else if(end_cnt_bit)begin
dout_vld <= 1'b1;
end
else begin
dout_vld <= 1'b0;
end
end
endmodule
串口收端仿真代码:
`timescale 1ns/1ns
module uart_rx_tb();
reg clk;
reg rst_n;
reg rx_din;
wire [7:0] rx_dout;
wire dout_vld;
always #(10) clk = ~clk;//每隔10ns翻转一次
initial begin//初始化信号
clk = 1'b0 ;//时钟信号初始化为0
rst_n = 1'b0 ;//复位信号初始化为0
#(10) ;//等待10ns
rst_n = 1'b1 ;//复位信号拉高电平
rx_din = 1'b0;
#(8681);
rx_din = 1'b0;
#(8681);
rx_din = 1'b0;
#(8681);
rx_din = 1'b0;
#(8681);
rx_din = 1'b0;
#(8681);
rx_din = 1'b1;
#(8681);
rx_din = 1'b1;
#(8681);
rx_din = 1'b1;
#(8681);
rx_din = 1'b1;
#(8681);
rx_din = 1'b1;
#(8681);
$stop;
end
uart_rx u_uart_rx_tb(
.clk(clk) ,
.rst_n(rst_n) ,
.rx_din(rx_din),
.rx_dout(rx_dout),
.dout_vld(dout_vld)
);
endmodule
串口收发顶层文件:
module uart(
input wire clk ,
input wire rst_n ,
input wire rx ,
output wire tx //串行输出
);
wire [7:0] rx_byte ;
wire rx_byte_vld ;
uart_rx u_uart_rx(
.clk (clk),
.rst_n (rst_n),
.rx_din (rx),//串行数据输入
.rx_dout (rx_byte),//并行数据输出
.dout_vld(rx_byte_vld)//串转并完成标志
);
uart_tx u_uart_tx(
.clk (clk),
.rst_n (rst_n),
.tx_req(rx_byte_vld),//发送请求
.tx_din(rx_byte),//并行数据输入
.tx_dout(tx),//串行数据输出
//.dout_vld()//并转串完成标志
);
endmodule