07-UART-串口接收模块

UART——串口接收模块

RTL代码

`timescale 1ns/1ps

module uart_byte_rx(
    clk,
    reset_n,
    uart_rx,
    baud_set,
    Data,
    Rx_Done
    );

    input clk;
    input reset_n;
    input uart_rx;
    input [3-1:0] baud_set;
    
    output reg [8-1:0] Data;
    output reg Rx_Done;
    
    // reg width name number          
    reg [3-1:0] r_data [8-1:0];
    reg [3-1:0] sta_bit;
    reg [3-1:0] sto_bit;
    
	
	//边沿检测(上升沿和下降沿)
	//通过两位宽的寄存器来实现
	//电路实现上面就是两级D触发器
    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[1]==0) && (uart_rx_r[0]==1));
    assign pedge_uart_rx = (uart_rx_r==2'b01);   
    
	//下降沿,两种写法
    wire nedge_uart_rx;
//    assign nedge_uart_rx = ((uart_rx_r[1]==1) && (uart_rx_r[0]==0));
    assign nedge_uart_rx = (uart_rx_r==2'b10);
    
	
	//波特率设置
	//波特率单位,每秒传输的bit数量,()bit/s
	//baud_DR计算为每一个bit所需要的时钟周期,这里的单位是ns
	//这里时钟周期以50MHz为例,也就是一个周期有20ns,因此是/20
	//同时在接收时,将1个bit的数据划分成为16位
    reg [8-1:0] Bps_DR;
    always@(*)
        case(baud_set)
            0: Bps_DR<=1_000_000_000/9600/20/16-1;
            1: Bps_DR<=1_000_000_000/19200/20/16-1;
            2: Bps_DR<=1_000_000_000/38400/20/16-1; 
            3: Bps_DR<=1_000_000_000/57600/20/16-1;
            4: Bps_DR<=1_000_000_000/115200/20/16-1;
            default: Bps_DR<=1_000_000_000/9600/20/16-1;
        endcase
    
	
	//接收使能
    reg Rx_EN;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Rx_EN<=0;
		//当接收当下降沿时,开始接收
        else if(nedge_uart_rx==1)
            Rx_EN<=1;
		//当接收成功时,将使能置0
		//为避免起始位误判,起始位若为高电平(也就是sta_bit>=4),也将使能置0
        else if((Rx_Done==1)||(sta_bit>=4))
            Rx_EN<=0;
    end
    
    //分频计数
    reg [8-1:0] div_cnt;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            div_cnt<=0;
		//在使能时,才开始计数
        else if(Rx_EN)begin
			//计数1bit所需周期数
            if(div_cnt==Bps_DR)
                div_cnt<=0;
            else
                div_cnt<=div_cnt+1;
        end
		//没有使能时,一直置0
        else 
            div_cnt<=0;
    end
    
    //数据传输标识符,只有在1bit数据计数到中间时
	//也就是div_cnt==Bps_DR/2时,标识符置1
	//相较于bps_clk,进一步16倍划分了
    wire bps_clk_16x;
    assign bps_clk_16x = (div_cnt==Bps_DR/2);
    
	//数据传输计数
	//bps_cnt标记着数据传输的数量
	//每次bps_clk出现时,bps_cnt才会加1或者清零
    reg [8-1:0] bps_cnt;
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            bps_cnt<=0;
        else if(Rx_EN)begin 
            if(bps_clk_16x)begin
				//16倍细分后,总共就需要计数16*(10位bit)=160
                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
          
	//核心:数据传输
	//对于1bit数据,将其细分为16份,检测中间部分
	//通过对中间部分的电平计数,判断该位的电平高低
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)begin
             sta_bit<=0;
             sto_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_16x) begin
			//对bps_cnt计数,当技术到每16个区间中间部分时,进行数据判断
			//每次都自加上uart_rx数据线上传来的内容
            case(bps_cnt)
                0:begin
                     sta_bit<=0;
                     sto_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:                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;
                54,55,56,57,58,59,60:           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
       end 
    end

	//在统计完每一位的数据后,进行电平判断
	//对bps_cnt计数到每一位的中间部分(7位)累加
	//最终高电平有0,1,2,3次出现,判断为低电平
	//有4,5,6次出现,则判断为高电平
	//判决门限??
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            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;
        end  
    end
    
	
    always@(posedge clk or negedge reset_n)begin
        if(reset_n==0)
            Rx_Done<=0;
		//当bps_cnt最终计数到第16个bit的最后一份(1/16)时(160)
		//再计满半个bit位所花费的周期(Bps_DR/2),则返回接收成功标识符
        else if((div_cnt==Bps_DR/2) && (bps_cnt==160))
            Rx_Done<=1;
        else 
            Rx_Done<=0; 
    end

endmodule

代码解析

  • 串口接收模块相较于发送模块有比较大的区别,最主要的一点在于电平判决上
  • 由于发送时,数据是已知的,只需要一位一位的发送出去,保证数据发送成功即可
  • 但是在接收时,需要将一位数据进行分解,选取中间部分的进行电平判决,从而避免错误接收
  • 需要特别注意,边沿检测和数据传输的写法,并行传输是fpga的优势,可以有效提高传输速率,要重点掌握
  • 对于bps_clk,还有rx_done,这类单脉冲信号,重要且有效,不仅对于数据传输的停止有用,在设计更加高层模块也有帮助

测试平台

`timescale 1ns/1ps

module uart_byte_rx_tb();

    reg clk;
    reg reset_n;
    reg uart_rx;
    wire [3-1:0] baud_set;
    
    wire [8-1:0] Data;
    wire Rx_Done;

    assign baud_set=4;

	//注意要赋予实例名称
    uart_byte_rx uart_byte_rx(
    clk, 
    reset_n,
    uart_rx,
    baud_set,
    Data,
    Rx_Done
    );
    
    
    initial begin
        clk=1;
        forever #10 clk=~clk;
     end
     
    initial begin
        reset_n=0;
        uart_rx = 1;
        #201;
        reset_n=1;
        #200;
        uart_tx_byte(8'h5a);
		//新语法,当Rx_Done为上升沿时,才会继续执行
        @(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
	//相较于rtl代码,在tb文件中语法会更加丰富自由
	可以通过直接在initial块中执行uart_tx_byte(),从而调用整个task块内容
    task uart_tx_byte;
        input [8-1:0] tx_data;
        begin
            uart_rx = 1;
            #20;
            uart_rx = 0;
			//刚好是1s/115200
			//也就是1bit所需要的周期时间
            #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

代码解析

  • testbench在写的时候需要注意initial块中的激励
  • 有些时候仿真波形的错误,可能源于设置的数据不正确,延迟的时间太短,还有关键信号没有置0,1等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值