上节内容讲解了计算机异步串行接口(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]
老师本节UART串行8为发送器中,ODD和EVEN在里面是什么含义,输入和输出端口都没有对对它们进行定义,只有在定义常量parity定义了ODD,EVEN没有定义和申明,编译器为社么不报错
ODD 和 EVEN 是UART 通讯中定义额一种数据格式, 即在在发送数据后在增加一个ODD/EVEN 的校验位, 用于接收端检测发送过来的数据是否有错误(检测一个bit 的错误),这个功能可以使用, 也可以不使用。 完全是有收发双方自行约定的。