UART 通信 协议 (二)

文章目录


前言

上一篇博客介绍了UART通信协议的内容以及UART 发送的实现。本文接着上篇博客:UART 通信 协议 (一),主要介绍UART 接收的FPGA实现。

        设计平台:Vivado IDE 2018.3

        硬件平台:武汉芯路恒科技ACX720开发板

        (建议PC端查看)


一、接收时序图

 由上图,对于接收到的电平信号,我们一般选择在中间时刻进行采样(上升沿采样)。因为中间时刻的电平相对稳定,对应的误码几率也很小,当然还是会存在误码情况的。实际应用中,为了应对强电磁干扰。会对同一个电平信号的不同时间段多次采样,然后对采样结果进行判决。最终确定电平值。

本次设计将对同一电平的不同时刻作 21 次采样。对中间的 7 次采样结果作判决,1 的个数多于 0 的个数则判为 1,否则为 0。

二、模块设计

1.总体方案

输入输出说明:
 

输入输出说明
信号 位宽 方向 功能
I_CLK 1 I 时钟信号
I_Rst_n 1 I 模块复位信号(低电平有效)
I_UART_RX 1 I 接受的UART 信号
I_BAUD_SELECT 3 I 波特率选择
O_DATA 8 O 输出的并行数据
O_FINISH 1 O 完成一次传输则有效(置1)

2.接受数据信号同步化

串口接收的UART信号是一个异步信号,和FPGA内部的逻辑不在同一个时钟域。如果不做跨时钟域处理,很容易出现亚稳态,影响数据接收。

这里用一种较为简单且常用的二级同步器来实现对数据信号 的同步化处理。

 一些高速的设计中为了提高平均故障时间,会采取三级同步器。

3.边沿检测设计

在没有数据接收时I_UART_RX保持高电平,一旦出现数据信号,起始信号拉低,那么此时会出现一个下降沿。下降沿的出现就要开始检测数据了,因此检测是否出现下降沿是能够正确接收数据的前提。

下降沿检测属于边沿检测的范畴。主要是利用信号的一级寄存、二级寄存的某种逻辑运算进行边沿检测。

下降沿检测的时序图:

 4.生成采样时钟

系统的时钟频率为50MHz。不同波特率对应的采样时钟计数值为:

不同波特率采样时钟的计数值
波特率 计数值
300 7936-1
1200 1984-1
2400 992-1
9600 248-1
19200 124-1
38400 62-1
57600 41-1
115200 21-1

 5.采样数据求和判决

通过对每个BIT信号的七个采样点求和,进行判决,最终确定电平值。如果结果大于等于4,那么判为1,小于等于三则判为0。由于结果大于等于4的最高位为1,小于等于3的最高位为0,所以可以直接将求和结果的最高位输出为判决结果。

三、编程实现

总体的模块编程:

module 		UART_RX 		(

//--------------------输入端口列表-----------------------//

input									I_CLK,
input									I_Rst_n,
input									I_UART_RX,
input				[2:0]				I_BAUD_SELECT,
    
    

//--------------------输出端口列表-----------------------//

output	reg			[7:0]				O_DATA,
output	reg								O_FINISH	
    
);

//------------------内部参数、变量定义-------------------//
//波特率 采样时钟 相关参数
localparam	BAUD_300_SAMPLE			= 			13'd7936-1;
localparam	BAUD_1200_SAMPLE		= 			13'd1984-1;
localparam	BAUD_2400_SAMPLE		= 			13'd992-1;
localparam	BAUD_9600_SAMPLE		= 			13'd248-1;
localparam	BAUD_19200_SAMPLE		= 			13'd124-1;
localparam	BAUD_38400_SAMPLE		= 			13'd62-1;
localparam	BAUD_57600_SAMPLE		= 			13'd41-1;
localparam	BAUD_115200_SAMPLE		= 			13'd21-1;

//模块状态信号
reg				 						R_STATE;// 模块状态 , 1表示处于busy,0表示处于idle

//二级寄存器 同步输入信号
reg				 						R1_UART_RX;
reg				 						R2_UART_RX;

//下降沿检测
reg				 						R_1_NEGEDGE_DETECT;
reg				 						R_2_NEGEDGE_DETECT;
wire									W_NEGEDGE_RES;

//波特率采样时钟 计数相关
reg				 	[12:0]				R_BAUD_SAMPLE_COUNT_MAX;
reg				 	[12:0]				R_BAUD_SAMPLE_COUNT;
reg				 	[7:0]				R_SAMPLE_CLK_COUNT;

