协议——UART(RS232)
一、UART簡介
UART(universal asynchronous receiver-transmitter)是一種采用異步串行通信方式的通用異步收發傳輸器。一般來說,UART總是和RS232成對出現,那RS232又是什么呢? RS232也就是我們計算機上的串口,它的全稱是EIA-RS-232C (簡稱232,或者是RS232 )。其中EIA(Electronic Industry Association)代表美國電子工業協會,RS是Recommended Standard的縮寫,代表推薦標準,232 是標識符,C表示修改次數,它被廣泛用于計算機串行接口外設連接。如果你的計算機上還有串口的話,那么你就可以在主機箱后面看到RS232的接口:
隨著時代的發展,這種借口已經很少用了,取而代之的是“USB轉串口”,功能和原先一樣,但接口更高效了。
串口的主要功能為:在發送數據時將并行數據轉換成串行數據進行傳輸,在接收數據時將接收到的串行數據轉換成并行數據。這應該是大多數人接觸電子后學習到的第一個通信協議吧。
?
二、通信格式
下面來說說串口的具體要點:
1.傳輸時序
UART串口通信需要兩個信號線來實現,一根用于串口發送,另外一根負責串口接收。一開始高電平,然后拉低表示開始位,接著8個數據位,然后校驗位,最后拉高表示停止位,并且進入空閑狀態,等待下一次的數據傳輸。
很多時候我們的校驗位是允許省略的,所以協議就變成了:開始+數據+停止。
2.傳輸速率:波特率
串口通信的速率用波特率表示,它表示麥苗傳輸二進制數據的位數,單位是bps(位/秒)。常用的波特率有9600、19200、35400、57600以及115200等。
FPGA開發串口時,設計波特率的方法:FPGA的時鐘頻率/波特率。例如我的FPGA開發板時鐘頻率為50Mhz,即50_000_000hz,我想使用的波特率為9600bps,因此我需要的計數為:50000000/9600≈5208。
?
三、實戰講解
現在用FPGA開發板做一個串口回環的實驗,要求是PC端通過串口助手發送數據給FPGA,FPGA接收到數據后返回給PC端,并在串口助手處顯示數值。即串口助手發什么就能收回什么。實驗框圖如下:
1.uart_rx
如果直接用串口助手發送會出現一半數據丟失的情況。原因是串口發送助手發送數據是停止位后緊挨著下一波的開始位(即沒有設置空閑位),導致下降沿檢測沒有辦法跟上而丟失掉一些數據。解決辦法是串口助手的停止位設置成1.5即可,即加0.5個空閑位。其他時候建議cnt1計數9下,這樣即使停止位是1也不會出現數據丟失的情況。
1 //========================================================================== 2 // --- 名稱 : uart_rx.v 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2019-06-24 5 // --- 描述 : 串口接收模塊,cnt1計數10下,是為配合發送模塊的計數10下。 6 // 此時需要將串口助手停止位設置為1.5,即加0.5個空閑位。 7 // 其他時候建議cnt1計數9下,停止位為1,以保證連續接收數據無誤。 8 //========================================================================== 9 10 module uart_rx 11 //---------------------<參數定義>------------------------------------------- 12 #( 13 parameter BPS = 5208 , //波特率 14 parameter BPS_W = 13 , //波特率位寬 15 parameter BPS_half = 2604 //波特率中間采樣點 16 ) 17 //---------------------<端口聲明>------------------------------------------- 18 ( 19 input wire clk , //時鐘,50Mhz 20 input wire rst_n , //復位,低電平有效 21 input wire din , //輸入數據 22 output reg [7:0] dout , //輸出數據 23 output reg dout_vld //輸出數據的有效指示 24 ); 25 //---------------------<信號定義>------------------------------------------- 26 reg rx0 ; 27 reg rx1 ; 28 reg rx2 ; 29 wire rx_en ; 30 reg flag ; 31 reg [BPS_W-1:0] cnt0 ; 32 wire add_cnt0 ; 33 wire end_cnt0 ; 34 reg [3:0] cnt1 ; 35 wire add_cnt1 ; 36 wire end_cnt1 ; 37 reg [7:0] data ; 38 39 //-------------------------------------------------------------------------- 40 //-- 消除亞穩態 + 下降沿檢測 41 //-------------------------------------------------------------------------- 42 always @(posedge clk) begin 43 {rx2,rx1,rx0} <= {rx1,rx0,din}; 44 end 45 46 assign rx_en = rx2 && ~rx1; 47 48 //-------------------------------------------------------------------------- 49 //-- 接收狀態指示 50 //-------------------------------------------------------------------------- 51 always @(posedge clk or negedge rst_n) begin 52 if(!rst_n) 53 flag <= 0; 54 else if(rx_en) 55 flag <= 1; 56 else if(end_cnt1) 57 flag <= 0; 58 end 59 60 //-------------------------------------------------------------------------- 61 //-- 波特率計數 62 //-------------------------------------------------------------------------- 63 always @(posedge clk or negedge rst_n) begin 64 if(!rst_n) 65 cnt0 <= 0; 66 else if(add_cnt0) begin 67 if(end_cnt0) 68 cnt0 <= 0; 69 else 70 cnt0 <= cnt0 + 1; 71 end 72 else 73 cnt0 <= cnt0; 74 end 75 76 assign add_cnt0 = flag; 77 assign end_cnt0 = add_cnt0 && cnt0== BPS-1; 78 79 //-------------------------------------------------------------------------- 80 //-- 開始位(不接收) + 8個數據位 + 停止位(不接收)= 10 81 //-------------------------------------------------------------------------- 82 always @(posedge clk or negedge rst_n) begin 83 if(!rst_n) 84 cnt1 <= 0; 85 else if(add_cnt1) begin 86 if(end_cnt1) 87 cnt1 <= 0; 88 else 89 cnt1 <= cnt1 + 1; 90 end 91 else 92 cnt1 <= cnt1; 93 end 94 95 assign add_cnt1 = end_cnt0; 96 assign end_cnt1 = add_cnt1 && cnt1==10-1; 97 98 //-------------------------------------------------------------------------- 99 //-- 緩存數據 100 //-------------------------------------------------------------------------- 101 always @ (posedge clk or negedge rst_n)begin 102 if(!rst_n) 103 data <= 8'd0; 104 else if(cnt1>=1 && cnt1<=8 && cnt0==BPS_half-1) //中間采樣 105 data[cnt1-1] <= rx2; //或 dout <= {rx2,dout[7:1]}; 106 end 107 108 //-------------------------------------------------------------------------- 109 //-- 輸出數據 110 //-------------------------------------------------------------------------- 111 always @ (posedge clk or negedge rst_n)begin 112 if(!rst_n) 113 dout <= 0; 114 else if(end_cnt1) 115 dout <= data; 116 end 117 118 always @ (posedge clk or negedge rst_n)begin 119 if(!rst_n) 120 dout_vld <= 0; 121 else if(end_cnt1) 122 dout_vld <= 1; 123 else 124 dout_vld <= 0; 125 end 126 127 128 129 endmodule2.uart_tx
設計和協議完全匹配。
1 //========================================================================== 2 // --- 名稱 : uart_tx.v 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2019-06-24 5 // --- 描述 : 串口發送模塊 6 //========================================================================== 7 8 module uart_tx 9 //---------------------<參數定義>------------------------------------------- 10 #( 11 parameter BPS = 5208 , //波特率 12 parameter BPS_W = 13 //波特率位寬 13 ) 14 //---------------------<端口聲明>------------------------------------------- 15 ( 16 input wire clk , //時鐘,50Mhz 17 input wire rst_n , //復位,低電平有效 18 input wire [7:0] din , //輸入數據 19 input wire din_vld , //輸入數據的有效指示 20 output reg dout //輸出數據 21 ); 22 //---------------------<信號定義>------------------------------------------- 23 reg flag ; 24 reg [7:0] din_tmp ; 25 reg [BPS_W-1:0] cnt0 ; 26 wire add_cnt0 ; 27 wire end_cnt0 ; 28 reg [3:0] cnt1 ; 29 wire add_cnt1 ; 30 wire end_cnt1 ; 31 wire [9:0] data ; 32 33 //-------------------------------------------------------------------------- 34 //-- 數據暫存(din可能會消失,暫存住) 35 //-------------------------------------------------------------------------- 36 always @ (posedge clk or negedge rst_n) begin 37 if(!rst_n) 38 din_tmp <=8'd0; 39 else if(din_vld) 40 din_tmp <= din; 41 end 42 43 //-------------------------------------------------------------------------- 44 //-- 發送狀態指示 45 //-------------------------------------------------------------------------- 46 always @(posedge clk or negedge rst_n)begin 47 if(!rst_n) 48 flag <= 0; 49 else if(din_vld) 50 flag <= 1; 51 else if(end_cnt1) 52 flag <= 0; 53 end 54 55 //-------------------------------------------------------------------------- 56 //-- 波特率計數 57 //-------------------------------------------------------------------------- 58 always @(posedge clk or negedge rst_n) begin 59 if(!rst_n) 60 cnt0 <= 0; 61 else if(add_cnt0) begin 62 if(end_cnt0) 63 cnt0 <= 0; 64 else 65 cnt0 <= cnt0 + 1; 66 end 67 else 68 cnt0 <= cnt0; 69 end 70 71 assign add_cnt0 = flag; 72 assign end_cnt0 = add_cnt0 && cnt0== BPS-1; 73 74 //-------------------------------------------------------------------------- 75 //-- 開始 + 數據 + 結束,共10位 76 //-------------------------------------------------------------------------- 77 always @(posedge clk or negedge rst_n) begin 78 if(!rst_n) 79 cnt1 <= 0; 80 else if(add_cnt1) begin 81 if(end_cnt1) 82 cnt1 <= 0; 83 else 84 cnt1 <= cnt1 + 1; 85 end 86 else 87 cnt1 <= cnt1; 88 end 89 90 assign add_cnt1 = end_cnt0; 91 assign end_cnt1 = add_cnt1 && cnt1== 10-1; 92 93 //-------------------------------------------------------------------------- 94 //-- 數據輸出(用case語句也行) 95 //-------------------------------------------------------------------------- 96 assign data = {1'b1,din_tmp,1'b0}; //結束,數據,開始 97 98 always @(posedge clk or negedge rst_n) begin 99 if(!rst_n) 100 dout <= 1'b1; 101 else if(flag==1) 102 dout <= data[cnt1]; 103 end 104 105 106 107 endmodule3.uart_top頂層文件
1 //========================================================================== 2 // --- 名稱 : uart_top.v 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 209-06-24 5 // --- 描述 : 串口實驗頂層文件 6 //========================================================================== 7 8 module uart_top 9 //---------------------<端口聲明>------------------------------------------- 10 ( 11 input wire clk , //時鐘,50Mhz 12 input wire rst_n , //復位,低電平有效 13 input wire uart_rx , //FPGA通過串口接收的數據 14 output wire uart_tx //FPGA通過串口發送的數據 15 ); 16 17 //---------------------<模塊連線>------------------------------------------- 18 wire [7:0] data ; 19 wire data_vld ; 20 21 //-------------------------------------------------------------------------- 22 //-- 模塊例化 23 //-------------------------------------------------------------------------- 24 uart_rx u_uart_rx 25 ( 26 .clk (clk ), 27 .rst_n (rst_n ), 28 .din (uart_rx ), 29 .dout (data ), 30 .dout_vld (data_vld ) 31 ); 32 33 uart_tx u_uart_tx 34 ( 35 .clk (clk ), 36 .rst_n (rst_n ), 37 .din_vld (data_vld ), 38 .din (data ), 39 .dout (uart_tx ) 40 ); 41 42 endmodule4.testbench
里面的data.txt是一個文本文件,其內容為:0 1 2 3 4 5 6 7 8 9 a b c d e f ,放到你的工作文件夾區即可。
1 `timescale 1ns/1ps //時間精度 2 `define Clock 20 //時鐘周期 3 4 module uart_top_tb; 5 6 //---------------------<端口定義>------------------------------------------- 7 reg clk ; //時鐘,50Mhz 8 reg rst_n ; //復位,低電平有效 9 reg uart_rx ; 10 wire uart_tx ; 11 12 //-------------------------------------------------------------------------- 13 //-- 模塊例化 14 //-------------------------------------------------------------------------- 15 uart_top u_uart_top 16 ( 17 .clk (clk ), 18 .rst_n (rst_n ), 19 .uart_rx (uart_rx ), 20 .uart_tx (uart_tx ) 21 ); 22 23 //---------------------------------------------------------------------- 24 //-- 時鐘信號和復位信號 25 //---------------------------------------------------------------------- 26 initial begin 27 clk = 1; 28 forever 29 #(`Clock/2) clk = ~clk; 30 end 31 32 initial begin 33 rst_n = 0; #(`Clock*20+1); 34 rst_n = 1; 35 end 36 37 //---------------------------------------------------------------------- 38 //-- tase任務 39 //---------------------------------------------------------------------- 40 reg [7:0] mem[15:0] ; //位寬為8,深度為16 41 integer i ; 42 integer j ; 43 44 //讀取外部數據 45 initial begin 46 $readmemh("./data.txt",mem); 47 end 48 49 //位 50 task rx_bit(input [7:0]data); 51 begin 52 for(i=0;i<10;i=i+1) begin //0-9 53 case(i) 54 0:uart_rx = 0; 55 1:uart_rx = data[i-1]; 56 2:uart_rx = data[i-1]; 57 3:uart_rx = data[i-1]; 58 4:uart_rx = data[i-1]; 59 5:uart_rx = data[i-1]; 60 6:uart_rx = data[i-1]; 61 7:uart_rx = data[i-1]; 62 8:uart_rx = data[i-1]; 63 9:uart_rx = 1; 64 endcase 65 #104260; //一個完整波特延時:5208*20=104160 66 end //考慮到空閑位,也可以設置得104160稍大一些 67 end 68 endtask 69 70 //字節 71 task rx_byte; 72 begin 73 for(j=0;j<16;j=j+1) 74 rx_bit(mem[j]); 75 end 76 endtask 77 78 //-------------------------------------------------------------------------- 79 //-- 調用task 80 //-------------------------------------------------------------------------- 81 initial begin 82 #(`Clock*20+1); 83 rx_byte(); 84 end 85 86 87 88 endmodule5.仿真波形,軟件:isim
上半波形是uart_rx模塊,下半波形是uart_tx模塊,其中紫色的信號為輸出。可以看到,波形和我們的設計相符合,能正確的接收和發送數據。
6.實際上板
軟件:友善串口助手。停止位是1.5位或2位,成功!
?
四、總結
UART是我學習FPGA后設計的第一個協議,之前一直是拿著教程demo跑一跑完事,自己親自設計才體會到時序的不容易。難怪串口的停止位上設置了選項:1位、1.5位、2位,看來這都是前人遇到過的坑啊!
總結一下:由于串口助手的發送機制是停止位和下一波的開始位連著(中間沒有空閑位),因此我們設計uart_rx接收串口助手的數據時,如果計數10下,則串口助手的停止位要設置成1.5或2位。如果停止位設置成1位,則計數必須改成9下。
?
五、更改!!!
等等哈,為什么要弄的這么復雜?不就是個串口嗎?別人的串口回環實驗中串口助手的停止位是1就行,到你這就要1.5位或2位?
經過思考,我發現一個很好的辦法:停止位計0.5下!這樣就解決了串口助手發送數據時,停止位和下一波的開始位連著(中間沒有空閑位)的問題啊!我自己制造一個0.5的空閑位,完美解決問題,美滋滋!
1.uart_rx更改
1 //========================================================================== 2 // --- 名稱 : uart_rx.v 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2019-06-27 5 // --- 描述 : 串口接收模塊,計數9.5下,其中停止位0.5下 6 // * 因為串口助手發送本次停止位和下次開始位中間沒有留空閑位 7 // * 若計滿10下,則才結束本次傳輸下次數據就來了,會來不及接收 8 //========================================================================== 9 10 module uart_rx 11 //---------------------<參數定義>------------------------------------------- 12 #( 13 parameter BPS = 5208 , //波特率 14 parameter BPS_W = 13 , //波特率位寬 15 parameter BPS_half = 2604 //波特率中間采樣點 16 ) 17 //---------------------<端口聲明>------------------------------------------- 18 ( 19 input wire clk , //時鐘,50Mhz 20 input wire rst_n , //復位,低電平有效 21 input wire din , //輸入數據 22 output reg [7:0] dout , //輸出數據 23 output reg dout_vld //輸出數據的有效指示 24 ); 25 //---------------------<信號定義>------------------------------------------- 26 reg rx0 ; 27 reg rx1 ; 28 reg rx2 ; 29 wire rx_en ; 30 reg flag ; 31 reg [BPS_W-1:0] cnt0 ; 32 wire add_cnt0 ; 33 wire end_cnt0 ; 34 reg [3:0] cnt1 ; 35 wire add_cnt1 ; 36 wire end_cnt1 ; 37 reg [7:0] data ; 38 39 //-------------------------------------------------------------------------- 40 //-- 消除亞穩態 + 下降沿檢測 41 //-------------------------------------------------------------------------- 42 always @(posedge clk) begin 43 {rx2,rx1,rx0} <= {rx1,rx0,din}; 44 end 45 46 assign rx_en = rx2 && ~rx1; 47 48 //-------------------------------------------------------------------------- 49 //-- 接收狀態指示 50 //-------------------------------------------------------------------------- 51 always @(posedge clk or negedge rst_n) begin 52 if(!rst_n) 53 flag <= 0; 54 else if(rx_en) 55 flag <= 1; 56 else if(end_cnt1) 57 flag <= 0; 58 end 59 60 //-------------------------------------------------------------------------- 61 //-- 波特率計數 62 //-------------------------------------------------------------------------- 63 always @(posedge clk or negedge rst_n) begin 64 if(!rst_n) 65 cnt0 <= 0; 66 else if(add_cnt0) begin 67 if(end_cnt0) 68 cnt0 <= 0; 69 else 70 cnt0 <= cnt0 + 1; 71 end 72 else 73 cnt0 <= cnt0; 74 end 75 76 assign add_cnt0 = flag; 77 assign end_cnt0 = cnt0== BPS-1 || end_cnt1; 78 79 //-------------------------------------------------------------------------- 80 //-- 開始1位(不接收) + 數據8位 + 停止0.5位(不接收),共10位 81 //-------------------------------------------------------------------------- 82 always @(posedge clk or negedge rst_n) begin 83 if(!rst_n) 84 cnt1 <= 0; 85 else if(add_cnt1) begin 86 if(end_cnt1) 87 cnt1 <= 0; 88 else 89 cnt1 <= cnt1 + 1; 90 end 91 else 92 cnt1 <= cnt1; 93 end 94 95 assign add_cnt1 = end_cnt0; 96 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_half-1; 97 98 //-------------------------------------------------------------------------- 99 //-- 緩存數據 100 //-------------------------------------------------------------------------- 101 always @ (posedge clk or negedge rst_n)begin 102 if(!rst_n) 103 data <= 8'd0; 104 else if(cnt1>=1 && cnt1<=8 && cnt0==BPS_half-1) //中間采樣 105 data[cnt1-1] <= rx2; //或 dout <= {rx2,dout[7:1]}; 106 end 107 108 //-------------------------------------------------------------------------- 109 //-- 輸出數據 110 //-------------------------------------------------------------------------- 111 always @ (posedge clk or negedge rst_n)begin 112 if(!rst_n) 113 dout <= 0; 114 else if(end_cnt1) 115 dout <= data; 116 end 117 118 always @ (posedge clk or negedge rst_n)begin 119 if(!rst_n) 120 dout_vld <= 0; 121 else if(end_cnt1) 122 dout_vld <= 1; 123 else 124 dout_vld <= 0; 125 end 126 127 128 129 endmodule2.uart_tx更改
1 //========================================================================== 2 // --- 名稱 : uart_tx.v 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2019-06-27 5 // --- 描述 : 串口接收模塊,計數9.5下,其中停止位0.5下 6 // * 因為極端情況是本次停止位和下次開始位中間沒有留空閑位 7 // * 若計滿10下,則才結束本次傳輸下次數據就來了,會來不及發送 8 //========================================================================== 9 10 module uart_tx 11 //---------------------<參數定義>------------------------------------------- 12 #( 13 parameter BPS = 5208 , //波特率 14 parameter BPS_W = 13 , //波特率位寬 15 parameter BPS_half = 2604 //波特率中間采樣點 16 ) 17 //---------------------<端口聲明>------------------------------------------- 18 ( 19 input wire clk , //時鐘,50Mhz 20 input wire rst_n , //復位,低電平有效 21 input wire [7:0] din , //輸入數據 22 input wire din_vld , //輸入數據的有效指示 23 output reg dout //輸出數據 24 ); 25 //---------------------<信號定義>------------------------------------------- 26 reg flag ; 27 reg [7:0] din_tmp ; 28 reg [BPS_W-1:0] cnt0 ; 29 wire add_cnt0 ; 30 wire end_cnt0 ; 31 reg [3:0] cnt1 ; 32 wire add_cnt1 ; 33 wire end_cnt1 ; 34 wire [9:0] data ; 35 36 //-------------------------------------------------------------------------- 37 //-- 數據暫存(din可能會消失,暫存住) 38 //-------------------------------------------------------------------------- 39 always @ (posedge clk or negedge rst_n) begin 40 if(!rst_n) 41 din_tmp <=8'd0; 42 else if(din_vld) 43 din_tmp <= din; 44 end 45 46 //-------------------------------------------------------------------------- 47 //-- 發送狀態指示 48 //-------------------------------------------------------------------------- 49 always @(posedge clk or negedge rst_n)begin 50 if(!rst_n) 51 flag <= 0; 52 else if(din_vld) 53 flag <= 1; 54 else if(end_cnt1) 55 flag <= 0; 56 end 57 58 //-------------------------------------------------------------------------- 59 //-- 波特率計數 60 //-------------------------------------------------------------------------- 61 always @(posedge clk or negedge rst_n) begin 62 if(!rst_n) 63 cnt0 <= 0; 64 else if(add_cnt0) begin 65 if(end_cnt0) 66 cnt0 <= 0; 67 else 68 cnt0 <= cnt0 + 1; 69 end 70 else 71 cnt0 <= cnt0; 72 end 73 74 assign add_cnt0 = flag; 75 assign end_cnt0 = cnt0== BPS-1 || end_cnt1; 76 77 //-------------------------------------------------------------------------- 78 //-- 開始1位 + 數據8位 + 停止0.5位,共10位 79 //-------------------------------------------------------------------------- 80 always @(posedge clk or negedge rst_n) begin 81 if(!rst_n) 82 cnt1 <= 0; 83 else if(add_cnt1) begin 84 if(end_cnt1) 85 cnt1 <= 0; 86 else 87 cnt1 <= cnt1 + 1; 88 end 89 else 90 cnt1 <= cnt1; 91 end 92 93 assign add_cnt1 = end_cnt0; 94 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_half-1; 95 96 //-------------------------------------------------------------------------- 97 //-- 數據輸出(用case語句也行) 98 //-------------------------------------------------------------------------- 99 assign data = {1'b1,din_tmp,1'b0}; //結束,數據,開始 100 101 always @(posedge clk or negedge rst_n) begin 102 if(!rst_n) 103 dout <= 1'b1; 104 else if(flag==1) 105 dout <= data[cnt1]; 106 end 107 108 109 110 endmodule3.實際上板
停止位是1位,成功!
?
六、后記
一個串口搞了我兩次!唉,不想說什么了,菜就是罪啊!
?
參考資料:
[1]明德揚FPGA教程
[2]正點原子FPGA教程
[2]威三學院FPGA教程
轉載于:https://www.cnblogs.com/xianyufpga/p/11086676.html
總結
以上是生活随笔為你收集整理的协议——UART(RS232)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux基础学习笔记整理——第二章 管
- 下一篇: sql2008转到sqk2000的步骤