FPGA-串列埠傳送模組
------------恢復內容開始------------
通用非同步收發傳輸器(Universal Asynchronous Receiver/Transmitter,UART)是一種非同步收發傳輸器,其在資料傳送時將並行資料轉換成序列資料來傳輸,在資料接收時將接收到的序列資料轉換成並行資料,可以實現全雙工傳輸和接收。它包括了 RS232、RS449、RS423、 RS422 和 RS485 等介面標準規範和匯流排標準規範。換句話說,UART 是非同步序列通訊的總稱。而 RS232、RS449、RS423、RS422 和 RS485 等,是對應各種非同步序列通訊口的介面標準和匯流排標準,它們規定了通訊口的電氣特性、傳輸速率、連線特性和介面的機械特性等內容。
圖1 串列埠傳送時序圖
圖2 串列埠傳送邏輯框圖
根據選擇的波特率計算出相應的波特率時鐘,如115200的波特率對應的波特率時鐘週期為8680ns,50M時鐘對應下時鐘計時數為433。波特率時鐘週期計時器為div_cnt。波特率時鐘計數器為bps_cnt,記錄波特率時鐘的個數。send_en為傳送標誌,當send_en時,開始傳送資料,資料傳送依據bps_cnt的數值來進行,由於串列埠傳送協議起始位之前需為1,因此bps_cnt=0時,uart_tx=1;bps_cnt=1-10時,uart_tx依次等於起始位、資料位、停止位,為了確保停止位保持一位的高電平,bps_cnt=11時,uart_tx=1,因此bps_cnt在0-11內迴圈,bps_cnt=11時立即清零。為了讓send_en訊號出現後能立即開始傳送資料,設定div_cnt每次為0時,bps_cnt就+1。十位資料傳送完成後,tx_done拉高,同時send_en拉低,標誌傳送完畢。
利用串列埠傳送模組實現一個每隔10ms,計數加1的計數器。計數從0開始,通過send_en和tx_done訊號配合,當count_10ms開始計數時,send_en訊號拉高,直到tx_done訊號到來時拉低,每當tx_done訊號到來時,計時數+1。
串列埠傳送模組程式碼:
module urat_tx( input wire clk , input wire rstn , inputwire [ 2:0] baudrate , input wire [ 7:0] data , input wire ena , output reg tx , output reg tx_done ); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Parameter & Signal +++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ reg [ 12:0] baud_clock ; reg [ 12:0] div_cnt ; reg [ 3:0] bps_cnt ; //reg ena ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Main code ++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ always @(*) begin //baudrate choose case (baudrate) 3'd0:baud_clock<=13'd433; 3'd1:baud_clock<=13'd867; 3'd2:baud_clock<=13'd1301; 3'd3:baud_clock<=13'd2603; 3'd4:baud_clock<=13'd5207; default: baud_clock<=13'd433; endcase end always @(posedge clk or negedge rstn) begin //div_cnt if(!rstn) div_cnt<=0; else if(ena)begin if(div_cnt==baud_clock) div_cnt<=0; else div_cnt<=div_cnt+1'b1; end else div_cnt<=0; end always @(posedge clk or negedge rstn) begin //bps_cnt if(!rstn) bps_cnt<=0; else if(ena)begin if(div_cnt==0) begin if(bps_cnt==11) bps_cnt<=0; else bps_cnt<=bps_cnt+1'b1; end end else bps_cnt<=0; end always @(posedge clk or negedge rstn) begin if(!rstn) tx<=0; else begin case (bps_cnt) 0:tx<=1; 1:tx<=0; 2:tx<=data[0]; 3:tx<=data[1]; 4:tx<=data[2]; 5:tx<=data[3]; 6:tx<=data[4]; 7:tx<=data[5]; 8:tx<=data[6]; 9:tx<=data[7]; 10:tx<=1; 11:tx<=1; default:tx<=1; endcase end end always @(posedge clk or negedge rstn) begin //tx_done if(!rstn) tx_done<=0; else if((bps_cnt==11)&&(div_cnt==1)) tx_done<=1; else tx_done<=0; end /*always @(posedge clk or negedge rstn) begin //ena if(!rstn) ena<=0; else if(bps_cnt==0) ena<=1; else if(tx_done) ena<=0; end*/ endmodule
每隔10ms計數器程式碼:
module uart_tx_count( clk, rstn, tx ); input clk ; input rstn ; output tx ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Parameter & Signal +++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ reg [ 7:0] data ; wire tx_done ; reg ena ; reg [ 18:0] count_10ms ; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++\ //++++++++++++++++ Main code ++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++/ urat_tx urat_tx_inst( .clk (clk ), .rstn (rstn ), .baudrate (3'd0 ), .data (data ), .ena (ena ), .tx (tx ), .tx_done (tx_done ) ); always @(posedge clk or negedge rstn) begin //count_10ms if(!rstn) count_10ms<=0; else if(count_10ms==499999) count_10ms<=0; else count_10ms<=count_10ms+1'b1; end always @(posedge clk or negedge rstn) begin //ena if(!rstn) ena<=0; else if(count_10ms==1) ena<=1; else if(tx_done) ena<=0; end always @(posedge clk or negedge rstn) begin if(!rstn) data<=0; else if(tx_done) data<=data+1'b1; end endmodule
testbench程式碼:
`timescale 1ns / 1ns module urat_tx_count_tb(); reg clk; reg rstn; wire tx; uart_tx_count u_uart_tx_count( .clk (clk ), .rstn (rstn ), .tx (tx ) ); initial clk=1; always#10 clk=!clk; initial begin rstn=0; #201; rstn=1; #10000000; $stop; end endmodule
Vivado模擬結果:
可以看到10ms計數週期中,只有傳送訊號時,ena訊號才為高電平,傳送結束標誌訊號tx_done到來後拉低,計數值加1。