基於FPGA的UART接口實現

串行通訊是指外部設備與計算機間只使用一根數據線(另外須要地線,還可能須要控制線)進行數據傳送的方式。數據在一根數據線上按位依次傳送。因爲CPU與接口之間按並行方式傳輸,接口與外設之間按串行方式傳輸,所以串行通訊的基本功能是:在發送時,把CPU送來的並行數據轉換爲串行數據,逐位依次發送出去;在接受時,把外部設備發送過來的的串行數據逐位地接受,組裝成並行數據,並行地送給CPU去處理。異步

異步通訊是以一個字符做爲傳輸方式,通訊中兩個字符間的時間間隔多少不固定,但同一個字符的兩個相鄰位的時間間隔固定。通訊過程當中沒有時鐘線。code

UART使用的是串行異步通訊。blog

基本的UART通訊協議:接口

波特率:表示每秒鐘傳輸字符數的多少input

起始位:通常爲邏輯「0」it

數據位:通常位6~8位之間可變,數據低位在前,高位在後class

校驗位:能夠爲無校驗位,奇校驗,偶校驗,常「0」或常「1」之間可選module

中止位:必須爲邏輯「1」sed

空閒位:處於邏輯「1」狀態並行

 

UART發送功能模塊:

`define     NONE    0       //無校驗位
`define     ODD     1       //奇校驗
`define     EVEN    2       //偶校驗
`define     MARK    3       //常「1」
`define     SPACE   4       //常「0」

`define     STOPBITS_ONE     0      //1位中止位
`define     STOPBITS_ONEHALF 1      //1.5位中止位
`define     STOPBITS_TWO     2      //2位中止位
module uart_tx  #(
parameter   CLK_FREQ = 50_000_000,          //時鐘頻率
parameter   BPS = 9600,                     //波特率
parameter   PARITY = `NONE,                 //數據位寬
parameter   DATA_BITS = 8,                  //校驗位
parameter   STOP_BITS = `STOPBITS_ONE       //中止位位寬
)
(
input                       clk,
input                       rst_n,
input       [DATA_BITS-1:0]  data,
input                       enable,     //定義一個發送使能信號

output      reg             tx,
output      wire            free        //定義一個發送端空閒狀態
    );
reg     [DATA_BITS-1:0]  data_2 = 0;
reg     flag = 0;                           //定義一個flag信號來鎖住enable
wire    stop_flag;                          //定義一個傳輸中止脈衝
reg     [31:0]  cnt = 0;
localparam  [31:0]  CNT_MAX =  CLK_FREQ/BPS;
reg     [2:0]   cstate = 0;
localparam  [2:0]
            FSM_IDEL    = 0,                //空閒位
            FSM_START   = 1,                //開始位
            FSM_DATA    = 2,                //數據位
            FSM_PARITY  = 3,                //校驗位
            FSM_STOP    = 4;                //中止位
reg     ifparity = 0;                       //有無校驗位
reg     parity_value = 0;                   //校驗位的值 
reg     [3:0]   num = 0;                    //定義一個計數num
           
