理论学习
通信协议基础
通用异步收发传输器(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可以实现全双工,即可以同时进行发送数据和接收数据。
RS232优点
串口RS232传输数据的距离虽然不远,传输速率也相对较慢,但是串口依然被广泛的用于电路系统的设计中,串口的好处主要表现在以下几个方面:
- 很多传感器芯片或CPU都带有串口功能,目的是在使用一些传感器或CPU时可以通过串口进行调试,十分方便;
- 在较为复杂的高速数据接口和数据链路集合的系统中往往联合调试比较困难,可以先使用串口将数据链路部分验证后,再把串口换成高速数据接口。如在做以太网相关的项目时,可以在调试时先使用串口把整个数据链路调通,然后再把串口换成以太网的接口;
- 串口的数据线一共就两根,也没有时钟线,节省了大量的管脚资源。
RS232信号线
在旧式的台式计算机中一般会有 RS-232 标准的 COM 口(也称 DB9
接口)。
名称 |
符号 |
数据方向 |
说明 |
载波检测 |
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端与线 路已接通 |
RS232协议基础
- RS232帧结构
在没有数据传输时,信道持续传输1,知道遇到一位0(起始位),标志着一帧数据的传输。
- 波特率:在信息传输通道中,携带数据信息的信号单元叫码元(因为串口是1bit进行传输的,所以其码元就是代表一个二进制数),每秒钟通过信号传输的码元数称为码元的传输速率,简称波特率,常用符号“Baud”表示,其单位为“波特每秒(Bps)”。串口常见的波特率有4800、9600、115200等。
- 比特率:每秒钟通信信道传输的信息量称为位传输速率,简称比特率,其单位为“每秒比特数(bps)”。比特率可由波特率计算得出,公式为:比特率=波特率
*
单个调制状态对应的二进制位数。如果使用的是9600的波特率,其串口的比特率为:9600Bps
* 1bit= 9600bps。
- 由计算得串口发送或者接收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 #( 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; reg start_neg; reg work_en; reg [12:0] baud_cnt; reg bit_flag; reg [3:0] bit_cnt; reg rx_flag; reg [7:0] rx_data;
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 #( 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
其他:
- 详解
| 还不懂串口通信?看这篇!