FPGA开发——UART回环实现之接收模块的设计

一、简介

        因为我们本次进行串口回环的实验的对象是FPGA开发板和PC端,所以在接收和发送模块中先编写接收模块,这样可以在后面更好的进行发送模块的验证。(其实这里先编写哪个模块)都不影响,这里看自己心情,反正都可以独立进行仿真。)

        在上一篇文章中,我们对于UART回环实现的总体系统框架做了一盒简单的构建,所以在实现时我们也按照那个框架来。这里就先对于接收模块进行一个设计。

二、接收模块的基本设计

        本次设计我们采用状态机的实现方式,将状态机划分为四个,第一个就是空闲状态,表示设备没有接收数据,第二个是开始状态,表示设备接收到起始位,第三个接收数据过程状态,用于表示设备接收数据的过程,最后一个就是停止位,表示设备接受数据完成。

三、接收模块的波形图绘制

根据上面的状态机,我们可以据此展开波形图的绘制,分别就是对于信号进行打拍,下降沿检测,两个状态,以及bit和波特率、输出数据等的表示。

        使用三级打拍,利用后两拍信号实现下降沿检测,当检测到下降沿,状态机由IDLE进入到START,然后利用波特率计数器计数1bit的起始位,来到DATA,利用波特率计数器和bit计数器用于接收数据,接收完数据之后进入STOP,最后利用波特率计数器计数1bit的停止位,状态又回到初始的IDLE状态。

 四、代码实现

1、设计文件的编写

        新建一个uart_rx.v文件,如下:在代码编写的过程中我们还需要注意的就是UART在进行通信时是串行通信,二我们的设备中数据时并行的,所以在代码中我们还要实现数据串并型的转换。

//---------<模块及端口声名>-------------------------------------------
module uart_rx( 
    input				clk		 ,
    input				rst_n	 ,
    input				din_rx   ,
    output		[7:0]	dout_data,
    output			    dout_flag	
);								 
//---------<参数定义>------------------------------------------------
parameter CLK_CLY=50_000_000;
parameter BAUD_115200=115200;
parameter BPS_CNT_MAX=CLK_CLY/BAUD_115200; 
parameter     IDLE  =4'b0001,
              START =4'b0010,
              DATA  =4'b0100,
              STOP  =4'b1000;
//---------<内部信号定义>--------------------------------------------
reg           uart_rx_d1;//对异步信号进行同步处理
reg           uart_rx_d2;
reg           uart_rx_d3;

reg     [3:0]  state_c;
reg     [3:0]  state_n;
wire            nedge;//起始位下降沿检测
reg     [8:0]  cnt_bps;//波特率计数器
wire           add_cnt_bps;
wire           end_cnt_bps;

reg     [2:0]  cnt_bit;//bit数据计数器
wire           add_cnt_bit;
wire           end_cnt_bit;

reg     [7:0]  uart_rx_r;//用于存储接收到的数据
reg            rx_flag;//接收数据完成标志位
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        state_c <=IDLE ;
    end 
    else begin 
        state_c <= state_n;
    end 
end
    
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
    case(state_c)
        IDLE  : begin
            if (nedge) 
                state_n=START ;
            else
                state_n=IDLE ;
        end
        START : begin
            if (end_cnt_bps) 
                state_n=DATA ;
            else
                state_n=START ;
        end
        DATA  : begin
            if (end_cnt_bit) 
                state_n=STOP ;
            else
                state_n=DATA ;
        end
        STOP  : begin
            if (end_cnt_bps) 
                state_n=IDLE ;
            else
                state_n=STOP ;
        end
        default : state_n=IDLE ;
    endcase
end
//对uart_rx进行打拍同步处理
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        uart_rx_d1 <= 1'b1;
        uart_rx_d2 <= 1'b1;
        uart_rx_d3 <= 1'b1;
    end  
    else begin 
        uart_rx_d1 <=din_rx;
        uart_rx_d2 <=uart_rx_d1;
        uart_rx_d3 <=uart_rx_d2;
    end 
end
//nedge下降沿检测
assign nedge=~uart_rx_d2 & uart_rx_d3;

//波特率计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bps <= 9'd0;
    end 
    else if(add_cnt_bps)begin 
        if(end_cnt_bps)begin 
            cnt_bps <= 'd0;
        end
        else begin 
            cnt_bps <= cnt_bps + 1'b1;
        end 
    end
end 

assign add_cnt_bps =(state_c != IDLE) ;
assign end_cnt_bps = add_cnt_bps && (cnt_bps ==(BPS_CNT_MAX-1)) ;

//bit计数器
always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_bit <= 9'd0;
    end 
    else if(add_cnt_bit)begin 
        if(end_cnt_bit)begin 
            cnt_bit <= 'd0;
        end
        else begin 
            cnt_bit <= cnt_bit + 1'b1;
        end 
    end
end 

assign add_cnt_bit =(state_c == DATA)&& end_cnt_bps ;
assign end_cnt_bit = add_cnt_bit && (cnt_bit ==(8-1)) ;

//将串行数据变为并行数据
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        uart_rx_r <= 1'b0;
    end 
    else if((state_c==DATA)&&(cnt_bps==BPS_CNT_MAX/2-1))begin 
        uart_rx_r<={uart_rx_d3,uart_rx_r[7:1]};
        //uart_rx_r[cnt_bit]<=uart_rx_d3;
    end 
end

