協議——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 //**************************************************************************
  2 // *** 名稱 : uart_rx.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2019-01-10
  6 // *** 描述 : 串口接收模塊,計數9.5下,其中停止位0.5下
  7 //            因為串口助手發送本次停止位和下次開始位中間沒有留空閑位
  8 //            若計滿10下,則才結束本次傳輸下次數據就來了,會來不及接收
  9 //**************************************************************************
 10 
 11 module uart_rx
 12 //========================< 參數 >==========================================
 13 #(
 14 parameter  CLK              = 50_000_000        , //系統時鍾,50Mhz
 15 parameter  BPS              = 9600              , //波特率
 16 parameter  BPS_CNT          = CLK/BPS             //波特率計數
 17 )
 18 //========================< 端口 >==========================================
 19 (
 20 input   wire                clk                 , //時鍾,50Mhz
 21 input   wire                rst_n               , //復位,低電平有效
 22 input   wire                din                 , //輸入數據
 23 output  reg   [7:0]         dout                , //輸出數據
 24 output  reg                 dout_vld              //輸出數據的有效指示
 25 );
 26 //========================< 信號 >==========================================
 27 reg                         rx0                 ;
 28 reg                         rx1                 ;
 29 reg                         rx2                 ;
 30 wire                        rx_en               ;
 31 reg                         flag                ;
 32 reg   [15:0]                cnt0                ;
 33 wire                        add_cnt0            ;
 34 wire                        end_cnt0            ;
 35 reg   [ 3:0]                cnt1                ;
 36 wire                        add_cnt1            ;
 37 wire                        end_cnt1            ;
 38 reg   [ 7:0]                data                ;
 39 
 40 //==========================================================================
 41 //==    消除亞穩態 + 下降沿檢測
 42 //==========================================================================
 43 always @(posedge clk or negedge rst_n) begin
 44     if(!rst_n) begin
 45         rx0 <= 1;
 46         rx1 <= 1;
 47         rx2 <= 1;
 48     end
 49     else begin
 50         rx0 <= din;
 51         rx1 <= rx0;
 52         rx2 <= rx1;
 53     end
 54 end
 55 
 56 assign rx_en = rx2 && ~rx1;
 57 
 58 //==========================================================================
 59 //==    接收狀態指示
 60 //==========================================================================
 61 always @(posedge clk or negedge rst_n) begin
 62     if(!rst_n)
 63         flag <= 0;
 64     else if(rx_en)
 65         flag <= 1;
 66     else if(end_cnt1)
 67         flag <= 0;
 68 end
 69 
 70 //==========================================================================
 71 //==    波特率計數
 72 //==========================================================================
 73 always @(posedge clk or negedge rst_n) begin
 74     if(!rst_n)
 75         cnt0 <= 0;
 76     else if(add_cnt0) begin
 77         if(end_cnt0)
 78             cnt0 <= 0;
 79         else
 80             cnt0 <= cnt0 + 1;
 81     end
 82 end
 83 
 84 assign add_cnt0 = flag;
 85 assign end_cnt0 = cnt0== BPS_CNT-1 || end_cnt1;
 86 
 87 //==========================================================================
 88 //==    開始1位(不接收) + 數據8位 + 停止0.5位(不接收),共10位
 89 //==========================================================================
 90 always @(posedge clk or negedge rst_n) begin 
 91     if(!rst_n)
 92         cnt1 <= 0;
 93     else if(add_cnt1) begin
 94         if(end_cnt1)
 95             cnt1 <= 0;
 96         else
 97             cnt1 <= cnt1 + 1;
 98     end
 99 end
