UART通信原理

  • UART(universal asynchronous receiver-transmitter)异步收发传输器是一种采用异步串行通信方式的通用异步收发传输器;它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。它包含RS232,RS499,RS423,RS485等接口标准规范和总线标准规范。
  • UART串口通信需要两根信号线来实现,一根用于发送,另外一根接收。
  • UART可以实现全双工,即可以同时进行发送数据和接收数据。

优缺点

  • 缺点:
    • 传输距离不远,速率相对较慢
  • 优点:
    • 很多传感器芯片或CPU都带有串口功能,目的是在使用一些传感器或CPU时可以通过串口进行调试。
    • 在较为复杂的高速数据接口和数据链路集合的系统中往往联合调试比较困难,可以先使用串口数据链路部分验证后,再把串口换成高速数据接口。
    • 串口的数据线一共两根,没有时钟线,节省了大量的管脚资源。

1. 协议层:通信协议(包括数据格式、传输速率等)

  • 传输速率

    串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bit/s(位/秒),简称bps;
    常用的波特率有9600、19200、38400、57600以及115200等
    波特率和比特率的基本知识

    比特率和波特率既有联系,又有区别。比特率描述的是数据,波特率描述的是信号。我们知道,数据是用信号来表示的。如果一个信号码元只能代表1 比特,那么波特率和比特率是相等的。如果一个码元可以表示n个比特信息,那么比特率是波特率的n倍。

  • 数据格式

    数据格式,一帧数据由4部分构成

    • 起始位(1bit)
    • 数据位(6/7/8bit)
    • 奇偶校验位(1bit)
    • 停止位(1bit\1.5bit\2bit)UART数据格式
      UART数据格式

2. 物理层:接口类型、电平标准等。

  • UART通信有两根信号线,发送数据端口线TX(Transmitter),接收数据端口线RX(Receiverr),PC的TX端连接的是FPGA的RX,PC的RX端连接的是FPGA的TX。
    UART_PC与FPGA

UART通信奇偶校验

  • 校验位分为奇校验和偶校验,用于检验数据再传输过程中是否出错。

    奇校验时,发送方应使数据位中1的个数与校验位中1的个数之和为奇数;接收方在接收数据时,对1的个数进行检查,若不为奇数,则说明数据在传输过程中出了差错。同样,偶校验则检查1的个数是否为偶数。
  • 奇校验:奇偶校验是一种通信中检验数据传输正确性的方法。通常采用哪种校验方式是收发双方事先约定好的,发送端发送的数据中包括原始数据和一 bit 校验位,若是奇校验则要保证发送的数据中 1 的 bit 数是奇数个,若是偶校验,则要保证发送的数据中 0 的 bit 数是偶数个。例如:采用奇校验时,若原始数据中 1的 bit 数是奇数个,则校验位此时应该为 0,若原始数据中 1 的 bit 数是偶数个,则校验位此时应该为 1;反之亦然。
    奇偶校验

1.UART通信奇偶校验-输入数据的校验方法

  • 奇校验实现方式:校验位默认为高电平,每检测到1,则状态翻转
  • 偶校验实现方式:校验位默认为低电平,每检测到1,则状态翻转

输入数据的校验方法程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module odd_even1(
input sys_clk ,
input in , //串行输入
input rst_n , //同步复位,高电平有效
output reg odd , //奇校验位
output reg even //偶校验位
);

always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
odd <= 1'b1;
even <= 1'b0;
end
else if(in)begin
odd <= ~odd;
even <= ~even;
end
else begin
odd <= odd;
even <= even;
end
end

endmodule

2.UART通信奇偶校验-输出数据的校验方法

  • 实现方法:根据异或的定义:相异为1,相同为0。可推出:偶数个1异或的结果为0,奇数个1异或的结果为1。
  • 偶校验:将输入数据按位异或
  • 奇校验:将输入数据按位异或再取反(与偶校验相反)

输出数据的校验方法程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module odd_even2(
input sys_clk ,
input [7:0] in , //并行输入
input rst_n , //同步复位,高电平有效
output reg odd , //奇校验位
output reg even //偶校验位
);

always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
odd <= 1'b0;
even <= 1'b0;
end
else begin
odd <= ~(^in);
even <= ^in;
end
end

endmodule