//接收数据完成标志wei
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        rx_flag <= 1'b0;
    end 
    else if(end_cnt_bit)begin 
        rx_flag<= 1'b1;
    end 
    else begin 
        rx_flag<= 1'b0;
    end 
end
assign dout_data = uart_rx_r;
assign dout_flag = rx_flag;
endmodule

2、测试文件的编写

`timescale  1us/1us
module  uart_rx_tb();

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg             clk          ;
reg             rst_n	     ;
reg             din_rx       ;
wire    [7:0]   dout_data    ;
wire            dout_flag    ;


uart_rx uart_rx_inst(
    /*input            */ .clk	     (clk        ),
    /*input            */ .rst_n     (rst_n	 ),        
    /*input            */ .din_rx    (din_rx     ),
    /*output  reg      */ .dout_data (dout_data  ),
    /*output  reg [7:0]*/ .dout_flag (dout_flag  )
);
parameter CLOCK_CYCLE=20;
//产生时钟
    initial clk = 1'b0;
    always #10 clk = ~clk;

//产生激励
    initial  begin 
        rst_n = 1'b1;
        din_rx = 1;//空闲为高电平
        #(CLOCK_CYCLE*2);
        rst_n = 1'b0;
        #(CLOCK_CYCLE*20);
        rst_n = 1'b1;
        #1002;

        //模拟UART接收模块的串行输入
        //起始位
        din_rx = 0;
        #(434*CLOCK_CYCLE);
        //数据位:8'b1011_0011
        din_rx = 1;//LSB
        #(434*CLOCK_CYCLE);
        din_rx = 1;
        #(434*CLOCK_CYCLE);
        din_rx = 0;
        #(434*CLOCK_CYCLE);
        din_rx = 0;
        #(434*CLOCK_CYCLE);
        din_rx = 1;
        #(434*CLOCK_CYCLE);
        din_rx = 1;
        #(434*CLOCK_CYCLE);
        din_rx = 0;
        #(434*CLOCK_CYCLE);
        din_rx = 1;
        #(434*CLOCK_CYCLE);
        //停止位
        din_rx = 1;
        #(434*CLOCK_CYCLE);

        #(CLOCK_CYCLE*100);
        $stop;
    end

endmodule 

五、波形图仿真

 在波形图中我们观察到dout_data的数据和发送数据不一样,这是因为UART是低位先发,所以在波形图中我们看到的输入和输出数据时相反的,这里需要我们注意一下。

### FPGA 实现串口回环的工作原理 #### 工作流程概述 FPGA实现串口回环的核心在于处理来自上位机的数据并将其原样返回。具体来说,当上位机通过USB转UART接口发送数据至FPGA时,这些数据被FPGA内部接收器捕获,并存储在一个寄存器中;随后,在适当的时候,该数据会被重新加载到发射器并通过同一通道发回到上位机[^1]。 #### 数据帧结构 为了确保正确无误地传输信息,每一组有效载荷前后都会附加额外的信息——起始位和停止位。起始位标志着一个新的字符即将到达,而停止位则用来指示当前字符已全部传输出去。此外,还可能包含奇偶校验位用于错误检测,但在本实例中并未采用这种机制[^3]。 #### 接收过程详解 一旦检测到来自外部设备(这里是计算机)的一个下降沿信号(即逻辑电平从高变低),这便触发了接收操作的启动。接着按照设定好的波特率逐位读入后续进入缓冲区内的每一位直到遇到预期中的终止条件为止。整个过程中保持严格的定时控制至关重要,因为任何偏差都可能导致解码失败或产生其他不可预见的问题[^4]。 ```verilog // Verilog代码片段展示如何定义一个简单的UART接收模块 module uart_rx( input clk_50m, input rst_n, input data_in, output reg [7:0] rx_data, output reg rx_done ); // ...省略部分细节... endmodule ``` #### 发送过程解析 相反方向上的动作同样遵循类似的规则:每当准备就绪要传送新一批次的数据之前先发出一个低位脉冲作为前导符告知对方接下来会有实际内容跟随其后;紧接着依次推送各个组成单元直至最后一个元素送出完毕之时再次恢复高位状态以宣告此次会话正式结束。值得注意的是,这里的每一个步骤都需要严格遵照预先协商一致的时间间隔来进行调度安排,从而保证双方能够顺利协调工作而不至于发生冲突或者误解。 ```verilog // Verilog代码片段展示如何定义一个简单的UART发送模块 module uart_tx( input clk_50m, input rst_n, input uart_tx_en, input [7:0] data_in, output wire tx_data, output wire tx_done ); // ...省略部分细节... endmodule ``` #### 完整链路集成 最后一步则是将上述独立的功能组件组合起来形成完整的解决方案。通常情况下这意味着要在顶层设计层面创建相应的端口映射关系以便于不同子系统间可以相互协作共同完成既定的任务目标。例如下面这段Verilog描述了一个典型的顶层实体声明方式及其内部连接情况: ```verilog module top_uart( input clk_50m, input rst_n, input rx_in, output wire tx_out, output wire tx_done ); wire [7:0] data; wire rx_done; uart_rx m0( .clk_50m(clk_50m), .rst_n(rst_n), .data_in(rx_in), .rx_data(data), .rx_done(rx_done) ); uart_tx m1( .clk_50m(clk_50m), .rst_n(rst_n), .uart_tx_en(rx_done), .data_in(data), .tx_data(tx_out), .tx_done(tx_done) ); endmodule ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子小芯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值