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

串口通訊
一、串口簡介
串行接口,COM接口,只須要兩根線就能實現兩臺設備之間的通訊。UART指的是異步的串行接口,通用異步收發。標準經常使用的是RS-232標準接口
如今電腦上沒有串口了,因此使用的是USB轉串口芯片,CH340芯片。

換句話說,只須要兩根數據線UART_RXD和UART_TXD,就能完成兩臺設備之間的通訊。安全

二、串口時序
兩根數據線各自獨立互不影響,兩者的時序是相同的。不一樣之處是UART_RXD是主機MASTER發送給從機SLAVE,UART_TXD是SLAVE發送給MASTER。異步

因爲兩根線的時序徹底相同且獨立,下面以UART_TXD爲例。
空閒狀態時,UART_TXD一直拉高,當要傳輸數據以前,拉低一個數據位,此後開始傳輸數據。數據以後有一個校驗位,校驗位以後是中止位,中止位以後進入下一個傳輸週期。至此,完成了一個數據包的傳輸。
注意:
(1)、傳輸的數據是從低比特位開始傳,好比101010,接受端的接受順序是010101。
(2)、傳輸數據的位數是MASTER與SLAVE約定好的,能夠是四、五、六、七、8位,時序圖中是以八位爲例。
(3)、校驗位通常是奇偶檢驗。固然,也能夠選擇沒有檢驗位,前提是MASTER與SLAVE約定好,在SLAVE解析接收到的數據的時候,不安排校驗位的解析。
(4)、中止位,中止位是保證兩段傳輸之間必定要有間隔。兩段傳輸之間能夠沒有空閒時間,可是,中止位必定要有。設計

三、時間的問題
從時序圖上能夠看出,串口的發送和接受是沒有時鐘的,換句話說,這是一個異步時序。那麼如何肯定每一個位所須要的時間就尤其重要。
這個問題的要點是波特率,每秒發送/接受單位的個數。咱們使用的串口是以比特爲單位,因此這裏波特率與咱們的比特率相同。常見的波特率的數值有9600,19200,38400,57600,115200等。以9600爲例,表示一秒鐘發送/接受9600個比特。全部咱們能夠計算出單個bit所佔用的時間爲 1s/9600 = 104166ns。咱們傳輸的起始標誌位,傳輸的數據的每一位,校驗位(無關緊要),在9600波特率的狀況下,各自佔據了104166ns的時間。
因此,假設從MASTER發送8'b11001110給SLAVE的的話,數據線的電平變化以下:blog

因此,FPGA在接收串口數據的時候,按照每一個位的時間,設計計數器。假設是50MHz的時鐘,那麼接受一個bit須要的時鐘週期是
104166ns/20ns = 5208個週期,因此在計數器數到5208/2 +1的時候,將UART_RXD的當前值寄存便可。注意接受數據的時候先接受到的是起始位,最後的空閒位置就沒有必要接受了。接口

四、代碼
項目要求:電腦經過串口發送八位數據給FPGA,FPGA經過這八位數據來控制八個LED燈的亮暗。波特率9600,無校驗位。input

代碼以下:it

`module uart(
clk ,
rst_n ,
rx_uart ,
led
);
input clk ;
input rst_n ;
input rx_uart ;
output reg[7:0] led ;module

reg flag_add;
//波特率9600 1s/9600 = 104166ns
//50M時鐘,一個週期是20ns,104166ns/20ns=5208個時鐘週期
//用計數器來計數時鐘週期,每到5208從rx_uart取出一個數據
//一個數據包是八個數據+一個起始位數據sed

//計數器cnt0用來計數每一個bit的5208個週期
reg[12:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;im

always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = flag_add;
assign end_cnt0 = add_cnt0 && cnt0 == 5208 - 1 ;

//計數器1用來計數接受的bit數量
reg[3:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;

always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1 == 9 - 1;

//flag_add的定義,rx_uart降低沿的時候拉高,end_cnt1的時候拉低
//須要一個rx_uart邊沿檢測電路,注意,rx_uart是一個異步信號,與本地時鐘無關。
//能夠把rx_uart放進敏感列表,可是他會被FPGA誤認爲是個時鐘,形成電路不穩定
//安全的作法:用本地時鐘對rx_uart進行延一拍,而後先後兩拍比較完成邊沿檢測
//可是,由於本項目中的rx_uart是個異步信號,可能在時鐘邊沿變化,
//不能知足setup time 和 保持時間 因此就延兩拍更安全。(兩拍之後的信號是穩定信號)
reg rx_uart_ff0;
reg rx_uart_ff1;
reg rx_uart_ff2;
always@(posedge clk or negedge rst_n)
if(!rst_n)begin
rx_uart_ff0 <= 1;
rx_uart_ff1 <= 1;
rx_uart_ff2 <= 1;
end
else begin
rx_uart_ff0 <= rx_uart;
//rx_uart_ff0是異步信號打一拍的結果,不能作條件用。之後的能夠
rx_uart_ff1 <= rx_uart_ff0;
rx_uart_ff2 <= rx_uart_ff1;
end

always@(posedge clk or negedge rst_n)
if(!rst_n)begin
flag_add <= 0;
end
else if(!rx_uart_ff1 & rx_uart_ff2) begin//降低沿
flag_add <= 1;
end
else if(end_cnt1)
flag_add <= 0;

always@(posedge clk or negedge rst_n)
if(!rst_n)begin
led <= 8'hff;
end
else if(add_cnt0 && cnt0 == 5208/2-1 && cnt1>0)begin
led[cnt1-1] <= rx_uart_ff1;
end

endmodule`

相關文章
相關標籤/搜索