FPGA学习-UART接收单字节(边沿检测+发送模块例化+高频采样抗干扰+二维寄存器应用)

UART接收单字节时序图:

(实验室环境下):对每一位的中点进行采样最稳定;

(工业环境下):有强电磁干扰,每位只采样一次作为电平判定依据是不保险的,需要多次采中间部分,求哪一种电平的概率高,来确定该位电平;

        如上图。对于每一位数据,考虑到开头和尾部不稳定,所以忽略掉。只取中间6~11这6次采样结果进行电平概率的判定依据。如6次结果为1/1/1/1/0/1,电平取1;0/1/0/0/0/1,电平取0;1/0/0/1/0/1,电平取0或1都可以;

设计思路:

        rs232_rx为外部异步信号,进入系统先用两个寄存器将信号同步到系统时钟以避免亚稳态,再用两个寄存器边沿检测(只检测下降沿,作为开始接收工作状态的判定依据)。

        bps_set选择各波特率对应的分频最大计数值,利用计数器生成分频器。一共10位,每一位采16次,接收一个字节的过程就要bps_cnt计数160次。(与发送相比,bps_clk要*16,bps_DR要/16)。

        设定每一位的中间6~11采的6次作为电平高低的有效判定依据。每一位的六次累加,大于3就是高电平,否则为低电平。

        判断出外部输入的一个字节(10位)后,在bps_cnt<=159时,一起寄存给data_byte输出,同时接收停止产生rx_down。

RTL程序:

module uart_byte_rx(clk50M,rst_n,rs232_rx,bps_set,uart_state,data_byte,rx_down);

	input clk50M;
	input rst_n;
	input rs232_rx;
	input [2:0] bps_set;

	output reg [7:0] data_byte;
	output reg rx_down;   //接受完成单脉冲标志信号
	output reg uart_state;

	reg s0_reg,s1_reg;    //两个同步寄存器,目的是消除外部异步输入信号(rs232_rx)亚稳态
	reg t0_reg,t1_reg;    //两个数据寄存器,目的是边沿检测
	wire nedge;           //定义下降沿为nedge

	reg [15:0] counter;   //波特率分频器计数器
	reg [15:0] bps_DR;    //波特率分频器计数最大计数值
	reg bps_clk;          //波特率时钟
	reg [15:0] bps_cnt;   //bps_clk计数

	reg [2:0] r_data_byte [7:0];//8位数据位的电平概率,最大为6最小为0,三位足够
	reg [2:0] start_bit;//起始位的电平概率,最大为6最小为0,三位足够
	reg [2:0] stop_bit;//停止位的电平概率,最大为6最小为0,三位足够


	/******************同步外部信号,消除亚稳态***************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n) begin
			s0_reg<=1'b0;
			s1_reg<=1'b0;
		end
		else begin
			s0_reg<=rs232_rx;
			s1_reg<=s0_reg;
		end
	end
	/*******************************************************/


	/*****************数据寄存器,边沿检测********************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n) begin
			t0_reg<=1'b0;
			t1_reg<=1'b0;
		end
		else begin
			t0_reg<=s1_reg;
			t1_reg<=t0_reg;
		end
	end
	/*******************************************************/


	/**********************下降沿检测************************/
	assign nedge=t1_reg&(!t0_reg);
	/*******************************************************/


	/*************波特率分频器计数最大计数值查找表*************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			bps_DR<=16'd324;
		else begin
			case(bps_set)
				0:bps_DR<=16'd324;//9600
				1:bps_DR<=16'd162;//19200
				2:bps_DR<=16'd80;//38400
				3:bps_DR<=16'd53;//57600
				4:bps_DR<=16'd26;//115200
				default:bps_DR<=16'd324;
			endcase
		end
	end
	/*******************************************************/


	/*********************计数器****************************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			counter<=16'd0;
		else if(uart_state) begin
			if(counter==bps_DR)
				counter<=16'd0;
			else
				counter<=counter+1'd1;
		end
		else 
			counter<=16'd0;
	end
	/*******************************************************/


	/********************bps_clk时钟生成*********************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			bps_clk<=1'b0;
		else if (counter==16'd1)
			bps_clk<=1'b1;
		else
			bps_clk<=1'b0;
	end
	/*******************************************************/


	/*******************bps_clk时钟脉冲计数******************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n) 
			bps_cnt<=16'd0;
		else if(bps_clk)
			bps_cnt<=bps_cnt+1'd1;
		else if(rx_down)
			bps_cnt<=16'd0;
		else 
			bps_cnt<=bps_cnt;
	end
	/*******************************************************/


	/**************rx_down发送完成单脉冲标志信号生成***********/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			rx_down<=1'b0;
		else if(bps_cnt==16'd159)
			rx_down<=1'b1;
		else 
			rx_down<=1'b0;
	end
	/*******************************************************/


	/**********采样,并且将采样电平寄存到r_data_byte***********/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n) begin
			start_bit<=3'd0;
			r_data_byte[0]<=3'd0;
			r_data_byte[1]<=3'd0;
			r_data_byte[2]<=3'd0;
			r_data_byte[3]<=3'd0;
			r_data_byte[4]<=3'd0;
			r_data_byte[5]<=3'd0;
			r_data_byte[6]<=3'd0;
			r_data_byte[7]<=3'd0;
			stop_bit<=3'd0;
		end 
		
		else if(bps_clk) begin
			case(bps_cnt)
				0                       : begin
													start_bit<=3'd0;	
													r_data_byte[0]<=3'd0;
													r_data_byte[1]<=3'd0;
													r_data_byte[2]<=3'd0;
													r_data_byte[3]<=3'd0;
													r_data_byte[4]<=3'd0;
													r_data_byte[5]<=3'd0;
													r_data_byte[6]<=3'd0;
													r_data_byte[7]<=3'd0;
													stop_bit<=3'd0;
												  end
				6,7,8,9,10,11           : start_bit<=start_bit+s1_reg;
				22,23,24,25,26,27       : r_data_byte[0]<=r_data_byte[0]+s1_reg;
				38,39,40,41,42,43       : r_data_byte[1]<=r_data_byte[1]+s1_reg;
				54,55,56,57,58,59       : r_data_byte[2]<=r_data_byte[2]+s1_reg;
				70,71,72,73,74,75       : r_data_byte[3]<=r_data_byte[3]+s1_reg;
				86,87,88,89,90,91       : r_data_byte[4]<=r_data_byte[4]+s1_reg;
				102,103,104,105,106,107 : r_data_byte[5]<=r_data_byte[5]+s1_reg;
				118,119,120,121,122,123 : r_data_byte[6]<=r_data_byte[6]+s1_reg;
				134,135,136,137,138,139 : r_data_byte[7]<=r_data_byte[7]+s1_reg;
				150.151,152,153,154,155 : stop_bit<=stop_bit+s1_reg;
				default                 : begin 
													  r_data_byte[0]<=r_data_byte[0];
													  r_data_byte[1]<=r_data_byte[1];
													  r_data_byte[2]<=r_data_byte[2];
													  r_data_byte[3]<=r_data_byte[3];
													  r_data_byte[4]<=r_data_byte[4];
													  r_data_byte[5]<=r_data_byte[5];
													  r_data_byte[6]<=r_data_byte[6];
													  r_data_byte[7]<=r_data_byte[7]; 
												  end
			endcase
		end
	end
	/*******************************************************/


	/*****************把接收到数据字节输出********************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			data_byte<=8'd0;
		else if(bps_cnt==16'd159) begin
			data_byte[0]<=r_data_byte[0][2];
			data_byte[1]<=r_data_byte[1][2];
			data_byte[2]<=r_data_byte[2][2];
			data_byte[3]<=r_data_byte[3][2];
			data_byte[4]<=r_data_byte[4][2];
			data_byte[5]<=r_data_byte[5][2];
			data_byte[6]<=r_data_byte[6][2];
			data_byte[7]<=r_data_byte[7][2];
		end 
	end
	/*******************************************************/


	/********************UART工作状态信号********************/
	always @ (posedge clk50M or negedge rst_n) begin
		if(!rst_n)
			uart_state<=1'b0;
		else if(nedge)
			uart_state<=1'b1;
		else if(rx_down)
			uart_state<=1'b0;
		else 
			uart_state<=uart_state;
	end
	/*******************************************************/