100 
101 assign add_cnt1 = end_cnt0;
102 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_CNT/2-1;
103 
104 //==========================================================================
105 //==    緩存數據
106 //==========================================================================
107 always @ (posedge clk or negedge rst_n)begin
108     if(!rst_n)
109         data <= 8'd0;
110     else if(cnt1>=1 && cnt1<=8 && cnt0==BPS_CNT/2-1) //中間采樣
111         data[cnt1-1] <= rx2;                         //或 dout <= {rx2,dout[7:1]};
112 end
113 
114 //==========================================================================
115 //==    輸出數據
116 //==========================================================================
117 always @ (posedge clk or negedge rst_n)begin
118     if(!rst_n)
119         dout <= 0;
120     else if(end_cnt1)   
121         dout <= data;
122 end
123 
124 always @ (posedge clk or negedge rst_n)begin
125     if(!rst_n)
126         dout_vld <= 0;
127     else if(end_cnt1)   
128         dout_vld <= 1;  
129     else    
130         dout_vld <= 0;
131 end
132 
133 
134 
135 endmodule

 

2.uart_tx

  1 //**************************************************************************
  2 // *** 名稱 : uart_tx.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2019-01-10
  6 // *** 描述 : 串口接收模塊,計數9.5下,其中停止位0.5下
  7 //            因為極端情況是本次停止位和下次開始位中間沒有留空閑位
  8 //            若計滿10下,則才結束本次傳輸下次數據就來了,會來不及發送
  9 //**************************************************************************
 10 
 11 module uart_tx
 12 //========================< 參數 >==========================================
 13 #(
 14 parameter  CLK              = 50_000_000        , //系統時鍾,50Mhz
 15 parameter  BPS              = 9600              , //波特率
 16 parameter  BPS_CNT          = CLK/BPS             //波特率計數
 17 )
 18 //========================< 端口 >==========================================
 19 (
 20 input   wire                clk                 , //時鍾,50Mhz
 21 input   wire                rst_n               , //復位,低電平有效
 22 input   wire  [7:0]         din                 , //輸入數據
 23 input   wire                din_vld             , //輸入數據的有效指示
 24 output  reg                 dout                  //輸出數據
 25 );
 26 //========================< 信號 >==========================================
 27 reg                         flag                ;
 28 reg   [ 7:0]                din_tmp             ;
 29 reg   [15:0]                cnt0                ;
 30 wire                        add_cnt0            ;
 31 wire                        end_cnt0            ;
 32 reg   [ 3:0]                cnt1                ;
 33 wire                        add_cnt1            ;
 34 wire                        end_cnt1            ;
 35 wire  [ 9:0]                data                ;
 36 
 37 //==========================================================================
 38 //==    數據暫存(din可能會消失,暫存住)
 39 //==========================================================================
 40 always @ (posedge clk or negedge rst_n) begin
 41     if(!rst_n)
 42         din_tmp <=8'd0;
 43     else if(din_vld)
 44         din_tmp <= din;
 45 end
 46 
 47 //==========================================================================
 48 //==    發送狀態指示
 49 //==========================================================================
 50 always  @(posedge clk or negedge rst_n)begin
 51     if(!rst_n)
 52         flag <= 0;
 53     else if(din_vld)
 54         flag <= 1;
 55     else if(end_cnt1)
 56         flag <= 0;
 57 end
 58 
 59 //==========================================================================
 60 //==    波特率計數
 61 //==========================================================================
 62 always @(posedge clk or negedge rst_n) begin
 63     if(!rst_n)
 64         cnt0 <= 0;
 65     else if(add_cnt0) begin
 66         if(end_cnt0)
 67             cnt0 <= 0;
 68         else
 69             cnt0 <= cnt0 + 1;
 70     end
 71 end
 72 
 73 assign add_cnt0 = flag;
 74 assign end_cnt0 = cnt0== BPS_CNT-1 || end_cnt1;
 75 
 76 //==========================================================================
 77 //==    開始1位 + 數據8位 + 停止0.5位,共10位
 78 //==========================================================================
 79 always @(posedge clk or negedge rst_n) begin 
 80     if(!rst_n)
 81         cnt1 <= 0;
 82     else if(add_cnt1) begin
 83         if(end_cnt1)
 84             cnt1 <= 0;
 85         else
 86             cnt1 <= cnt1 + 1;
 87     end
 88 end
 89 
 90 assign add_cnt1 = end_cnt0;
 91 assign end_cnt1 = cnt1==10-1 && cnt0==BPS_CNT/2-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)
