串行通信:指利用一條數(shù)據(jù)線將資料一位位的順序傳輸。
異步通信:以一個字符為傳輸單位,通信中兩個字符間的時間間隔是不固定的,然而在同一個字符的兩個相鄰位代碼間的時間間隔是固定的。
通信協(xié)議:指通信雙方約定的一些規(guī)則。在使用串口通信的時候,規(guī)定有:空閑位、起始位、數(shù)據(jù)位、奇偶校驗(yàn)位、停止位。
02串口通信時序
這個協(xié)議在 FPGA 內(nèi)部是除 SPI 之外最簡單的接口吧,其實(shí)就是發(fā)送方與接收方相互認(rèn)定的協(xié)議(暗號),這種接口數(shù)據(jù)一般是單向傳輸,所以發(fā)送方和接收方通信一般需要兩根數(shù)據(jù)線。
圖1 URAT時序圖
數(shù)據(jù)線在沒有數(shù)據(jù)傳輸時保持高電平,當(dāng)需要傳輸數(shù)據(jù)時,發(fā)送方把數(shù)據(jù)線拉低一段時間,告訴接收方開始傳輸數(shù)據(jù)了。之后把數(shù)據(jù)從低位到高位或者高位到低位(這個根據(jù)通信雙方的要求確定)依次發(fā)送給對方(數(shù)據(jù)的位數(shù)雙方應(yīng)該事先確認(rèn)好,通常5~8位數(shù)據(jù))。數(shù)據(jù)發(fā)送完,可能會發(fā)送一位奇偶校驗(yàn)(這部分在下一節(jié)構(gòu)建完整UART協(xié)議時細(xì)說)。最后就是將數(shù)據(jù)線拉高一段時間表示數(shù)據(jù)傳輸結(jié)束。
在這之間就會有疑問,每位數(shù)據(jù)電平持續(xù)時間到底是多久?
這就引出波特率,通常就是說每秒能傳輸多少位數(shù)據(jù),比如波特率為9600bit/s,就是指1秒傳輸9600位數(shù)據(jù)(當(dāng)然這是包含起始位,校驗(yàn)位,停止位在內(nèi)的,所以有效數(shù)據(jù)其實(shí)并沒有這么多)。當(dāng)使用該波特率時,那每個電平持續(xù)時間不就是1/9600秒么。
03串口接收模塊設(shè)計
首先確定模塊接口信號,肯定有個串口的輸入信號uart_rx吧,然后時鐘信號clk和復(fù)位信號rst_n也是不可能少的。接收到數(shù)據(jù)后肯定要輸出吧,所以在加一個uart_rx,注意該信號位寬應(yīng)該是可以改變的(因?yàn)榇趨f(xié)議的數(shù)據(jù)位可以改變)。一般還要有一個信號用于指示接收到的數(shù)據(jù)什么時候是有效的,便于后續(xù)模塊使用uart_rx,即uart_tx_vld(為高電平時,表示uart_rx有效)。
信號 | I/O | 位寬 | 含義 |
clk | I | 1 | 系統(tǒng)時鐘,50MHZ |
rst_n | I | 1 | 系統(tǒng)復(fù)位,低電平有效 |
uart_rx | I | 1 | UART接口輸入信號 |
rx_data | O | 8 | 數(shù)據(jù)輸出信號 |
rx_vld | O | 1 | 數(shù)據(jù)有效指示信號,高電平有效 |
模塊總體思路:有了輸入輸出信號后,模塊內(nèi)部就是根據(jù)輸入信號生成輸出信號而已。通過觀察圖1時序知道,每位數(shù)據(jù)傳輸需要使用 1/波特率 的時間,每次需要傳輸?shù)?“數(shù)據(jù)” 包括起始位,數(shù)據(jù)位,校驗(yàn)位,結(jié)束位。那么以上是不是就對應(yīng)兩個計數(shù)器?所以使用計數(shù)器data_num來計數(shù)一位數(shù)據(jù)傳輸需要的時間(需要將1/波特率轉(zhuǎn)換為系統(tǒng)時鐘個數(shù)作為data_num的結(jié)束條件),使用計數(shù)器cnt來計數(shù)目前傳輸?shù)牡趲孜粩?shù)據(jù)了。整體思路就是如此,大致如下圖,接下來就是細(xì)節(jié):
圖2 計數(shù)器架構(gòu)
計數(shù)器data_num該從什么時候計數(shù)?
當(dāng)發(fā)送方發(fā)送起始位時會把數(shù)據(jù)線拉低,并且在之后一段時間內(nèi)發(fā)送起始位,數(shù)據(jù)位等數(shù)據(jù),那么data_num在此期間都要計數(shù),直到停止位接收完成為止。由此引入一個標(biāo)志信號flag,該信號為高電平時,計數(shù)器data_num就計數(shù),當(dāng)計數(shù)到時鐘頻率/波特率(1/波特率對應(yīng)的時鐘個數(shù))時清零。
故計數(shù)器data_num初始值為0,計數(shù)條件add_data_num = flag,結(jié)束條件為end_data_num = add_data_num && data_num == BSP_NUM - 1。
flag當(dāng)然就是檢測到數(shù)據(jù)線下降沿時拉高,當(dāng)計數(shù)器cnt計數(shù)結(jié)束時拉低,其余時間保持不變了。
故flag拉高條件:檢測到uart_rx下降沿,拉低條件為end_cnt。
接下來就是計數(shù)器cnt了,cnt表示數(shù)據(jù)線此時傳輸?shù)氖堑趲孜粩?shù)據(jù)了。當(dāng)計數(shù)器data_num計數(shù)結(jié)束時,表示一位數(shù)據(jù)傳輸完成了,此時cnt就應(yīng)該加一了。當(dāng)計數(shù)器計數(shù)到 起始位數(shù)+數(shù)據(jù)位數(shù)+校驗(yàn)位數(shù)+停止位數(shù) 時表示數(shù)據(jù)傳輸完成了,此時cnt計數(shù)結(jié)束并清零,其余時間保持不變。
故計數(shù)器cnt初始值為0,計數(shù)條件add_cnt = end_data_num,計數(shù)器清零條件end_data_num = add_cnt && cnt == CNT_W - 1。CNT_W = 起始位數(shù)+數(shù)據(jù)位數(shù)+校驗(yàn)位數(shù)+停止位數(shù) 。
接下來就是接收數(shù)據(jù)并產(chǎn)生輸出信號了,一般會在計數(shù)器data_num計數(shù)的中部將數(shù)據(jù)線上的數(shù)據(jù)取下來進(jìn)行保存,此時的數(shù)據(jù)是比較穩(wěn)定的。由于最終需要輸出的只是數(shù)據(jù)位,本文不考慮校驗(yàn)位,傳輸?shù)?位是起始位,不需要保存。cnt==1時表示傳輸?shù)?位數(shù)據(jù),需要保存到輸出信號上的最低位(這是由于串口調(diào)試助手是先發(fā)的最低位,實(shí)際情況要看發(fā)送方先發(fā)高位還是低位)。
flag拉高后,計數(shù)器data_num進(jìn)行計數(shù),當(dāng)計數(shù)完一位數(shù)據(jù)后清零,并且cnt計數(shù)器進(jìn)行計數(shù),當(dāng)cnt大于等于1,小于等于8時,表示此時接收的是數(shù)據(jù)位,將接收到的數(shù)據(jù)保存到rx_data對應(yīng)位(最好是在data_num為容量的一半時進(jìn)行保存),當(dāng)cnt計數(shù)器計數(shù)完成,表示一組數(shù)據(jù)接收完成,此時有效指示信號拉高,并且flag信號拉低,結(jié)束一組數(shù)據(jù)的接收;所以當(dāng)cnt=1 && data_num == BSP_CNT/2-1時(BSP_CNT表示波特率對應(yīng)的時鐘個數(shù)),有rx_data[0] <= uart_rx。
經(jīng)過對其它位的詳細(xì)分析,最終會得到這樣的結(jié)果:當(dāng)cnt >=1 && cnt <= DATA_W && data_num == BSP_CNT/2-1 && add_data_num 時(DATA_W表示每次發(fā)送的數(shù)據(jù)位位數(shù)),rx_data[cnt - 1] <= uart_rx;這樣就產(chǎn)生了輸出數(shù)據(jù)信號。
之后就是產(chǎn)生輸出有效指示信號,該信號當(dāng)然是接收完數(shù)據(jù)時產(chǎn)生的,其實(shí)可以在計數(shù)器cnt計數(shù)結(jié)束時產(chǎn)生。但數(shù)據(jù)在接收完數(shù)據(jù)位后,其實(shí)數(shù)據(jù)就已經(jīng)接收完成了,此時就可以把輸出有效指示信號拉高了,這樣后續(xù)模塊就可以提前使用接收到的數(shù)據(jù)。所以當(dāng)cnt == DATA_W && add_data_num && data_num == BSP_NUM/2-1時將rx_data_vld拉高,其余時間拉低。
如果想要保證輸出數(shù)據(jù)線上數(shù)據(jù)比較干凈,不出現(xiàn)接收過程中的無效數(shù)據(jù),那么可以將rx_data和rx_data_vld在rx_data_vld有效時才進(jìn)行輸出,其余時間保持不變。
最后還要注意,數(shù)據(jù)線是其他芯片或者設(shè)備輸入的信號,為了減小亞穩(wěn)態(tài)出現(xiàn)的機(jī)率,一般需要將數(shù)據(jù)線上的信號通過寄存器寄存兩個時鐘。由于還需要檢測數(shù)據(jù)線的下降沿,所以還要把該信號延遲一個時鐘,最終將接收到的信號uart_rx打三拍(前兩拍用于同步處理,最后一拍用于檢測輸入信號的下降沿),然后通過uart_rx_ff1和uart_rx_ff2檢測出下降沿,把標(biāo)志信號flag拉高。
整體時序圖如下:
圖3 整體時序
時序圖可能在手機(jī)上沒法看,所以將上圖各個部分截圖放在下面:
圖4 準(zhǔn)備傳輸數(shù)據(jù)
當(dāng)計數(shù)器data_num=BSP_NUM/2-1的時候,將uart_rx_ff2的數(shù)據(jù)保存到rx_data的第cnt-1位,下圖為最低位,至于為什么是uart_rx_ff2,而不是uart_rx_ff1,通過下圖可知uart_rx_ff2與計數(shù)器data_num是對齊的,所以該信號會更準(zhǔn)。由于串口傳輸數(shù)據(jù)還是比較慢的,使用這兩個信號對結(jié)果基本沒有影響。
圖5 接收最低位數(shù)據(jù)
接收完8位數(shù)據(jù),將輸出使能信號拉高,rx_data的x表示不確定,因?yàn)閳D4~圖6只能確定接收的最高位和最低位數(shù)據(jù),其余時序沒有畫,中間的時序類似,所以省略雙波浪線表示中間的數(shù)據(jù)傳輸省略。
最后接收完停止位后,end_cnt拉高表示接收一次數(shù)據(jù)傳輸完成,將兩個計數(shù)器清零,并且將標(biāo)志信號flag拉低。
圖6 接收完8位數(shù)據(jù)
上述將模塊內(nèi)部信號講完了,如果要實(shí)現(xiàn)功能完全夠了,但是在調(diào)用模塊時,我們往往不習(xí)慣去改模塊內(nèi)部的參數(shù),這就需要通過parameter和localparam添加一些參數(shù),來自動設(shè)置計數(shù)器位寬,計數(shù)器結(jié)束條件等等。其實(shí)人為需要設(shè)置的就是波特率、數(shù)據(jù)位位數(shù)、校驗(yàn)位數(shù)、停止位數(shù)(起始位是必須的,故不考慮設(shè)置參數(shù)),由于計算波特率對應(yīng)是時鐘個數(shù)時還需要知道系統(tǒng)時鐘頻率,所以增加一個系統(tǒng)時鐘頻率參數(shù)。
所以parameter就定義波特率BPS、時鐘頻率FCLK、數(shù)據(jù)位數(shù)DATA_W、校驗(yàn)位數(shù)CHECK_W 、停止位數(shù)STOP_W 。而localparam需要通過parameter定義的參數(shù)得到波特率對應(yīng)的 時鐘數(shù)BPS_CNT=時鐘頻率FCLK/波特率BPS ,計數(shù)器data_num需要計數(shù)到BPS_CNT,所以需要通過BPS_CNT計算出計數(shù)器data_num的位寬BPS_CNT_W,可以通過以下函數(shù)實(shí)現(xiàn)。
function integer clogb2(input integer depth);begin if(depth==0) clogb2 = 1; else if(depth!=0) for(clogb2=0;depth>0;clogb2=clogb2+1) depth=depth>>1; end endfunction
接下來就是cnt計數(shù)器的結(jié)束條件了,可以由localparam定義CNT_NUM=DATA_W + CHECK_W + STOP_W。在利用上面函數(shù)計算出該計數(shù)器的位寬CNT_NUM_W就行了,內(nèi)部信號根據(jù)這些常量變化即可。
由此設(shè)計的模塊在例化時,只需要修改parameter的幾個常量即可,不要對模塊內(nèi)部代碼做任何處理,這部分操作不會占用額外資源,在綜合工具對齊進(jìn)行綜合時就會處理,不會消耗FPGA的除法器之類的資源。
根據(jù)以上分析,直接得到以下代碼,基本上不需要仿真調(diào)試。
04 參考代碼
//--############################################################################################### //--# Designer : 發(fā)送一位數(shù)據(jù)所需系統(tǒng)時鐘數(shù)計算方式BPS_CNT = 1000_000_000/(Tclk*比特率), //Tclk是系統(tǒng)時鐘周期,單位ns。 //--############################################################################################### module uart_rx #( parameter FCLK = 50_000_000 ,//系統(tǒng)時鐘頻率,默認(rèn)50MHZ; parameter BPS = 9600 ,//串口波特率; parameter DATA_W = 8 ,//接收數(shù)據(jù)位數(shù)以及輸出數(shù)據(jù)位寬; parameter CHECK_W = 0 ,//校驗(yàn)位,0代表無校驗(yàn)位; parameter STOP_W = 1 //1位停止位; )( input clk ,//系統(tǒng)工作時鐘50MHZ input rst_n ,//系統(tǒng)復(fù)位信號,低電平有效 input uart_rx ,//UART接口輸入信號 output reg [DATA_W-1:0] rx_out ,//數(shù)據(jù)輸出信號 output reg rx_out_vld //數(shù)據(jù)有效指示信號 ); localparam BPS_CNT = FCLK/BPS;//波特率為9600bit/s,當(dāng)波特率為115200bit/s時,DATA_115200==434; localparam BPS_CNT_W = clogb2(BPS_CNT-1);//根據(jù)BPS_CNT調(diào)用函數(shù)自動計算計數(shù)器data_num位寬; localparam CNT_NUM = DATA_W + CHECK_W + STOP_W;//計數(shù)器計數(shù)值; localparam CNT_NUM_W = clogb2(CNT_NUM);//根據(jù)計數(shù)器cnt的值,利用函數(shù)自動計算此計數(shù)器的位寬; reg rx_vld ;//表示接收完一組串口發(fā)來的數(shù)據(jù)了; reg uart_rx_ff0 ; reg uart_rx_ff1 ; reg uart_rx_ff2 ; reg flag ; reg [BPS_CNT_W-1:0] data_num ; reg [CNT_NUM_W-1:0] cnt ; reg [DATA_W-1:0] rx_data ; wire add_data_num ; wire end_data_num ; wire add_cnt ; wire end_cnt ; /******************注釋開始****************** 自動計算信號位寬; ******************注釋結(jié)束******************/ function integer clogb2(input integer depth);begin if(depth==0) clogb2 = 1; else if(depth!=0) for(clogb2=0;depth>0;clogb2=clogb2+1) depth=depth>>1; end endfunction /******************注釋開始****************** 接收一位數(shù)據(jù)所用時間計數(shù)器data_num,初始值為0,當(dāng)接收到數(shù)據(jù)時進(jìn)行計數(shù), 當(dāng)一位數(shù)據(jù)接收完成時清零; ******************注釋結(jié)束******************/ always@(posedge clk or negedge rst_n)begin if(!rst_n)begin data_num <= {{BPS_CNT_W}{1'b0}}; end else if(add_data_num)begin if(end_data_num) data_num <= {{BPS_CNT_W}{1'b0}}; else data_num <= data_num + {{{BPS_CNT_W-1}{1'b0}},1'b1}; end end assign add_data_num = flag; assign end_data_num = add_data_num && data_num==BPS_CNT-1; //接受一組數(shù)據(jù)所用時間; always@(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt <= {{CNT_NUM_W}{1'b0}}; end else if(add_cnt)begin if(end_cnt) cnt <= {{CNT_NUM_W}{1'b0}}; else cnt <= cnt + {{{CNT_NUM_W-1}{1'b0}},1'b1}; end end assign add_cnt = end_data_num; assign end_cnt = add_cnt && cnt== CNT_NUM-1; /******************注釋開始****************** PC端相對應(yīng)于FPGA為異步接口,為預(yù)防亞穩(wěn)態(tài)產(chǎn)生,對接收數(shù)據(jù)進(jìn)行打兩拍處理,由于需要采集信號下降沿,故打三拍處理; ******************注釋結(jié)束******************/ always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//三個寄存器組成移位寄存器,初始化為0; {uart_rx_ff2,uart_rx_ff1,uart_rx_ff0} <= 3'd0; end else begin//時鐘上升沿時,將uart_rx信號移入移位寄存器,其余位左移一位; {uart_rx_ff2,uart_rx_ff1,uart_rx_ff0} <= {uart_rx_ff1,uart_rx_ff0,uart_rx}; end end always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin flag <= 1'b0; end else if(uart_rx_ff2==1 && uart_rx_ff1==0)begin//取UART_RX信號下降沿 flag <= 1'b1; end else if(end_cnt)begin//一組數(shù)據(jù)接收完畢; flag <= 1'b0; end end //在中間時刻對輸入數(shù)據(jù)進(jìn)行采集,并且將數(shù)據(jù)存入rx_data; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_data <= {{DATA_W}{1'b0}}; end else if(cnt>=1 && cnt<=DATA_W && add_data_num && data_num==BPS_CNT/2-1)begin rx_data[cnt-1] <= uart_rx_ff2; end end //在接收完數(shù)據(jù)后,指示產(chǎn)生rx_data信號有效; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_vld <= 1'b0; end else begin rx_vld <= (cnt==CNT_NUM-1 && add_data_num && data_num==BPS_CNT/2-1); end end //當(dāng)接收完一組數(shù)據(jù)后,將接收到的數(shù)據(jù)經(jīng)過一組觸發(fā)器暫存后輸出; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin// rx_out <= 0; end else if(rx_vld)begin rx_out <= rx_data; end end //在接收完數(shù)據(jù)后,拉高一個時鐘,指示產(chǎn)生rx_out信號有效; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin rx_out_vld <= 1'b0; end else begin rx_out_vld <= rx_vld; end end endmodule05 modelism仿真
仿真部分的代碼,通過一個任務(wù)task實(shí)現(xiàn)串口數(shù)據(jù)的發(fā)送,由于上述設(shè)計不支持校驗(yàn)位,所以這個模塊設(shè)置校驗(yàn)位也是沒有用的。
發(fā)送數(shù)據(jù)只需要調(diào)用tx();任務(wù)即可,內(nèi)部直接輸入待發(fā)送數(shù)據(jù),數(shù)據(jù)位寬依舊通過DATA_W設(shè)置,波特率BPS設(shè)置。
參考代碼:
`timescale 1 ns/1 ns module uart_rx_test(); parameter CYCLE = 20;//The unit is ns. The default value is 10ns; parameter RST_TIME = 10;//Reset time: Reset 3 clock widths by default; parameter STOP_TIME = 1000;//Time for simulation running after reset (unit: clock cycle). Simulation stops after 1000 clocks are run by default; // uart_rx Parameters parameter FCLK = 50_000_000;//系統(tǒng)時鐘頻率; parameter BPS = 9600 ;//串口波特率; parameter BPS_CNT = FCLK/BPS ;//波特率對應(yīng)時鐘數(shù),不用手動修改該參數(shù); parameter DATA_W = 8 ;//接收數(shù)據(jù)位數(shù)以及輸出數(shù)據(jù)位寬; parameter CHECK_W = 2'b01 ;//校驗(yàn)位,2'b00代表無校驗(yàn)位,2'b01表示奇校驗(yàn),2'b10表示偶校驗(yàn),2'b11無效。 parameter STOP_W = 2'b11 ;//停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位; // uart_rx Inputs reg clk ; reg rst_n ; reg uart_tx ; // uart_rx Outputs wire [DATA_W-1:0] rx_out ; wire rx_out_vld ; //例化串口接收模塊; uart_rx #( .FCLK (FCLK ), .BPS (BPS ), .DATA_W (DATA_W ), .CHECK_W (CHECK_W ), .STOP_W (STOP_W )) u_uart_rx ( .clk ( clk ), .rst_n ( rst_n ), .uart_rx ( uart_tx ), .rx_out ( rx_out ), .rx_out_vld ( rx_out_vld ) ); //The local clock is generated at 100 MB; initial begin clk = 0; forever #(CYCLE/2) clk=~clk; end //Generate reset signal; initial begin rst_n = 1; #2; rst_n = 0; #(RST_TIME*CYCLE);//復(fù)位完成; rst_n = 1; end //Input signal din assignment method; initial begin #1;uart_tx = 1; //初始化時輸入高電平; #(100*CYCLE); //Start assigning values; tx(8'ha5); //以串口形式發(fā)送8'h5a; #(500*CYCLE); //發(fā)送完成后延遲500個時鐘; tx(8'h5a); //之后發(fā)送數(shù)據(jù)8'h59; #(500*CYCLE); //發(fā)送完成后延遲500個時鐘; $stop; //Stop simulation; end //模擬串口發(fā)送函數(shù),1位起始位,1位停止位,無校驗(yàn)位,8位數(shù)據(jù),先發(fā)低位; integer i;//用于控制循環(huán)次數(shù); task tx( input [DATA_W-1:0] data //串口待發(fā)送數(shù)據(jù); ); begin @(posedge clk);//延遲一個時鐘后發(fā)送起始位; #1; uart_tx = 1'b0; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個時鐘; for(i=0 ; i<8 ; i=i+1)begin #1; uart_tx = data[i]; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個時鐘; end if(CHECK_W == 2'b01)begin #1;uart_tx = ~(^data);//奇校驗(yàn)時,發(fā)送數(shù)據(jù); repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個時鐘; end else if(CHECK_W == 2'b10)begin #1;uart_tx = (^data);//偶校驗(yàn)時,發(fā)送數(shù)據(jù); repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個時鐘; end @(posedge clk);//延遲一個時鐘后發(fā)送停止位; #1; uart_tx = 1'b1; if(STOP_W == 2'b01)//1位停止位; repeat(BPS_CNT) @(posedge clk);//延遲BPS_CNT個時鐘; else if(STOP_W == 2'b10)//2位停止位; repeat(2*BPS_CNT) @(posedge clk);//延遲2*BPS_CNT個時鐘; else if(STOP_W == 2'b11)//1.5位停止位; repeat(BPS_CNT*3/2) @(posedge clk);//延遲1.5*BPS_CNT個時鐘; end endtask endmodule
仿真運(yùn)行結(jié)果(rx_out先接收到8’ha5,后接收到8’h5a):
圖7仿真結(jié)果
查看細(xì)節(jié):開始接收數(shù)據(jù)(起始位)片段:
圖8起始位仿真
接收最低位數(shù)據(jù)仿真如下:
圖9接收第一位數(shù)據(jù)
接收最后一位數(shù)據(jù),并且產(chǎn)生輸出有效指示信號,下一個時鐘將數(shù)據(jù)輸出,此時串口傳輸實(shí)際上并沒有完成,最后一位數(shù)據(jù)才傳輸一半(data_num計數(shù)器才2603==5208/2-1),但已經(jīng)接收到完整數(shù)據(jù),所以直接輸出,節(jié)省時間,但flag信號依舊位高電平,表示該模塊還處于工作狀態(tài)。
圖10接收完最后一位數(shù)據(jù)
計數(shù)器data_num計數(shù)到5208-1并且計數(shù)器cnt計數(shù)器到8,表示一次傳輸完成,flag信號拉低,并且兩個計數(shù)器清零,表示完成傳輸,仿真如下:
圖11接收完停止位
06 綜合測試
這個工程很久了,之前學(xué)的時候使用quartus綜合的,綜合效果如下所示:
圖12quartus綜合工程
對應(yīng)的RTL模塊視圖(由于時鐘頻率FCLK和波特率BPS參數(shù)設(shè)置會影響計數(shù)器cnt和data_num的位寬,所以不同數(shù)據(jù)匯總和出不同的電路,下圖為時鐘頻率50MHZ,波特率9600的RTL視圖):
圖13RTL視圖
對系統(tǒng)時鐘頻率進(jìn)行約束后,最大時鐘頻率為120.86MHZ,遠(yuǎn)大于實(shí)際的50MHZ,滿足時序要求;
圖14系統(tǒng)最大工作時鐘頻率
signal tap II 測試
將程序下載到FPGA,打開串口調(diào)試助手,設(shè)置波特率9600,發(fā)送數(shù)據(jù)0XA5,使用signal tap II抓取數(shù)據(jù)8'hA5。
圖15串口助手發(fā)送數(shù)據(jù)
串口調(diào)試助手發(fā)送數(shù)據(jù)0XB3,使用signal tap II抓取數(shù)據(jù)8'hB3。
圖16signal tap接收串口助手發(fā)送數(shù)據(jù)
串口調(diào)試助手發(fā)送數(shù)據(jù)0X5a,使用signal tap II抓取數(shù)據(jù)8'h5A。
圖17 調(diào)試
07總結(jié)
其實(shí)最主要的就是能夠根據(jù)協(xié)議找到合適的主架構(gòu),然后根據(jù)該架構(gòu)去產(chǎn)生輸出信號。
本文就利用兩個計數(shù)器作為主架構(gòu),根據(jù)計數(shù)器的狀態(tài)生成輸出信號,切記我們需要的并不是計數(shù)器,而是計數(shù)器生成的輸出信號,如果使用parameter要考慮模塊內(nèi)部各種會改變的數(shù)據(jù)與這些參數(shù)的關(guān)系。
最好不要留需要手動修改的數(shù)據(jù),這種數(shù)據(jù)如果忘記修改,會對后續(xù)設(shè)計造成很大影響,浪費(fèi)調(diào)試時間。
來源:本文轉(zhuǎn)載自數(shù)字站公眾號
-
FPGA
+關(guān)注
關(guān)注
1630文章
21799瀏覽量
606194 -
接收模塊
+關(guān)注
關(guān)注
1文章
21瀏覽量
10506 -
串口
+關(guān)注
關(guān)注
14文章
1559瀏覽量
77105 -
uart
+關(guān)注
關(guān)注
22文章
1244瀏覽量
101797 -
串行通信
+關(guān)注
關(guān)注
4文章
579瀏覽量
35601
原文標(biāo)題:基于FPGA的UART串口接收模塊
文章出處:【微信號:FPGA研究院,微信公眾號:FPGA研究院】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
求助:fpga接收串口命令并解析 ,如何才能實(shí)現(xiàn)?
基于FPGA的串行接收模塊的設(shè)計
用FPGA/CPLD設(shè)計UART
基于EasyFPGA030的串口接收顯示設(shè)計
FPGA與CPLD實(shí)現(xiàn)UART
![<b class='flag-5'>FPGA</b>與CPLD實(shí)現(xiàn)<b class='flag-5'>UART</b>](https://file.elecfans.com/web2/M00/49/3D/pYYBAGKhtECARB9FAAAQDfYs21M972.jpg)
uart是什么意思?認(rèn)識uart串口
![<b class='flag-5'>uart</b>是什么意思?認(rèn)識<b class='flag-5'>uart</b><b class='flag-5'>串口</b>](https://file1.elecfans.com//web2/M00/A7/08/wKgZomUMQfGAMlbDAAAPNXMvs1o824.jpg)
UART串口WiFi模塊的工作原理及應(yīng)用
![<b class='flag-5'>UART</b><b class='flag-5'>串口</b>WiFi<b class='flag-5'>模塊</b>的工作原理及應(yīng)用](https://file.elecfans.com/web1/M00/81/71/o4YBAFwzGCaAC8v4AABN2mEsE1M875.jpg)
UART串口WiFi模塊的工作原理及應(yīng)用
![<b class='flag-5'>UART</b><b class='flag-5'>串口</b>WiFi<b class='flag-5'>模塊</b>的工作原理及應(yīng)用](https://file.elecfans.com/web1/M00/82/06/pIYBAFw0fieAVZlAAAHQsCJYhIw979.png)
以FPGA為基礎(chǔ)的UART模塊的詳細(xì)設(shè)計方案
![以<b class='flag-5'>FPGA</b>為基礎(chǔ)的<b class='flag-5'>UART</b><b class='flag-5'>模塊</b>的詳細(xì)設(shè)計方案](https://file.elecfans.com/web1/M00/C0/9F/pIYBAF8EKjeAMwuxAACGdqjK_UM626.png)
使用FPGA和模塊化設(shè)計方法實(shí)現(xiàn)UART的設(shè)計論文
![使用<b class='flag-5'>FPGA</b>和<b class='flag-5'>模塊</b>化設(shè)計方法實(shí)現(xiàn)<b class='flag-5'>UART</b>的設(shè)計論文](https://file.elecfans.com/web1/M00/C0/35/o4YBAF8ET0aASM9mAADJLFALJkY828.png)
評論