反向学习Uart串口通信
一、Uart串口通信的基本原理
UART(Universal Asynchronous Receiver/Transmitter,通用异步收发传输器)是一种常见的串行通信协议,用于在设备之间发送和接收数据。与并行通信方式不同,串口通信通过一个信号线(TX、RX)依次传输数据位,减少了线路的数量。
UART的基本工作原理
数据传输方式
UART是一种异步通信协议。这意味着数据传输不需要时钟信号的同步,而是通过数据位的起始位、数据位、停止位等来确保传输的顺序和可靠性。传输数据的组成
每次通过UART发送的数据,都会被分为若干位(通常是8位)。这8位数据通过一条数据线(TX线)发送到接收设备。接收端会按照相同的格式(位、开始位、停止位)来接收和解析数据。数据帧结构
每个数据帧通常由以下几个部分组成:- 起始位:表示数据传输的开始,通常为一个低电平。
- 数据位:实际传输的数据,常见为8位。
- 奇偶校验位(可选):用于错误检测,判断是否接收到正确的数据。
- 停止位:表示数据传输的结束,通常为1位高电平。
- 下图为没有奇偶校验位的数据帧格式

4. 全双工与半双工
UART通信支持全双工(Full Duplex)和半双工(Half Duplex)两种模式。在全双工模式下,发送和接收可以同时进行;而在半双工模式下,发送和接收是交替进行的。
UART的工作流程
发送端
在发送端,数据通过TX(Transmit)线以串行形式发送出去。数据在传输时会被切分成位,先发送起始位,再发送数据位,最后发送停止位。接收端
接收端通过RX(Receive)线接收到数据。当接收端接收到数据时,它会首先读取起始位,然后逐位接收数据位,最后检查停止位。接收端会对数据进行校验,确保数据没有发生错误。
二、Uart串口发送
(一)设计代码

module uart_byte_tx(
Clk,
Reset_n,
Data,
Send_Go,
Baud_set,
uart_tx,
//Tx_done
);模块声明部分
input Clk;
input Reset_n;
input [7:0]Data;
input Send_Go;
input [2:0]Baud_set;
output reg uart_tx;外部端口声明
reg Send_en;
reg Tx_done;//output reg Tx_done;内部信号声明
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Send_en <= 0;
else if(Send_Go)//当外部应用给一个脉冲Send_Go信号时,内部Send_en信号开始工作,检测到Tx_done发送完成信号,就停止工作。
Send_en <= 1;
else if(Tx_done)
Send_en <= 0;
reg [7:0]r_Data;//构建一个寄存器r_Data,用来存储信号,当Send_Go高电平信号来的时候,保证此刻的外部Data数据能够保留住。
always@(posedge Clk)
if(Send_Go)
r_Data <= Data;
else
r_Data <= r_Data;
//Baud_set = 0,就让波特率 = 9600;
//Baud_set = 1,就让波特率 = 19200;
//Baud_set = 2,就让波特率 = 38400;
//Baud_set = 3,就让波特率 = 57600;
//Baud_set = 4,就让波特率 = 115200;
reg [17:0]bps_DR;
always@(*)
case(Baud_set)
0:bps_DR = 1000000000/9600/20;
1:bps_DR = 1000000000/19200/20;
2:bps_DR = 1000000000/38400/20;
3:bps_DR = 1000000000/57600/20;
4:bps_DR = 1000000000/115200/20;
default:bps_DR = 1000000000/9600/20;
endcase
wire bps_clk;
assign bps_clk = (div_cnt == 1);
reg [17:0]div_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
div_cnt <=0;
else if(Send_en)begin
if(div_cnt == bps_DR – 1)
div_cnt <= 0;
else
div_cnt <= div_cnt + 1’b1;
end
else
div_cnt <=0;
reg [3:0]bps_cnt;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
bps_cnt <=0;
else if(Send_en)begin
if(bps_clk)begin// if(div_cnt == bps_DR – 1)begin,bps_DR – 1的计满将导致bps_cnt计数不及时,进而导致uart_tx的起始位发送不及时。
if(bps_cnt == 11)
bps_cnt <= 0;
else
bps_cnt <= bps_cnt + 1’b1;
end
end
else
bps_cnt <= 0;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)begin
uart_tx <= 1’b1;
Tx_done <= 0;
end
else begin
case(bps_cnt)
1:uart_tx <= 0; //当bps_cnt为1时,uart_tx一直保持为0,这里发送起始位。
2:uart_tx <= r_Data[0];
3:uart_tx <= r_Data[1];
4:uart_tx <= r_Data[2];
5:uart_tx <= r_Data[3];
6:uart_tx <= r_Data[4];
7:uart_tx <= r_Data[5];
8:uart_tx <= r_Data[6];
9:uart_tx <= r_Data[7];
10:uart_tx <= 1;
11:uart_tx <= 1;
default:uart_tx <= 1;
endcase
end
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Tx_done <= 0;
else if((bps_clk ==1) && (bps_cnt == 11))
Tx_done <= 1;
else
Tx_done <= 0;
// 0:begin uart_tx <= 0;Tx_done <=1’b0;end 当bps_cnt为0时,uart_tx一直保持为0(这不是我们想要的1)
// 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;
// 10:begin uart_tx <= 1;Tx_done <=1’b1;end
// default:uart_tx <= 1;功能逻辑部分
(二)Testbench验证
`timescale 1ns / 1ps仿真总时长1ps,仿真精度1ns
module uart_byte_tx_tb();模块定义
reg sys_clk;
reg sys_rst_n;
reg [7:0]Data;
reg Send_en;
wire [2:0]Baud_set;
wire Rx_Done;
wire [7:0]Data_all;信号声明
uart_loopback uart_loopback(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.Send_en(Send_en),
.Baud_set(Baud_set),
.Data(Data),
.Data_all(Data_all),
.Rx_Done(Rx_Done)
// output Tx_done
//input uart_rx,
//output uart_tx
);被测试模块实例化(DUT)
//wire Tx_done;
reg uart_rx;
//wire uart_tx;
assign Baud_set = 3’d4;
initial sys_clk = 1;
always#10 sys_clk = ~sys_clk;时钟生成 使用initial
块初始化系统时钟,并通过always
块创建时钟周期,使时钟周期为20ns(即频率为50MHz)
initial begin
sys_rst_n = 0;
Data = 0;
Send_en = 0;
#201;
sys_rst_n = 1;
#100;
uart_rx = 0;
Data = 8’h57;
Send_en = 1;
#2000;
@(posedge Rx_Done);
Send_en = 0;
#100;
uart_rx = 0;
Data = 8’h75;
Send_en = 1;
#2000;
@(posedge Rx_Done);
Send_en = 0;
uart_rx = 1;
$stop;
end输入激励信号的生成 通过initial
块设置激励信号,模拟系统的不同输入状态。这里主要对sys_rst_n
、Data
、Send_en
、uart_rx
等信号进行控制,以测试UART模块的不同功能
endmodule
三、Uart串口接收
(一)设计代码
(二)Testbench验证
initial
块初始化系统时钟,并通过always
块创建时钟周期,使时钟周期为20ns(即频率为50MHz)- 作品属于:hanhhsir.cn
- 本文链接: http://www.hanhhsir.cn/index.php/2025/02/17/反向学习uart串口通信/
- 作品采用: 《 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 》许可协议授权。