assign  free = ~flag;                       
assign  stop_flag = (cstate == FSM_STOP && ((STOP_BITS == `STOPBITS_ONE && cnt == CNT_MAX-1) || (STOP_BITS == `STOPBITS_ONEHALF && num == 1 && cnt == CNT_MAX>>1) || (STOP_BITS == `STOPBITS_TWO && num == 1 && cnt == CNT_MAX-1))) ? 1'b1 : 1'b0;

always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        data_2 <= 0;
    else    if  (enable && flag == 0)
        data_2 <= data;
    else    data_2 <= data_2;


always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)       
        flag <= 0;
    else    if  (enable)
        flag <= 1;
    else    if  (stop_flag)
        flag <= 0;
    else    flag <= flag;
    
   
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        cnt <= 0;
    else    if  (cnt == CNT_MAX-1 || flag == 0 )
        cnt <= 0;
    else    if  (flag)
        cnt <= cnt + 1;
    else    cnt <= cnt;
    
    
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        cstate <= FSM_IDEL; 
    else    case    (cstate)
                FSM_IDEL    :   begin
                                    tx <= 1;
                                    if  (flag)
                                        cstate <= FSM_START;
                                    else    cstate <= FSM_IDEL;
                                end
                FSM_START   :   begin
                                    tx <= 0;
                                    if  (cnt == CNT_MAX-1)
                                        cstate <= FSM_DATA;
                                    else    cstate <= FSM_START;      
                                end
                FSM_DATA    :   begin
                                    tx <= data_2 [num];
                                    if  (cnt == CNT_MAX-1 && num == DATA_BITS-1)
                                        begin
                                            num <= 0;
                                            cstate <= ifparity ? FSM_PARITY : FSM_STOP;
                                        end
                                    else    if  (cnt == CNT_MAX-1)
                                        num <= num + 1;
                                    else    num <= num;
                                end
                FSM_PARITY  :   begin
                                    tx <=  parity_value;
                                    if  (cnt == CNT_MAX-1)
                                         cstate <= FSM_STOP;
                                    else    cstate <= FSM_PARITY;
                                end
                FSM_STOP    :   begin
                                    tx <= 1;
                                    if  (STOP_BITS == `STOPBITS_ONE && cnt == CNT_MAX-1)
                                          cstate <= FSM_IDEL;
                                    else    if  (STOP_BITS == `STOPBITS_ONEHALF)  begin  
                                        if  (num == 1 && cnt == CNT_MAX>>1)
                                            begin   cstate <= FSM_IDEL; num <= 0; end
                                        else    if  (cnt == CNT_MAX-1)
                                            num <=1;
                                        else  cstate <= FSM_STOP;
                                    end
                                    else    if  (STOP_BITS == `STOPBITS_TWO)  begin
                                        if   (num == 1 && cnt == CNT_MAX-1)
                                            begin   cstate <= FSM_IDEL; num <= 0; end
                                        else    if  (cnt == CNT_MAX-1)
                                            num <=1;
                                        else  cstate <= FSM_STOP;
                                    end                                      
                                end
            default         :   cstate <= FSM_IDEL; 
            endcase
            
                
                
always  @(*)
    if  (!rst_n)
        begin
           ifparity = 0;    
           parity_value = 0; 
        end
    else    case    (PARITY)
            `NONE    :   begin
                            ifparity = 0;     
                            parity_value = 0;  
                         end
            `ODD     :   begin
                            ifparity = 1;     
                            parity_value = ~(^data_2);   
                         end  
            `EVEN    :   begin
                            ifparity = 1;     
                            parity_value = ^data_2;  
                         end  
            `MARK    :   begin
                            ifparity = 1;     
                            parity_value = 1; 
                         end  
            `SPACE   :   begin
                            ifparity = 1;     
                            parity_value = 0;
                         end
            default :    begin
                            ifparity = 0;    
                            parity_value = 0; 
                         end  
            endcase

UART接收功能模塊:

`define     NONE    0       //無校驗位
`define     ODD     1       //奇校驗
`define     EVEN    2       //偶校驗
`define     MARK    3       //常"1"
`define     SPACE   4       //常"0"

`define     STOPBITS_ONE        0       //1位中止位
`define     STOPBITS_ONEHALF    1       //1.5位中止位
`define     STOPBITS_TWO        2       //2位中止位
module uart_rx  #(
parameter   BPS = 9600,                 //波特率
parameter   CLK_FREQ = 50_000_000,      //時鐘頻率
parameter   DATA_BITS = 8,              //數據位寬
parameter   PARITY = `NONE,             //校驗位
parameter   STOP_BITS = `STOPBITS_ONE   //中止位位寬
)
(
input       clk,
input       rst_n,
input       rx,

output      reg    [DATA_BITS-1:0] data,
output      reg     done,
output      reg     err
    );

    
reg     [1:0]   in;
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        in <= 0;
    else    in <= {in[0],rx};
    
wire    en;
assign  en = (in[1] == 1 && in[0] == 0) ? 1 : 0;

reg     flag = 0;                               //定義一個接收信號標誌位  
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        flag <= 0;
    else    if  (en)
        flag <= 1;
    else    if  (cstate == FSM_STOP && sampling)
        flag <= 0;
    else    flag <= flag;
    
reg     [31:0]  cnt = 0;
localparam  [31:0]  CNT_MAX = CLK_FREQ/BPS;
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        cnt <= 0;
    else    if  (cnt == CNT_MAX-1 || flag == 0)
        cnt <= 0;
    else    if  (flag)
        cnt <= cnt + 1;
    else    cnt <= cnt;
    
reg     sampling = 0;                               //定義一個採樣脈衝信號     
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)
        sampling = 0;
    else    if  (cnt == CNT_MAX>>1)
        sampling = 1;
    else     sampling = 0;
    
    
reg     [3:0]   num = 0;                                //定位計數num
reg     ifparity = 0;                                   //有無校驗位
reg     parity_value = 0;                               //定義校驗位的值
reg     parity_rx = 0;                                  //定義接受信號中校驗位的值
reg     [2:0]   cstate = 0;
localparam  [2:0]
            FSM_IDEL    = 0,                            //空閒位
            FSM_START   = 1,                            //開始位
            FSM_DATA    = 2,                            //數據位
            FSM_PARITY  = 3,                            //校驗位
            FSM_STOP    = 4;                            //中止位
            
always  @(posedge   clk or  negedge rst_n)
    if  (!rst_n)    begin
        cstate <= FSM_IDEL;
        parity_rx <= 0;
        done <= 0;
        err <= 0;
        data <= 0;       
        num <= 0;
    end
    else    case    (cstate)
                FSM_IDEL    :   if  (flag)
                                    cstate <= FSM_START;
                                else    cstate <= FSM_IDEL;           
                FSM_START   :   begin
                                    done <= 0;
                                    if  (sampling)
                                        cstate <= FSM_DATA;
                                    else    cstate <= FSM_START;
                                end          
                FSM_DATA    :   if  (sampling && num <= DATA_BITS-1)
                                    begin
                                        data[num] <= rx;
                                        num <= num + 1;    
                                    end
                                else    if  (num == DATA_BITS)    begin
                                    num <= 0;
                                    cstate <= ifparity ? FSM_PARITY : FSM_STOP ;
                                end
                FSM_PARITY  :   if  (sampling)
                                    begin
                                        parity_rx <= rx;
                                        cstate <= FSM_STOP;
                                    end
                                else    cstate <= FSM_PARITY;
                FSM_STOP    :   if  (sampling)
                                    begin
                                        done <= 1;
                                        cstate <= FSM_IDEL;
                                        err <= ifparity ? (parity_rx == parity_value ? 0 : 1) : 0;
                                    end
                                else    cstate <= FSM_STOP;
            default         :   begin
                                    cstate <= FSM_IDEL;
                                    parity_rx <= 0;
                                    done <= 0;
                                    err <= 0;       
                                end
            endcase
                
    
always  @(*)
    if  (!rst_n)    begin
        ifparity = 0;
        parity_value = 0;
    end
    else    case    (PARITY)
                `NONE    :  begin   ifparity = 0; parity_value = 0; end         
                `ODD     :  begin   ifparity = 1; parity_value = ~(^data);  end  
                `EVEN    :  begin   ifparity = 1; parity_value = ^data;  end
                `MARK    :  begin   ifparity = 1; parity_value = 1;  end 
                `SPACE   :  begin   ifparity = 1; parity_value = 0;  end
             default     :  begin   ifparity = 0; parity_value = 0; end
             endcase
    
endmodule
相關文章
相關標籤/搜索