UART接收模块设计与验证

项目名称

UART接收模块设计与验证

项目要求

利用串口助手向fpga发送数据用来点亮led

项目说明

rs232协议示意图如下,当PC未给FPGA通过串口发送数据的时候,串口处于空闲状态(高电平),当PC端需要开始发送数据时,需要给一个起始位,然后就是发送一个字节的数据,从低位到高位依次发送,数据线从高电平转为低电平代表一个起始位。发送一帧数据之后校验位,在本项目中未使用。

这个项目比较简单,就直接开始进行设计,串口的波特率为9600bit/s,就是每秒传送9600位,每一位需要传送的时                 为1/9600 s=1_000_000_000/9600ns=104167ns ,那么传送一位数据需要计数器计数104167/20=5208(取整)次。

好了开始进行设计,下图是设计的时序图,rx_data3,是同步之后的rs232数据线上的数据,bpa_cnt是波特率计数器,data_flag是数据有效标志,data_sample是计数一半的时候开始采样数据,比较稳定,data_cnt是接收数据计数,flag表示接收数据完成标志,data为接收的8位数据。

             

每个人的设计思路不一样,笔者就从自己的设计思路出发进行说明,虽然看起来比较简单,但是也有许多小细节要注意,否则可能会出问题,当检测到rx_data3下降沿之后就开始 进行发送数据,先是发送一个低电平的起始位,当数据有效的时候,波特率小于5207,就一直+1,每次计数一半的时候进行采样数据,将data_sample置一拍高电平。当data_sample为高电平的时候,下一个时钟沿到来的时候,接收数据计数器开始计数。

这里需要注意的是data_cnt在复位的时候为0,当计数到10的时候,下一个采样时间到来的时候就从1开始计数,为什么要从1开始计数,因为下一个采样时间到来的时候对应的是上图中的第一个起始位采样点,上图中的采样点到来data_cnt为1,如果从0开始计数那么就会出错,笔者也是在这里出错了,所以也花费了一些时间。

第二个需要注意的是当data_cnt为1的时候data_sample为高电平的时候开始接收数据。

第三个要注意的是接收数据完成标志信号flag是在接收完8位数据之后开始置高,当data_cnt为9的时候,bps_cnt为计数中间值加3的时候置高,因为我想要一个时钟周期的高电平,如果只是当data_cnt为9的时候置高,将有5208个时钟周期的高电平,加3是因为,在data_cnt为8的最后一个时钟周期为计数中间值,随便加上一个1-5207都可以,这里没有用到标志信号,笔者直接将接收的数据给led显示。最后进行代码设计。

代码设计

顶层模块设计

module uart_rx_top(
	input			clk,
	input			rst_n,
	input			rs232_data,
	output		led595_dout,		
	output		led595_clk,	
	output		led595_latch	

);

wire [7:0] data;
wire flag;
uart_rx
#(.bps_cnt_end(5207))
uart_rx
(
	.clk(clk),
	.rst_n(rst_n),
	.rs232_data(rs232_data),
	.data(data),
	.flag(flag)
);


led_74595_driver led_74595_driver
(
	.clk(clk),  		
	.rst_n(rst_n),		
	.led595_dout(led595_dout),		
	.led595_clk(led595_clk),		
	.led595_latch(led595_latch),	
	.led_data(data)		
);
endmodule

接收模块设计

module uart_rx
#(parameter bps_cnt_end=5207)
(
	input 			clk,
	input				rst_n,
	input				rs232_data,
	output 	reg [7:0]data,
	output reg		flag
);

//串口数据同步处理
reg rx_data1;
reg rx_data2;
reg rx_data3;
always@(posedge clk or negedge rst_n)
	if(!rst_n)begin
		rx_data1<=0;
	   rx_data2<=0;
	   rx_data3<=0;
	end
	else begin
		rx_data1<=rs232_data;
		rx_data2<=rx_data1;
		rx_data3<=rx_data2;
	end
	
//下降沿起始位检测
wire nedge= !rx_data2 & rx_data3;

reg data_flag;
reg [12:0]bps_cnt;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_cnt<=0;
	else if(data_flag)begin
		if(bps_cnt<5207)
			bps_cnt<=bps_cnt+1;
		else
			bps_cnt<=0;
	end
	else
		bps_cnt<=0;

reg [3:0] data_cnt;		
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		data_flag<=0;
	else if(nedge)
		data_flag<=1;
	else if(bps_cnt==5205 && data_cnt==10)
		data_flag<=0;

reg data_sample;
reg [12:0] bps_cnt_mid=bps_cnt_end/2+1;
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		data_sample<=0;
	else if(data_flag && bps_cnt==bps_cnt_mid)
		data_sample<=1;
	else	
		data_sample<=0;
		
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		data_cnt<=0;
	else if(data_sample)begin
		if(data_cnt<10)
			data_cnt<=data_cnt+1;
		else
			data_cnt<=1;
	end
	else
		data_cnt<=data_cnt;
		
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		flag<=0;
	else if(data_cnt==9 && bps_cnt==bps_cnt_mid+3)
		flag<=1;
	else
		flag<=0;
		
//接收数据
always@(posedge clk or negedge rst_n)
	if(!rst_n)
		data<=0;
	else if(data_sample && data_cnt>=1 && data_cnt<=8)
		data<={rx_data3,data[7:1]};
	else 
		data<=data;


endmodule


74hc595驱动


