目录
一、UART介绍
二、Verilog代码
2.1 TOP
2.2 发送模块
2. 3 接收模块
UART是一种采用异步串行通信方式的通用异步收发传输器(universal asynchronous receiver-transmitter),它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。
UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。UART在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位,如图所示。其中,起始位标志着一帧数据的开始,停止位标志着一帧数据的结束,数据位是一帧数据中的有效数据。校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错。 奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时, 对1的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查1 的个数是否为偶数。
UART通信过程中的数据格式及传输速率是可设置的,为了正确的通信,收发双方应约定并遵循同样的设置。数据位可选择为5、6、7、8位,其中8位数据位是最常用的,在实际应用中一般都选择8位数据位;校验位可选择奇校验、偶校验或者无校验位;停止位可选择1位(默认),1.5或2位。串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps(位 /秒),常用的波特率有9600、19200、38400、57600以及115200等。
在设置好数据格式及传输速率之后,UART负责完成数据的串并转换,而信号的传输则由外部驱动电路实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有RS232、RS422、RS485等,它们定义了接口不同的电气特性,如RS-232是单端输入输出,而RS-422/485为差分输入输出等。 RS232接口标准出现较早,可实现全双工工作方式,即数据发送和接收可以同时进行。在传输距离较短时(不超过15m),RS232是串行通信最常用的接口标准。
RS-232标准的串口最常见的接口类型为DB9,样式如图 16.1.2所示,工业控制领域中用到的工控机一般都配备多个串口,很多老式台式机也都配有串口。但是笔记本电脑以及较新一点的台式机都没有串口,它们一般通过USB转串口线(图 16.1.3)来实现与外部设备的串口通信。
DB9接口定义以及各引脚功能说明如下图所示,我们一般只用到其中的2(RXD)、3 (TXD)、5(GND)引脚,其他引脚在普通串口模式下一般不使用。
串口数据环回实验系统框图:
在编写代码之前,我们首先要确定串口通信的数据格式及波特率。在这里我们选择串口比
module uart_top(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output uart_txd
);
parameter FREQ = 50000000;
parameter BPS = 9600;
wire en;
wire [7:0] uart_data;
uart_recv #( //串口接收模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_recv(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_rxd (uart_rxd),
.uart_txd (uart_data),
.rx_done (en)
);
uart_send #( //串口发送模块
.FREQ (FREQ), //设置系统时钟频率
.BPS (BPS))
u_uart_send(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.uart_din (uart_data),
.tx_en (en),
.uart_txd (uart_txd)
);
endmodule
module uart_send(
input sys_clk,
input sys_rst_n,
input [7:0] uart_din,
input tx_en,
output reg uart_txd
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ / BPS;
wire start_flag;
reg tx_en_d0;
reg tx_en_d1;
reg tx_flag; //发送过程标志
reg [15:0] bps_cnt;
reg [3:0] tx_cnt;
reg [7:0] tx_data;
assign start_flag = tx_en && (~tx_en_d1);
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_en_d0 <= 1'b0;
tx_en_d1 <= 1'b0;
end
else begin
tx_en_d0 <= tx_en;
tx_en_d1 <= tx_en_d0;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
bps_cnt <= 16'd0;
tx_cnt <= 4'd0;
end
else if(bps_cnt < BPS_CNT-1)begin
bps_cnt <= bps_cnt+1'b1;
tx_cnt <= tx_cnt;
end
else begin
bps_cnt <= 16'd0;
tx_cnt <= tx_cnt+1'b1;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else if(start_flag)begin
tx_flag <= 1'b1;
tx_data <= uart_din;
end
else if((tx_cnt==4'd9)&&(bps_cnt==BPS_CNT/2))begin
tx_flag <= 1'b0;
tx_data <= 8'd0;
end
else begin
tx_flag <= tx_flag;
tx_data <= tx_data;
end
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
uart_txd <= 1'b1; //空闲状态,发送端为高电平
else if(tx_flag)
case(tx_cnt)
4'd0 : uart_txd <= 1'b0;//起始位
4'd1 : uart_txd <= tx_data[0];
4'd2 : uart_txd <= tx_data[1];
4'd3 : uart_txd <= tx_data[2];
4'd4 : uart_txd <= tx_data[3];
4'd5 : uart_txd <= tx_data[4];
4'd6 : uart_txd <= tx_data[5];
4'd7 : uart_txd <= tx_data[6];
4'd8 : uart_txd <= tx_data[7];
4'd9 : uart_txd <= 1'b1;//停止位
default : uart_txd <= 1'b1;
endcase
else
uart_txd <= 1'b1;
end
endmodule
module uart_recv(
input sys_clk,
input sys_rst_n,
input uart_rxd,
output reg[7:0] uart_txd,
output reg rx_done
);
parameter BPS = 9600;
parameter FREQ = 50000000;
localparam BPS_CNT = FREQ / BPS;
wire start_flag; //对uart_rxd下降沿进行检测
reg uart_rxd_d0;
reg uart_rxd_d1;
reg rx_flag; //接收数据标志信号
reg [3:0] rx_cnt; //对接收数据计数
reg [15:0] bps_cnt; //对时钟的计数
reg [7:0] tx_data; //对接收数据进行寄存
assign start_flag = uart_rxd_d1 & (~uart_rxd_d0); //下降沿检测电路
//对uart_rxd延迟两个时钟单位
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
uart_rxd_d0 <= 1'b0;
uart_rxd_d1 <= 1'b0;
end
else begin
uart_rxd_d0 <= uart_rxd;
uart_rxd_d1 <= uart_rxd_d0;
end
end
//当start_flag来临时,开始接收数据
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
rx_flag <= 1'b0;
else
if(start_flag)
rx_flag <= 1'b1;
else if((bps_cnt == BPS_CNT/2) && (rx_cnt == 4'd9)) //接收了8个bit后,接收状态关闭
rx_flag <= 1'b0;
else
rx_flag <= rx_flag;
end
//进入接收过程后,启动系统时钟计数器与接收数据计数器
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
else if(rx_flag)
if(bps_cnt < BPS_CNT -1'b1)begin
bps_cnt <= bps_cnt + 1'b1;
rx_cnt <= rx_cnt;
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= rx_cnt + 1'b1;
end
else begin
bps_cnt <= 16'd0;
rx_cnt <= 4'd0;
end
end
//通过计数器来寄存接收到的数据,tx_data
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
tx_data <= 8'd0;
else if(rx_flag)
if(bps_cnt == BPS_CNT/2)begin
case(rx_cnt)
4'd1 : tx_data[0] <= uart_rxd_d1;
4'd2 : tx_data[1] <= uart_rxd_d1;
4'd3 : tx_data[2] <= uart_rxd_d1;
4'd4 : tx_data[3] <= uart_rxd_d1;
4'd5 : tx_data[4] <= uart_rxd_d1;
4'd6 : tx_data[5] <= uart_rxd_d1;
4'd7 : tx_data[6] <= uart_rxd_d1;
4'd8 : tx_data[7] <= uart_rxd_d1;
default : ;
endcase
end
else
tx_data <= tx_data;
else
tx_data <= 8'd0;
end
//接收数据寄存到uart_txd,并输出接收完成标志位
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
rx_done <= 1'b0;
uart_txd <= 8'd0;
end
else if(rx_cnt == 4'd9)begin
rx_done <= 1'b1;
uart_txd <= tx_data;
end
else begin
rx_done <= 1'b0;
uart_txd <= 8'd0;
end
end
endmodule