5. 串口接收数据——基于FPGA的串口发送数据实验

发布时间 2023-09-27 16:53:40作者: daqiaobugong

1. 串口接收原理与思路

1.1 基本原理

  • 采样:每位数据采多次,统计高低电平出现的次数,次数多的就是该位的电平值
  • 起始位检测:边沿检测,使用两个计数器来判断Bps_Clk的下降沿/上升沿(前一个时钟上升沿为高电平/低电平,后一个时钟上升沿为低电平/高电平),两个触发器即可设计得到。
  • 新语法:
    reg [2:0]R_Data[7:0],意为声明8个数的数组,每个数的位数为3位。

1.2 设计

附新语法:reg [2:0]R_Data[9:0],二维数组,每个位宽为3位。

  • 使用的变量

    • Clk 时钟
    • Reset_N 复位
    • Baud_Set 波特率
    • Uart_Rx 接收到的数据
    • Data 显示接收到的数据
    • Rx_Done 接收完成
  1. 两个D触发器检测边沿:[1:0]Uart_Rx_R。用来判断Uart_Rx的上升沿(Pedge_Uart_Rx)和下降沿(Nedge_Uart_Rx)。
    reg [1:0]Uart_Rx_R;
    always@(posedge Clk)begin
        Uart_Rx_R[0] <= Uart_Rx;
        Uart_Rx_R[1] <= Uart_Rx_R[0];
    end
    wire Pedge_Uart_Rx;
    assign Pedge_Uart_Rx = (Uart_Rx_R == 2'b01);
    wire Nedge_Uart_Rx;
    assign Nedge_Uart_Rx = (Uart_Rx_R == 2'b10);
  1. 把每次接收的信号位分成16份(一次性接收10位则有160份),把每份中间的计数电平作为时钟Bps_Clk_16x信号。时钟信号设计:
  • 既然每一个电平信号的中间作为计数时刻,那么需要一个计数器Div_Cnt循环计数Bps_Dr - 1(一个位的计数值),每计数到一半产生一个电平信号。
  • 计数器开始计数,需要一个控制信号Rx_En进行控制,当接收信号的第一个位到来时开始计数。
    wire Bps_Clk_16x;
    assign Bps_Clk_16x = (Div_Cnt == Bps_Dr / 2);
    
    reg Rx_En;
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)
        Rx_En <= 0;
    else if(Nedge_Uart_Rx)
        Rx_En <= 1'b1;
    else if(Rx_Done || (Sta_Bit >= 4))//发送结束和接收的起始电平异常
        Rx_En <= 0;

    reg [8:0]Div_Cnt;
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)
        Div_Cnt <= 0;
    else if(Rx_En)begin
        if(Div_Cnt == Bps_Dr - 1)
            Div_Cnt <= 0;
        else
            Div_Cnt <= Div_Cnt + 1'b1;
    end
    else
        Div_Cnt <= 0;
  1. 接收过程的设计
  • 首先设计160的计数器Bps_Cnt,以时钟Bps_Clk_16x为计数时钟(接收的10个位综合考虑)。注:计算160个间隔,需要161个点。因此计数值选择0-160。
reg [8:0]Bps_Cnt;
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)
        Bps_Cnt <= 0;
    else if(Rx_En)
        if(Bps_Clk_16x)
            if(Bps_Cnt == 160)
                Bps_Cnt <= 0;
            else
                Bps_Cnt <= Bps_Cnt + 1'b1;
        else
            Bps_Cnt <= Bps_Cnt;
    else
        Bps_Cnt <= 0;
  • 在特定计数值处接收数据。累加每个接收位的中间七个间隔的电平,如果计数值大于4则为高电平。
    reg [2:0]R_Data[7:0];
    reg [2:0]Sta_Bit;
    reg [2:0]Sto_Bit;
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)begin
        Sta_Bit <= 0;
        R_Data[0] <= 0;
        R_Data[1] <= 0;
        R_Data[2] <= 0;
        R_Data[3] <= 0;
        R_Data[4] <= 0;
        R_Data[5] <= 0;
        R_Data[6] <= 0;
        R_Data[7] <= 0;
        Sto_Bit <= 0;
    end
    else
        case(Bps_Cnt)
        0: begin
        Sta_Bit <= 0;
        R_Data[0] <= 0;
        R_Data[1] <= 0;
        R_Data[2] <= 0;
        R_Data[3] <= 0;
        R_Data[4] <= 0;
        R_Data[5] <= 0;
        R_Data[6] <= 0;
        R_Data[7] <= 0;
        Sto_Bit <= 0;
        end
        5,6,7,8,9,10,11: Sta_Bit <= Sta_Bit + Uart_Rx;
        21,22,23,24,25,26,27: R_Data[0] <= R_Data[0] + Uart_Rx;
        37,38,39,40,41,42,43: R_Data[1] <= R_Data[1] + Uart_Rx;
        53,54,55,56,57,58,59: R_Data[2] <= R_Data[2] + Uart_Rx;
        69,70,71,72,73,74,75: R_Data[3] <= R_Data[3] + Uart_Rx;
        85,86,87,88,89,90,91: R_Data[4] <= R_Data[4] + Uart_Rx;
        101,102,103,104,105,106,107: R_Data[5] <= R_Data[5] + Uart_Rx;
        117,118,119,120,121,122,123: R_Data[6] <= R_Data[6] + Uart_Rx;
        133,134,135,136,137,138,139: R_Data[7] <= R_Data[7] + Uart_Rx;
        149,150,151,152,153,154,155: Sto_Bit <= Sto_Bit + Uart_Rx;
        default:;
        endcase
    
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)
        Data <= 0;
    else if(Bps_Clk_16x && (Bps_Cnt == 160))begin
