12 ABC串口接收原理与思路

1. 串口接收原理

基本原理:通过数据起始位判断要是否要开始接收的数据,通过采样的方式确定每一位数据是0还是1。

如何判断数据起始位到来:通过边沿检测电路检测起始信号的下降沿

如何采样:一位数据采多次,统计得到高电平出现的次数,次数多的就是该位的电平值

2. 自己写的代码(不完善)

设计代码

cpp 复制代码
module uart_byte_rx(
    clk,
    rstn,
    uart_byte_rx,
    blaud_set,
    data,
    rx_done
);

    input clk;
    input rstn;
    input uart_byte_rx;
    input blaud_set;
    output reg [7:0] data;
    output reg rx_done;
    
    //Blaud_set = 0时,波特率 = 9600;
    //Blaud_set = 1时,波特率 = 19200;
    //Blaud_set = 2时,波特率 = 38400;
    //Blaud_set = 3时,波特率 = 57600;
    //Blaud_set = 4时,波特率 = 115200;
    
    reg[17:0] bps_dr;
    always@(*)
        case(blaud_set)
            0: bps_dr = 1000000000/9600/20;
            1: bps_dr = 1000000000/19200/20;
            2: bps_dr = 1000000000/38400/20;
            3: bps_dr = 1000000000/57600/20;
            4: bps_dr = 1000000000/115200/20;
        endcase

    reg [1:0] test;
    reg get_en;
    always@(posedge clk or negedge rstn) //边沿检测,使能后续的采样
    if(!rstn) begin
        data <= 0;
        test <= 0;
        get_en <= 0;
        rx_done <= 0;
    end
    else begin
        test[0] <= uart_byte_rx;
        test[1] <= test[0];
        if((test[0] ==0 )&&(test[1] == 1))
            get_en <= 1;
            rx_done <= 0;
    end

    reg [17:0] div_cnt;
    reg [3:0] counter;
    reg [3:0] tx_counter;
    always@(posedge clk or negedge rstn) //计数时钟,一个计数周期代表一位数据
    if(!rstn) 
        div_cnt <= 0;
    else if(get_en)begin
        if(div_cnt == 434 - 1)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'd1;
    end        
    
    wire bps_clk;
    assign bps_clk = (div_cnt == 1);
    always@(posedge clk or negedge rstn) //数据段,包含起始位和终止位,共十段
    if(!rstn) 
        tx_counter <= 0;
    else if(get_en)begin
         if(tx_counter == 11)
             tx_counter <= 0;
         else if(div_cnt == 1)
             tx_counter <= tx_counter + 1'd1;
     end       
            
     always@(posedge clk or negedge rstn) //数据采样,每段数据采样8次。
     if(!rstn)
        counter <= 0;
     else if(div_cnt == 3)
        counter <= 0;
     else if((div_cnt == 1*433/8 - 10)||
     (div_cnt == 2*433/8 - 10)||
     (div_cnt == 3*433/8 - 10)||
     (div_cnt == 4*433/8 - 10)||
     (div_cnt == 5*433/8 - 10)||
     (div_cnt == 6*433/8 - 10)||
     (div_cnt == 7*433/8 - 10)||
     (div_cnt == 8*433/8 - 10))
        counter <= counter + uart_byte_rx;
               
     always@(posedge clk or negedge rstn) //
     if(!rstn) 
        counter <= 0;
     else if(div_cnt == 2)
     case(tx_counter)
        2:if(counter > 4 )data[0] <= 1; else if(counter <4) data[0] <= 0;
        3:if(counter > 4 )data[1] <= 1; else if(counter <4) data[1] <= 0; 
        4:if(counter > 4 )data[2] <= 1; else if(counter <4) data[2] <= 0; 
        5:if(counter > 4 )data[3] <= 1; else if(counter <4) data[3] <= 0; 
        6:if(counter > 4 )data[4] <= 1; else if(counter <4) data[4] <= 0; 
        7:if(counter > 4 )data[5] <= 1; else if(counter <4) data[5] <= 0; 
        8:if(counter > 4 )data[6] <= 1; else if(counter <4) data[6] <= 0; 
        9:if(counter > 4 )data[7] <= 1; else if(counter <4) data[7] <= 0;
        11:begin rx_done <= 1; get_en <= 0; div_cnt <= 0; end
        default: begin rx_done <= 0; data <= data; end
     endcase        
endmodule

仿真波形

3. 看完视频后写的代码(完善)

设计代码

3.1 需学习的点:

1.将div_cnt划分为最小时间段

2.某些判断信号直接用assign利用,而不需要利用寄存器

3.仿真代码中task的使用