endmodule

注意 :reg [2:0] r_data_byte [7:0];这句话的意思是r_data_byte有8位,每一位不只是0/1表示,这里最大用到6,所以[2:0]三位足够。 模板:reg [数据位宽:0] r_data_byte [地址位宽:0];

testbench测试文件:

`timescale 1ns/1ns
`define clock_period 20

module uart_byte_rx_tb;

	reg clk;
	reg rst_n;
	reg rs232_rx;
	reg [2:0] bps_set;
	wire uart_state_r;
	wire [7:0] data_byte_r;
	wire rx_down;

	reg [7:0]data_byte_t;
	reg tx_en;
	wire rs232_tx;
	wire tx_down;
	wire uart_state_t;
	
	uart_byte_rx uart_byte_rx(
		.clk50M(clk),
		.rst_n(rst_n),
		.rs232_rx(rs232_tx),           //把发送输出数据和接收输入数据连接起来
		.bps_set(bps_set),
		.uart_state(uart_state_r),     //接收状态与发送状态区分开
		.data_byte(data_byte_r),
		.rx_down(rx_down)
	);

	uart_byte_tx uart_byte_tx(
		.clk50M(clk),
		.rst_n(rst_n),
		.data_byte(data_byte_t),
		.tx_en(tx_en),
		.bps_set(bps_set),
		.rs232_tx(rs232_tx),
		.tx_down(tx_down),
		.uart_state(uart_state_t)		//接收状态与发送状态区分开
	);


	initial clk=1;
	always #(`clock_period/2) clk=~clk;
	initial begin
		//首先激励初始化,波特率查找表默认选4,也就是115200.然后复位结束,系统工作
		rst_n=0;               
		data_byte_t=0;
		tx_en=0;
		bps_set=4'd4;
		#(`clock_period*20);
		rst_n=1;
		#(`clock_period*50);
		
		//发送0xaa,等待tx_down到来时延时五千个系统周期
		tx_en=1;
		data_byte_t=8'haa;
		#(`clock_period);
		tx_en=0;
		@(posedge tx_down)
		#(`clock_period*5000);

		//发送0x37,等待tx_down到来时延时五千个系统周期。然后停止仿真
		tx_en=1;
		data_byte_t=8'h37;
		#(`clock_period);
		tx_en=0;
		@(posedge tx_down)
		#(`clock_period*5000);
		$stop;
	end
