天天看点

FPGA PL 串口UART通讯

串口通信虽然是一种比较早就问世的通讯方式,但是在工业控制中应用比较广泛,这里提供一种FPGA PL端的串口通信逻辑。

UART串口通信需要两根信号线来实现,一根用于串口发送,另外一根负责串口接收。

UART在发送或接收过程中的一帧数据由4部分组成,起始位、数据位、奇偶校验位和停止位。

其中,起始位标志着一帧数据的开始,必须要有;停止位标志着一帧数据的结束,可以选择,一般选择一位停止位;数据位是一帧数据中的有效数据,一般是八位;校验位分为奇校验和偶校验,用于检验数据在传输过程中是否出错,一般不用;停止位也是必须有的,发送结束后拉高电平。

串口通信的速率用波特率表示,它表示每秒传输二进制数据的位数,单位是bps(位/秒),常用的波特率有9600、19200、38400、57600以及115200 等。

这里的FPGA串口发送逻辑由三个基本模块构成:时钟模块、发送模块和接收模块。

时钟模块按照要求的通讯波特率,根据板载晶振,产生采样时钟,为串口收发提供时钟;接收模块在时钟模块的驱动下按照固定频率读取接收引脚的电平状态,当读到低电平,也就是起始位的时候,开始连续读八次,然后转入等待读取状态;发送模块在发送信号以后,从发送引脚发出起始位+8个数据位+一个停止位。

时钟模块:

module clk_div(
    input   sys_clk,
    input   sys_rst_n,
    output  clk_out  
    );
reg clk_out_reg;
parameter   BAUD_RATE   =   115200;  
parameter   CRYSTAL     =   50_000_000;
parameter   CLK_DIV     =   CRYSTAL/BAUD_RATE;
reg     [15:0]  counter;
always@(posedge sys_clk ,negedge sys_rst_n)begin
    if(!sys_rst_n)
        counter <=  16'd0;
    else    begin
        if(counter  ==  CLK_DIV) begin
            counter <=  16'd0;
            clk_out_reg <=  1'd1;
        end
        else    begin
            counter <= counter  +1'd1;
            clk_out_reg <=  1'd0;
        end
    end
end
    
assign    clk_out   =     clk_out_reg;
endmodule
           

接收模块:

module uart_rx(
    input           uart_clk,
    input           sys_rst_n,
    input           uart_rx,
    output          receive_end,
    output  [7:0]   receive_data
    );
reg [7:0]   data_i;
localparam  S0  =   2'b00;          //空闲状态
localparam  S1  =   2'b01;          //接收状态
localparam  S2  =   2'b10;          //接收完成

reg [1:0]   cur_st,nxt_st;
reg [3:0]   count;
always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        cur_st  <=  S0;
    else    begin
        cur_st  <=  nxt_st;
    end
end
always@(*)begin
    nxt_st  =   cur_st;
    case(cur_st)
        S0:if(!uart_rx)
            nxt_st  =   S1;
        S1:if(count ==  7)
            nxt_st  =   S2;
        S2:
            nxt_st  =   S0;
    default:nxt_st  =   S0;
    endcase
end

always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        count   <=  4'd0;
    else    begin
        if(cur_st   ==  S1)
            count   <=  count   +1'b1;
        else
            count   <=  4'd0;
    end
end


always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        data_i  <=  8'd0;
    else
        if(cur_st   ==  S1)begin 
            data_i[6:0] <=  data_i[7:1];
            data_i[7]   <=  uart_rx; 
        end
end
assign  receive_end     =   (cur_st   ==  S2)?1:0;
assign  receive_data    =   data_i;
endmodule
           

发送模块:

module uart_tx(
    input           uart_clk,
    input           sys_rst_n,
    input   [7:0]   data_in,
    input           send_signal,
    output          uart_tx 
    );
reg [1:0]   cur_st,nxt_st;
localparam  S0  =   2'b00;          //空闲状态
localparam  S1  =   2'b01;          //发送状态
localparam  S2  =   2'b10;          //发送完成
reg [3:0]   count;
reg [7:0]   data_temp;
reg uart_tx_reg;
always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        cur_st  <=  S0;
    else    begin
        cur_st  <=  nxt_st;
    end
end
always@(*)begin
    nxt_st  =   cur_st;
    case(cur_st)
        S0:if(send_signal)
            nxt_st  =   S1;
        S1:
            nxt_st  =   S2;
        S2:if(count ==  7)
            nxt_st  =   S0;
    default:nxt_st  =   S0;
    endcase
end
always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        count   <=  4'd0; 
    else    begin
        if(cur_st   ==  S2)
            count   <=  count   +1'b1;
        else
            count   <=  4'd0;
    end
end


always@(posedge uart_clk,negedge sys_rst_n)begin
    if(!sys_rst_n)
        data_temp  <=  8'd0;
    else
        if(cur_st   ==  S1)begin 
            uart_tx_reg <=  1'b0;
            data_temp   <=  data_in; 
        end
        else    if(cur_st   ==  S2)begin
            uart_tx_reg <=  data_temp[0];
            data_temp[6:0]  <=  data_temp[7:1];
        end
        else    if(cur_st   ==  S0)
            uart_tx_reg <=  1'b1;
end
assign  uart_tx =   uart_tx_reg;
endmodule
           

最后是顶层模块:

module uart_top(
    input   sys_clk,
    input   sys_rst_n,
    input   uart_rx,
    output  uart_tx
    );
wire            uart_clk;
wire            signal;
wire    [7:0]   data;
clk_div u_clk_div(
    .sys_clk            (sys_clk),
    .sys_rst_n          (sys_rst_n),
    .clk_out            (uart_clk)
    );    
    
uart_rx u_uart_rx(
    .uart_clk           (uart_clk),
    .sys_rst_n          (sys_rst_n),
    .uart_rx            (uart_rx),
    .receive_end        (signal),
    .receive_data       (data)
    );  
    
uart_tx u_uart_tx(
    .uart_clk           (uart_clk),
    .sys_rst_n          (sys_rst_n),
    .data_in            (data),
    .send_signal        (signal),
    .uart_tx            (uart_tx)
);     

endmodule
           

补充状态转移图:

FPGA PL 串口UART通讯

继续阅读