102         dout <= data[cnt1];
103 end
104 
105 
106 
107 endmodule

 

3.top層

 1 //**************************************************************************
 2 // *** 名稱 : uart_top.v
 3 // *** 作者 : xianyu_FPGA
 4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
 5 // *** 日期 : 2019-01-10
 6 // *** 描述 : 串口實驗頂層文件
 7 //**************************************************************************
 8 
 9 module uart_top
10 //========================< 端口 >==========================================
11 (
12 input  wire                 clk                 , //時鍾,50Mhz
13 input  wire                 rst_n               , //復位,低電平有效
14 input  wire                 uart_rx             , //FPGA通過串口接收的數據
15 output wire                 uart_tx               //FPGA通過串口發送的數據
16 );
17 
18 //========================< 連線 >==========================================
19 wire [7:0]                  data                ;
20 wire                        data_vld            ;
21 
22 //==========================================================================
23 //==    模塊例化
24 //==========================================================================
25 uart_rx
26 #(
27     .BPS_CNT                (52                 )    //仿真用
28 )
29 u_uart_rx
30 (
31     .clk                    (clk                ),
32     .rst_n                  (rst_n              ),
33     .din                    (uart_rx            ),
34     .dout                   (data               ),
35     .dout_vld               (data_vld           )
36 );
37 
38 uart_tx
39 #(
40     .BPS_CNT                (52                 )   //仿真用
41 )
42 u_uart_tx
43 (
44     .clk                    (clk                ),
45     .rst_n                  (rst_n              ),
46     .din_vld                (data_vld           ),
47     .din                    (data               ),
48     .dout                   (uart_tx            )
49 );
50 
51 
52 
53 endmodule

 

四、仿真調試

1、testbench

 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 //==    task任務
39 //==========================================================================
40 reg  [7:0]              mem[15:0]           ; //位寬為8,深度為16個數據
41 integer                 i                   ;
42 integer                 j                   ;
43 
44 //讀取外部數據
45 initial $readmemh("./data.txt",mem);
46 
47 //位賦值
48 task rx_bit
49 (
50     input [7:0]         data
51 );
52     begin
53         for(i=0;i<=9;i=i+1) begin   //10個bit為
54             case(i)
55                  0: uart_rx = 1'b0;
56                  1: uart_rx = data[i-1];
57                  2: uart_rx = data[i-1];
58                  3: uart_rx = data[i-1];
59                  4: uart_rx = data[i-1];
60                  5: uart_rx = data[i-1];
61                  6: uart_rx = data[i-1];
62                  7: uart_rx = data[i-1];
63                  8: uart_rx = data[i-1];
64                  9: uart_rx = 1'b1;
65             endcase 
66             #1040; //一個完整波特延時:52*20=1040
67         end        //考慮到空閑位,也可以設置得1040稍大一些
68     end
69 endtask 
70 
71 //字節賦值
72 task rx_byte;
73     begin
74         for(j=0;j<=15;j=j+1) //16個byte數據
75             rx_bit(mem[j]);
76     end
77 endtask
78 
79 //==========================================================================
80 //==    調用task
81 //==========================================================================
82 initial begin
83     #(`Clock*20+1);
84     rx_byte();
85 end
86 
87 initial begin
88     #180000;
89     $stop;
90 end
91 
92 endmodule

 

2、data.txt

  testbench中調用了一個 data.txt 文本文檔,里面存儲了此次仿真的16個數據,將其放置到 Modelsim 軟件的工程目錄中(非 work)即可。

0
1
2
3
4
5
6
7
8
9
a
b
c
d
e
f

 

3、仿真波形

  由波形可以看到,本次設計應該是成功的。

 

五、上板驗證

  本次上位機采用友善串口助手,無校驗位,停止位為1。當串口助手發送數據給FPGA后,FPGA很快又將原數據返回給上位機。

  經上板驗證,本次設計成功!

 

參考資料:

[1]明德揚FPGA教程

[2]正點原子FPGA教程

[2]威三學院FPGA教程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM