1. 程式人生 > 其它 >FPGA-串列埠傳送模組

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                       ,
    input
wire [ 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。