串口通信整理:三、数据接收

首先再次回顾一下通信协议UART
本质就是并行数据转串行数据
关键参数:起始位(0)、数据位(非任意位)、停止位(1)
假设发送8位数据,那实际上要发送10位数据,上升沿有效的话,需经历11个上升沿。
波特率计数计算:假设时钟周期为20ns,波特率为115200: 1000000000/115200/20=bps
接口规范: RS232、RS449、RS423、RS422、RS485

工程描述:构建串口接收模块,并进行仿真测试(此部分已在上述链接验证过),仿真通过后,对其上板测试。

具体工程如下:
主要模块 : 一个设计模块:serial_recv 一个tb模块:serial__tb
工程分析:
接收与发送不同,由于某些场合信号干扰或波特率不匹配或其他原因,很可能会导致数据接收的不准确性,因此在数据接收模块必须采用一些数据检测手段,本文所述的设计模块采用两个触发器构建
边沿检测电路**,为保证接收到数据的准确性,将每一比特平均分成16段,去掉前5段和后四段,只保留中间7段,采样周期为bps/16。对于一个8位数据,加上起始位和停止位共10位,也就是需要160次采样。

代码如下:

serial_recv模块:

module serial_recv(
clk,
reset_n,
uart_recv,
data,
recv_done,
baud_set
    );
    input clk;
    input reset_n;
    input [2:0]baud_set;
    input uart_recv;
    output reg [7:0] data;
    output reg recv_done;
    //采样周期计数
    reg[7:0] bps_cnt;
    //只持续一个时钟周期,每一位数据的16段分之一的中点时刻
    wire bps_clk;
    assign bps_clk=(div_cnt==bps_dir/2);
    //两个寄存器依次保存数据,弄清前后顺序
    reg [1:0] bytedect;
    always@(posedge clk)begin 
        bytedect[0]<=uart_recv;         //0:每次传来最新的数据值
        bytedect[1]<=bytedect[0];      //1:前一个数据的值   
    end
    
    //边沿检测
    //前一个时刻为低电平,后一个时刻为高电平:01
    wire pedge;
    assign pedge=(bytedect==1);   
    //前一个时刻为高电平 后一个时刻为低电平:10
    wire nedge;
    assign nedge=(bytedect==2);
    
    reg[9:0] bps_dir;
    always@(*)begin
        case(baud_set)
            0:bps_dir=1000000000/9600/16/20-1;
            1:bps_dir=1000000000/19200/16/20-1;
            2:bps_dir=1000000000/38400/16/20-1;
            3:bps_dir=1000000000/57600/16/20-1;
            4:bps_dir=1000000000/115200/16/20-1;
            default:bps_dir=1000000000/9600/16/20-1;
        endcase    
    end
 
  //一个字节8位数据,所以需要8个寄存器,每个数据有8位
    reg[2:0] r_data[7:0];
    reg[2:0] start_bit;
    reg[2:0] stop_bit; 
    always@(posedge clk,negedge reset_n)begin
        if(!reset_n)begin
            start_bit<=0;
            stop_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;
        end
        else if(bps_clk) begin
            case(bps_cnt)
                0:begin
                        start_bit<=0;
                        stop_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;
                end
                //掐头去尾
                5,6,7,8,9,10,11:start_bit<=start_bit+uart_recv;
                21,22,23,24,25,26,27:r_data[0]<=r_data[0]+uart_recv;
                37,38,39,40,41,42,43:r_data[1]<=r_data[1]+uart_recv;
                53,54,55,56,57,58,59:r_data[2]<=r_data[2]+uart_recv;
                69,70,71,72,73,74,75:r_data[3]<=r_data[3]+uart_recv;
                85,86,87,88,89,90,91:r_data[4]<=r_data[4]+uart_recv;
                101,102,103,104,105,106,107:r_data[5]<=r_data[5]+uart_recv;
                117,118,119,120,121,122,123:r_data[6]<=r_data[6]+uart_recv;
                133,134,135,136,137,138,139:r_data[7]<=r_data[7]+uart_recv;
                149,150,151,152,153,154,155:stop_bit<=stop_bit+uart_recv;  
                default:;
            endcase          
                end     
    end
    
    
     reg recv_en;
    always@(posedge clk,negedge reset_n)begin
    if(!reset_n)
        recv_en<=0;
    else  if(nedge)
        recv_en<=1;
    else if(recv_done||(start_bit>=4))begin
        recv_en<=0;
    end
    end
    
        //切分之后的每小段时钟计数
    reg [8:0]div_cnt;
    always@(posedge clk,negedge reset_n)begin
        if(!reset_n)
            div_cnt<=0;
        else if(recv_en)begin
            if(div_cnt==bps_dir)
                div_cnt<=0;
            else
                div_cnt<=div_cnt+1;
        end   
        else
            div_cnt<=0;                           
    end
    
    
    always@(posedge clk,negedge reset_n)begin
        if(!reset_n)
            bps_cnt<=0;
        else if(recv_en) begin
            if(bps_clk)begin
                if(bps_cnt==160)
                    bps_cnt<=0;
                else
                    bps_cnt<=bps_cnt+1;
            end 
            else
            bps_cnt<=bps_cnt;   
        end
        else
            bps_cnt<=0;
    end    
   
    always@(posedge clk,negedge reset_n)begin
        if(!reset_n)begin
            data<=0;
        end
        else if(bps_clk&&(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;
        end
    end
    always@(posedge clk,negedge reset_n)begin
        if(!reset_n)
            recv_done<=0;
        else if((div_cnt==bps_dir/2)&&(bps_cnt==160))
            recv_done<=1;
        else
            recv_done<=0;     
    end
endmodule

serial_recv_tb模块:

`timescale 1ns / 1ns
module serial_recv_tb ();

    reg clk;
    reg reset_n;
    reg recv;
    wire  [7:0] data;
    wire  recv_done;

serial_recv  r1(
.clk(clk),
.reset_n(reset_n),
.uart_recv(recv),
.data(data),
.recv_done(recv_done),
.baud_set(4)
    );
initial clk=1;
always#10 clk=~clk;

initial begin
reset_n=0;
#201;
reset_n=1;
#200;
serial_send(8'h73);
#100000;
serial_send(8'h91);
#100000;
$stop;
end

task serial_send;
    input[7:0] data;
    begin
        recv=1;
        #200;
        //起始位
        recv=0;
        #9000;
        recv=data[0];
        #9000;
        recv=data[1];
        #9000;
        recv=data[2];
       #9000;
        recv=data[3];
       #9000;
        recv=data[4];
       #9000;
        recv=data[5];
        #9000;
        recv=data[6];
        #9000;
        recv=data[7];
        #9000;
        //停止位
        recv=1;
        #9000;  
    end
endtask

endmodule

仿真结果图如下:

图1:
在这里插入图片描述

本次工程由于采样周期太长,因此我主要对数据接收截止位做说明,如图1所示,
1、当采样计数到达160次时,对应的tb模块接受的第一个数据73也随之完整采样,此时recv_done拉高,随着recv_done拉高,recv_en也置低电平,的第一个数据这轮采样对应的起始计数周期div_cnt是从上轮计数的14开始,到这轮计数的13,加起来正好为1000000000/115200/16/20=27 次,

本次工程较为复杂,不好一一道来,具体分为以下几点进行阐述:
1、对一位数据进行多次采用,采样虽为16段,但为了采取更有效的数据,我们再次将16段掐头去尾采取其中间7段,并对这7段数据进行计数处理,每次对采样进来的数据进行自加,并进行判断是否大于等于4,若满足条件则采样进来的为高电平,否则为低电平。
2、起始位置采用两个寄存器设计边沿检测电路(后期应该会专门针对此电路设计来一篇文章做总结)
3、如图1所示,当采样计数到达160次时,对应的tb模块接受的第一个数据73也随之完整采样,此时recv_done拉高,随着recv_done拉高,recv_en也置低电平。
4、第10位数据的最后一段数据采样(也就是总共十位数据的第160段采样),在这轮采样对应的起始计数周期div_cnt是从第159段计数的14开始,到这轮计数的13,加起来正好是1000000000/115200/16/20=27 次,一切都很nice。
5、至此8位数据接收模块设计结束。

附RTL仿真电路图:

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值