RS232通信协议

  • RS232是UART的一种,没有时钟线,只有两根数据线。发送数据端口线TX(Transmitter),接收数据端口线RX(Receiverr)。都为1bit位宽。
  • 当PC通过串口调试助手向FPGA发送8bit数据时,FPGA通过接收数据端口线RX,从PC接收一位一位的数据,在FPGA内把连续的8bit的一位宽数据拼接成8bit数据。
  • 当FPGA通过串口向PC机发送8bit数据时,FPGA通过数据端口线TX,一位一位的传给PC,从最低位到最高位依次发送,最后上位机通过串助手按照RS232协议把数据拼接成8bit数据。
  • 串口数据的发送和接受是基础帧结构的,即一帧一帧的发送与接收。每一帧除了中间的8bit有效数据外,还在每帧的开头都有一个起始位,固定为0;在每帧的结束时有一个停止位,固定为1。不包含校验位的情况下,一帧有10Bit。在不发送和接受收的情况下,RX,TX处于空闲状态,且都为高电平。
  • 有数据帧传输时,首先会有一个起始位,然后8bit数据位,最后1bit停止位,然后RX,TX继续进入空闲状态,等待下一次数据传输。

RS232设计思路

  • 接收模块RX(Receiverr):首先检测PC上位机发送的输入数据的下降沿,通过两级寄存器将接收到的数据进行同步,再利用一级寄存器来检测输入数据的下降沿。
    检测到下降沿后,表示开始接收数据。每计数1bit的中间数,移位寄存器右移一位(相当于保存当前数据)。当bit计数器计数到8,表示有效数据已经接收完毕,当计数到9结束时,接收到结束信号,完成一帧数据的传输,拉高数据有效标志信号,输出有效的并行8bit数据。
  • 控制模块(Ctrl):接收接收模块传来的8bit数据,在数据有效标志信号拉高,且FIFO写非满时进行写入FIFO,在TX端非忙且读非空时,进行FIFO读出8bit数据到TX端的操作。
  • 发送模块TX(Transmitter):在数据有效信号有效时,开始拉高busy(忙信号),在10bit数据发送完成时,拉低busy(忙信号)。寄存接收控制模块发送来的8bit并行数据,通过拼接符拼接第一bit的低电平0,最高位的bit高电平1;在每bit的计数的第一个baud就把当前bit位的单bit数据发送给PC上位机,进行数据的回环。
    UART结构

接收模块程序

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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
module uart_rx_fsm (
input sys_clk ,
input rst_n ,
input rx ,
output [7:0]uart_rx_data ,
output uart_rx_lvd
);



////////////////////////////////////////////////////////////////////////////////////////////
parameter IDLE = 4'b0001 ,
START = 4'b0010 ,
RD_DATA = 4'b0100 ,
STOP = 4'b1000 ; //参数声明
////////////////////////////////////////////////////////////////////////////////////////////
wire idle2start ;
wire start2idle ;
wire start2rd_data ;
wire rd_data2stop ;
wire stop2idle ; //内部参数声明
wire nedge ;
////////////////////////////////////////////////////////////////////////////////////////////
reg [3:0]state_c ;
reg [3:0]state_n ;
////////////////////////////////////////////////////////////////////////////////////////////
reg[9:0]uart_rx_data_r ;
////////////////////////////////////////////////////////////////////////////////////////////
reg [2:0]rx_r ;
////////////////////////////////////////////////////////////////////////////////////////////
reg [15:0]cnt_baud ;
wire add_cnt_baud ;
wire end_cnt_baud ;
////////////////////////////////////////////////////////////////////////////////////////////
reg [3:0]cnt_bit ;
wire add_cnt_bit ;
wire end_cnt_bit ;
////////////////////////////////////////////////////////////////////////////////////////////
assign nedge = (!rx_r[1])&&(rx_r[2]) ;
////////////////////////////////////////////////////////////////////////////////////////////
assign idle2start = (state_c == IDLE) && (nedge ) ;
assign start2idle = (state_c == START) && (uart_rx_data_r[9] ) && (end_cnt_baud) ;
assign start2rd_data = (state_c == START) && (~uart_rx_data_r[9]) && (end_cnt_baud) ;
assign rd_data2stop = (state_c == RD_DATA) && (cnt_bit == 8 ) && (end_cnt_baud) ;
assign stop2idle = (state_c == STOP) && (end_cnt_bit ) ;
////////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
state_c <= IDLE ;
end
else
state_c <= state_n;
end //时序逻辑


////////////////////////////////////////////////////////////////////////////////////////////


always @(*) begin
case (state_c)
IDLE :begin
if (idle2start) begin
state_n = START ;
end
else state_n = IDLE ;
end

START :begin
if (start2rd_data) begin
state_n = RD_DATA;
end
else if (start2idle) begin
state_n = IDLE ;
end
else
state_n = START ;
end

RD_DATA:begin
if (rd_data2stop) begin
state_n = STOP ;
end
else
state_n = RD_DATA;
end

