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

【厉害了FPGA】Verilog实现接收帧数据的一种方法(帧数据同步搜索检测)

程序员文章站 2022-03-17 10:38:44
...

    FPGA和其他设备进行通信的时候,如果传输的是大量数据,肯定需要打包(组帧)进行传输,而且都需要有帧头和校验位来确保帧数据传输正确。今天说一下最近自己做的一个项目涉及到的这个问题。

    当FPGA作为接收端去接收帧数据的时候,即使保证一帧数据的帧头是正确的,而且校验位是正确的也不能百分百保证这帧数据正确接收了,可能情况:1、数据传输有错误,但是错误的数据也得到了一样正确的校验位;2、数据中正好有一位数据是帧头,而我们把这个数据当做了帧头,这个按帧头接收完一帧数据,帧尾的校验位也正好算出来也是对的,等其他小概率事件。对于第一种情况我们可以通过设计好的校验方式(CRC或者其他校验方式)来使这种概率减低,还有数据是突发的还是一直连续不断发送的,这些都是我们在接收机设计的时候必须考虑的。

    为了避免这种情况,在每次接收数据的时候,我们设置接收数据的时候有两种状态,一种是同步态,一种是捕获态(如下图)。在同步态连续接收到几帧数据(在此处我们叫做权值)的时候,才能进入捕获态,在捕获态我们接收到的数据才视作正确的数据。这样我们设计的权值越大,那么第二种情况发生的概率越低,但是付出的代价是丢弃了好多数据帧。

【厉害了FPGA】Verilog实现接收帧数据的一种方法(帧数据同步搜索检测)

    话不多说,程序的状态转移图如图所示:

【厉害了FPGA】Verilog实现接收帧数据的一种方法(帧数据同步搜索检测)

    本次是基于Verilog的一个接收机设计,模块按阻塞赋值(顺序执行)的方式。数据是从PC用串口按8位数据的方式传输到FPGA,帧长是344位,即34字节。帧头是两字节,校验位是7位数据,校验和的方式。

[email protected](posedge i_rx_state_1 or negedge i_reset)//一个i_rx_state_1的上升沿代表接收了一字节数据(阻塞赋值)
begin
	if(!i_reset)
		begin
		rx_frame_temp     = 344'd0;
		rx_frame          = 344'd0;
		work_state        = 8'd0;
		frameheader_cache = 16'd0;
		rx_byte           = 6'd0;
		syn_weight        = 2'd0;
		capture_signal    = 1'b0;
		end
	else 
		begin
		case(work_state)
			
			syn_check_frameheader:
						begin
						syn_weight        = 2'd0;
						rx_frame_temp = 344'd0;
						frameheader_cache = frameheader_cache<<8;
						frameheader_cache[7:0] = iv_rx_data_1;
						if(frameheader_cache==16'hA555)//判断是帧头a555的话,进入同步态,否则继续检测
							begin
							work_state = synchronous_state;
							rx_frame_temp[15:0] = frameheader_cache;
							rx_byte = 6'd2;
							end
						else 
							work_state = syn_check_frameheader;
						end
			synchronous_state:
						begin
						frameheader_cache = 16'd0;//清除缓存
						if(rx_byte>=6'd43)//如果接收满数据,则rx_byte一直处于满状态
							rx_byte = rx_byte;
						else 
							rx_byte = rx_byte+1'b1;
						rx_frame_temp = rx_frame_temp<<8;
						rx_frame_temp[7:0] = iv_rx_data_1;
						if(rx_byte>=6'd43)//接收完一帧数据(满状态)
							begin
							check_byte = ;//计算校验位,自己按照自己需求设计,为了节省代码空间,省去
							if((rx_frame_temp[7:0]==check_byte)&&(rx_frame_temp[343:328]==16'hA555))//校验位正确,等待权值为2的时候进入捕获态,否则继续接收数据
								begin
									syn_weight = syn_weight+1'b1;
									if(syn_weight>=2'd2)//连续两个帧数据接收正确进入捕获态
										work_state = capture_check_frameheader;
									else //权值<2,再次检测下一个帧头
										work_state = syn_check_frameheader;
								end
							else //校验位不正确,权值减一;下一次滑动接收数据,并检验帧头和校验位是否正确,不断循环,直到接收到正确的数据
								begin
								if(syn_weight==2'd0)//权值为0 滑动检测
									begin
									work_state = synchronous_state;
									syn_weight = 2'd0;
									end
								else 
									begin
									syn_weight = syn_weight-1'b1;
									work_state = syn_check_frameheader;
									end
								end
							end
						end
			capture_check_frameheader:
						begin
						syn_weight        = 2'd0;
						rx_frame_temp         = 344'd0;
						capture_signal        = 1'b0;//复位捕获信号
						rx_byte               = 6'd0;//清除接收计数缓存
						frameheader_cache     =frameheader_cache<<8;
						frameheader_cache[7:0]=iv_rx_data_1;
						if(frameheader_cache==16'hA555)//判断是帧头的话,进入同步态,否则继续检测
							begin
							work_state = capture_state;
							rx_frame_temp[15:0] = frameheader_cache;
                                                        rx_byte = 6'd0;//清除接收计数缓存
							end
						else 
							work_state = capture_check_frameheader;
						end
			capture_state:
						begin
						rx_byte = rx_byte+1'b1;
						rx_frame_temp = rx_frame_temp<<8;
						rx_frame_temp[7:0] = iv_rx_data_1;
						if(rx_byte>=43)//接收完一帧数据
							begin
							capture_signal = 1'b1;//产生捕获信号
							check_byte = ;//计算校验位
							if((rx_frame_temp[7:0]==check_byte)&&(rx_frame_temp[343:328]==16'hA555))
								begin
								work_state = capture_check_frameheader;
								rx_frame = rx_frame_temp;
								end
							else 
								begin
								rx_byte = 6'd0;//清除接收计数缓存
								work_state = syn_check_frameheader;
								rx_frame_temp = 344'd0;
								frameheader_cache = 16'd0;
								syn_weight        = 2'd0;
								end
							end
						end
			default:
				begin
				rx_byte = 6'd0;//清除接收计数缓存
				work_state = syn_check_frameheader;
				rx_frame_temp = 344'd0;
				frameheader_cache = 16'd0;
				syn_weight        = 2'd0;
				end	
		endcase
		end
end