基于Vivado的FPGA学习笔记八之从串口接收

一、设计思路

1、怎么检测起始位?

a.采用两个D触发器进行边缘检测

       

4405a565397f442eb0fef9528551f11d.jpeg

截自小梅哥FPGA文档

        

2、采样时钟和采样频率应该怎么设计?

a.将一位数据分为16份,采样频率为波特率的16倍,假定波特率为115200,则计数器计数周期为

bps_DR:1_000_000_000 / 115200 / 20(时钟频率为50HZ) / 16

每次系统时钟上升沿到来,div_cnt开始计数,计数到bps_DR清零

b.每次采样都在一个计数周期内的中心点进行采样

assign bps_clk = (div_cnt == (bps_DR / 2))1?0

3、怎么判断接收到的数据是1还是0?

a.每次检测到bps_clk上升沿的时候,将采样的数据存在变量bps_cnt中(1位起始位8位数据位1位停止位,每位数据分成16份,所以bps_cnt一共160个数据)

b9ed88bb86aa4628b075624d59e5b56f.jpeg

截自小梅哥FPGA文档

b.起始位:将bps_cnt中的6、7、8、 9、 10、 11六位数据进行相加,结果一共有7种可能性:0,1,2,3,4,5,6。结果大于3认为是数据1,否则认为是数据0。数据位和停止位处理办法一样。

二、代码实现

`timescale 1ns / 1ps

module my_uart_rx_byte(
    Clk,
    Reset_n,
    Uart_rx,
    Baud_set,
    Rx_byte,
    Rx_done
);
    input Clk;
    input Reset_n;
    input Uart_rx;
    input [2:0]Baud_set;
    output reg[7:0]Rx_byte;
    output reg Rx_done;
    
    reg[1:0]r_rx_data;
    wire pos_edge;
    wire neg_edge;
    reg [8:0]bps_DR;
    reg Rx_EN;
    reg [8:0]bps_div;
    wire bps_clk;
    reg [7:0]r_rx_num;
    reg [2:0]start_bit;
    reg [2:0]Data_bit[7:0];
    reg [2:0]stop_bit;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        r_rx_data <= 2'd0;
    else begin
        r_rx_data[0] <= Uart_rx;
        r_rx_data[1] <= r_rx_data[0];
    end
    
    assign pos_edge = (r_rx_data == 2'b01)?1:0;
    assign neg_edge = (r_rx_data == 2'b10)?1:0;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bps_DR <= 9'd324;
    else begin
        case(Baud_set)
            0:bps_DR <= 9'd324;
            1:bps_DR <= 9'd162;
            2:bps_DR <= 9'd80;
            3:bps_DR <= 9'd53;
            4:bps_DR <= 9'd26;
            default:bps_DR <= 9'd324;
        endcase
    end
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_EN <= 1'd0;
    else if((neg_edge == 1) && (start_bit < 3'd4))
        Rx_EN <= 1'b1;
    else if((Rx_done) || (start_bit >= 3'd4))
        Rx_EN <= 1'd0;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        bps_div <= 9'd0;
    else if(Rx_EN)begin
        if(bps_div == bps_DR)
            bps_div <= 9'd0;
        else 
            bps_div <= bps_div + 1'b1;
    end
    else
        bps_div <= 9'd0;

    assign bps_clk = (bps_div == (bps_DR/2))?1:0;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n) 
        r_rx_num <= 8'd0;
    else if(Rx_EN)begin
        if(bps_clk)begin
            if(r_rx_num == 8'd159)
                r_rx_num <= 8'd0;
            else 
                r_rx_num <= r_rx_num + 1'b1;
        end
    end
    else
        r_rx_num <= 0;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        start_bit <= 3'd0;
        Data_bit[0] <= 3'd0;
        Data_bit[1] <= 3'd0;
        Data_bit[2] <= 3'd0;
        Data_bit[3] <= 3'd0;
        Data_bit[4] <= 3'd0;
        Data_bit[5] <= 3'd0;
        Data_bit[6] <= 3'd0;
        Data_bit[7] <= 3'd0;
        stop_bit <= 3'd0;
    end
    else if(bps_clk)begin
        case(r_rx_num)
            0:begin     start_bit <= 3'd0;
                        Data_bit[0] <= 3'd0;
                        Data_bit[1] <= 3'd0;
                        Data_bit[2] <= 3'd0;
                        Data_bit[3] <= 3'd0;
                        Data_bit[4] <= 3'd0;
                        Data_bit[5] <= 3'd0;
                        Data_bit[6] <= 3'd0;
                        Data_bit[7] <= 3'd0;
                        stop_bit <= 3'd0;
            end
            6,7,8,9,10,11:start_bit <= start_bit + Uart_rx;
            22,23,24,25,26,27:Data_bit[0] <= Data_bit[0] + Uart_rx;
            38,39,40,41,42,43:Data_bit[1] <= Data_bit[1] + Uart_rx;
            54,55,56,57,58,59:Data_bit[2] <= Data_bit[2] + Uart_rx;
            70,71,72,73,74,75:Data_bit[3] <= Data_bit[3] + Uart_rx;
            86,87,88,89,90,91:Data_bit[4] <= Data_bit[4] + Uart_rx;
            102,103,104,105,106,107:Data_bit[5] <= Data_bit[5] + Uart_rx;
            118,119,120,121,122,123:Data_bit[6] <= Data_bit[6] + Uart_rx;
            134,135,136,137,138,139:Data_bit[7] <= Data_bit[7] + Uart_rx;
            150,151,152,153,154,155:stop_bit <= stop_bit + Uart_rx;
            default:begin   start_bit <= start_bit;
                            Data_bit[0] <= Data_bit[0];
                            Data_bit[1] <= Data_bit[1];
                            Data_bit[2] <= Data_bit[2];
                            Data_bit[3] <= Data_bit[3];
                            Data_bit[4] <= Data_bit[4];
                            Data_bit[5] <= Data_bit[5];
                            Data_bit[6] <= Data_bit[6];
                            Data_bit[7] <= Data_bit[7];
                            stop_bit <= stop_bit;
            end
        endcase
    end
       
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_byte <= 0;
    else if(r_rx_num == 8'd159)begin
        Rx_byte[0] <= (Data_bit[0] >= 4)?1:0;
        Rx_byte[1] <= (Data_bit[1] >= 4)?1:0;
        Rx_byte[2] <= (Data_bit[2] >= 4)?1:0;
        Rx_byte[3] <= (Data_bit[3] >= 4)?1:0;
        Rx_byte[4] <= (Data_bit[4] >= 4)?1:0;
        Rx_byte[5] <= (Data_bit[5] >= 4)?1:0;
        Rx_byte[6] <= (Data_bit[6] >= 4)?1:0;
        Rx_byte[7] <= (Data_bit[7] >= 4)?1:0;
    end
    else
        Rx_byte <= Rx_byte;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_done <= 1'd0;    
    else if((bps_clk)&&(r_rx_num == 8'd159))
        Rx_done <= 1'b1;
    else
        Rx_done <= 1'd0;
        
endmodule

三、遇到的问题和解决办法

问题1:

        没有考虑开始接收数据的条件

解决办法:

        设计一个RX_EN信号,当检测到下降沿,且起始位的数据是0,则将RX_EN信号置1,当出现Rx_done信号,或起始位的数据不为0(检测到下降沿,但不是真正的起始位)则将RX_EN信号置0

    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        Rx_EN <= 1'd0;
    else if((neg_edge == 1) && (start_bit < 3'd4))
        Rx_EN <= 1'b1;
    else if((Rx_done) || (start_bit >= 3'd4))
        Rx_EN <= 1'd0;

四、功能仿真

1、添加仿真文件

`timescale 1ns / 1ps