//采样时钟 产生
reg				 						R_SAMPLE_CLK;

//中间7位采样数据 求和 结果存放
reg				 	[2:0]				R_SUM_START;
reg				 	[2:0]				R_SUM_BIT0;
reg				 	[2:0]				R_SUM_BIT1;
reg				 	[2:0]				R_SUM_BIT2;
reg				 	[2:0]				R_SUM_BIT3;
reg				 	[2:0]				R_SUM_BIT4;
reg				 	[2:0]				R_SUM_BIT5;
reg				 	[2:0]				R_SUM_BIT6;
reg				 	[2:0]				R_SUM_BIT7;
reg				 	[2:0]				R_SUM_END;

//--------------------模块的程序设计---------------------//
//*** 接收数据数据同步 跨时钟域处理
always @ (posedge I_CLK)
     begin
         if(!I_Rst_n)
         	begin
         		R1_UART_RX <= 0;
  				R2_UART_RX <= 0;
         	end
        else 
         	begin
         	    R1_UART_RX <= I_UART_RX;
         	    R2_UART_RX <= R1_UART_RX;
         	end         
     end

//*** 下降沿检测
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_1_NEGEDGE_DETECT <= 0;
        		R_2_NEGEDGE_DETECT <= 0;
        	end
        else 
        	begin
        	    R_1_NEGEDGE_DETECT <= R2_UART_RX;
        	    R_2_NEGEDGE_DETECT <= R_1_NEGEDGE_DETECT;
        	end       
    end

assign	W_NEGEDGE_RES		= 	(!R_1_NEGEDGE_DETECT) & (R_2_NEGEDGE_DETECT);

//*** 生成采样时钟
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_BAUD_SAMPLE_COUNT_MAX <= 0;
        	end
        else 
        	begin
        	    case (I_BAUD_SELECT) 
        	         0:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_300_SAMPLE;
        	            end
        	        1:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_1200_SAMPLE;
        	            end
        	        2:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_2400_SAMPLE;
        	            end
        	        3:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_9600_SAMPLE;
        	            end
        	        4:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_19200_SAMPLE;
        	            end
        	        5:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_38400_SAMPLE;
        	            end
        	        6:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_57600_SAMPLE;
        	            end
        	        7:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_115200_SAMPLE;
        	            end                   
        	        default:
        	            begin
        	                R_BAUD_SAMPLE_COUNT_MAX <= BAUD_9600_SAMPLE;
        	            end
        	    endcase       	    
        	end        
    end

always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_BAUD_SAMPLE_COUNT <= 0;
        	end
        else if(R_STATE)
            begin
                if (R_BAUD_SAMPLE_COUNT == R_BAUD_SAMPLE_COUNT_MAX) 
                    begin
                        R_BAUD_SAMPLE_COUNT <= 0;
                    end
                else 
                	begin
                	    R_BAUD_SAMPLE_COUNT <= R_BAUD_SAMPLE_COUNT + 1;
                	end          
            end
        else 
        	begin
        	    R_BAUD_SAMPLE_COUNT <= 0;
        	end       
    end
