本文为明德扬原创文章,转载请注明出处! 
	 串口接收模块的功能:接收上位机通过串口发送过来的数据,进行串并转换之后送给下游模块。 
	      注:串口波特率9600,无奇偶校验位。 
	一、设计架构 
	上图是与上位机通信的串口的时序图。我们从图中可以获取到如下关键信息。 
	
 
	1. 串口数据线位宽为1bit,默认状态下为高电平。 
	2. 每次上游模块发送数据,都是先发送1位的起始位0,然后发送8位的数据,最后是1位的停止位1。 
	3. 每1位所占的时间,可以通过波特率来计算。计算方法如下: 
	      波特率是指1s内发送或接受了多少比特数据,若波特率为9600 bit/s,则1s可以传输9600bit,那么传输1bit的时间为:1/9600(s)。以mp801开发板为例,时钟频率为50M,那么发送1bit就需要5208个时钟周期。 
 
	串口接收模块采用两个计数器的架构,这两个计数器分别表示接收1bit需要的时间和共需要接收多少bit。其结构图如下: 
	      计数器cnt0:对接收1bit数据需要的时间进行计数;接收1bit需要5208个时钟周期。该计数器的计数周期为5208。 
	      计数器cnt1:对接收多少bit的数据进行计数;停止位不参与,起始位加上数据位共9bit。该计数器的计数周期为9。 
	
 
	      本工程使用了检测信号下降沿的方法,信号下降沿的检测方法: 
	
 
	 检查uart_rx的下降沿,就要用到FPGA里的边沿检测技术。所谓的边沿检测,就是检测输入信号,或者FPGA内部逻辑信号的跳变,即上升沿或者下降沿的检测。就比如前面uart_rx由1变0时,就出现了下降沿,接着一次指令结束,uart_rx由0变1时,就出现了上升沿,边沿检测技术这在FPGA电路设计泛。电路图如下: 
	
 
	  对应的信号列表如下图所示: 
	
 
	      中间信号,trigger连到触发器的信号输入端D,触发器的输出器连的是tri_ff0。将trigger取反,与tri_ff0相与,就得到信号neg_edge,如果neg_edge=1就表示检测到trigger的下降沿。将tri_ff0取反,与trigger相与,就得到信号pos_edge,如果pos_edge=1,就表示检测到trigger的上升沿。 
	我们来讲解这个原理,信号的波形图如下: 
	 Tri_ff0是触发器的输出,因此tri_ff0的信号与trigger信号相似,但是相差了一个时钟周期。我们也可以理解为:每个时钟上升沿看到的tri_ff0的值,其实就是triffer信号上一个时钟看到的值,也就是tri_ff0是trigger之前的值。 
	我们在看第3时钟上升沿,此时trigger值为0,而tri_ff0的值为1,即当前trigger的值为0,之前的值为1,这就是下降沿,此时neg_edge为1。当看到neg_edge为1,就表示检测到trigger的下降沿了。同理在第7个时钟上升沿,看到trigger值为1,而之前值为0,pos_edge为1,表示检测到trigger的上升沿。 
	      本模块输入信号uart_rx是异步信号,异步信号都需要经验同步化后,才能够使用。异步信号同步化如下: 
	在前面讨论边沿检测的波形中,我们把trigger当做理想的同步信号来考虑,也就是trigger满足D触发器的建立和保持时间,在同步系统中实现边沿检测不是问题。但如果trigger不是理想的同步信号,例如外部按键信号,以及本工程的uart_rx信号。这些信号什么时候产生变化,是外部传输指令给FPGA,对于FPGA来说完全是随机的。很有可能出现,信号在时钟上升沿产生变化的情况,从而无法满足触发器的建立时间和保持时间要求,出现亚稳态的情况,从而导致系统崩溃。如下图,我们先将信号用2个触发器进行了寄存,确定了信号的稳定性,然后再进行边沿检测,达成了同步系统中实现边沿检测的需求。 
	       那么边沿检测的代码设计就需要先进行触发器的设计,假设输入的信号trigger不是同步信号,要将该信号用2个触发器进行寄存,得到tri_ff0和tri_ff1。需要特别注意的是,在第一个触发器阶段,信号依旧有亚稳态的情况,因此tri_ff0绝对不可以拿来当条件使用,只能使用tri_ff1。接着进行检测边沿,根据前面所说,得出同步信号后再用寄存器寄存,得到tri_ff2。根据tri_ff1和tri_ff2,我们就可以得到边沿检测结果。当tri_ff1==1且tri_ff2==0时,上升沿的pos_edge有效;当tri_ff1==0且tri_ff2==1时,下降沿的neg_edge有效,代码如下: 
	
		
			
				
					
						
							1 
						 
						
							2 
						 
						
							3 
						 
						
							4 
						 
						
							5 
						 
						
							6 
						 
						
							7 
						 
						
							8 
						 
						
							9 
						 
						
							10 
						 
						
							11 
						 
						
							12 
						 
						
							13 
						 
						
							14 
						 
						
							15 
						 
						
							16 
						 
						
							17 
						 
						
							18 
						 
						
							19 
						 
						
							1 
						 
					 
				 | 
				
					
						
						
							always  @(posedgeclk or negedgerst_n)begin 
						 
						
							    if(rst_n==1'b0)begin 
						 
						
							        tri_ff0 <= 0; 
						 
						
							        tri_ff1 <= 0; 
						 
						
							        tri_ff2 <= 0; 
						 
						
							    end 
						 
						
							    else begin 
						 
						
							        tri_ff0 <= trigger ; 
						 
						
							        tri_ff1 <= tri_ff0 ; 
						 
						
							        tri_ff2 <= tri_ff1 ; 
						 
						
							    end 
						 
						
							end 
						 
						
							[size=9.0000pt] 
						 
						
							assign neg_edge = tri_ff1==0 && tri_ff2==1; 
						 
						
							assign pos_edge = tri_ff1==1 && tri_ff2==0; 
						 
					 
				 | 
			
		
	
 
	 综上所述,如果进来的信号是异步信号,那么就需要先进行同步化,再做检测,即通过打两拍的方式,实现了信号的同步化;再通过打一拍的方式,实现边沿检测电路。反之,如果进来的信号本身就是同步信号,那就没有必要做同步化了,可以直接做边沿检测。 
	二、信号的意义 
	
		
			| 
				 
					信号 
				 
			 | 
			
				
			 | 
			
				
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					串口接收数据线,位宽为1bit,空闲时为高电平,开始接收时, 
				 
				
					会变为低电平,持续1/9600(s),然后是数据、停止位等。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					串口接收数据有效指示信号。当其为高电平时,对应的输出rx_data有效, 
				 
				
					表示接收到1个字节的数据。注意,1个时钟的高电平表示接收到1个字节数据。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
					
						输入进来的uart_rx信号寄存一拍后的信号。 
					 
					
						该信号的目的是为了做时序同步。 
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					对uart_ff0寄存一拍后的信号。 
				 
				
					该信号的目的是为了做时序同步。该信号就是同步化后的, 
				 
				
					可以使用的信号。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
					
						对uart_ff1寄存一拍的信号。 
					 
					
						该信号的目的,是与uart_ff1配合,检测uart_ff1的下降沿。 
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					模块处于接收数据状态的指示信号,当其为1时, 
				 
				
					表示正在接收数据;为0时,表示空闲状态。 
				 
				
					产生逻辑是:当检测到uart_ff1的下降沿时变高, 
				 
				
					当接收完整个字节数据时(计数器cnt1数完了)就变低。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					对每输入1bit数据的时间进行计数,接收1bit需要5208个时钟周期。 
				 
				
					该计数器的计数周期为5208。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
					
						计数器cnt0计数有效信号。当处于处于接收状态时,该信号有效。 
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					计数器cnt0的结束条件。接收1bit需要5208个时钟周期, 
				 
				
					所以数到5208个就结束。 
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					对接收多少bit的数据进行计数,停止位不参与,
				 
				
					起始位加上数据位共9bit。该计数器的计数周期为9。
				 
				
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				
					
						计数器cnt1的加一条件。接收完1位(end_cnt0),就有效。 
					 
				 
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
						计数器cnt1的结束条件,共接收9bit数据,
					 
					
						所以数到9个就结束。
					 
				
			 | 
		
		
			| 
				
			 | 
			
				
			 | 
			
				 
					 
				 
				
					uart_ff1下降沿有效指示信号。 
				 
				
					当检测到接收的数据前一时刻uart_ff2为高电平,
				 
				
					当前时刻uart_ff1为低电平时,就有效。
				 
				
					 
				 
			 | 
		
	
	三、参考代码 
	 下面展出本模块的设计,欢迎进一步交流,如果需要整个项目源代码,欢迎与明德扬联系。 
	
		
			
				
					
						
							1 
						 
						
							2 
						 
						
							3 
						 
						
							4 
						 
						
							5 
						 
						
							6 
						 
						
							7 
						 
						
							8 
						 
						
							9 
						 
						
							10 
						 
						
							11 
						 
						
							12 
						 
						
							13 
						 
						
							14 
						 
						
							15 
						 
						
							16 
						 
						
							17 
						 
						
							18 
						 
						
							19 
						 
						
							20 
						 
						
							21 
						 
						
							22 
						 
						
							23 
						 
						
							24 
						 
						
							25 
						 
						
							26 
						 
						
							27 
						 
						
							28 
						 
						
							29 
						 
						
							30 
						 
						
							31 
						 
						
							32 
						 
						
							33 
						 
						
							34 
						 
						
							35 
						 
						
							36 
						 
						
							37 
						 
						
							38 
						 
						
							39 
						 
						
							40 
						 
						
							41 
						 
						
							42 
						 
						
							43 
						 
						
							44 
						 
						
							45 
						 
						
							46 
						 
						
							47 
						 
						
							48 
						 
						
							49 
						 
						
							50 
						 
						
							51 
						 
						
							52 
						 
						
							53 
						 
						
							54 
						 
						
							55 
						 
						
							56 
						 
						
							57 
						 
						
							58 
						 
						
							59 
						 
						
							60 
						 
						
							61 
						 
						
							62 
						 
						
							63 
						 
						
							64 
						 
						
							65 
						 
						
							66 
						 
						
							67 
						 
						
							68 
						 
						
							69 
						 
						
							70 
						 
						
							71 
						 
						
							72 
						 
						
							73 
						 
						
							74 
						 
						
							75 
						 
						
							76 
						 
						
							77 
						 
						
							78 
						 
						
							79 
						 
						
							80 
						 
						
							81 
						 
						
							82 
						 
						
							83 
						 
						
							84 
						 
						
							85 
						 
						
							86 
						 
						
							87 
						 
						
							88 
						 
						
							89 
						 
						
							90 
						 
						
							91 
						 
						
							92 
						 
						
							93 
						 
						
							94 
						 
						
							95 
						 
						
							96 
						 
						
							97 
						 
						
							98 
						 
						
							99 
						 
						
							100 
						 
						
							101 
						 
						
							102 
						 
						
							103 
						 
						
							104 
						 
						
							105 
						 
						
							106 
						 
						
							107 
						 
						
							108 
						 
						
							109 
						 
						
							110 
						 
						
							111 
						 
						
							112 
						 
						
							113 
						 
						
							114 
						 
						
							115 
						 
						
							116 
						 
					 
				 | 
				
					
						
							module uart_rx( 
						 
						
							    clk     , 
						 
						
							    rst_n   , 
						 
						
							    uart_rx , 
						 
						
							    rx_vld  , 
						 
						
							    rx_data 
						 
						
							    ); 
						 
						
							[size=9.0000pt] 
						 
						
							    parameter      CNT_BTL =     20'd2604; 
						 
						
							    parameter      CNT_MID =     20'd1302; 
						 
						
							    parameter      CNT_RX =         4'd9; 
						 
						
							    parameter      DATA_W =         8; 
						 
						
							[size=9.0000pt] 
						 
						
							    input               clk             ; 
						 
						
							    input               rst_n           ; 
						 
						
							    input               uart_rx         ; 
						 
						
							[size=9.0000pt] 
						 
						
							    wire                uart_rx         ; 
						 
						
							    output[DATA_W-1:0]  rx_data         ; 
						 
						
							    output               rx_vld         ; 
						 
						
							[size=9.0000pt] 
						 
						
							    reg   [DATA_W-1:0]  rx_data         ; 
						 
						
							    reg                 rx_vld          ; 
						 
						
							[size=9.0000pt] 
						 
						
							    reg                 uart_rx_ff0     ; 
						 
						
							    reg                 uart_rx_ff1     ; 
						 
						
							    reg                 uart_rx_ff2     ; 
						 
						
							    reg                 flag_add_add            ; 
						 
						
							    reg   [19:0]        cnt0            ; 
						 
						
							    wire                add_cnt0        ; 
						 
						
							    wire                end_cnt0        ; 
						 
						
							[size=9.0000pt] 
						 
						
							    reg   [3:0]         cnt1            ; 
						 
						
							    wire                add_cnt1        ; 
						 
						
							    wire                end_cnt1        ; 
						 
						
							    wire                add_en          ; 
						 
						
							[size=9.0000pt] 
						 
						
							    always @(posedge clk or negedge rst_n)begin 
						 
						
							        if(!rst_n)begin 
						 
						
							            cnt0 <= 0; 
						 
						
							        end 
						 
						
							        else if(add_cnt0)begin 
						 
						
							            if(end_cnt0) 
						 
						
							                cnt0 <= 0; 
						 
						
							            else 
						 
						
							                cnt0 <= cnt0 + 1; 
						 
						
							        end 
						 
						
							    end 
						 
						
							[size=9.0000pt] 
						 
						
							    assign add_cnt0 = flag_add[size=9.0000pt]; 
						 
						
							    assign end_cnt0 = add_cnt0 && cnt0== CNT_BTL-1; 
						 
						
							[size=9.0000pt] 
						 
						
							    always @(posedge clk or negedge rst_n)begin 
						 
						
							        if(!rst_n)begin 
						 
						
							            cnt1 <= 0; 
						 
						
							        end 
						 
						
							        else if(add_cnt1)begin 
						 
						
							            if(end_cnt1) 
						 
						
							                cnt1 <= 0; 
						 
						
							            else 
						 
						
							                cnt1 <= cnt1 + 1; 
						 
						
							        end 
						 
						
							    end 
						 
						
							[size=9.0000pt] 
						 
						
							    assign add_cnt1 = end_cnt0; 
						 
						
							    assign end_cnt1 = add_cnt1 && cnt1== CNT_RX-1; 
						 
						
							[size=9.0000pt] 
						 
						
							    always  @(posedge clk or negedge rst_n)begin 
						 
						
							        if(rst_n==1'b0)begin 
						 
						
							            uart_rx_ff0  <= 1'b1; 
						 
						
							            uart_rx_ff1 <= 1'b1; 
						 
						
							            uart_rx_ff2 <= 1'b1; 
						 
						
							        end 
						 
						
							        else begin 
						 
						
							            uart_rx_ff0  <= uart_rx; 
						 
						
							            uart_rx_ff1 <= uart_rx_ff0; 
						 
						
							            uart_rx_ff2 <= uart_rx_ff1; 
						 
						
							        end 
						 
						
							    end 
						 
						
							    assign  add_en = uart_rx_ff2&&~uart_rx_ff1; 
						 
						
							    always  @(posedge clk or negedge rst_n)begin 
						 
						
							        if(rst_n==1'b0)begin 
						 
						
							            flag_add <= 1'b0; 
						 
						
							        end 
						 
						
							        else if(add_en)begin 
						 
						
							            flag_add <= 1'b1; 
						 
						
							        end 
						 
						
							        else if(end_cnt1)begin 
						 
						
							            flag_add <= 1'b0; 
						 
						
							        end 
						 
						
							    end 
						 
						
							    always  @(posedge clk or negedge rst_n)begin 
						 
						
							        if(rst_n==1'b0)begin 
						 
						
							            rx_vld <= 1'b0; 
						 
						
							        end 
						 
						
							        else if(end_cnt1)begin 
						 
						
							            rx_vld <= 1'b1; 
						 
						
							        end 
						 
						
							        else begin 
						 
						
							            rx_vld <= 1'b0; 
						 
						
							        end  
						 
						
							    end 
						 
						
							    always  @(posedge clk or negedge rst_n)begin 
						 
						
							        if(rst_n==1'b0)begin 
						 
						
							            rx_data <= 8'b0; 
						 
						
							        end 
						 
						
							        else if(cnt1!=0&&add_cnt0&&cnt0==CNT_MID-1)begin 
						 
						
							            rx_data[cnt1-1] <= uart_rx_r2; 
						 
						
							        end 
						 
						
							    end 
						 
						
							endmodule 
						 
					 
				 | 
			
		
	
 
	  
	
 
	以上就是基于FPGA的边缘检测工程串口接收模块的代码分享,关注明德扬获取边缘检测工程完整的源代码!