module led_74595_driver
(
	input			clk,  			//50MHz
	input			rst_n,			//global reset


	output			led595_dout,	//74hc595 serial data input	
	output			led595_clk,		//74hc595 shift clock (rising edge)
	output			led595_latch,	//74hc595 latch clock (rising edge)


	input	[7:0]	led_data		//led data input	
);



reg	[7:0]	led_data_r = 8'h00;
reg	update_flag;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
		led_data_r <= 0;
		update_flag <= 1;	
		end
	else
		begin
		led_data_r <= led_data;
		update_flag <= (led_data_r != led_data) ? 1'b1 : 1'b0;
		end
end


localparam	DELAY_CNT	=	3'd7;
reg	[2:0]	delay_cnt;                  
reg	shift_state;	
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		delay_cnt <= 0;
	else if(shift_state == 1'b1)
		delay_cnt <= (delay_cnt < DELAY_CNT) ? delay_cnt + 1'b1 : 3'd0;
	else
		delay_cnt <= 0;
end
wire	shift_flag = (delay_cnt ==  DELAY_CNT) ? 1'b1 : 1'b0;
wire	shift_clk = (delay_cnt > DELAY_CNT/2) ? 1'b1 : 1'b0;



reg	[3:0]	led_cnt;
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
		shift_state <= 0;
		led_cnt <= 0;
		end
	else 
		begin
		case(shift_state)
		0:	begin
			led_cnt <= 0;
			if(update_flag)
				shift_state <= 1;
			else
				shift_state <= 0;
			end
		1:	begin
			if(shift_flag)
				begin
				if(led_cnt < 4'd8)
					begin
					led_cnt <=  led_cnt + 1'b1;
					shift_state <= 1'd1;
					end
				else
					begin
					led_cnt <= 0;
					shift_state <= 0;
					end
				end
			else
				begin
				led_cnt <= led_cnt;
				shift_state <= shift_state;
				end
			end
		endcase
		end
end
assign	led595_dout = (shift_state == 1'b1 && led_cnt < 4'd8) ? led_data[3'd7 - led_cnt] : 1'b0; 
assign	led595_clk = (shift_state == 1'b1 && led_cnt < 4'd8) ? shift_clk : 1'b0; 
assign	led595_latch = (shift_state == 1'b1 && led_cnt == 4'd8) ? 1'b1 : 1'b0; 

endmodule

代码仿真

就只是对串口接收模块进行了仿真,对顶层模块仿真方法也是一样的,就不在赘述了。模拟串口发送11,55,22,以16进制发送。

`timescale 1ns/1ns
module uart_rx_tb;
	reg 				clk;
	reg				rst_n;
	reg				rs232_data_tx;
	wire		[7:0] data;
	wire				flag;

uart_rx
#(.bps_cnt_end(5207))
uart_rx
(
	.clk(clk),
	.rst_n(rst_n),
	.rs232_data(rs232_data_tx),
	.data(data),
	.flag(flag)
);

initial clk=0;
always #10 clk=~clk;

reg [7:0] temp_data;
initial begin
	rst_n=0;
	temp_data=8'h0;
	rs232_data_tx=0;
	#200;
	rst_n=1;
	temp_data=8'h11;
	rs232_data_tx=1;
	#200;
	rs232_data_tx=0;
	
	#104160;
	rs232_data_tx=temp_data[0];
	#104160;
	rs232_data_tx=temp_data[1];
	#104160;
	rs232_data_tx=temp_data[2];
	#104160;
	rs232_data_tx=temp_data[3];
	#104160;
	rs232_data_tx=temp_data[4];
	#104160;
	rs232_data_tx=temp_data[5];
	#104160;
	rs232_data_tx=temp_data[6];
	#104160;
	rs232_data_tx=temp_data[7];
	#104160;
	rs232_data_tx=1;
	
	#104160;
	rs232_data_tx=0;
	
	#104160;
	
	temp_data=8'h55;
	rs232_data_tx=1;
	#200;
	rs232_data_tx=0;
	
	#104160;
	rs232_data_tx=temp_data[0];
	#104160;
	rs232_data_tx=temp_data[1];
	#104160;
	rs232_data_tx=temp_data[2];
	#104160;
	rs232_data_tx=temp_data[3];
	#104160;
	rs232_data_tx=temp_data[4];
	#104160;
	rs232_data_tx=temp_data[5];
	#104160;
	rs232_data_tx=temp_data[6];
	#104160;
	rs232_data_tx=temp_data[7];
	#104160;
	rs232_data_tx=1;
	#104160;

	
	
	temp_data=8'h22;
	rs232_data_tx=1;
	#200;
	rs232_data_tx=0;
	
	#104160;
	rs232_data_tx=temp_data[0];
	#104160;
	rs232_data_tx=temp_data[1];
	#104160;
	rs232_data_tx=temp_data[2];
	#104160;
	rs232_data_tx=temp_data[3];
	#104160;
	rs232_data_tx=temp_data[4];
	#104160;
	rs232_data_tx=temp_data[5];
	#104160;
	rs232_data_tx=temp_data[6];
	#104160;
	rs232_data_tx=temp_data[7];
	#104160;
	rs232_data_tx=1;
	
	#104160;
	rs232_data_tx=0;
	$stop;
	
end

endmodule

仿真结果

可以看到能正确接收到数据11,55和22。

项目验证

以16进制发送22点亮led。

                                    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值