UART接收的工作过程

Uart 简介

UART 全称为 Universal Asynchronous Receiver/Transmitter,译为通用异步收发传输器。它是一种通用串行数据总线,将数据在串行通信与并行通信之间进行转换,用于异步通信。

Uart 一般有以下几个特点:

◆ 可完成数据的串并、并串转换;

◆ 可双向通信,实现全双工传输和接收;

◆ 协议简单,最少使用 2 根信号线和一根地线即可完成数据收发,但抗干扰能力差;

◆ 传输速率低,一般不到 1Mbps。当传输速率超过 3Mbps 时,收发波形不均匀,误码率高;

◆ 传输距离有限,TTL 电平标准下最长可传输 3 米左右,RS232 电平标准最长支持传输长度为 15 米左右;

◆ 应用广泛,常用于数据采集与通信、电路辅助测试、电路控制等。例如通过 USB/网线转串口模块,可以时序计算机与串口设备的连接,串口设备可以是打印机、开发板等。

Uart 管脚

Uart 一般使用 9 针的 DB9 接口来完成数据终端设备 (DTE) 与数据通讯设备 (DCE) 之间的数据交换。实际 Uart 的 DB9 接口图如下所示。

如上图所示,DB9 公头从左到右、从上倒下,分别对应的针管脚标号为 1 到 9。各个管脚的功能简单表述如下表所示。需要注意的是,为了能和公头相连接,母头管脚标号从右上角开始。

Uart 协议

Uart 常用的传输协议示意图如下:

◆ 起始位:为低电平,表示传输数据的开始。

◆ 数据位:近邻起始位之后,表示要传输的数据。数据长度可以为 5、6、7、8 ,但经常使用的数据长度为 8,用于表示一个 ASCII 字符。数据从低位到高位依次传输。

◆ 奇偶校验位:数据位加上此位数据后,使得 “1” 的位数为偶数 (偶校验) 或奇数 (奇校验)。例如,当为奇校验时,如果 8 位数据中 1 的数量为奇数,则此校验位为 1;如果 8 位数据中 1 的数量为偶数,则此校验位为 1。偶校验同理。校验位也可以去除不传输。

◆ 停止位:一针数据传输结束的标志,可以是 1 位、1.5 位、2 位的高电平。停止位个数越多,数据传输越稳定,但是数据传输速度也越慢。

◆ 空闲位:当总线处于空闲状态时,所有信号线要保持为高电平,表示没有数据传输。

Uart 接收时大致的工作过程描述如下:

当总线由高电平变为低电平时,表示数据开始传输。起始位之后可接收到 8 个数据位。如果设置了奇偶校验功能,可根据接收的数据位和奇偶校验位进行检测。校验出错,则数据丢掉。最后,接收到高电平停止位。如果停止位不为高电平,表示此次传输出错,数据也要丢掉。最后总线保持为高电平状态,表示一次接收完毕。同时,Uart 内部将接收到的串行数据转换为并行数据。

Uart 发送过程与接收过程类似,这里不再描述。

◆ 波特率

传输过程中,1bit 数据的传输速率用波特率来描述,单位为 bps (bit per second)。一般经常使用的波特率都为 9600、19200、115200 等,表示 Uart 每秒传输多少比特数据。

由于起始位、停止位的存在,实际的传输速率并不等于波特率。例如,不考虑校验位,传输 8 位有效数据实际需要传输 10 位数据(8 位数据和起始位、停止位),所以实际的数据传输速率为波特率的 80%。

◆ 误码率

当传输 8bit 真实数据时,不考虑校验位。假如发送数据和接收数据都存在误差,这种误差可以累加,那么极限情况就是在接收最后 1bit 数据的结束时发生错误。加上已经正确传输的 19bit 数据,那么波特率最大误差为 1/20=5% 。

实际中如果不是连续传输,即 Uart 发送和接收中间的延迟时间比较长时,那么要求的波特率误差将会更小。

Uart 实现

◆ 参数设计

下面使用 Verilog 设计一个 Uart 模块,参数如下:

波特率:115200

数据位宽度:8

校验位:无

工作时钟:50 Mhz

◆ 接收模块

Uart 接收端口说明如下表所示:

Uart 接收数据状态示意图如下所示:

(1) 上电后 Uart 进入空闲状态 S_IDLE ;

(2) 当输入端 rx_pin 变低时,表示传输开始,进入开始状态 S_START ;

(3) 等待 1bit 的时间,进入接收数据状态 S_REC_BYTE ;

(4) 接收完 8bit 数据接收后,进入停止状态 S_STOP ;

(5) 停止状态后增加一个 S_DATA 状态,用于将接收到的数据输出 ;

(6) 最后回到空闲状态 S_IDLE,等待下一次接收。

接收代码描述如下:

module uart_rx

#(

parameter CLK_FRE = 50, //clock frequency(Mhz)

parameter BAUD_RATE = 115200 //serial baud rate

)

(

input clk, //clock input

input rst_n, //asynchronous reset input, low active

output reg[7:0] rx_data, //received serial data

output reg rx_data_valid, //received serial data is valid

input rx_data_ready, //data receiver module ready

input rx_pin //serial data input

);

//calculates the clock cycle for baud rate

localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;

//state machine code

localparam S_IDLE = 1;

localparam S_START = 2; //start bit

localparam S_REC_BYTE = 3; //data bits

localparam S_STOP = 4; //stop bit

localparam S_DATA = 5;

reg[2:0] state;

reg[2:0] next_state;

reg rx_d0; //delay 1 clock for rx_pin

reg rx_d1; //delay 1 clock for rx_d0

wire rx_negedge; //negedge of rx_pin

reg[7:0] rx_bits; //temporary storage of received data

reg[15:0] cycle_cnt; //baud counter

reg[2:0] bit_cnt; //bit counter

assign rx_negedge = rx_d1 && ~rx_d0;

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

begin

rx_d0 <= 1’b0;

rx_d1 <= 1’b0;

end

else

begin

rx_d0 <= rx_pin;

rx_d1 <= rx_d0;

end

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

state <= S_IDLE;

else

state <= next_state;

end

always@(*) begin

case(state)

S_IDLE:

if(rx_negedge)

next_state <= S_START;

else

next_state <= S_IDLE;

S_START:

if(cycle_cnt == CYCLE – 1)//one data cycle

next_state <= S_REC_BYTE;

else

next_state <= S_START;

S_REC_BYTE:

if(cycle_cnt == CYCLE – 1 && bit_cnt == 3’d7) //receive 8bit data

next_state <= S_STOP;

else

next_state <= S_REC_BYTE;

S_STOP:

if(cycle_cnt == CYCLE/2 – 1)//half bit cycle,to avoid missing the next byte receiver

next_state <= S_DATA;

else

next_state <= S_STOP;

S_DATA:

if(rx_data_ready) //data receive complete

next_state <= S_IDLE;

else

next_state <= S_DATA;

default:

next_state <= S_IDLE;

endcase

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

rx_data_valid <= 1’b0;

else if(state == S_STOP && next_state != state)

rx_data_valid <= 1’b1;

else if(state == S_DATA && rx_data_ready)

rx_data_valid <= 1’b0;

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

rx_data <= 8’d0;

else if(state == S_STOP && next_state != state)

rx_data <= rx_bits;//latch received data

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

begin

bit_cnt <= 3’d0;

end

else if(state == S_REC_BYTE)

if(cycle_cnt == CYCLE – 1)

bit_cnt <= bit_cnt + 3’d1;

else

bit_cnt <= bit_cnt;

else

bit_cnt <= 3’d0;

end

always@(posedge clk or negedge rst_n)

begin

if(rst_n == 1’b0)

cycle_cnt <= 16’d0;

else if((state == S_REC_BYTE && cycle_cnt == CYCLE – 1) || next_state != state)

cycle_cnt <= 16’d0;

else

cycle_cnt <= cycle_cnt + 16’d1;

end

//receive serial data bit data

//避免对串行输入数据的误采样,在波特率计数器的中间值时刻进行采样

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

rx_bits <= 8’d0;

else if(state == S_REC_BYTE && cycle_cnt == CYCLE/2 – 1)

rx_bits[bit_cnt] <= rx_pin;

else

rx_bits <= rx_bits;

end

