FPGA 串口UART學習筆記1串口通信


串口通信
1、串口簡介
串行接口,COM接口,只需要兩根線就能實現兩台設備之間的通信。UART指的是異步的串行接口,通用異步收發。標准常用的是RS-232標准接口 現在電腦上沒有串口了,所以使用的是USB轉串口芯片,CH340芯片。
換句話說,只需要兩根數據線UART_RXD和UART_TXD,就能完成兩台設備之間的通信。
2、串口時序
兩根數據線各自獨立互不影響,二者的時序是相同的。不同之處是UART_RXD是主機MASTER發送給從機SLAVE,UART_TXD是SLAVE發送給MASTER。 由於兩根線的時序完全相同且獨立,下面以UART_TXD為例。 空閑狀態時,UART_TXD一直拉高,當要傳輸數據之前,拉低一個數據位,此后開始傳輸數據。數據之后有一個校驗位,校驗位之后是停止位,停止位之后進入下一個傳輸周期。至此,完成了一個數據包的傳輸。
注意:
(1)、傳輸的數據是從低比特位開始傳,比如101010,接受端的接受順序是010101。
(2)、傳輸數據的位數是MASTER與SLAVE約定好的,可以是4、5、6、7、8位,時序圖中是以八位為例。
(3)、校驗位一般是奇偶檢驗。當然,也可以選擇沒有檢驗位,前提是MASTER與SLAVE約定好,在SLAVE解析接收到的數據的時候,不安排校驗位的解析。
(4)、停止位,停止位是保證兩段傳輸之間一定要有間隔。兩段傳輸之間可以沒有空閑時間,但是,停止位一定要有。
3、時間的問題
從時序圖上可以看出,串口的發送和接受是沒有時鍾的,換句話說,這是一個異步時序。那么如何確定每個位所需要的時間就尤為重要。 這個問題的要點是波特率,每秒發送/接受單位的個數。我們使用的串口是以比特為單位,所以這里波特率與我們的比特率相同。常見的波特率的數值有9600,19200,38400,57600,115200等。以9600為例,表示一秒鍾發送/接受9600個比特。所有我們可以計算出單個bit所占用的時間為 1s/9600 = 104166ns。我們傳輸的起始標志位,傳輸的數據的每一位,校驗位(可有可無),在9600波特率的情況下,各自占據了104166ns的時間。 所以,假設從MASTER發送8'b11001110給SLAVE的的話,數據線的電平變化如下:
所以,FPGA在接收串口數據的時候,按照每個位的時間,設計計數器。假設是50MHz的時鍾,那么接受一個bit需要的時鍾周期是 104166ns/20ns = 5208個周期,所以在計數器數到5208/2 +1的時候,將UART_RXD的當前值寄存即可。注意接受數據的時候先接受到的是起始位,最后的空閑位置就沒有必要接受了。
4、代碼 項目要求:電腦通過串口發送八位數據給FPGA,FPGA通過這八位數據來控制八個LED燈的亮暗。波特率9600,無校驗位。 代碼如下: ``

 1 module uart(
 2     clk        ,
 3     rst_n    ,
 4     rx_uart    ,
 5     led
 6 );
 7 input            clk        ;
 8 input            rst_n    ;
 9 input            rx_uart    ;
10 output reg[7:0] led        ;
11 
12 reg flag_add;
13 //波特率9600 1s/9600 = 104166ns
14 //50M時鍾,一個周期是20ns,104166ns/20ns=5208個時鍾周期
15 //用計數器來計數時鍾周期,每到5208從rx_uart取出一個數據
16 //一個數據包是八個數據+一個起始位數據
17 
18 //計數器cnt0用來計數每個bit的5208個周期
19 reg[12:0] cnt0   ;
20 wire    add_cnt0    ;
21 wire    end_cnt0    ;
22 
23 always @(posedge clk or negedge rst_n)begin
24     if(!rst_n)begin
25         cnt0 <= 0;
26     end
27     else if(add_cnt0)begin
28         if(end_cnt0)
29             cnt0 <= 0;
30         else
31             cnt0 <= cnt0 + 1;
32         end
33     end
34 assign add_cnt0 = flag_add;       
35 assign end_cnt0 = add_cnt0 && cnt0 == 5208 - 1 ;   
36 
37 //計數器1用來計數接受的bit數量
38 reg[3:0] cnt1   ;
39 wire    add_cnt1    ;
40 wire    end_cnt1    ;
41 
42 always @(posedge clk or negedge rst_n)begin
43     if(!rst_n)begin
44         cnt1 <= 0;
45     end
46     else if(add_cnt1)begin
47         if(end_cnt1)
48             cnt1 <= 0;
49         else
50             cnt1 <= cnt1 + 1;
51         end
52     end
53 assign add_cnt1 = end_cnt0;       
54 assign end_cnt1 = add_cnt1 && cnt1 == 9 - 1;   
55 
56 //flag_add的定義,rx_uart下降沿的時候拉高,end_cnt1的時候拉低
57 //需要一個rx_uart邊沿檢測電路,注意,rx_uart是一個異步信號,與本地時鍾無關。
58 //可以把rx_uart放進敏感列表,但是他會被FPGA誤認為是個時鍾,造成電路不穩定
59 //安全的做法:用本地時鍾對rx_uart進行延一拍,然后前后兩拍比較完成邊沿檢測
60 //但是,因為本項目中的rx_uart是個異步信號,可能在時鍾邊沿變化,
61 //不能滿足setup time 和 保持時間 所以就延兩拍更安全。(兩拍以后的信號是穩定信號)
62 reg rx_uart_ff0;
63 reg rx_uart_ff1;
64 reg rx_uart_ff2;
65 always@(posedge clk or negedge rst_n)
66     if(!rst_n)begin
67         rx_uart_ff0 <= 1;
68         rx_uart_ff1 <= 1;
69         rx_uart_ff2 <= 1;
70     end
71     else begin
72         rx_uart_ff0 <= rx_uart;        
73         //rx_uart_ff0是異步信號打一拍的結果,不能做條件用。以后的可以
74         rx_uart_ff1 <= rx_uart_ff0;
75         rx_uart_ff2 <= rx_uart_ff1;
76     end
77 
78 always@(posedge clk or negedge rst_n)
79     if(!rst_n)begin
80         flag_add <= 0;
81     end
82     else if(!rx_uart_ff1 & rx_uart_ff2) begin//下降沿
83         flag_add <= 1;
84     end
85     else if(end_cnt1)
86         flag_add <= 0;
87         
88 always@(posedge clk or negedge rst_n)
89     if(!rst_n)begin
90         led <= 8'hff;
91     end
92     else if(add_cnt0 && cnt0 == 5208/2-1 && cnt1>0)begin
93         led[cnt1-1] <= rx_uart_ff1;
94     end
95 
96 
97 endmodule

 


免責聲明!

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



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