FPGA串口接收

串口接收(E:/FPGACode/uart_rx)

一、原理与思路

1、 基本原理:采样
2、起始位检测:通过边沿检测电路(下降沿)
在这里插入图片描述
使用两个寄存器,当第一个clk上升沿时得到q1传到reg2,第二个clk上升沿时reg2获得q1
传过来的q2,并且reg1获得新的q1.若q2=1,q1=0,则为下降沿,反之为上升沿。

//边沿检测
	reg [1:0] uart_tx_r;//两个D触发器
	always@(posedge Clk)begin
		uart_tx_r[0]<=uart_rx;
		uart_tx_r[1]<=uart_tx_r[0];
	end
	
	wire posedge_uart_tx,nedge_uart_tx;
	assign posedge_uart_tx=(uart_tx_r==2'b01)?1:0;
	assign nedge_uart_tx=(uart_tx_r==2'b10)?1:0;

二、技巧

一位数据采样多次,统计得到高电平出现的次数,次数多的就是该位的电平值。(采样7次,0、1、2、3为低电平,4、5、6、7为高电平)

  • 将1位数据分成16段,舍弃前5段和后4段,取中间7段进行采样。
  • 设波特率位9600
    每一位时间:1000,000,000/9600
    每一小段时间:1000,000,000/9600/16
    每一小段的计数周期:1000,000,000/9600/16/20=325(次)
  • 设波特率位115200——27次
    在这里插入图片描述
  • Bps_DR为上图每一个小块的clk计数值
  • 一开始写的是if(nedge_uart_tx),是因为下降沿开始接收,但是nedge_uart_tx=1只维持一个时钟周期,如果要保持一个状态,必须要加一个RX_EN,所以写了一个RX_EN的always块
	wire bps_clk_16x;//每一个bps_clk分成16份,每一份取中间,为一个脉冲
	assign bps_clk_16x=(div_cnt==Bps_DR/2)?1:0;//脉冲在中间
	
	//分频计数器
	reg [8:0]div_cnt;
	always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			div_cnt<=0;
		else if(RX_EN)begin//一开始写的是if(nedge_uart_tx),但是nedge_uart_tx=1只维持一个时钟周期,如果要保持一个状态,必须要加一个RX_EN
			if(div_cnt==Bps_DR)
				div_cnt<=0;
			else 
				div_cnt<=div_cnt+1'b1;
		end
		else
			div_cnt<=0;
	end

	always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			RX_EN<=0;
		else if(nedge_uart_tx)
			RX_EN<=1;
		else if(Rx_Done==1 || (start_bit>=4))//传输结束或者起始位采样之后发现并不是低电平0
			RX_EN<=0;
	end
	
	//分段采样——1位分为16段,一共10位(开始+data+结束)
	//16段舍弃前5段和后4段,取中间7段进行采样
	reg [7:0] bps_cnt;//160段
	always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			bps_cnt<=0;
		else if(RX_EN) begin
			if(bps_clk_16x)begin
				if(bps_cnt==159)
					bps_cnt<=0;
				else
					bps_cnt<=bps_cnt+1'b1;
			end
			else
				bps_cnt<=bps_cnt;
			end
		else
			bps_cnt<=0;
	end
	always@(posedge Clk or negedge Reset_n)begin
			if(!Reset_n)
				Rx_Done<=0;
			else if(bps_clk_16x && (bps_cnt==159))
				Rx_Done<=1;
			else begin
				Rx_Done<=0;
			end
		end

关于采样:
一开始我以为是当bps_clk_16x=1&&bps_cnt=1时,是16x的第一段,因为当bps_clk_16x=1时,bps_cnt++。
但是根据老师写的代码发现:bps_cnt=5,6,7,8,9,10,11:start_bit<=start_bit+uart_rx
那这是为什么呢?后来查看波形图发现:mark1(第一条蓝色线)到mark2为1位分成16段中的一段。由于时序逻辑+非阻塞赋值,此时代码中posedge Clk&&bps_clk_16x=1&&bps_cnt=0时(图中黄色线),根据波形图检测到的就是第1段,并不是所认为bps_cnt==1时才是第一段。
也可以这样去理解:在bps_clk_16x=、=1也就是黄色线那里,bps_cnt++,即从0变成1,而且根据非阻塞赋值,此时取到的bps_cnt的值为0,处于16段中的第一段。所以当bps_cnt能取到1时,说明它是由1变成了2,就是bps_clk_16x第二次为1,也就是第2段。
由上述分析可知:当bps_cnt=n-1时,也就是第n段。
在这里插入图片描述

//采样
	reg [2:0] r_data[7:0];//3位r_data8个-数据位
	reg [2:0] start_bit;//起始位
	reg [2:0] stop_bit;//结束位
	
	always@(posedge Clk or 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_16x) 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_rx;//一位分成16份,前5份不要
				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;
				53,54,55,56,57,58,59: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:stop_bit<= stop_bit + uart_rx;
				default:;
			endcase
		end
	end
	
	//根据采样的中间7位判断,此位是为高电平还是低电平
	always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			Data<=0;
		else if(bps_clk_16x &&(bps_cnt==159))begin
			Data[0]<=(r_data[0]>=4)?1'b1:1'b0;
			Data[1]<=(r_data[1]>=4)?1'b1:1'b0;
			Data[2]<=(r_data[2]>=4)?1'b1:1'b0;
			Data[3]<=(r_data[3]>=4)?1'b1:1'b0;
			Data[4]<=(r_data[4]>=4)?1'b1:1'b0;
			Data[5]<=(r_data[5]>=4)?1'b1:1'b0;
			Data[6]<=(r_data[6]>=4)?1'b1:1'b0;
			Data[7]<=(r_data[7]>=4)?1'b1:1'b0;
		end
	end

三、仿真测试

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

	reg uart_rx,Clk,Reset_n;
	wire [7:0] Data;
	wire Rx_Done;
	uart_byte_rx uart_byte_rx(
		uart_rx,
		Clk,
		Reset_n,
		4,
		Data,
		Rx_Done
		);
	initial Clk=1;
	always #10 Clk=~Clk;

	initial begin
		Reset_n=0;
		#201;
		Reset_n=1;
		#200;
		uart_tx_byte(8'h5a);
		@(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 uart_tx_byte;
		input [7:0] tx_data;
		begin
			uart_rx=1'b1;
			#20;
			uart_rx=1'b0;
			#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'b1;
			#8680;
		end
	endtask	
endmodule

四、问题

1、只接收了第一个数据,后面就一直检测不到posedge Rx_Done
原因:由波形图可得,当uart_rx传输了停止位到结束只有8.09,但是代码中是经过#8680ns停止(uart_tx_byte任务执行完成)。所以经过8680ns后检测到的Rx_Done已经为0了,所以检测不到posedge
在这里插入图片描述
一开始的代码是else if(bps_clk_16x && (bps_cnt==159))
就相当关于最后一个停止位的最后一块只计数了一半,让它计满即可。

always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			Rx_Done<=0;
		else if((div_cnt==Bps_DR) && (bps_cnt==159))
			Rx_Done<=1;
		else begin
			Rx_Done<=0;
		end
	end

2、bps_cnt计数的160比159小一半
分析:div_cnt应该是从计数14->26->0->13结束。所以修改

always@(posedge Clk or negedge Reset_n)begin
		if(!Reset_n)
			Rx_Done<=0;
		else if(bps_clk_16x && (bps_cnt==160))
			Rx_Done<=1;
		else begin
			Rx_Done<=0;
		end
	end

在这里插入图片描述
3、还是提前到来
由于Verilog的误差,后面有小数,仿真时间走不到实际值。所以Rx_Done会提前。

initial begin
		Reset_n=0;
		#201;
		Reset_n=1;
		#200;
		uart_tx_byte(8'h5a);
		#90000;
		uart_tx_byte(8'ha5);
		#90000;
		uart_tx_byte(8'h86);
		#90000;
		$stop;
	end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值