Menu Close

异步串行收发(UART)协议详解(1)

上节内容讲解了计算机异步串行接口(UART)的基本常识及与之配合使用的电平标准如RS232,RS485,RS422等,由于计算机标准串口(如DB9)中用于握手的协议使用越来越少,更多的只是使用TXD,RXD用于收发,因此本节内容将着重详细讲解UART 通信协议中TXD,RXD部分内容。

1.UART在通信分类中的位置

在通信中根据不同的分类方式,把通信分成若干类,那么UART是哪一类呢?UART(universal asynchronous receiver-transmitter)是属于异步、串行、全双工通信。一般配合双绞线使用,因此也是有线通信类。UART在通信过程中分为RXD与TXD,通信双方在物理是交叉连接的,即发送方的TXD接到接收方的RXD上。反之亦然。如图1,

图1

为了能更好在数据端恢复数据,UART的数据帧是通过精心设计的。

2. UART数据帧结构

 

图2

如图2所示,在UART串行收发的过程中,数据帧一般分为空闲(idle)、起始(start)、数据(data)、奇偶校验(parity),停止(stop),再空闲等几个环节,下面就这几个步骤进行详细解释。通信的流程如下:

  • 空闲(idle)–>
  • 起始位(start  bit,1 bit)–>
  • 数据(data–>5,6,7, 8 bits)–>
  • 校验位(parity ,1 bit,可选)–>
  • 停止位(1,1.5,2bits)–>
  • 空闲(可选)–>
  • 起始位(下一字符帧的start bit)–>….。

收发双方如图3,

图3

  • 空闲

空闲是指发送方没有数据发送,处在空闲状态。此时在UART的TXD端应该发送1,或维持TXD为高电平。高电平的持续时间不定,一直持续到发送端有数据需要发送为止。空闲位一般不计在数据帧中。

  • 起始位

起始位以0作为数据帧的开始,一般情况下由空闲状态到数据发送的标志性转换,也是数据帧的第一位。一般起始位的前一位是空闲位,也可能是上一帧数据的停止位,如图4。起始位的宽度是一个标准数据位的宽度。

 

图4

  • 数据

在异步串行通信中,数据一般可以使用5位,6位,7位或8位,由于在计算机的数据组织结构中都是按照字节组织的,因此数据一般都使用8位的方式进行传输。当然也不排除一些特殊的应用场景使用5 bits,6 bits或7 bits。发送时低位在前(先发LSB),高位在后(后发MSB),见例1。

  • 校验位

在UART的结构中,校验位是可选的,也就是可带校验位也可以不带校验位;是否需要校验位看实际的应用场景对数据的准确性要求程度。校验一般采用奇校验或偶校验,校验位附在数据之后。至于是采用奇校验还是偶校验是收发双方事先约定的。奇偶校验只能判别8个数据位中出现1位错误,但不能指明哪一位发生错误。也就是只能检错不能纠错。

    • 奇校验

奇校验是指数据位中1数量(个数)与校验位为1的总数为奇数个。例:

如发送的8位数据位8’b1001_0011,如果采用奇校验,由于数据中1的个数为4,不是奇数,因此需要在数据之后补1,形成奇数个1(5个1),最终为9’b1001_0011_1。再如发送的数据为8’b1101_1010,加上奇校验后为9’b1101_1010_0。

    • 偶校验

偶校验是指数据与校验位合在一起后1的个数为偶数,同样的8’b1001_0011,采用偶校验时需在后面补零,形成9位数据,即9’b1001_0011_0,最终9位数据中1的个数为偶数个。

  • 停止位

停止位以1作为数据帧的结束,停止位可以为1位,1.5位或2位。1.5位用的比较少,一般会设1位或2位。一般在收发双方的时钟或波特率比较准确时可以采用1位停止位,当双方的波特率存在误差时,一般采用2位停止位。用于消除误差积累。

例1: 4个byte数据分别以奇校验,偶校验,无校验,停止位为1位时写出待发送的数据。(数据为 8’b1010_0011,8’b0010_1111,8’b1110_1110,8’b1111_1111;)

(1)奇校验,1个停止位

8’b1010_0011–>11’b 0_1100_0101_1_1

起始位(0)–数据(1100_0101低位在前)–奇校验位(1)–停止位(1)

8’b0010_1111 –>11’b0_1111_0100_0_1

8’b1110_1110 –>11’b0_0111_0111_1_1

8’b1111_1111–> 11’b0_1111_1111_1_1

(2)偶校验,1个停止位

8’b1010_0011–>11’b 0_1100_0101_0_1

起始位(0)–数据(1100_0101低位在前)–偶校验位(0)–停止位(1)

8’b0010_1111 –>11’b0_1111_0100_1_1

8’b1110_1110 –>11’b0_0111_0111_0_1

8’b1111_1111–> 11’b0_1111_1111_0_1

(3)无校验,1个停止位

8’b1010_0011–>10’b 0_1100_0101_1

起始位(0)–数据(1100_0101低位在前)–停止位(1)

8’b0010_1111 –>10’b0_1111_0100_1

8’b1110_1110 –>10’b0_0111_0111_1

8’b1111_1111–> 10’b0_1111_1111_1

例2:利用Verilog描述8位数据的串行发送器,要求参数化奇偶校验部分和停止位数量,假设时钟频率为9600 Hz。

module tx
#(
    parameter PARITY   = "ODD", // "ODD" "EVEN" "NONE"
    parameter STOP_BIT = 1
)
(
    input  rst,
    input  clk,
    input  [ 7: 0 ] tx_data,
    input  tx_rdy,
    output reg tx_ack,
    output reg tx
);


