UART通信协议
串口通信的信号线只需要两条线就可以完成,TX和RX TX发送端 RX为接收端。
起始位,数据线从高变低,低有效为0,数据传输开始。
数据位,起始位传输之后便是数据位开始,一般为8位,传输时低位(LSB)在前,高位(MSB)在后。
校验位,校验位可以认为是一个特殊的数据位,通常使用的是奇偶校验,使用串口协议时通常取消奇偶校验位。
停止位,停止位高有效为1,他表示这一个个字节传输结束。
6.位时间,起始位、数据位、校验位的位宽度是一致的,停止位有0.5位、1位、1.5位格式,一般为1位。
空闲位,持续的高电平。
帧:从起始位开始到停止位结束的时间间隔称之为一帧。
START
D0~D7
P
STOP
起始位
数据位
校验位
停止位
模块编写
首先约定波特率为9600(可变),无校验位
将数据线打两拍,防止亚稳态并捕获其的下降沿信号(起始位0)
检测到下降沿后拉高数据传输标志,意味着数据开始接收;并在传输第九个数据(终止位1)的正中(数据比较稳定)将数据传输标志拉低,意味着数据接收完成
对时钟进行计数,每一个周期(在设定波特率条件下传输一位所需要的时间)对数据线进行采样并将数据寄存,同时记录传输个数(记录当前共接收了多少个数据)
接收到第九个数据(终止位1)后,拉高接收完成标志位。
将8位或者多位数据拆分为一位一位的发送出去的过程称为并转串。将一位一位接收的数据合并为8位或者多位数据的过程称为串转并。
对于串行通信设备来说,发送方都是在执行并转串,接收方都是在执行串转并。UART设备为串行通信设备。
代码
UART_RX
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 module uart_rx( input sys_clk, input sys_rst_n, input uart_rxd, output reg uart_rx_done, output reg [7 :0 ]uart_rx_data ); parameter BPS=9600 ; parameter SYS_CLK_FRE=50_000_000 ; localparam BPS_CNT=SYS_CLK_FRE/BPS; reg uart_rx_d0; reg uart_rx_d1; reg [15 :0 ] clk_cnt; reg [3 :0 ] rx_cnt; reg rx_flag; reg [7 :0 ] uart_rx_data_reg; wire neg_uart_rx_data; assign neg_uart_rx_data=uart_rx_d1 & (~uart_rx_d0); always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin uart_rx_d0<=1'b0 ; uart_rx_d1<=1'b0 ; end else begin uart_rx_d0<=uart_rxd; uart_rx_d1<=uart_rx_d0; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n) rx_flag<=1'b0 ; else begin if (neg_uart_rx_data) rx_flag<=1'b1 ; else if ((rx_cnt==4'd9 )&&(clk_cnt==BPS_CNT/2 )) rx_flag<=1'b0 ; else rx_flag<=rx_flag; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin rx_cnt<=4'd0 ; clk_cnt<=16'd0 ; end else if (rx_flag)begin if (clk_cnt<BPS_CNT-1'b1 )begin clk_cnt<=clk_cnt+1'b1 ; rx_cnt<=rx_cnt; end else begin clk_cnt<=16'd0 ; rx_cnt<=rx_cnt+1'b1 ; end end else begin rx_cnt<=4'd0 ; clk_cnt<=16'd0 ; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n) uart_rx_data_reg<=8'd0 ; else if (rx_flag) if (clk_cnt==BPS_CNT/2 ) begin case (rx_cnt) 4'd1 :uart_rx_data_reg[0 ]<=uart_rxd; 4'd2 :uart_rx_data_reg[1 ]<=uart_rxd; 4'd3 :uart_rx_data_reg[2 ]<=uart_rxd; 4'd4 :uart_rx_data_reg[3 ]<=uart_rxd; 4'd5 :uart_rx_data_reg[4 ]<=uart_rxd; 4'd6 :uart_rx_data_reg[5 ]<=uart_rxd; 4'd7 :uart_rx_data_reg[6 ]<=uart_rxd; 4'd8 :uart_rx_data_reg[7 ]<=uart_rxd; default :; endcase end else uart_rx_data_reg<=uart_rx_data_reg; else uart_rx_data_reg<=8'd0 ; end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin uart_rx_done<=1'b0 ; uart_rx_data<=8'd0 ; end else if (rx_cnt==4'd9 )begin uart_rx_done<=1'b1 ; uart_rx_data<=uart_rx_data_reg; end else begin uart_rx_done<=1'b0 ; uart_rx_data<=8'd0 ; end end endmodule
UART_TX
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 module uart_tx( input sys_clk, input sys_rst_n, input [7 :0 ] uart_data, input uart_tx_en, output reg uart_txd ); parameter SYS_CLK_FRE=50_000_000 ; parameter BPS=9_600 ; localparam BPS_CNT=SYS_CLK_FRE/BPS; reg uart_tx_en_d0; reg uart_tx_en_d1; reg tx_flag; reg [7 :0 ] uart_data_reg; reg [15 :0 ] clk_cnt; reg [3 :0 ] tx_cnt; wire pos_uart_en_txd; assign pos_uart_en_txd= uart_tx_en_d0 && (~uart_tx_en_d1); always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin uart_tx_en_d0<=1'b0 ; uart_tx_en_d1<=1'b0 ; end else begin uart_tx_en_d0<=uart_tx_en; uart_tx_en_d1<=uart_tx_en_d0; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin tx_flag<=1'b0 ; uart_data_reg<=8'd0 ; end else if (pos_uart_en_txd)begin uart_data_reg<=uart_data; tx_flag<=1'b1 ; end else if ((tx_cnt==4'd9 ) && (clk_cnt==BPS_CNT/2 ))begin tx_flag<=1'b0 ; uart_data_reg<=8'd0 ; end else begin uart_data_reg<=uart_data_reg; tx_flag<=tx_flag; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n)begin clk_cnt<=16'd0 ; tx_cnt <=4'd0 ; end else if (tx_flag) begin if (clk_cnt<BPS_CNT-1 )begin clk_cnt<=clk_cnt+1'b1 ; tx_cnt <=tx_cnt; end else begin clk_cnt<=16'd0 ; tx_cnt <=tx_cnt+1'b1 ; end end else begin clk_cnt<=16'd0 ; tx_cnt<=4'd0 ; end end always @(posedge sys_clk or negedge sys_rst_n)begin if (!sys_rst_n) uart_txd<=1'b1 ; else if (tx_flag) case (tx_cnt) 4'd0 : uart_txd<=1'b0 ; 4'd1 : uart_txd<=uart_data_reg[0 ]; 4'd2 : uart_txd<=uart_data_reg[1 ]; 4'd3 : uart_txd<=uart_data_reg[2 ]; 4'd4 : uart_txd<=uart_data_reg[3 ]; 4'd5 : uart_txd<=uart_data_reg[4 ]; 4'd6 : uart_txd<=uart_data_reg[5 ]; 4'd7 : uart_txd<=uart_data_reg[6 ]; 4'd8 : uart_txd<=uart_data_reg[7 ]; 4'd9 : uart_txd<=1'b1 ; default :; endcase else uart_txd<=1'b1 ; end endmodule
UART_TOP
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 module uart_top( input sys_clk, input sys_rst_n, input uart_rxd, output uart_txd ); parameter UART_BPS=9600 ; parameter CLK_FREQ=50_000_000 ; wire uart_en_w;wire [7 :0 ] uart_data_w; uart_tx#( .BPS (UART_BPS), .SYS_CLK_FRE (CLK_FREQ)) u_uart_tx( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_tx_en (uart_en_w), .uart_data (uart_data_w), .uart_txd (uart_txd) ); uart_rx #( .BPS (UART_BPS), .SYS_CLK_FRE (CLK_FREQ)) u_uart_rx( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_rxd (uart_rxd), .uart_rx_done (uart_en_w), .uart_rx_data (uart_data_w) ); endmodule
TestBench
由于涉及到的模块较少使用VScode进行简单的仿真,需要生成vcd波形文件,添加了额外的代码,一次八位数据为$01010101_2$即$55_{16}$。
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 `timescale 1ns/1ns module uart_top_tb();reg sys_clk; reg sys_rst_n; reg uart_rxd; wire uart_txd; uart_top #( .UART_BPS (9600 ), .CLK_FREQ (50_000_000 ) ) u_uart_top( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .uart_rxd (uart_rxd), .uart_txd (uart_txd) ); localparam CNT=50_000_000 /9600 *20 ; initial begin sys_clk <=1'b0 ; sys_rst_n<=1'b0 ; uart_rxd<=1'b1 ; #20 sys_rst_n<=1'b1 ; #(CNT/2) uart_rxd<=1'b0 ; #CNT uart_rxd<=1'b1 ; #CNT uart_rxd<=1'b0 ; #CNT uart_rxd<=1'b1 ; #CNT uart_rxd<=1'b0 ; #CNT uart_rxd<=1'b1 ; #CNT uart_rxd<=1'b0 ; #CNT uart_rxd<=1'b1 ; #CNT uart_rxd<=1'b0 ; #CNT uart_rxd<=1'b1 ; #20000 $finish ; end always begin #10 sys_clk=~sys_clk; end initial begin $dumpfile ("wave.vcd" ); $dumpvars (0 , uart_top_tb); end endmodule
图1 仿真结果
全双工通信、半双工通信和单工通信
全双工通信是指在同一时刻信息可以进行双向传输。例如:打电话,说的同时也能听。
半双工通信是指在同一时刻信息只能单向传输,双方都可以进行发送和接收,但是不能够同时发送和接收。例如:对讲机通信。
单工通信是指在通信过程中,只能够设备A发送,设备B接收。例如:听收音机。
UART的通信电平标准
两个设备之间能够互相通信的基础条件为电平标准相同。UART的接口标准有很多,有RS232、RS485等等。台式PC上一般会有一个DB9,接口标准为RS232。
此接口在各个工业板上也有很多。随着技术的发展,PC上的DB9的接口逐渐被淘汰,换成了USB接口。
现如今的开发板上多为USB接口,便于和PC进行通信。
FPGA芯片是无法(较为复杂)发出对应的电平标准,如:RS485、RS232、USB接口电平等。在大多数板卡设计时,都会在FPGA外围添加电平转换器,将FPGA的电平标准转换为通信的电平标准。
SoCoco's Blog
Record some useless things
This piece of writing is an original article, utilizing theCC BY-NC-SA 4.0 Agreement. For complete reproduction, please acknowledge the source as Courtesy ofSoCoco's Blog