STOP :begin
if (stop2idle) begin
state_n = IDLE ;
end
else
state_n = STOP ;
end
default: state_n = IDLE ;
endcase
end //次态的组合逻辑


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
rx_r <= 3'd1;
end
else begin
rx_r[0] <= rx;
rx_r[1] <= rx_r[0];
rx_r[2] <= rx_r[1]; //打拍检测下降沿
end
end


///////////////////////////////////////////////////////////////////////////////////////////


// reg add_cnt_baud_flag;


///////////////////////////////////////////////////////////////////////////////////////////


// always @(posedge sys_clk or negedge rst_n ) begin
// if (!rst_n) begin
// add_cnt_baud_flag <= 1'd0;
// end
// else if (idle2start) begin
// add_cnt_baud_flag <= 1'd1;
// end
// else if (start2idle) begin
// add_cnt_baud_flag <= 1'd0; //当idle2start开始计数,start2idle,stop2idle计数停止
// end
// else if (stop2idle) begin
// add_cnt_baud_flag <= 1'd0;
// end
// end //计数标志信号
//state_c !=IDLE代替了add_cnt_baud_flag

///////////////////////////////////////////////////////////////////////////////////////////


parameter BAUD_MAX = 16'd5208 ;
assign add_cnt_baud = (state_c !=IDLE) ;
assign end_cnt_baud = add_cnt_baud && (cnt_baud == BAUD_MAX - 16'd1) ;


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
cnt_baud <= 16'd0;
end
else if (start2idle) begin
cnt_baud <= 16'd0;
end
else if (add_cnt_baud) begin
if (end_cnt_baud) begin
cnt_baud <= 16'd0;
end
else cnt_baud <= cnt_baud + 16'd1;
end
end
//baud计数

///////////////////////////////////////////////////////////////////////////////////////////


parameter BIT_MAX = 4'd9 ;
assign add_cnt_bit = end_cnt_baud ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == BIT_MAX);


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
cnt_bit <= 4'd0;
end
else if (start2idle) begin
cnt_bit <= 4'd0;
end
else if (add_cnt_bit) begin
if (end_cnt_bit) begin
cnt_bit <= 4'b0;
end
else cnt_bit <= cnt_bit + 4'd1;
end //bit计数
end


///////////////////////////////////////////////////////////////////////////////////////////


assign uart_rx_data = uart_rx_data_r[8:1];


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
uart_rx_data_r <= 10'd0;
end
else if (start2idle) begin
uart_rx_data_r <= 10'd0;
end
else if (cnt_baud == BAUD_MAX>>1) begin
uart_rx_data_r <= {rx,uart_rx_data_r[9:1]};
end

end //把每个baud中间的值寄存在uart_rx_data_r


///////////////////////////////////////////////////////////////////////////////////////////


reg uart_rx_lvd_r ;
assign uart_rx_lvd = uart_rx_lvd_r ;

///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n ) begin
if (!rst_n) begin
uart_rx_lvd_r <= 1'b0;
end
else if (stop2idle) begin
uart_rx_lvd_r <= 1'b1;
end
else uart_rx_lvd_r <= 1'b0;
end //有效标志拉高

//输出逻辑

///////////////////////////////////////////////////////////////////////////////////////////


endmodule

控制模块程序

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
module crtl (
input sys_clk,
input rst_n,
input busy,
input [7:0]rx_data,
input uart_rx_lvd,
output [7:0]fifo_data,
output tx_data_lvd
);


////////////////////////////////////////////////////////////////////////////////////


wire [7:0]data_sig ;
wire rdreq_sig ;
wire wrreq_sig ;
wire empty_sig ;
wire full_sig ;
wire [7:0]q_sig ;
wire [7:0]usedw_sig ;


////////////////////////////////////////////////////////////////////////////////////

fifo_1 fifo_1_inst (
.aclr ( !rst_n ),
.clock ( sys_clk ),
.data ( data_sig ),
.rdreq ( rdreq_sig ),
.wrreq ( wrreq_sig ),
.empty ( empty_sig ),
.full ( full_sig ),
.q ( q_sig ),
.usedw ( usedw_sig )
);


assign data_sig = rx_data ;
assign wrreq_sig = (uart_rx_lvd) && (!full_sig) ;
assign rdreq_sig = (!busy) && (!empty_sig) ;
assign fifo_data = q_sig ;
assign tx_data_lvd = rdreq_sig ;



endmodule

发送模块程序

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
module uart_tx (
input sys_clk,
input rst_n,
input [7:0]fifo_data_i,
input tx_data_lvd_i,
output busy,
output tx_data
);