cpp 复制代码
module uart_byte_rx1(
    clk,
    rstn,
    blaud_set,
    uart_rx,
    data,
    rx_done
);

    input clk;
    input rstn;
    input [2:0]blaud_set;
    input uart_rx;
    output reg [7:0] data;
    output rx_done;

    reg [8:0] bps_dr;
    always@(*)
        case(blaud_set)
            0:bps_dr = 1000000000/9600/16/20;
            1:bps_dr = 1000000000/19200/16/20;
            2:bps_dr = 1000000000/38400/16/20;
            3:bps_dr = 1000000000/57600/16/20;
            4:bps_dr = 1000000000/115200/16/20;
            default : bps_dr = 1000000000/9600/16/20;
        endcase
        
    //边沿信号检测
    reg [1:0] uart_rx_r; //用两位寄存器分别存储两个时间沿的uart_rx信号
    always@(posedge clk) begin
        uart_rx_r[0] <= uart_rx;
        uart_rx_r[1] <= uart_rx_r[0];
    end
    
    //将两位寄存器的值直接通过导线输出进行判断(不需要再使用寄存器)
    wire nedge_uart_rx;  //掌握一下这个方法,之前一直使用的是寄存器
    //法一:
    //assign nedge_uart_rx = ((uart_rx_r[0] == 0)&&(uart_rx_r == 1));
    //法二:
    assign nedge_uart_rx = (uart_rx_r == 2'b10);
    
    reg rx_en;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        rx_en <= 0;
    else if(nedge_uart_rx)
        rx_en <= 1;
    else if(rx_done)
        rx_en <= 0;
    
    
    //周期计数器
    reg [8:0] div_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        div_cnt <= 0;
    else if(rx_en) begin
        if(div_cnt == bps_dr - 1)
            div_cnt <= 0;
        else
            div_cnt <= div_cnt + 1'd1;
    end
    else
        div_cnt <= 0;
    
    
    wire [3:0]bps_clk_16x; //(一定要记得加位宽)采样信号,这种写法很灵活
    assign bps_clk_16x = bps_dr/2; //采样每一段的中点值,同时也可以用它来计数。
    
    //发送一字节的数据有需要十个数据位,每位数据有16个小段供采样,共160
    reg [7:0]bps_cnt;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        bps_cnt <= 0;
    else if(rx_en) begin
        if(bps_cnt == 159)
            bps_cnt <= 0;
        else if(div_cnt ==bps_clk_16x)
            bps_cnt <= bps_cnt + 1'd1; 
    end
    else  
        bps_cnt <= 0;
    
    reg[2:0] r_data[7:0];//二维数据,代表八个r_data,每个r_data有3位寄存器存储数值。
    reg[2:0] sta_data;
    reg[2:0] sto_data;
    always@(posedge clk or negedge rstn)
    if(!rstn)begin
        sta_data <= 0;
        sto_data <= 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;        
    end
    else if(div_cnt == bps_clk_16x - 1)
    case(bps_cnt) //下面合在一起的写法是允许的
        0:begin
        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;
        end   
        5,6,7,8,9,10,11: sta_data <= sta_data + 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_data <= sto_data + uart_rx;
        default:;
    endcase        
    
    reg rx_done;
    always@(posedge clk or negedge rstn)
    if(!rstn)
        rx_done <= 0;
    else if(bps_cnt == 159) begin
        rx_done <= 1;
    end
    else
        rx_done <= 0;
    
    //数据接收完成后赋值给data输出
    always@(posedge clk or negedge rstn)
    if(!rstn)
        data <= 0;
    else if(rx_done)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;
    end

    // data[1] <= r_data[1][2]
    
    // 0:3'd000
    // 1:3'd001
    // 2:3'd010
    
    // 4:3'd100
    // 5:3'd101
    // 6:3'd110
    // 7:3'd111 利用第3位的区别给data赋值

endmodule

仿真代码

cpp 复制代码
`timescale 1ns/1ns

module uart_byte_rx1_tb();
    
    reg clk;
    reg rstn;
    reg uart_rx;
    wire [2:0]blaud_set;
    wire [7:0]data;
    wire rx_done;
    
    uart_byte_rx1 uart_byte_rx_inst1(
        .clk(clk),
        .rstn(rstn),
        .blaud_set(blaud_set),
        .uart_rx(uart_rx),
        .data(data),
        .rx_done(rx_done)
    );
    
    assign blaud_set = 3'd4;
    
    initial clk = 1;
    always #10 clk = ~clk;
    
    initial begin
        rstn = 0;
        uart_rx = 1;
        #201;
        rstn = 1;
        #200;
        uart_tx_byte(8'h5a);
        @(posedge rx_done)
        #5000;
        uart_tx_byte(8'ha5);
        @(posedge rx_done)
        #5000;
        uart_tx_byte(8'h86);
        @(posedge rx_done)
        #5000;
        $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];
        end
    endtask

endmodule

仿真波形

相关推荐
∑狸猫不是猫1 分钟前
(15)CT137A- 按键消抖设计
fpga开发
我爱C编程5 分钟前
基于FPGA的2ASK+帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可设置SNR
fpga开发·信道·帧同步·snr·2ask·误码统计
羊村懒哥43 分钟前
tomcat-安装笔记(包含虚拟主机配置)
java·笔记·tomcat
百流1 小时前
scala基础学习_运算符
开发语言·学习·scala
百流1 小时前
scala基础学习(数据类型)-数组
开发语言·学习·scala
虾球xz2 小时前
游戏引擎学习第61天
java·学习·游戏引擎
qq_430583972 小时前
QT笔记- QTreeView + QFileSystemModel 当前位置的保存与恢复 #选中 #保存当前索引
开发语言·笔记·qt
小王爱吃月亮糖2 小时前
QT-QVariant类应用
开发语言·c++·笔记·qt·visual studio
三万棵雪松2 小时前
3.系统学习-熵与决策树
学习·算法·决策树
红色的山茶花2 小时前
YOLOv9-0.1部分代码阅读笔记-hubconf.py
笔记·深度学习·yolo