// 采样时钟 脉宽为一个时钟周期的窄脉冲信号
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_SAMPLE_CLK <= 0;
        	end
        else if(R_BAUD_SAMPLE_COUNT == 13'd2)
            begin
                R_SAMPLE_CLK <= 1;
            end
        else 
        	begin
        	    R_SAMPLE_CLK <= 0;
        	end       
    end

//*** 采样时钟 计数
always @ (negedge I_CLK)//下降沿
    begin
        if(!I_Rst_n)
        	begin
        		R_SAMPLE_CLK_COUNT <= 0;
        	end
        else if((R_SAMPLE_CLK_COUNT == 8'd209) || (R_SAMPLE_CLK_COUNT == 8'd15 && (R_SUM_START >= 3)))
            begin
                R_SAMPLE_CLK_COUNT <= 0;
            end
        else if(R_SAMPLE_CLK)
        	begin
        	    R_SAMPLE_CLK_COUNT <= R_SAMPLE_CLK_COUNT + 1;
        	end
        else 
        	begin
        	    R_SAMPLE_CLK_COUNT <= R_SAMPLE_CLK_COUNT;
        	end       
    end

//*** 采样数据求和
always @ (negedge I_CLK)//下降沿
    begin
        if(!I_Rst_n)
        	begin
        		R_SUM_START <=3'd0;
				R_SUM_BIT0	<=3'd0;
				R_SUM_BIT1	<=3'd0;
				R_SUM_BIT2	<=3'd0;
				R_SUM_BIT3	<=3'd0;
				R_SUM_BIT4	<=3'd0;
				R_SUM_BIT5	<=3'd0;
				R_SUM_BIT6	<=3'd0;
				R_SUM_BIT7	<=3'd0;
				R_SUM_END	<=3'd0;
        	end
        else if(R_SAMPLE_CLK)
            begin
                case (R_SAMPLE_CLK_COUNT) 
                     0:
        	            begin
        	                R_SUM_START <=3'd0;
							R_SUM_BIT0	<=3'd0;
							R_SUM_BIT1	<=3'd0;
							R_SUM_BIT2	<=3'd0;
							R_SUM_BIT3	<=3'd0;
							R_SUM_BIT4	<=3'd0;
							R_SUM_BIT5	<=3'd0;
							R_SUM_BIT6	<=3'd0;
							R_SUM_BIT7	<=3'd0;
							R_SUM_END	<=3'd0;
        	            end
        	        8,9,10,11,12,13,14:
        	            begin
        	                R_SUM_START <= R_SUM_START + R2_UART_RX;
        	            end
        	        29,30,31,32,33,34,35:
        	            begin
        	                R_SUM_BIT0 <= R_SUM_BIT0 + R2_UART_RX;
        	            end
        	        50,51,52,53,54,55,56:
        	            begin
        	                R_SUM_BIT1 <= R_SUM_BIT1 + R2_UART_RX;
        	            end
        	        71,72,73,74,75,76,77:
        	            begin
        	                R_SUM_BIT2 <= R_SUM_BIT2 + R2_UART_RX;
        	            end
        	        92,93,94,95,96,97,98:
        	            begin
        	                R_SUM_BIT3 <= R_SUM_BIT3 + R2_UART_RX;
        	            end
        	        113,114,115,116,117,118,119:
        	            begin
        	                R_SUM_BIT4 <= R_SUM_BIT4 + R2_UART_RX;
        	            end
        	        134,135,136,137,138,139,140:
        	            begin
        	                R_SUM_BIT5 <= R_SUM_BIT5 + R2_UART_RX;
        	            end 
        	        155,156,157,158,159,160,161:
        	            begin
        	                R_SUM_BIT6 <= R_SUM_BIT6 + R2_UART_RX;
        	            end
        	        176,177,178,179,180,181,182:
        	            begin
        	                R_SUM_BIT7 <= R_SUM_BIT7 + R2_UART_RX;
        	            end
        	        197,198,199,200,201,202,203:
        	            begin
        	                R_SUM_END <= R_SUM_END + R2_UART_RX;
        	            end                     
        	        default:
        	            begin
        	                R_SUM_START <= R_SUM_START;
							R_SUM_BIT0	<= R_SUM_BIT0 ;
							R_SUM_BIT1	<= R_SUM_BIT1 ;
							R_SUM_BIT2	<= R_SUM_BIT2 ;
							R_SUM_BIT3	<= R_SUM_BIT3 ;
							R_SUM_BIT4	<= R_SUM_BIT4 ;
							R_SUM_BIT5	<= R_SUM_BIT5 ;
							R_SUM_BIT6	<= R_SUM_BIT6 ;
							R_SUM_BIT7	<= R_SUM_BIT7 ;
							R_SUM_END	<= R_SUM_END  ;
        	            end
        	    endcase       	                   
            end        
    end

//判决
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		O_DATA <= 0;
        	end
        else if(R_SAMPLE_CLK_COUNT == 8'd209)
            begin
                O_DATA[0] <= R_SUM_BIT0[2];
                O_DATA[1] <= R_SUM_BIT1[2];
                O_DATA[2] <= R_SUM_BIT2[2];
                O_DATA[3] <= R_SUM_BIT3[2];
                O_DATA[4] <= R_SUM_BIT4[2];
                O_DATA[5] <= R_SUM_BIT5[2];
                O_DATA[6] <= R_SUM_BIT6[2];
                O_DATA[7] <= R_SUM_BIT7[2];
            end       
    end


//*** 传输完成信号
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		O_FINISH <= 0;
        	end
        else if(R_SAMPLE_CLK_COUNT == 8'd209)
            begin
                O_FINISH <= 1;
            end
        else 
        	begin
        	    O_FINISH <= 0;
        	end       
    end

//*** 模块状态信号
always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_STATE <= 0;
        	end
        else if(W_NEGEDGE_RES)
            begin
                R_STATE <= 1;
            end
        else if(O_FINISH || (R_SAMPLE_CLK_COUNT == 8'd15 && (R_SUM_START >= 3)) || (R_SAMPLE_CLK_COUNT == 8'd204 && (R_SUM_END <= 3)))
            begin
                R_STATE <= 0;
            end
        else 
        	begin
        	    R_STATE <= R_STATE;
        	end
        
    end

endmodule

四、仿真验证

利用了上一篇博客,UART发送模块与UART接收模块进行对接。编写testbench:

`timescale 1ns/1ps
module		TB_UART_RX	();

//-----------------被测试模块输入接口声明--------------------//
// TX
reg									I_CLK;
reg									I_Rst_n;
reg									I_VALID;
reg				[7:0]				I_DATA;
// RX
wire								I_UART_RX;	

//----------------被测试模块的输出接口声明-------------------//
// RX
wire			[7:0]				O_DATA;
wire								O_FINISH_RX;	
// TX 
wire								O_STATE;
wire								O_FINISH_TX;	

//-----------------------测试程序---------------------------//
//产生激励时钟
`define			CLK_PERIOD	20		
initial 		I_CLK			=	0 ;
always #(`CLK_PERIOD/2) I_CLK	=	~ I_CLK;

//初始化控制信号
	initial
		begin
			I_Rst_n = 0;
			I_DATA = 8'b0;
			I_VALID = 0;
			// I_BAUD_SELECT = 3'd3;
			#(`CLK_PERIOD * 10 +2);
			I_Rst_n = 1;
			#(`CLK_PERIOD * 10);

			//发送数据 1
			I_DATA = 8'b0110_1001;
			I_VALID = 1;
			#(`CLK_PERIOD * 1);
			I_VALID = 0;

			@(posedge O_FINISH_TX);
			#(`CLK_PERIOD * 1000);

			//发送数据 2
			I_DATA = 8'b1110_1011;
			I_VALID = 1;
			#(`CLK_PERIOD * 1);
			I_VALID = 0;

			@(posedge O_FINISH_TX);
			#(`CLK_PERIOD * 50);
			$stop;			
		end


//被测试模块例化 TX 	
UART_TX inst_UART_TX
	(
		.I_CLK         (I_CLK),
		.I_Rst_n       (I_Rst_n),
		.I_VALID       (I_VALID),
		.I_DATA        (I_DATA),
		.I_BAUD_SELECT (3'd3),
		.O_STATE       (O_STATE),
		.O_DATA        (I_UART_RX),
		.O_FINISH      (O_FINISH_TX)
	);


//被测试模块例化 RX 
UART_RX inst_UART_RX
	(
		.I_CLK         (I_CLK),
		.I_Rst_n       (I_Rst_n),
		.I_UART_RX     (I_UART_RX),
		.I_BAUD_SELECT (3'd3),
		.O_DATA        (O_DATA),
		.O_FINISH      (O_FINISH_RX)
	);


endmodule

仿真波形:

 五、板级调试验证

验证思路:利用VIO IP核进行数据接收。配置如下:


 

 编写顶层模块:

module 		TOP_UART_RX_TEST 		(

//--------------------输入端口列表-----------------------//

input									I_CLK,
input									I_Rst_n,
input									I_UART_RX
     
);

//------------------内部参数、变量定义-------------------//

wire			[7:0]						O_DATA;
wire										O_FINISH;

reg				[7:0]						R_O_DATA;


//--------------------模块的程序设计---------------------//

UART_RX inst_UART_RX
	(
		.I_CLK         (I_CLK),
		.I_Rst_n       (I_Rst_n),
		.I_UART_RX     (I_UART_RX),
		.I_BAUD_SELECT (3'd3),
		.O_DATA        (O_DATA),
		.O_FINISH      (O_FINISH)
	);

always @ (posedge I_CLK)
    begin
        if(!I_Rst_n)
        	begin
        		R_O_DATA <= 0;
        	end
        else if(O_FINISH)
        	begin
        	    R_O_DATA <= O_DATA;
        	end 
        else 
           	begin
           	    R_O_DATA <= R_O_DATA;
           	end
           	      
    end



UART_VIO_RX your_instance_name (
  .clk(I_CLK),              // input wire clk
  .probe_in0(R_O_DATA)  // input wire [7 : 0] probe_in0
);

endmodule

然后对设计进行综合、布局布线、约束、实现,下载比特流文件。结合串口助手进行相关调试。

 

参考说明

[1].小梅哥 Xilinx FPGA 自学教程 v2.0

THE END
< <上一篇
下一篇>>