///////////////////////////////////////////////////////////////////////////////////////////

parameter TX_BAUD_MAX = 16'd5208;
parameter TX_BIT_MAX = 4'd9 ;


reg [15:0]tx_baud ;
reg [3:0] tx_bit ;
reg tx_data_flag ;


wire add_tx_baud ;
wire end_tx_baud ;
wire add_tx_bit ;
wire end_tx_bit ;

assign add_tx_baud = tx_data_flag ;
assign end_tx_baud = add_tx_baud && ( tx_baud == TX_BAUD_MAX - 16'd1 ) ;

assign add_tx_bit = end_tx_baud ;
assign end_tx_bit = add_tx_bit && (tx_bit == TX_BIT_MAX) ;
assign busy = tx_data_flag ;


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
tx_baud <= 16'd0;
end
else if (add_tx_baud) begin
if (end_tx_baud) begin
tx_baud <= 16'd0;
end
else tx_baud <= tx_baud + 16'd1;
end //baud计数
end


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
tx_bit <= 4'd0;
end
else if (add_tx_bit) begin
if (end_tx_bit) begin
tx_bit <= 4'd0;
end
else tx_bit <= tx_bit + 4'd1;
end //bit计数
end


///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_flag <= 1'd0;
end
else if (tx_data_lvd_i) begin
tx_data_flag <= 1'd1;
end
else if (end_tx_bit) begin
tx_data_flag <= 1'd0;
end //baud加数标志
end


///////////////////////////////////////////////////////////////////////////////////////////


reg [9:0]tx_data_r;



///////////////////////////////////////////////////////////////////////////////////////////


always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_r <= 10'h3ff;
end
else if ( tx_data_lvd_i ) begin
tx_data_r <= {1'b1,fifo_data_i,1'b0}; //先进先出
end
end //把有效的8位数据转换为标准10位数据


///////////////////////////////////////////////////////////////////////////////////////////


reg tx_data_o ;
assign tx_data = tx_data_o ;


///////////////////////////////////////////////////////////////////////////////////////////
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
tx_data_o <= 1'b1;
end
else if (tx_baud == 16'd1) begin
tx_data_o <= tx_data_r[tx_bit];
end
end








endmodule

仿真模块程序

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
`timescale 1ns/1ps

module rs232_tb ();
/*input*/ reg sys_clk ;
/*input*/ reg rst_n ;
/*input [7:0]*/reg [7:0]fifo_data_i;
/*input*/ reg tx_data_lvd_i ;
/*input*/ wire rx ;
/*output*/ wire tx ;



defparam u_rs232.u_uart_rx_fsm.BAUD_MAX=100,
u_rs232.u_uart_tx.TX_BAUD_MAX=100;
defparam u1.TX_BAUD_MAX=100;



rs232 u_rs232(
/* input */ .sys_clk (sys_clk ),
/* input */ .rst_n (rst_n),
/* input */ .rx (rx ),
/* output */ .tx (tx )
);


uart_tx u1 (
/*input*/ .sys_clk(sys_clk),
/*input*/ .rst_n(rst_n),
/*input [7:0]*/.fifo_data_i(fifo_data_i),
/*input*/ .tx_data_lvd_i(tx_data_lvd_i),
/*output*/ .busy(),
/*output*/ .tx_data(rx)
);

//产生时钟
localparam CLK_PERIOD = 20;
initial sys_clk=1'b0;
always #(CLK_PERIOD/2) sys_clk=~sys_clk;
//复位3个周期
initial begin
rst_n=1'b0;
#(CLK_PERIOD*3);
rst_n=1'b1;
end

//激励
initial begin
/* rx=1;
#3;
#(CLK_PERIOD*5);

//发送5帧数据
repeat(5) begin
//发开始位
rx=0;
#(CLK_PERIOD*100);
//发数据位
repeat(8) begin
rx={$random};//rx={$random}%2;
#(CLK_PERIOD*100);
end
//发停止位
rx=1;
#(CLK_PERIOD*100);
//空闲状态200个CLK_PERIOD
#(CLK_PERIOD*200);
end
//干扰信号
rx=0;
#(CLK_PERIOD*30);
rx=1;
#(CLK_PERIOD*3000);
$stop; */
fifo_data_i=8'd0;
tx_data_lvd_i=1'b0;
#(CLK_PERIOD*10);
repeat(7) begin
fifo_data_i={$random}%256;
tx_data_lvd_i=1'b1;
#(CLK_PERIOD*1);
tx_data_lvd_i=1'b0;
#(CLK_PERIOD*1500);

end
#(CLK_PERIOD*1500);
$stop;


end













endmodule