串口发送模块——verilog实现
1、串口原理
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,UART)
是一种异步收发传输器,其在数据发送时将并行数据转换成串行数据来传输,
在数据接收时将接收到的串行数据转换成并行数据,可以实现全双工传输和接收。
它包括了 RS232、RS449、RS423、RS422 和 RS485 等接口标准规范和总线标准规范。
换句话说,UART 是异步串行通信的总称。而 RS232、RS449、RS423、RS422 和 RS485 等,
是对应各种异步串行通信口的接口标准和总线标准,
它们规定了通信口的电气特性、传输速率、连接特性和接口的机械特性等内容。
串口的引脚很多,但是常用引脚为RXD、TXD、GND。
2、串口发送时序
串口默认数据位为8位;
常用的波特率为9600, 19200, 115200 等;
奇偶效验位跟在数据位后来判断数据是否出错,但是不常用;
数据位前面有一位低电平作为起始位;
数据位后面有一位高电平作为停止位;(没有奇偶效验位时)
3、串口发送模块
(1)模块代码
`timescale 1ns / 1ps
module uart_byte_tx(
input [7:0] Data,
input Send_en,
input Clk,
input Rst_n,
output reg Uart_Tx,
output reg Tx_done
);
parameter TX_BAUD = 9600;
parameter CLK_FQC = 50_000_000;
parameter BAUD_CNT = CLK_FQC/TX_BAUD;
reg [15:0] baud_cnt;
reg [ 3:0] bit_cnt;
reg Send_en_r;
reg Send_en_rr;
reg Tx_flag;
always @(posedge Clk) begin
Send_en_r <= Send_en;
Send_en_rr <= Send_en_r;
end
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
Tx_flag <= 0;
else if(~Send_en_rr & Send_en_r)
Tx_flag <= 1'b1;
else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
Tx_flag <= 1'b0;
end
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
baud_cnt <= 0;
else if(baud_cnt == BAUD_CNT - 1)
baud_cnt <= 0;
else if(Tx_flag)
baud_cnt <= baud_cnt + 1'b1;
end
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
bit_cnt <= 0;
else if(bit_cnt == 10 - 1 & baud_cnt == BAUD_CNT - 1)
bit_cnt <= 0;
else if(baud_cnt == BAUD_CNT - 1)
bit_cnt <= bit_cnt + 1'b1;
end
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
Uart_Tx <= 1'b1;
else if(Tx_flag == 0)
Uart_Tx <= 1'b1;
else case(bit_cnt)
0: Uart_Tx <= 1'b0;
1: Uart_Tx <= Data[0];
2: Uart_Tx <= Data[1];
3: Uart_Tx <= Data[2];
4: Uart_Tx <= Data[3];
5: Uart_Tx <= Data[4];
6: Uart_Tx <= Data[5];
7: Uart_Tx <= Data[6];
8: Uart_Tx <= Data[7];
9: Uart_Tx <= 1'b1;
default: Uart_Tx <= 1'b1;
endcase
end
always @(posedge Clk or negedge Rst_n) begin
if(Rst_n == 0)
Tx_done <= 1'b0;
else if(bit_cnt == 9 & baud_cnt == BAUD_CNT - 1)
Tx_done <= 1'b1;
else
Tx_done <= 1'b0;
end
endmodule
(2)仿真文件
`timescale 1ns / 1ps
module uart_byte_tx_tb();
reg CLK_50M;
reg RST_N;
reg [7:0] Data;
reg Send_en;
uart_byte_tx uart_byte_tx_inst(
.Data (Data),
.Send_en (Send_en),
.Clk (CLK_50M),
.Rst_n (RST_N),
.Uart_Tx (),
.Tx_done ()
);
defparam uart_byte_tx_inst.BAUD_CNT = 10;
always #10 CLK_50M <= ~CLK_50M;
initial begin
CLK_50M <= 1'b0;
RST_N <= 1'b0;
Data <= 8'b0;
Send_en <= 1'b0;
#100
RST_N <= 1'b1;
Send_en <= 1'b1;
Data <= 8'b10111001;
#20
Send_en <= 1'b0;
#2500
Send_en <= 1'b1;
#20
Send_en <= 1'b0;
#5000
$stop;
end
endmodule
4、应用示例
这里将发送模块应用为一个每隔10ms以115200的波特率发送一个数据,每次发送的数据比前一个大1的例子.
(1)应用示例代码
`timescale 1ns / 1ps
module Tx_test(
input Clk,
input Rst_n,
output Uart_Tx
);
reg [18:0] cnt_10ms;
reg [ 7:0] Data;
reg Send_en;
wire Tx_done;
reg Tx_done_r;
reg Tx_done_rr;
reg wait_10ms;
parameter CNT_10MS = 500_000;
uart_byte_tx uart_byte_tx_inst(
.Data (Data),
.Send_en (Send_en),
.Clk (Clk),
.Rst_n (Rst_n),
.Uart_Tx (Uart_Tx),
.Tx_done (Tx_done)
);
defparam uart_byte_tx_inst.TX_BAUD = 115200;
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
cnt_10ms <= 0;
else if(cnt_10ms == CNT_10MS - 1)
cnt_10ms <= 0;
else if(wait_10ms == 1)
cnt_10ms <= cnt_10ms + 1'b1;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
Send_en <= 0;
else if(cnt_10ms == CNT_10MS - 1)
Send_en <= 1'b1;
else
Send_en <= 0;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
Data <= 8'b0;
else if(Send_en == 1)
Data <= Data + 1'b1;
else
Data <= Data;
end
always @(posedge Clk ) begin
Tx_done_r <= Tx_done;
Tx_done_rr <= Tx_done_r;
end
always @(posedge Clk or posedge Rst_n) begin
if(Rst_n == 0)
wait_10ms <= 1'b1;
else if(Tx_done_rr & ~Tx_done_r)
wait_10ms <= 1'b1;
else if(cnt_10ms == CNT_10MS - 1)
wait_10ms <= 0;
end
endmodule
(2)应用示例仿真
`timescale 1ns / 1ps
module Tx_test_tb();
reg CLK_50M;
reg RST_N;
Tx_test Tx_test_inst(
.Clk (CLK_50M),
.Rst_n (RST_N ),
.Uart_Tx ()
);
defparam Tx_test_inst.uart_byte_tx_inst.BAUD_CNT = 10;
defparam Tx_test_inst.CNT_10MS = 50;
always #10 CLK_50M <= ~CLK_50M;
initial begin
CLK_50M <= 1'b0;
RST_N <= 1'b0;
#20
RST_N <= 1'b1;
end
endmodule
(3)应用示例上板验证
将编译好的程序烧入开发板,然后打开串口调试助手,选好端口和波特率后再点击开始可得到如下结果。
参考的内容
【【零基础轻松学习FPGA】小梅哥Xilinx FPGA基础入门到项目应用培训教程】 https://www.bilibili.com/video/BV1va411c7Dz/?share_source=copy_web&vd_source=c6135c3b3a9878c08e2ddc91acdf6853