欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

串口收发之ram存取

程序员文章站 2022-06-23 17:15:56
...

项目名称

串口收发之ram存取

具体要求

串口发送6个数据到FPGA,通过双端口ram将数据缓存,每按下一个按键,上位机接收一个数据,按下按键6次接收5位数据完毕

设计说明

下图为设计框架,除了ram_ctrl模块,剩下的模块在前面都介绍过,将前设计的模块进行调用总体比较简单。

串口收发之ram存取

这里将前面设计的串口发送模块重新设计一下,根据以下的时序图可以很轻松设计出串口发送模块。

串口收发之ram存取

 需要注意的是笔者在进行ram控制模块设计的时候遇到了一个问题,在进行读地址的时候,如果按以下的方式进行设计,也是没有问题的,问题是当发送使能为1的时候,下一个时钟上升沿才执行if下的语句,而在这期间就开始读出0地址的数据,在下一个时钟上升沿读出1地址的数据,但是0地址的数据并没有被读出,应该是在时钟上升沿的时候才开始被执行吧。

aaa@qq.com(posedge clk or negedge rst_n)
   if(!rst_n)    
        rdaddr<=0;
    else if(send_en)begin
        if(rdaddr==5)
            rdaddr<=0;
        else
            rdaddr<=rdaddr+1;
    end
   else 
     rdaddr<=rdaddr;

笔者将上面的代码进行修改用状态机来实现可以正常读取出存入的5位数据。 

reg state;                                                
aaa@qq.com(posedge clk or negedge rst_n)
    if(!rst_n)    begin
        rdaddr<=0;
        state<=0;
    end
    else if(send_en)begin
            case(state)
                0:    begin
                        rdaddr<=0;
                        state<=1;
                    end
                1:begin
                        if(rdaddr==5)begin
                            rdaddr<=0;
                            state<=1;
                        end
                        else
                            rdaddr<=rdaddr+1;
                    end
            endcase
        end
            else 
        rdaddr<=rdaddr;

代码设计

 顶层模块

module uart_top(
	input			clk,
	input			rst_n,
	input			rs232_data,
	input			key_in,
	output 	 	rs232_tx
);

wire [7:0] data;
wire flag;
wire key_flag;
wire key_state;

uart_rx 	uart_rx(
	.clk(clk),
	.rst_n(rst_n),
	.rs232_data(rs232_data),
	.data(data),
	.flag(flag)
);

key_filter key_filter(
	.	clk(clk),
	.	rst_n(rst_n),
	.	key_in(key_in),
	.	key_flag(key_flag),
	.	key_state(key_state)
);

wire send_en;
wire wren;
wire [7:0] wraddr;
wire [7:0] rdaddr;
wire [7:0] q;
wire tx_done;

ram_ctrl  ram_ctrl(
	.clk(clk),
	.rst_n(rst_n),
	.flag(flag),
	.key_flag(key_flag),
	.key_state(key_state),
	.send_en(send_en),
	.tx_done(tx_done),
	.wren(wren),
	.wraddr(wraddr),
	.rdaddr(rdaddr)
);

 my_ram  my_ram(
	.clock(clk),
	.data(data),
	.rdaddress(rdaddr),
	.wraddress(wraddr),
	.wren(wren),
	.q(q)
);
	
uart_tx  uart_tx(
	.clk(clk),
	.rst_n(rst_n),
	.send_en(send_en),
	.data_byte(q),
	.rs232_tx(rs232_tx),
	.tx_done()
);	


endmodule