endmodule

◆ 发送模块

Uart 发送端口说明如下表所示:

Uart 发送数据状态示意图如下所示:

(1) 上电后 Uart 进入空闲状态 S_IDLE ;

(2) 如果有发送请求,进入开始状态 S_START ;

(3) 等待 1bit 的时间,进入发送数据状态 S_SEND_BYTE ;

(4) 发送完 8bit 数据接收后,进入停止状态 S_STOP ;

(5) 最后回到空闲状态 S_IDLE,等待下一次数据发送。

发送模块代码描述如下:

module uart_tx

#(

parameter CLK_FRE = 50, //clock frequency(Mhz)

parameter BAUD_RATE = 115200 //serial baud rate

)

(

input clk, //clock input

input rst_n, //asynchronous reset input, low active

input[7:0] tx_data, //data to send

input tx_data_valid, //data to be sent is valid

output reg tx_data_ready, //send ready

output tx_pin //serial data output

);

//calculates the clock cycle for baud rate

localparam CYCLE = CLK_FRE * 1000000 / BAUD_RATE;

//state machine code

localparam S_IDLE = 1;

localparam S_START = 2;//start bit

localparam S_SEND_BYTE = 3;//data bits

localparam S_STOP = 4;//stop bit

reg[2:0] state;

reg[2:0] next_state;

reg[15:0] cycle_cnt; //baud counter

reg[2:0] bit_cnt;//bit counter

reg[7:0] tx_data_latch; //latch data to send

reg tx_reg; //serial data output

assign tx_pin = tx_reg;

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

state <= S_IDLE;

else

state <= next_state;

end

always@(*) begin

case(state)

S_IDLE:

if(tx_data_valid == 1’b1)

next_state <= S_START;

else

next_state <= S_IDLE;

S_START:

if(cycle_cnt == CYCLE – 1)

next_state <= S_SEND_BYTE;

else

next_state <= S_START;

S_SEND_BYTE:

if(cycle_cnt == CYCLE – 1 && bit_cnt == 3’d7)

next_state <= S_STOP;

else

next_state <= S_SEND_BYTE;

S_STOP:

if(cycle_cnt == CYCLE – 1)

next_state <= S_IDLE;

else

next_state <= S_STOP;

default:

next_state <= S_IDLE;

endcase

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

begin

tx_data_ready <= 1’b0;

end

else if(state == S_IDLE)

if(tx_data_valid == 1’b1)

tx_data_ready <= 1’b0;

else

tx_data_ready <= 1’b1;

else if(state == S_STOP && cycle_cnt == CYCLE – 1)

tx_data_ready <= 1’b1;

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

begin

tx_data_latch <= 8’d0;

end

else if(state == S_IDLE && tx_data_valid == 1’b1)

tx_data_latch <= tx_data;

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

begin

bit_cnt <= 3’d0;

end

else if(state == S_SEND_BYTE)

if(cycle_cnt == CYCLE – 1)

bit_cnt <= bit_cnt + 3’d1;

else

bit_cnt <= bit_cnt;

else

bit_cnt <= 3’d0;

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

cycle_cnt <= 16’d0;

else if((state == S_SEND_BYTE && cycle_cnt == CYCLE – 1) || next_state != state)

cycle_cnt <= 16’d0;

else

cycle_cnt <= cycle_cnt + 16’d1;

end

always@(posedge clk or negedge rst_n) begin

if(rst_n == 1’b0)

tx_reg <= 1’b1;

else

case(state)

S_IDLE,S_STOP:

tx_reg <= 1’b1;

S_START:

tx_reg <= 1’b0;

S_SEND_BYTE:

tx_reg <= tx_data_latch[bit_cnt];

default:

tx_reg <= 1’b1;

endcase

end

endmodule

◆ Uart 模块整合

将收发模块进行整合,组成一个完整的 Uart 模块。

module uart #(

parameter CLK_FRE = 50,

parameter BAUD_RATE = 115200

)

(

input clk,

input rst_n,

input rx,

input rx_ready,

output [7:0] rx_data,

output rx_data_valid,

input [7:0] tx_data,

input tx_data_valid ,

output tx ,

output tx_ready

);

uart_rx #(

