UART介绍
通用异步收发器(Univeral Asynchronous Receiver/Transmitter,UART)。UART相关特点的总结如下表所示。
特点 | UART | 解释 |
---|---|---|
信号线 | RX TX | RX负责接收、TX负责发送。 |
通信方式 | 全双工通信 | 通信允许数据在两个方向上同时传输,即RX接收数据时,TX可以发送数据。 |
通信特征 | 异步串行 | 数据以帧为单位串行传输,一帧可以包含5/6/7/8位数据。由于UART为异步通信协议,不含同步时钟,收发双方通过约定相同的波特率来传输数据,波特率的单位为bit/s(位/秒),常用的波特率为115200 bit/s或9600 bit/s。 |
传输单位 | 帧 | UART以帧为单位传输数据,一个帧由4部分组成,分别是起始位、数据位、奇偶校验位、停止位。起始位表示一帧数据的开始,一般为1 bit的0;数据位保存需要传递的数据,一般为5/6/7/8 bit数据;奇偶检验位负责对传输的数据进行检查,用于检验数据在传输时是否出错;停止位表示一帧数据的结束,一般为1 bit、1.5 bit或2 bit的1。 |
UART实现
约定UART的帧格式和波特率。
- 帧格式
- 起始位:1 bit,0有效。
- 数据位:8 bit。
- 奇偶校验位:0 bit
- 停止位:1 bit,1有效。
- 波特率
- 9600 bit/s
传输模块
`timescale 1ns / 1ps
module uart_tx (
input wire clk_i,
input wire rst_i,
input wire [7:0] data_i,
input wire tx_en_i,
output reg tx_data_o
);
// 波特率 9600
parameter BPS = 9600;
// 时钟频率 50MHz
parameter CLK_FREQ = 50000000;
localparam BPS_CNT = CLK_FREQ / BPS;
localparam IDLE = 0;
localparam START = 1;
localparam SEND = 2;
localparam STOP = 3;
// 分频计数
reg [31:0] count;
// 已传输的bit数
reg [31:0] send_count;
reg [1:0] cur_state;
reg [1:0] next_state;
// 三段式状态机
// cur_state
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
cur_state <= IDLE;
end else begin
cur_state <= next_state;
end
end
// next_state
always @ (*) begin
case(cur_state)
IDLE : begin
if(tx_en_i == 1'b1) begin
next_state = START;
end else begin
next_state = IDLE;
end
end
START : begin
if(count == BPS_CNT - 1) begin
next_state = SEND;
end else begin
next_state = START;
end
end
SEND : begin
if(send_count == 8 - 1 && count == BPS_CNT - 1) begin
next_state = STOP;
end else begin
next_state = SEND;
end
end
STOP : begin
if(count == BPS_CNT - 1) begin
next_state = IDLE;
end else begin
next_state = STOP;
end
end
default : next_state = IDLE;
endcase
end
// count
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
count <= 0;
end else begin
if(cur_state != IDLE) begin
if(count == BPS_CNT - 1) begin
count <= 0;
end else begin
count <= count + 1;
end
end else begin
count <= 0;
end
end
end
// send_count
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
send_count <= 0;
end else begin
if(cur_state == SEND) begin
if(count == BPS_CNT - 1) begin
send_count <= send_count + 1;
end else begin
send_count <= send_count;
end
end else begin
send_count <= 0;
end
end
end
// tx_data_o
always @ (*) begin
case(cur_state)
IDLE : tx_data_o = 1;
START : tx_data_o = 0;
SEND : begin
case(send_count)
0 : tx_data_o = data_i[0];
1 : tx_data_o = data_i[1];
2 : tx_data_o = data_i[2];
3 : tx_data_o = data_i[3];
4 : tx_data_o = data_i[4];
5 : tx_data_o = data_i[5];
6 : tx_data_o = data_i[6];
7 : tx_data_o = data_i[7];
default : tx_data_o = 1;
endcase
end
STOP : tx_data_o = 1;
default : tx_data_o = 1;
endcase
end
endmodule
接收模块
`timescale 1ns / 1ps
module uart_rx (
input wire clk_i,
input wire rst_i,
input wire rx_data_i,
output reg rx_done_o,
output reg [7:0] data_o
);
// 波特率 9600
parameter BPS = 9600;
// 时钟频率 50MHz
parameter CLK_FREQ = 50000000;
localparam BPS_CNT = CLK_FREQ / BPS;
localparam IDLE = 0;
localparam START = 1;
localparam RECV = 2;
localparam STOP = 3;
// 分频计数
reg [31:0] count;
// 已接收的bit数
reg [31:0] recv_count;
// 下降沿检测
reg rx_data_i_ff;
reg [1:0] cur_state;
reg [1:0] next_state;
// rx_data_i_ff
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
rx_data_i_ff <= 0;
end else begin
rx_data_i_ff <= rx_data_i;
end
end
// 三段式状态机
// cur_state
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
cur_state <= 0;
end else begin
cur_state <= next_state;
end
end
// next_state
always @ (*) begin
case(cur_state)
IDLE : begin
// 下降沿检测
if(rx_data_i_ff == 1'b1 && rx_data_i == 1'b0) begin
next_state = START;
end else begin
next_state = IDLE;
end
end
START : begin
// 下降沿检测占用一个周期
if(count == BPS_CNT - 2) begin
next_state = RECV;
end else begin
next_state = START;
end
end
RECV : begin
if(recv_count == 8 - 1 && count == BPS_CNT - 1) begin
next_state = STOP;
end else begin
next_state = RECV;
end
end
STOP : begin
if(count == BPS_CNT - 1) begin
next_state = IDLE;
end else begin
next_state = STOP;
end
end
default : next_state = IDLE;
endcase
end
// count
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
count <= 0;
end else begin
case(cur_state)
IDLE : count <= 0;
START : begin
if(count == BPS_CNT - 2) begin
count <= 0;
end else begin
count <= count + 1;
end
end
RECV : begin
if(count == BPS_CNT - 1) begin
count <= 0;
end else begin
count <= count + 1;
end
end
STOP : begin
if(count == BPS_CNT - 1) begin
count <= 0;
end else begin
count <= count + 1;
end
end
default : count <= 0;
endcase
end
end
// recv_count
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
recv_count <= 0;
end else begin
if(cur_state == RECV) begin
if(count == BPS_CNT - 1) begin
recv_count <= recv_count + 1;
end else begin
recv_count <= recv_count;
end
end else begin
recv_count <= 0;
end
end
end
// data_o
always @ (posedge clk_i) begin
if(rst_i == 1'b1) begin
data_o <= 0;
end
if(cur_state == RECV) begin
if(count == BPS_CNT / 2 - 1) begin
case(recv_count)
0 : data_o[0] <= rx_data_i;
1 : data_o[1] <= rx_data_i;
2 : data_o[2] <= rx_data_i;
3 : data_o[3] <= rx_data_i;
4 : data_o[4] <= rx_data_i;
5 : data_o[5] <= rx_data_i;
6 : data_o[6] <= rx_data_i;
7 : data_o[7] <= rx_data_i;
default : data_o <= data_o;
endcase
end else begin
data_o <= data_o;
end
end else begin
data_o <= data_o;
end
end
// rx_done_o
always @ (*) begin
if(rst_i == 1'b1) begin
rx_done_o = 0;
end else begin
if(cur_state == STOP) begin
rx_done_o = 1;
end else begin
rx_done_o = 0;
end
end
end
endmodule
测试文件
测试传输模块(uart_tx)和接收模块(uart_rx)。uart_tx发送数据给uart_rx,检查uart_rx接收的数据和uart_tx发送数据是否一致。
`timescale 1ns / 1ps
module tx2rx_testbench ();
// 时钟频率50MHz,周期为20ns
localparam CLK_PERIOD = 20;
reg rst;
reg clk;
// 测试数据
reg [7:0] test_data = 8'b00000000;
// 开始传输标志
reg tx_en;
reg first_flag;
// tx传输数据
wire tx_data;
// 下降沿检测
reg rx_done_ff;
// 接收完成
wire rx_done;
// 接收数据
wire [7:0] data;
initial begin
clk = 1;
rst = 1;
tx_en = 0;
# 100
rst = 0;
# 40
first_flag = 1;
# 20
first_flag = 0;
end
always @(*) begin
if(rst == 1'b1) begin
test_data <= test_data;
end else begin
if(tx_en == 1) begin
test_data = test_data + 1;
end else begin
test_data = test_data;
end
end
end
// 下降沿检测
always @(posedge clk) begin
if(rst == 1'b1) begin
rx_done_ff <= 0;
end else begin
rx_done_ff <= rx_done;
end
end
always @ (*) begin
if(rst == 1'b1) begin
tx_en = 0;
end else begin
if(first_flag == 1'b1) begin
tx_en = 1;
end else begin
if(rx_done == 1'b0 && rx_done_ff == 1'b1) begin
tx_en = 1;
end else begin
tx_en = 0;
end
end
end
end
always begin
# (CLK_PERIOD / 2) clk = ~clk;
end
uart_tx uart_tx_inst(
.clk_i(clk),
.rst_i(rst),
.data_i(test_data),
.tx_en_i(tx_en),
.tx_data_o(tx_data)
);
uart_rx uart_rx_inst(
.clk_i(clk),
.rst_i(rst),
.rx_data_i(tx_data),
.rx_done_o(rx_done),
.data_o(data)
);
endmodule