module my_uart_rx_byte_tb();

    reg Clk;
    reg Reset_n;
    wire [2:0]Baud_set;
    reg Uart_rx;
    wire[7:0]Rx_byte;
    wire Rx_done;
    
    assign Baud_set = 3'd4;    
        
    my_uart_rx_byte my_uart_rx_byte_tb(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Uart_rx(Uart_rx),
        .Baud_set(Baud_set),
        .Rx_byte(Rx_byte),
        .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;            
        end
    endtask   

endmodule

、观察波形图

e14ed13e456e476ca6ea60fd35a40b3c.jpeg

五 、设计串口接收多字节数据应用

5.1、设计分析

 5.1.1、定义串口传输协议帧

55 - A5 - XX - XX - XX - XX - XX - F0

协议帧由帧头(0x55 0xA5) + 5字节数据 + 帧尾(0xF0)组成

5.1.2、串口控制LED灯原理和思路

a.接收来自电脑的串行数据并将其转化为多个单字节数据输出

b.对单字节数据进行8字节缓存,判断数据是否为有效帧,提取出正确的控制字输出

c.5字节数据对应5个LED灯,当出现AA时点亮LED灯,其他数据让LED灯保持熄灭

5.2、代码实现

`timescale 1ns / 1ps

module my_uart_rx_ctrl_led(
    Clk,
    Reset_n,
    Uart_rx,
    LED
);
    input Clk;
    input Reset_n;
    input Uart_rx;
    output reg[4:0]LED;
    
    reg [7:0]Mem_Data[7:0];
    wire [7:0]Rx_byte;
    wire Rx_done;
    
    my_uart_rx_byte my_uart_rx_byte(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Uart_rx(Uart_rx),
        .Baud_set(4),
        .Rx_byte(Rx_byte),
        .Rx_done(Rx_done)
    );
    
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)begin
        Mem_Data[0] <= 0;
        Mem_Data[1] <= 0;
        Mem_Data[2] <= 0;
        Mem_Data[3] <= 0;
        Mem_Data[4] <= 0;
        Mem_Data[5] <= 0;
        Mem_Data[6] <= 0;
        Mem_Data[7] <= 0;
    end
    else if(Rx_done)begin
        Mem_Data[7] <= Rx_byte;
        Mem_Data[6] <= Mem_Data[7];
        Mem_Data[5] <= Mem_Data[6];
        Mem_Data[4] <= Mem_Data[5];
        Mem_Data[3] <= Mem_Data[4];
        Mem_Data[2] <= Mem_Data[3];
        Mem_Data[1] <= Mem_Data[2];
        Mem_Data[0] <= Mem_Data[1];
    end
    
    reg r_rx_done;
    always@(posedge Clk)
        r_rx_done <= Rx_done;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        LED  <= 0;
    else if(r_rx_done)begin
        if((Mem_Data[7] == 8'hF0) && (Mem_Data[1] == 8'hA5) && (Mem_Data[0] == 8'h55))begin
            LED[0] = (Mem_Data[2] == 8'hAA)?1:0;
            LED[1] = (Mem_Data[3] == 8'hAA)?1:0;
            LED[2] = (Mem_Data[4] == 8'hAA)?1:0;
            LED[3] = (Mem_Data[5] == 8'hAA)?1:0;
            LED[4] = (Mem_Data[6] == 8'hAA)?1:0;
        end
    end
    
endmodule

5.3功能仿真

5.3.1、添加仿真文件

`timescale 1ns / 1ps

module my_uart_rx_ctrl_led_tb();
    
    reg Clk;
    reg Reset_n;
    reg Uart_rx;
    wire [4:0]LED;

    my_uart_rx_ctrl_led my_uart_rx_ctrl_led_tb(
        .Clk(Clk),
        .Reset_n(Reset_n),
        .Uart_rx(Uart_rx),
        .LED(LED)
    );

    initial Clk = 1;
    always #10 Clk = ~Clk;
    
    initial begin
        Reset_n = 0;
        Uart_rx = 1;
        #201;
        Reset_n = 1;
        #200;
        uart_tx_byte(8'h55);
        #90000;
        uart_tx_byte(8'ha5);
        #90000;
        
        uart_tx_byte(8'haa);
        #90000;
        uart_tx_byte(8'h34);
        #90000;
        uart_tx_byte(8'haa);
        #90000;
        uart_tx_byte(8'h78);
        #90000;
        uart_tx_byte(8'haa);
        #90000;
        
        uart_tx_byte(8'hf0);
        #90000;
        
        uart_tx_byte(8'h55);
        #90000;
        uart_tx_byte(8'ha5);
        #90000;
        
        uart_tx_byte(8'haa);
        #90000;
        uart_tx_byte(8'h78);
        #90000;
        uart_tx_byte(8'haa);
        #90000;
        uart_tx_byte(8'h34);
        #90000;
        uart_tx_byte(8'h12);
        #90000;
        
        uart_tx_byte(8'hf1);
        #90000;
        
        #900000;
        $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

5.3.2、观察波形图

f4a69a60a55141efa39eb9f5a283f4cb.jpeg

 6、遇到的问题及解决办法

问题1:板机验证时需要连续发送两组八字节数据,LED灯才会产生变化

问题分析:通过观察波形图发现,LED错过了最后一次数据发送完时产生的RX_done上升沿,直到下一个数据来临时产生Rx_done上升沿时LED才发生变化

32fc8843b6694ca5be12af8677c73ca0.png

解决办法:

将RX_done延迟一拍 

    reg r_rx_done;
    always@(posedge Clk)
        r_rx_done <= Rx_done;
    
    always@(posedge Clk or negedge Reset_n)
    if(!Reset_n)
        LED  <= 0;
    else if(r_rx_done)begin
        if((Mem_Data[7] == 8'hF0) && (Mem_Data[1] == 8'hA5) && (Mem_Data[0] == 8'h55))begin
            LED[0] = (Mem_Data[2] == 8'hAA)?1:0;
            LED[1] = (Mem_Data[3] == 8'hAA)?1:0;
            LED[2] = (Mem_Data[4] == 8'hAA)?1:0;
            LED[3] = (Mem_Data[5] == 8'hAA)?1:0;
            LED[4] = (Mem_Data[6] == 8'hAA)?1:0;
        end
    end

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值