localparam [ 3: 0 ]
            IDLE         = 0,
            START        = 1,
            FIRST_BIT    = 2,
            SEC_BIT      = 3,
            THIRD_BIT    = 4,
            FOUTH_BIT    = 5,
            FIF_BIT      = 6,
            SIX_BIT      = 7,
            SEVN_BIT     = 8,
            EIGTH_BIT    = 9,
            PAR_BIT      = 10,  //奇偶校验位
            STOP1_BIT    = 11,  //停止位
            STOP2_BIT    = 12;  //停止位

reg [ 7: 0 ] tx_data_r;
wire odd = ^ tx_data_r;
reg [ 3: 0 ] tx_st;

always@( posedge clk or posedge rst )
if ( rst )
begin
    tx <= 1'b1;
    tx_st <= 0;
    tx_data_r <= 0;
    tx_ack <= 1'b0;
end
else
case ( tx_st )
    IDLE:
    begin
        tx_ack <= 1'b0;
        tx <= 1'b1;

        if ( tx_rdy )
        begin
            tx_data_r <= tx_data;
            tx_ack <= 1'b1;
            tx_st <= START;
        end
    end
    START:
    begin
        tx <= 1'b0;
        tx_ack <= 1'b0;
        tx_st <= FIRST_BIT;
    end
    FIRST_BIT:
    begin
        tx <= tx_data_r[ 0 ];
        tx_st <= SEC_BIT;
    end
    SEC_BIT:
    begin
        tx <= tx_data_r[ 1 ];
        tx_st <= THIRD_BIT;
    end
    THIRD_BIT:
    begin
        tx <= tx_data_r[ 2 ];
        tx_st <= FOUTH_BIT;
    end
    FOUTH_BIT:
    begin
        tx <= tx_data_r[ 3 ];
        tx_st <= FIF_BIT;
    end
    FIF_BIT :
    begin
        tx <= tx_data_r[ 4 ];
        tx_st <= SIX_BIT;
    end
    SIX_BIT:
    begin
        tx <= tx_data_r[ 5 ];
        tx_st <= SEVN_BIT;
    end
    SEVN_BIT:
    begin
        tx <= tx_data_r[ 6 ];
        tx_st <= EIGTH_BIT;
    end
    EIGTH_BIT:
    begin
        tx <= tx_data_r[ 7 ];
        if ( ( PARITY == "ODD" ) || ( PARITY == "EVEN" ) )
            tx_st <= PAR_BIT;
        else
            tx_st <= STOP1_BIT;
    end
    PAR_BIT :
    begin
        tx_st <= STOP1_BIT;
        if ( PARITY == "ODD" )
            tx <= ~odd;
        else
            tx <= odd;

    end
    STOP1_BIT:
    begin
        tx <= 1'b1;
        if ( STOP_BIT == 1 )
        begin
            if ( tx_rdy )
            begin
                tx_data_r <= tx_data;
                tx_ack <= 1'b1;
                tx_st <= START;
            end
            else
                tx_st <= IDLE;
        end
        else
            tx_st <= STOP2_BIT;
    end
    STOP2_BIT :
    begin
        tx <= 1'b1;
        if ( tx_rdy )
        begin
            tx_data_r <= tx_data;
            tx_ack <= 1'b1;
            tx_st <= START;
        end
        else
            tx_st <= IDLE;
    end
    default:
        tx_st <= IDLE;
endcase

endmodule

 

 

上例中首先利用localparam定义了13种常量用作发送状态,程序利用UART中的协议顺序按状态进行一个bit接一个bit发送,直到发送完规定的停止位,如果紧接着有新数据要发送,就进入下一个字节的发送周期,如果没有就退回到IDLE状态。

仿真激励程序(testbench) tb.v 如下:

`timescale 1 ns / 1 ps

module tb ();

parameter PERIOD = 104167 ; //9600HZ

reg clk;
reg rst;
reg [ 7: 0 ] tx_data;
reg tx_rdy;
wire tx_ack;

initial
begin
    clk = 0;
    #( PERIOD / 2 );

    forever
        #( PERIOD / 2 ) clk = ~clk;
end

initial
begin
    rst = 1;
    #( PERIOD * 2 );
    rst = 0;
end

always@( posedge clk or posedge rst )
if ( rst )
begin
    tx_rdy <= 0;
    tx_data <= 0;
end
else
begin
    if ( tx_ack )
    begin
        tx_rdy <= 0;
        tx_data <= tx_data + 1;
    end
    else if ( !tx_rdy )
    begin
        tx_rdy <= 1'b1;
    end
end

tx #
(
    .PARITY     ( "ODD" ),
    .STOP_BIT   ( 1 )
)
tx_inst
(
    .rst    ( rst ),
    .clk    ( clk ),
    .tx_data( tx_data ),
    .tx_rdy ( tx_rdy ),
    .tx_ack ( tx_ack ),
    .tx     ( tx )
);

endmodule

 

图5

关于UART协议其它部分在下节内容介绍。

 

对应视频:

[content_control]

[/content_control]

Posted in FPGA, FPGA 教材教案, Quartus II, Verilog, Verilog, 教材与教案, 文章

2 Comments

  1. 陈迪中

    老师本节UART串行8为发送器中,ODD和EVEN在里面是什么含义,输入和输出端口都没有对对它们进行定义,只有在定义常量parity定义了ODD,EVEN没有定义和申明,编译器为社么不报错

  2. William

    ODD 和 EVEN 是UART 通讯中定义额一种数据格式, 即在在发送数据后在增加一个ODD/EVEN 的校验位, 用于接收端检测发送过来的数据是否有错误(检测一个bit 的错误),这个功能可以使用, 也可以不使用。 完全是有收发双方自行约定的。

发表回复

相关链接