上节内容讲解了计算机异步串行接口(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。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
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 如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
`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]