//        Data[0] <= (R_Data[0] >= 4)?1:0;
//        Data[1] <= (R_Data[1] >= 4)?1:0;
//        Data[2] <= (R_Data[2] >= 4)?1:0;
//        Data[3] <= (R_Data[3] >= 4)?1:0;
//        Data[4] <= (R_Data[4] >= 4)?1:0;
//        Data[5] <= (R_Data[5] >= 4)?1:0;
//        Data[6] <= (R_Data[6] >= 4)?1:0;
//        Data[7] <= (R_Data[7] >= 4)?1:0;
        Data[0] <= R_Data[0][2];
        Data[1] <= R_Data[1][2];
        Data[2] <= R_Data[2][2];
        Data[3] <= R_Data[3][2];
        Data[4] <= R_Data[4][2];
        Data[5] <= R_Data[5][2];
        Data[6] <= R_Data[6][2];
        Data[7] <= R_Data[7][2];
    end
    always@(posedge Clk or negedge Reset_N)
    if(!Reset_N)
        Rx_Done <= 0;
    else if(Bps_Clk_16x && (Bps_Cnt == 160))
        Rx_Done = 1;
    else
        Rx_Done <= 0;
  1. 仿真文件
  • 新语法
    task Uart_Tx_Byte;//变量名
        input [7:0]Tx_Data;//参数
        begin
            Uart_Rx = 1;
            #20;
            Uart_Rx = 0;
            #8680;
            ···
            Uart_Rx = 1;
            #8680;
        end
    endtask
`timescale 1ns / 1ns
module Uart_Byte_Rx_tb;

    reg Clk;
    reg Reset_N;
    reg Uart_Rx;
    wire [7:0]Data;
    wire Rx_Done;

    Uart_Byte_Rx Uart_Byte_Rx(
        .Clk(Clk),
        .Reset_N(Reset_N),
        .Baud_Set(4),
        .Uart_Rx(Uart_Rx),
        .Data(Data),
        .Rx_Done(Rx_Done)
        );
    
    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_N = 0;
        Uart_Rx = 1;
        #201;
        Reset_N = 1;
        #200;
        Uart_Tx_Byte(8'h5a);
//        @(posedge Rx_Done);
        #90000;
        Uart_Tx_Byte(8'ha5);
//        @(posedge Rx_Done);
        #90000;
        Uart_Tx_Byte(8'h86);
//        @(posedge Rx_Done);
        #90000;
        $stop;
    end

    task Uart_Tx_Byte;
        input [7:0]Tx_Data;
        begin
            Uart_Rx = 1;
            #20;
            Uart_Rx = 0;
            #8680;
            Uart_Rx = Tx_Data[0];
            #8680;
            Uart_Rx = Tx_Data[1];
            #8680;
            Uart_Rx = Tx_Data[2];
            #8680;
            Uart_Rx = Tx_Data[3];
            #8680;
            Uart_Rx = Tx_Data[4];
            #8680;
            Uart_Rx = Tx_Data[5];
            #8680;
            Uart_Rx = Tx_Data[6];
            #8680;
            Uart_Rx = Tx_Data[7];
            #8680;
            Uart_Rx = 1;
            #8680;
        end
    endtask

endmodule

1.3 仿真结果

  • 仿真中出现的问题
    时间无法匹配,tb文件中不要等到Rx_Done信号到来为结束标志。在仿真文件的接收中,一个位的接收时间为540*16=8640ns,而接收信号Uart_Rx位的持续时间则为8680ns。可能会产生在没有接收完成时,后续Rx_Done信号已经到来的情况。

图1-1接收仿真