.CLK_FRE(CLK_FRE),

.BAUD_RATE(BAUD_RATE))

u_rx(

.clk (clk ),

.rst_n (rst_n ),

.rx_pin (rx ), //input

.rx_data_ready (rx_ready ),

.rx_data (rx_data ), //output

.rx_data_valid (rx_data_valid )

);

uart_tx #(

.CLK_FRE(CLK_FRE),

.BAUD_RATE(BAUD_RATE))

u_tx (

.clk (clk ),

.rst_n (rst_n ),

.tx_data (tx_data ), //input

.tx_data_valid (tx_data_valid ),

.tx_pin (tx ), //output

.tx_data_ready (tx_ready )

);

endmodule

◆ Testbench

Uart 测试时采用自回环的测试方法,即 Uart 发送端接口与接收端接口相连,实现自发自收。此测试方法简单且有效。

`timescale 1ns/1ns

module test;

//clock and reset

reg clk;

always #10 clk = ~clk ;

reg rstn ;

initial begin

rstn = 0 ;

clk = 0 ;

# 4.5 ;

rstn = 1 ;

end

reg [7:0] tx_data ;

reg tx_data_valid ;

wire [7:0] rx_data ;

wire rx_data_valid ;

wire tx2rx, tx2rx_ready;

//test in loop

uart u_uart1 (

.clk (clk),

.rst_n (rstn),

.rx (tx2rx), //in

.rx_ready (tx2rx_ready),

.rx_data (rx_data[7:0]), //out

.rx_data_valid (rx_data_valid),

.tx_data (tx_data[7:0]), //in,

.tx_data_valid (tx_data_valid),

.tx (tx2rx), //out

.tx_ready (tx2rx_ready)

);

initial begin

tx_data = 0 ;

tx_data_valid = 0 ;

#100;

//send data

wait (tx2rx_ready);

@(negedge clk);

tx_data = 8’h35 ;

tx_data_valid = 1 ;

@(negedge clk);

tx_data_valid = 0 ;

repeat(15) begin

@(negedge clk);

end

wait (tx2rx_ready);

@(negedge clk);

tx_data = 8’h18 ;

tx_data_valid = 1 ;

@(negedge clk);

tx_data_valid = 0 ;

repeat(15) begin

@(negedge clk);

end

wait (tx2rx_ready);

@(negedge clk);

tx_data = 8’ha6 ;

tx_data_valid = 1 ;

@(negedge clk);

tx_data_valid = 0 ;

repeat(15) begin

@(negedge clk);

end

#100 ;

end // initial begin

//receive parallel data

reg [1:0] rx_data_valid_r ;

always @(posedge clk or negedge rstn) begin

if (!rstn) begin

rx_data_valid_r <= 1’b0 ;

end

else begin

rx_data_valid_r <= {rx_data_valid_r[0], rx_data_valid} ;

end

end

wire rx_data_valid_pos = rx_data_valid_r == 2’b01 ;

reg [7:0] check_data ;

integer check_num ;

always @(posedge clk or negedge rstn) begin

if (!rstn) begin

check_data <= ‘b0 ;

check_num <= ‘b0 ;

end

else if(rx_data_valid_pos === 1’b1) begin

check_data <= rx_data ;

check_num <= check_num + 1’b1 ;

end

end

//check 3 byte data

initial begin

#1 ;

forever begin

@(negedge clk) ;

if (check_num == 1) begin

if (check_data !== 8’h35) begin

$display(“—III— 1st data Failed: %h”, check_data);

end

end

else if (check_num == 2) begin

if (check_data !== 8’h18) begin

$display(“—III— 2nd data Failed: %h”, check_data);

end

end

else if (check_num == 3) begin

if (check_data !== 8’ha6) begin

$display(“—III— 3rd data Failed: %h”, check_data);

$display(“—III— It’s a FAILURE!!!”);

end

else begin

#1000000 ;

$display(“—III— It’s a SUCCESS!!!”);

$display(“”);

$display(“”);

end

$finish ;

end

end // forever begin

end // initial begin

endmodule

仿真中的自检验成功截图如下所示。

仿真波形图如下所示。

由图可知,波特率、首发数据均正确。

发表评论