endmodule 

上一个发送字节例程的uart_byte_tx.v程序: 

module uart_byte_tx(clk50M,rst_n,data_byte,tx_en,bps_set,rs232_tx,tx_down,uart_state);
input clk50M;
input rst_n;
input [7:0] data_byte;        //输入想要发送的8位数据
input [2:0] bps_set;          //波特率选择查找表的输入
input tx_en;                  //发送开始的标志单脉冲信号
output reg rs232_tx;          //输出发送数据端
output reg tx_down;           //发送停止的标志单脉冲信号
output reg uart_state;        //UART工作状态信号,1为发送,0为空闲

reg [15:0] counter;           //分频器计数寄存器
reg [15:0] bps_DR;            //选定的波特率的最大计数值
reg [3:0] bps_cnt;            //1~11的位计数
reg [7:0] r_data_byte;        //data_byte进来 先寄存一次,以便后续稳定发送
reg bps_clk;                  //波特率时钟


localparam start_bit = 1'b0;  //定义起始位为0低电平
localparam stop_bit = 1'b1;   定义停止位为1高电平

/*************波特率对应的分频计数值查找表**************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_DR<=16'd5207;
	else begin
		case(bps_set)
			3'd0:bps_DR<=16'd5207;//9600
			3'd1:bps_DR<=16'd2603;//19200
			3'd2:bps_DR<=16'd1301;//38400
			3'd3:bps_DR<=16'd867;//57600
			3'd4:bps_DR<=16'd433;//115200
			default:bps_DR<=16'd5207;
		endcase
	end
end
/****************************************************/


/********************分频计数器生成********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		counter<=16'd0;
	else if(uart_state) begin
		if(counter==bps_DR)
			counter<=16'd0;
		else
			counter<=counter+1'd1;
	end
	else
		counter<=16'd0;
end
/****************************************************/


/********************分频时钟生成*********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_clk<=1'b0;
	else if(counter==16'd1)
		bps_clk<=1'b1;
	else
		bps_clk<=1'b0;
end
/****************************************************/


/****************bps_clk计数(最大11)****************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		bps_cnt<=4'd0;
	else if(bps_cnt==4'd11)
		bps_cnt<=4'd0;
	else if(bps_clk)
		bps_cnt<=bps_cnt+1'd1;
	else
		bps_cnt<=bps_cnt;
end
/****************************************************/



/************tx_down发送停止单脉冲标志信号*************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		tx_down<=1'b0;
	else if(bps_cnt==4'd11)
		tx_down<=1'b1;
	else
		tx_down<=1'b0;
end
/****************************************************/



/******************寄存一下data_byte******************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		r_data_byte<=8'd0;
	else if(tx_en)
		r_data_byte<=data_byte;
	else 
		r_data_byte<=r_data_byte;
end
/****************************************************/



/**********************发送模块***********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		rs232_tx<=1'b1;
	else begin
		case(bps_cnt)
			0:rs232_tx<=1'b1;
			1:rs232_tx<=start_bit;
			2:rs232_tx<=r_data_byte[0];
			3:rs232_tx<=r_data_byte[1];
			4:rs232_tx<=r_data_byte[2];
			5:rs232_tx<=r_data_byte[3];
			6:rs232_tx<=r_data_byte[4];
			7:rs232_tx<=r_data_byte[5];
			8:rs232_tx<=r_data_byte[6];
			9:rs232_tx<=r_data_byte[7];
			10:rs232_tx<=stop_bit;
			default:rs232_tx<=1'b1;
		endcase
	end
end
/****************************************************/



/********************状态标志信号*********************/
always @ (posedge clk50M or negedge rst_n)begin
	if(!rst_n)
		uart_state<=1'b0;
	else if(tx_en)
		uart_state<=1'b1;
	else if(bps_cnt==4'd11)
		uart_state<=1'b0;
	else
		uart_state<=uart_state;	
end
/****************************************************/

endmodule

 这里用到了上一个发送字节例程的uart_byte_tx.v程序。

        首先将上一个例程的uart_byte_tx.v代码复制到本例程中,然后需要让uart_byte_rx.v设置为Top文件。

        在testbench文件中将uart_byte_tx模块例化。并且对uart_byte_rx、uart_byte_tx进行连线。

        激励初始化就是用上一发送例程的testbench文件中的激励产生方式,使发送模块发送数据,连接到接收模块。当发送结束时,接收也结束,产生rx_down的同时,输出波形可以看到接收到的数据。

波形:

 

 

 

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值