rs232

理论学习

通信协议基础

​ 通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART。UART是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。它包括了RS 232、RS499、RS423、RS422和RS485等接口标准规范和总线标准规范。

​ 串口作为常用的三大低速总线(UART、SPI、IIC)之一,在设计众多通信接口和调试时占有重要地位。但UART和SPI、IIC不同的是,它是异步通信接口,异步通信中的接收方并不知道数据什么时候会到达,所以双方收发端都要有各自的时钟,在数据传输过程中是不需要时钟的,发送方发送的时间间隔可以不均匀,接受 方是在数据的起始位和停止位的帮助下实现信息同步的。而SPI、IIC是同步通信接口(后面的章节会做详细介绍),同步通信中双方使用频率一致的时钟,在数据传输过程中时钟伴随着数据一起传输,发送方和接收方使用的时钟都是由主机提供的。

UART基础

​ UART通信只有两根信号线,一根是发送数据端口线叫tx(Transmitter),一根是接收数据端口线叫rx(Receiver),对于PC来说它的tx要和对于FPGA来说的rx连接,同样PC的rx要和FPGA的tx连接。UART可以实现全双工,即可以同时进行发送数据和接收数据。

RS232002

RS232优点

​ 串口RS232传输数据的距离虽然不远,传输速率也相对较慢,但是串口依然被广泛的用于电路系统的设计中,串口的好处主要表现在以下几个方面:

  1. 很多传感器芯片或CPU都带有串口功能,目的是在使用一些传感器或CPU时可以通过串口进行调试,十分方便;
  2. 在较为复杂的高速数据接口和数据链路集合的系统中往往联合调试比较困难,可以先使用串口将数据链路部分验证后,再把串口换成高速数据接口。如在做以太网相关的项目时,可以在调试时先使用串口把整个数据链路调通,然后再把串口换成以太网的接口;
  3. 串口的数据线一共就两根,也没有时钟线,节省了大量的管脚资源。

RS232信号线

​ 在旧式的台式计算机中一般会有 RS-232 标准的 COM 口(也称 DB9 接口)。

img
名称 符号 数据方向 说明
载波检测 DCD DTE→DCE Data Carrier Detect,数据载波检测,用于 DTE告知对方,本机是否收到对方的载波信 号
接收数据 RXD DTE<DCE ReceiveData,数据接收信号,即输入。
发送数据 TXD DTE→DCE Transmit Data,数据发送信号,即输出。两个 设备之间的TXD与RXD应交叉相连
数据终端 (DTE)就 绪 DTR DTE→DCE Data Terminal Ready,数据终端就绪,用于 DTE向对方告知本机是否已准备好
信号地 GND 地线,两个通讯设备之间的地电位可能不一 样,这会影响收发双方的电平信号,所以两 个串口设备之间必须要使用地线连接,即共 地。
数据设备 (DCE)就 绪 DSR DTE<DCE Data Set Ready,数据发送就绪,用于DCE告 知对方本机是否处于待命状态
请求发送 RTS DTE→DCE Request To Send,请求发送,DTE请求DCE 本设备向DCE端发送数据
允许发送 CTS DTE<DCE Clear To Send,允许发送,DCE回应对方的 RTS发送请求,告知对方是否可以发送数据
响铃指示 RI DTE<DCE Ring Indicator,响铃指示,表示DCE端与线 路已接通
RS232007

RS232协议基础

  1. RS232帧结构

​ 在没有数据传输时,信道持续传输1,知道遇到一位0(起始位),标志着一帧数据的传输。

RS232008
  1. 波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是1bit进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有4800、9600、115200等。
  2. 比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率 * 单个调制状态对应的二进制位数。如果使用的是9600的波特率,其串口的比特率为:9600Bps * 1bit= 9600bps。
  3. 由计算得串口发送或者接收1bit数据的时间为一个波特,即1/9600秒,如果用50MHz(周期为20ns)的系统时钟来计数,需要计数的个数为cnt = (1s * 10^9)ns / 9600bit)ns / 20ns ≈ 5208个系统时钟周期,即每个bit数据之间的间隔要在50MHz的时钟频率下计数5208次。

项目实战

RS232 接收端模块

该模块功能为:将串行数据转化为8位并行数据

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
module uart_rx		// rs232接受模块 : 需要把串行数据转化为并行数据
#(
parameter UART_BPS = 'd9600, //串口波特率
parameter CLK_FREQ = 'd50_000_000 //时钟频率
)
(
input sys_clk,sys_rst_n,
input rx,

output reg po_flag,
output reg [7:0] po_data
);

parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;

reg rx_reg1,rx_reg2,rx_reg3; // 将po_data打两拍消除亚稳态,reg3再打一拍检测下降沿(起始位)
reg start_neg; // 数据开始标志位
reg work_en; // 工作状态
reg [12:0] baud_cnt; // 波特计数器
reg bit_flag; // bit加信号
reg [3:0] bit_cnt; // bit计数器
reg rx_flag; // 输出传输完成信号
reg [7:0] rx_data; // 数据移位寄存器

// 第一级寄存器,复位状态为1 :
// 当没有数据传输时,信道一直传输1,直到发现起始位0,产生一个下降沿,表示数据传输开始
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg1 <= 1'b1;
else
rx_reg1 <= rx;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg2 <= 1'b1;
else
rx_reg2 <= rx_reg1;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_reg3 <= 1'b1;
else
rx_reg3 <= rx_reg2;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
start_neg <= 1'b0;
else if(rx_reg2 == 1'b0 && rx_reg3 == 1'b1 && work_en == 1'b0)
start_neg <= 1'b1;
else
start_neg <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(start_neg == 1'b1)
work_en <= 1'b1;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
work_en <= 1'b0;
else
work_en <= work_en;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
begin
if(baud_cnt == BAUD_CNT_MAX - 1)
baud_cnt <= 13'b0;
else
baud_cnt <= baud_cnt + 1;
end
else
baud_cnt <= 13'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == (BAUD_CNT_MAX >> 1))
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if(bit_flag == 1'b1)
begin
if(bit_cnt == 4'd8)
bit_cnt <= 4'b0;
else
bit_cnt <= bit_cnt + 1;
end
else
bit_cnt <= bit_cnt;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_data <= 8'b0;
else if(bit_flag == 1'b1 && (bit_cnt >= 1 && bit_cnt <= 8))
rx_data <= {rx_reg3,rx_data[7:1]};
else
rx_data <= rx_data;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rx_flag <= 1'b0;
else if(bit_cnt == 4'd8 && bit_flag == 1'b1)
rx_flag <= 1'b1;
else
rx_flag <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_data <= 8'b0;
else if(rx_flag == 1'b1)
po_data <= rx_data;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
po_flag <= 1'b0;
else if(rx_flag == 1'b1)
po_flag <= rx_flag;
endmodule

RS232发送端模块

该模块功能为:将8位并行数据转化为串行数据

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
module uart_tx	// rs232发送模块,把并行数据转化为串行
#(
parameter UART_BPS = 'd9600,
parameter CLK_FREQ = 'd50_000_000
)
(
input sys_clk,sys_rst_n,
input pi_flag,
input [7:0] pi_data,

output reg tx
);

parameter BAUD_CNT_MAX = CLK_FREQ / UART_BPS;

reg [12:0] baud_cnt;
reg bit_flag;
reg [3:0] bit_cnt;
reg work_en;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
work_en <= 1'b0;
else if(pi_flag == 1'b1)
work_en <= 1'b1;
else if(bit_cnt == 4'd9 && bit_flag == 1'b1)
work_en <= 1'b0;
else
work_en <= work_en;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
baud_cnt <= 13'b0;
else if(work_en == 1'b1)
begin
if(baud_cnt == BAUD_CNT_MAX - 1)
baud_cnt <= 13'b0;
else
baud_cnt <= baud_cnt + 1;
end
else
baud_cnt <= 13'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_flag <= 1'b0;
else if(baud_cnt == 1)
bit_flag <= 1'b1;
else
bit_flag <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
bit_cnt <= 4'b0;
else if(bit_flag == 1'b1 && bit_cnt == 4'd9)
bit_cnt <= 4'b0;
else if(bit_flag == 1'b1 && work_en == 1'b1)
bit_cnt <= bit_cnt + 1;
else
bit_cnt <= bit_cnt;

always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tx <= 1'b1;
else if(bit_flag == 1'b1)
case(bit_cnt)
0 : tx <= 1'b0;
1 : tx <= pi_data[0];
2 : tx <= pi_data[1];
3 : tx <= pi_data[2];
4 : tx <= pi_data[3];
5 : tx <= pi_data[4];
6 : tx <= pi_data[5];
7 : tx <= pi_data[6];
8 : tx <= pi_data[7];
9 : tx <= 1'b1;
default : tx <= 1'b1;
endcase

endmodule

学习链接

笔记摘自:野火fpga开发实战指南:串口RS232

其他:

  1. 详解 | 还不懂串口通信?看这篇!