串口接收模块

 module uart_rx(
	input 			clk,
	input				rst_n,
	input				rs232_data,
	output 	reg [7:0]data,
	output reg		flag
);
localparam bps_cnt_end=5207;
//串口数据同步处理
reg rx_data1;
reg rx_data2;
reg rx_data3;
aaa@qq.com(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;
aaa@qq.com(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;		
aaa@qq.com(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;
aaa@qq.com(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;
		
aaa@qq.com(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;
		
aaa@qq.com(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;
		
//接收数据
aaa@qq.com(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


按键消抖模块

module key_filter(
	input				clk,
	input				rst_n,
	input				key_in,
	output	reg		key_flag,
	output	reg		key_state
);

//异步信号处理
reg key_s0,key_s1;
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_s0<=0;
		key_s1<=0;
	end
	else begin
		key_s0<=key_in;
		key_s1<=key_s0;
	end

//上升沿与下降沿检测
reg key_in0;
reg key_in1;
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_in0<=0;
		key_in1<=0;
	end
	else begin
		key_in0<=key_s1;
		key_in1<=key_in0;
	end
wire nedge=key_in1 && (!key_in0);//下降沿检测
wire podge=(!key_in1) && key_in0;//上升沿检测

//20ms的计数
reg [24:0] cnt;
reg en_cnt;//使能计数
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)begin
		cnt<=0;
	end
	else if(en_cnt)begin
		if(cnt<25'd1_000_000-1)
			cnt<=cnt+1'b1;
		else
			cnt<=0;
	end
	else
		cnt<=0;
wire delay_20ms_done=(cnt==25'd1_000_000-1) && (en_cnt);//计满标志信号

//状态机的设计
reg [2:0] state; 
localparam IDLE   =3'd0;
localparam FILTER1=3'd1;
localparam DOWM   =3'd2;
localparam FILTER2=3'd3;
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)begin
		key_flag<=0;
		key_state<=1;
		state<=IDLE;
		en_cnt<=0;
	end
	else begin
		case(state)
			IDLE	 :	begin
							if(nedge)begin
								state<=FILTER1;
								en_cnt<=1;
							end
							else
								state<=IDLE;
						end
			FILTER1:	begin
							if(delay_20ms_done)begin
								if(podge)begin
									state<=IDLE;
									en_cnt<=0;
								end
								else begin
									state<=DOWM;
									en_cnt<=0;
									key_flag<=1;
									key_state<=0;
								end
							end
							else
								state<=FILTER1;
						end
			DOWM	 :	begin
							key_flag<=0;
							if(podge)begin
								state<=FILTER2;
								en_cnt<=1;
							end
							else begin
								state<=DOWM;
							end	
						end
			FILTER2:	begin
							if(delay_20ms_done)begin
								if(nedge)begin
									state<=DOWM;
									en_cnt<=0;
								end
								else begin
									state<=IDLE;
									en_cnt<=0;
									key_state<=1;
								end
							end
							else begin
								state<=FILTER2;
							end
								
								
						end
			default:	begin
							key_flag<=0;
							key_state<=1;
							state<=IDLE;
							en_cnt<=0;
						end
		endcase
	end
		

endmodule

ram控制模块


module 	ram_ctrl(
	input			clk,
	input			rst_n,
	input			flag,
	input			key_flag,
	input			key_state,
	input			tx_done,
	output	reg	send_en,
	output		wren,
	output	reg[7:0]	wraddr,
	output	reg[7:0]	rdaddr
);

assign wren=flag;
		
//写地址,写使能一旦打开就开始写数据,从0地址开始写数据
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		wraddr<=0;
	else if(flag)begin
		if(wraddr==5)
			wraddr<=0;
		else
			wraddr<=wraddr+1;
	end
	else
		wraddr<=wraddr;
		
//读数据,检测到按键信号就开始发送数据(未检测到按键信号ram中q一直是0地址中的数据)	
reg state;	
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)	begin
		rdaddr<=0;
		state<=0;
	end
	else if(send_en)begin
			case(state)
				0:	begin
						rdaddr<=0;
						state<=1;
					end
				1:begin
						if(rdaddr==5)begin
							rdaddr<=0;
							state<=1;
						end
						else
							rdaddr<=rdaddr+1;
					end
			endcase
		end
			else 
		rdaddr<=rdaddr;


//aaa@qq.com(posedge clk or negedge rst_n)
//	if(!rst_n)	
//		rdaddr<=0;
//	else if(send_en)begin
//		if(rdaddr==5)
//			rdaddr<=0;
//		else
//			rdaddr<=rdaddr+1;
//	end
//	else 
//		rdaddr<=rdaddr;

//发送使能		
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		send_en<=0;
	else if(key_flag && (!key_state))
		send_en<=1;
	else 
		send_en<=0;
		


endmodule

串口发送模块

module uart_tx(
	input				clk,
	input				rst_n,
	input				send_en,
	input	[7:0]		data_byte,
	output	reg	rs232_tx,
	output	reg	tx_done
);

//数据同步,消除亚稳态
reg [7:0] data_byte_r;
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		data_byte_r<=0;
	else
		data_byte_r<=data_byte;
		
//波特率计数
reg [15:0] bps_cnt;
reg bps_cnt_en;//波特率使能计数
reg [3:0] bps_clk_cnt;//波特率时钟计数
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_cnt_en<=0;
	else if(send_en)
		bps_cnt_en<=1;
	else if(bps_clk_cnt==11)
		bps_cnt_en<=0;
	else
		bps_cnt_en<=bps_cnt_en;
		
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_cnt<=0;
	else if(bps_cnt_en)begin
		if(bps_cnt==5207)
			bps_cnt<=0;
		else
			bps_cnt<=bps_cnt+1;
	end
	else
		bps_cnt<=0;
		
//生成波特率时钟
reg bps_clk;
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_clk<=0;
	else if(bps_cnt_en && bps_cnt==1)
		bps_clk<=1;
	else
		bps_clk<=0;
		
//波特率时钟计数
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		bps_clk_cnt<=0;
	else if(bps_cnt_en)begin
		if(bps_clk)
			bps_clk_cnt<=bps_clk_cnt+1;
		else
			bps_clk_cnt<=bps_clk_cnt;
	end
	else
		bps_clk_cnt<=0;
		
//数据发送完成标志
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		tx_done<=0;
	else if(bps_clk_cnt==11)
		tx_done<=1;
	else
		tx_done<=0;
	
//发送数据
aaa@qq.com(posedge clk or negedge rst_n)
	if(!rst_n)
		rs232_tx<=0;
	else begin
		case(bps_clk_cnt)
			1:	rs232_tx<=0;
			2:	rs232_tx<=data_byte_r[0];
			3:	rs232_tx<=data_byte_r[1];
			4:	rs232_tx<=data_byte_r[2];
			5:	rs232_tx<=data_byte_r[3];
			6:	rs232_tx<=data_byte_r[4];
			7:	rs232_tx<=data_byte_r[5];
			8:	rs232_tx<=data_byte_r[6];
			9:	rs232_tx<=data_byte_r[7];
			10:rs232_tx<=1;
			11:rs232_tx<=1;
			default:rs232_tx<=1;
		endcase
	end
		
		
endmodule

实验结果

通过串口发送11,22,33,44,55,66到fpga,按下按键发现数据可以正常被读出。

               串口收发之ram存取