0.序言
使用vivado联合modelsim实现UART协议进行数据发送与接收。
1.UART协议简介
(1)定义
通用异步收发传输器,是一种异步全双工串行接口协议,是异步串行通信的总称。存在多种通信接口和总线标准,包括RS232/RS449/RS422等。
(2)结构
RXD:数据接收引脚;
TXD:数据发送引脚;
GND:公共地,通讯时设备需要同时接地;
(3)接口协议
起始位:一般以逻辑0表示;
有效数据位:通常有效数据位的长度有5/6/7/8位;
校验位:校验传输的正确性,奇校验/偶校验等;
停止位:可有0.5,1,1.5,2个逻辑1表示;
波特率:单位时间内传输的码元个数,常用的波特率为9600/19200/38400/15200,通信双方的波特率要一致;
2.实例
(1)结构框图及时序图
(A)结构框图
测试模块提供需要传输的数据和数据有效标志,通过发送模块(MCU1_send)进行编码成UART协议格式数据进行发送,并在接收模块(MCU2_receive)还原数据,最后输出还原好的数据。
(B)发送模块时序图
Data_in:首先给发送模块一个需要发送的数据,首先会将数据缓存;
Data_valid:数据有效标志,也标志着一个数据传输的开始;
Tx_flag:发送过程有效标志,在整个传输帧拉高,传输结束拉低,拉低标志是uart_done_flag的下降沿;
Clk_cnt:波特率周期计数器,波特率使用的是9600,系统时钟位52MHz,每个数据发送的周期是52M /9600=5208;
Tx_cnt:UART发送数据状态计数,共有10个状态;
Txd:发送数据线上的数据;
Uart_done_flag:发送结束标志信号;
注意:tx_cnt/txd和uart_done_flag每个状态对应5208个时钟周期
(C)接收数据时序图
rxd:接收数据线接收到的数据;
Rx_flag:接收过程有效标志,开始于rxd空闲状态开始的下降沿,结束于rx_done_flag的下降沿;
Clk_cnt:波特率周期计数器,同发送一致,要与rxd每个数据对齐;
Tx_cnt:UART接收数据状态计数,共有10个状态;
rx_done_flag:接收结束标志信号;
data_out[7:0]:接收的数据进行输出
注意:
rx_cnt/rxd和uart_done_flag每个状态对应5208个时钟周期
为了保证数据的稳定性,我们一般取值在数据周期的中间,即clk_cnt计数到2603时将传输的数据取出
(2)代码
(A)test_bench
说明:例化了顶层模块,并间隔一段时间产生一个数据和数据有效标志,这里连续发送两个数据
`timescale 1ns / 1ps
module tb_UART_interface;
reg clk ;
reg rst_n ;
reg [7:0] data_in ;
reg data_valid ;
wire [7:0] data_out ;
parameter T = 20;
always#(T/2)clk = ~clk;
UART_interface u_UART_interface(
.clk (clk ),
.rst_n (rst_n ),
.data_in (data_in ),
.data_valid (data_valid),
.data_out (data_out )
);
initial begin
clk = 1'b1;
rst_n = 1'b0;
data_in = 8'd0;
data_valid <= 1'b0;
#T;
rst_n = 1'b1;
#T;
data_in = 8'b0011_0101;
data_valid = 1;
#T;
data_in = 8'b0;
data_valid = 0;
#(T*60000);
data_in = 8'b1110_0001;
data_valid = 1;
#T;
data_in = 8'b0;
data_valid = 0;
end
endmodule
(B)顶层模块
说明:例化了发送数据模块和接收数据模块
module UART_interface(
input clk ,
input rst_n ,
input [7:0] data_in ,
input data_valid ,
output [7:0] data_out
);
wire UART_txd;
UART_send u_UART_send(
.clk (clk),
.rst_n (rst_n),
.data_in (data_in),
.data_valid (data_valid),
.UART_txd(UART_txd)
);
UART_receive u_UART_receive(
.clk (clk),
.rst_n (rst_n),
.UART_rxd (UART_txd),
.data_out (data_out)
);
endmodule
(C)发送数据模块
module UART_send(
input clk ,
input rst_n ,
input [7:0] data_in ,
input data_valid ,
output reg UART_txd
);
//输入数据暂存
reg [7:0] data_in_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
data_in_r <= 8'd0;
else if(data_valid)
data_in_r <= data_in;
else
data_in_r <= data_in_r;
end
//发送过程有效标志
reg tx_done_flag;
reg tx_done_flag_r;
reg tx_flag;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_flag <= 1'b0;
else if(data_valid == 1'b1)
tx_flag <= 1'b1;
else if(tx_done_flag_r&(~tx_done_flag))
tx_flag <= 1'b0;
else
tx_flag <=tx_flag;
end
//波特率周期计数器,在发送过程有效标志有效时计数
reg [12:0] clk_cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_cnt <= 13'd0;
else if(tx_flag == 1'b1)begin
if(clk_cnt == 13'd5207)
clk_cnt <= 13'd0;
else
clk_cnt <= clk_cnt + 1'b1;
end
else
clk_cnt <= 13'd0;
end
//UART发送数据状态计数,共有10个状态
reg [3:0] tx_cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_cnt <= 4'd0;
else if(tx_flag == 1'b1)begin
if(clk_cnt == 13'd5207)begin
if(tx_cnt < 4'd9)
tx_cnt <= tx_cnt + 1'b1;
else
tx_cnt <= 4'd0;
end
else
tx_cnt <= tx_cnt;
end
else
tx_cnt <= 4'd0;
end
//txd赋值,默认值即空闲状态为高电平
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
UART_txd <= 1'b1;
else if(tx_flag == 1'b1) begin
case(tx_cnt)
4'd0:begin UART_txd <= 1'b0; end
4'd1:begin UART_txd <= data_in_r[0]; end
4'd2:begin UART_txd <= data_in_r[1]; end
4'd3:begin UART_txd <= data_in_r[2]; end
4'd4:begin UART_txd <= data_in_r[3]; end
4'd5:begin UART_txd <= data_in_r[4]; end
4'd6:begin UART_txd <= data_in_r[5]; end
4'd7:begin UART_txd <= data_in_r[6]; end
4'd8:begin UART_txd <= data_in_r[7]; end
4'd9:begin UART_txd <= 1'b1; end
default:begin UART_txd <= 1'b1; end
endcase
end
else
UART_txd <= 1'b1;
end
//发送完成标志位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_done_flag <= 1'b0;
else if(tx_cnt == 4'd9)
tx_done_flag <= 1'b1;
else
tx_done_flag <= 1'b0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
tx_done_flag_r <= 1'b0;
else
tx_done_flag_r <= tx_done_flag;
end
endmodule
(D)接收数据模块
module UART_receive(
input clk ,
input rst_n ,
input UART_rxd,
output reg [7:0] data_out
);
//接收过程有效标志信号,用过数据的下降沿得到,接收结束后归0
reg uart_rxd_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
uart_rxd_r <= 1'b0;
else
uart_rxd_r <= UART_rxd;
end
reg rx_done_flag;
reg rx_done_flag_r;
reg rx_flag;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_flag <= 1'b0;
else if(uart_rxd_r&(~UART_rxd))
rx_flag <= 1'b1;
else if(rx_done_flag_r&(~rx_done_flag))
rx_flag <= 1'b0;
else
rx_flag <= rx_flag;
end
//波特率周期计数器,在发送过程有效标志有效时计数
reg [12:0] clk_cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_cnt <= 13'd0;
else if(rx_flag == 1'b1)begin
if(clk_cnt == 13'd5207)
clk_cnt <= 13'd0;
else
clk_cnt <= clk_cnt + 1'b1;
end
else
clk_cnt <= 13'd0;
end
//UART接收数据状态计数,共有10个状态
reg [3:0] rx_cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_cnt <= 4'd0;
else if(rx_flag == 1'b1)begin
if(clk_cnt == 13'd5207)begin
if(rx_cnt < 4'd9)
rx_cnt <= rx_cnt + 1'b1;
else
rx_cnt <= 4'd0;
end
else
rx_cnt <= rx_cnt;
end
else
rx_cnt <= 4'd0;
end
//rxd赋值,将传输的有效数据取出
reg [7:0] data_out_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_out <= 8'd0;
data_out_r <= 8'd0;
end
else if(rx_flag == 1'b1) begin
case({rx_cnt,clk_cnt})
{4'd1,13'd2603}:begin data_out_r[0] <= UART_rxd; end
{4'd2,13'd2603}:begin data_out_r[1] <= UART_rxd; end
{4'd3,13'd2603}:begin data_out_r[2] <= UART_rxd; end
{4'd4,13'd2603}:begin data_out_r[3] <= UART_rxd; end
{4'd5,13'd2603}:begin data_out_r[4] <= UART_rxd; end
{4'd6,13'd2603}:begin data_out_r[5] <= UART_rxd; end
{4'd7,13'd2603}:begin data_out_r[6] <= UART_rxd; end
{4'd8,13'd2603}:begin data_out_r[7] <= UART_rxd; end
{4'd9,13'd0}:begin data_out <= data_out_r; end
default:begin data_out_r <= data_out_r; end
endcase
end
else begin
data_out_r <= 8'd0;
data_out <= data_out;
end
end
//接收完成标志位
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_done_flag <= 1'b0;
else if(rx_cnt == 4'd9)
rx_done_flag <= 1'b1;
else
rx_done_flag <= 1'b0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_done_flag_r <= 1'b0;
else
rx_done_flag_r <= rx_done_flag;
end
endmodule
(3)结果
(A)顶层模块
发送了两个数据,8’b0011_0101和8’b1110_0001,最终收到的数据是一致的;
(B)发送数据模块
以发送第一个数据8’b0011_0101为例;
(C)接收数据模块
以接收第一个数据8’b0011_0101为例