UART接收的工作过程

接口/总线/驱动

1139人已加入

描述

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

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

电路控制

仿真波形图如下所示。

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

电路控制

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分