FPGA解析B码----连载8(完结篇)

前言

    前两个完结篇介绍了B码的结构,B码保护程序和B码的1PPS产生程序,下面介绍B码的UTC时间产生。当然B码中含有UTC时间和UTC时间的关键信息。程序的整体思路是差不多的,翻过来调过去也就是那点东西,就看怎么去一步一步的去解析里面的信息帧。解析过程和程序无关,和B码的结构有关。当然顺序执行的程序都是这个逻辑,只不过现在用顺序解析的办法去编写并行程序。

写在前面

    从FPGA的双精度double数加和乘,整数转双精度double数,再到串口发送,再到SPI采集的主机和从机程序,再到现在的B码解析。感觉FPGA的编程模式已经差不多浮现出来,至于怎么精简程序和一个clk都不出现问题,这个倒没有太过在意。可能大神的程序就是一个clk都不出现错误吧。想着咱们用FPGA编程,一般的容错率还是挺强的,因为FPGA是一个周期一个周期的,而咱们需要的输出量是在很长一段时间内输出就行。所以只要最后结果能输出就好,管他几个clk呢。当然如果需要非常精确的场合,还是得一个clk一个clk的去看得到的值。

    疑问:都说一个领域需要去沉淀,需要好好去做,小伙伴们说做到现在这个程度算是精通么?我始终都不太理解说的这个要有深度是需要深度到什么程度。你说做变频器,做到什么程序是精通呢?各个问题都能知道在哪,都知道怎么解决?现在编写STM32的程序,做到什么程度呢?里面的每个状态都能知道?每个寄存器都知道怎么用?如果只是这个程序,那每个都精通了。但是大神眼里的精通应该不是这个程度。所以这个概念非常模糊,也可能自己做的东西太杂,已经不知道精通是什么意思了,也挺无奈的!

第一章:输出介绍

    先看UTC时间输出报文吧。

     先说下报文格式:报头:5AA5,光电口标志:55,UTC时间:62 FD A9 BE,UTC其他关键信息:08 00,校验:22。

    只看UTC时间和UTC关键信息,62 FD A9 BE转化为十进制为:1660791230,对应标准时间为:

     通过转化可以看出转化的时间是正确的。

    后面的0800,08代表东八区,也就是北京时间,之后的0代表UTC时间精确度,之后的0代表闰秒什么的,当然需要什么关键信息可以自己添加。不过UTC时间主要的关键信息也就这几个:时区,闰秒,精度,其他的解析出来也没太大作用,也可能是我自己不用吧。

    关于闰秒问题:这个地方考虑了,但是很鸡肋。产品能用几年呢,等不到闰秒就淘汰了,还管啥闰秒啊,这个我就没有很好的解析!但是从严格的角度还是需要考虑的,看小伙伴的自己的需求。对于军工之类的,这个肯定得好好解析,对于其他的,还是算了,太麻烦!

第二章:流程介绍

    前篇文章介绍了2个P标志位锁存,前面是一样的!

     详细说下锁存之后做的事情。检测到两个连续P标志电平后,flag置1,并且将保存的cnt置成2。当flag等于1之后,下一次B码下降沿来的时候,cnt会++,也就是下一个B码单元存到第3个单元里面。所以设置100个寄存器,1和2是预留给2个P标志帧的,而真正的存放B码单元值是从3开始的。从3存到100,所以之后解析的时候也是按照这个顺序解析的,当然你也可以按照自己的方式去存储这些值,最后解析的时候对应就可以了。

    放入寄存器的时候肯定是从3开始存放,当检测不到2个P帧的时候cnt是不++的,也不会存放到cnt对应的寄存器,这点非常重要。

    每次检测到2个P标志之后才开始存储和计数,无论中间出现什么状况,总是从2个P帧标志开始,所以确保了程序1s内存放是正确的。

     当flag置1之后,启动time计数器,对应时间到最后1个B码的时候,level置1,并且一定时间后置0。这个时间是固定的,level的下降沿falling时候,time计数器复位。这样计数器和flag就完成了闭环。

    在下降沿之后,开始计算后面的数据。正好time计数器走到1s的最后1帧。开始和结尾都是可控的,所以故障不会出现在下1s。

    此时如果丢失B码,根据上篇文章中的11ms下降沿,将flag复位,同时将time计数器复位。这样出现错误B码的时候也达到了闭环。

    另一个就是cnt问题:如果B码都是正确的,开始是2,那么下次开始也肯定是2,都是从2个P帧标志开始的,相当于有一个复位。但是如果B码中间消失,那么下次开始时,就不一定是2了。所以cnt在B码出现丢失或者错误时也必须形成闭环。也就是11ms下降沿时,也需要cnt复位成2。这样cnt参数也形成了闭环。

     当计数器的下降沿,也就是1sB码的最后一帧采集完毕后,开始对所有3到100的B码进行01转换,然后计算UTC时间,计算闰年天数,计算UTC秒数,计算校验,然后给串口发送。具体怎么计算下面程序会详细介绍。

    以上就是UTC的解析过程,里面的各个参数都形成闭环,能把故障控制在1s之内。

第三章:程序介绍

    下面详细介绍下程序的编写,这部分比较绕,有很多延迟下降沿,这部分也是没办法,顺序执行的程序,只能一个参数准备好,才能进行下一步计算。当然能不能一下子计算,我自己试验了下,也是没问题的,但是为了将来自己能读懂自己的程序,还是一步一步的去计算,并且能保证数据的准确性。

//B码接收
//接收到2个P标志位后,bcodevalidflag置1,根据时间,B码最后一位结束后置0
(*noprune*)reg    bcodevalidflag;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		bcodevalidflag <= 1'd0;
	else if (resettime1_falling_flag == 1'b1)begin 
      if((bcodelevellatch1 == 2'd3)&&(bcodelevellatch2 == 2'd3))	
	       bcodevalidflag <= 1'd1; 
   end
	else if (bcodevalidtime_falling_flag == 1'b1)begin 
	   bcodevalidflag <= 1'd0; 
   end
	else if (code10ms_time_falling_flag == 1'b1)begin 
	   bcodevalidflag <= 1'd0; 
   end		
end


(*noprune*)reg [31:0]      bcodevalidtimer;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		bcodevalidtimer <= 32'd0;                     
	else if (bcodevalidflag == 1'b1)        
    	bcodevalidtimer <= bcodevalidtimer + 32'd1;                                    
	else if (bcodevalidflag == 1'b0)        
    	bcodevalidtimer <= 32'd0; 	 
	else if (code10ms_time_falling_flag == 1'b1)        
    	bcodevalidtimer <= 32'd0;   		
end
reg        bcodevalidlevel;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		bcodevalidlevel <= 1'd0;                     
	else if (bcodevalidtimer == 32'd49025000)    // 991ms 置高,994ms置低  
    	bcodevalidlevel <= 1'b1;                     
	else if (bcodevalidtimer == 32'd49075000)     
    	bcodevalidlevel <= 1'b0;   		
end

reg        bcodevalidtime_en_d0; 
reg        bcodevalidtime_en_d1; 
wire       bcodevalidtime_falling_flag;
wire       bcodevalidtime_rasing_flag;
assign bcodevalidtime_falling_flag = (~bcodevalidtime_en_d0) & bcodevalidtime_en_d1;
assign bcodevalidtime_rasing_flag  = (~bcodevalidtime_en_d1) & bcodevalidtime_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        bcodevalidtime_en_d0 <= 1'b0;                                  
        bcodevalidtime_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        bcodevalidtime_en_d0 <= bcodevalidlevel;                               
        bcodevalidtime_en_d1 <= bcodevalidtime_en_d0;     		  
    end
end


reg        bcodebegin_en_d0; 
reg        bcodebegin_en_d1; 
wire       bcodebegin_falling_flag;
wire       bcodebegin_rasing_flag;
assign bcodebegin_falling_flag = (~bcodebegin_en_d0) & bcodebegin_en_d1;
assign bcodebegin_rasing_flag  = (~bcodebegin_en_d1) & bcodebegin_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        bcodebegin_en_d0 <= 1'b0;                                  
        bcodebegin_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        bcodebegin_en_d0 <= bcodevalidlevel;                               
        bcodebegin_en_d1 <= bcodebegin_en_d0;     		  
    end
end

    2个P标志位之后,flag置1,加上B码错误判断。计数器到最后一帧停止复位。

//100个计数单元
(*noprune*)reg [6:0] bcodebitcnt;  /*synthesis noprune*/
(*noprune*)reg       bcodeflag;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
	    bcodebitcnt <= 7'd0;  
	else if ((bpluse_falling_flag == 1)&&(bcodevalidflag == 1'b1))
	    bcodebitcnt <= bcodebitcnt + 1'b1; 
	else if (bcodebegin_rasing_flag == 1)
	    bcodebitcnt <= 7'd2;
	else if (code10ms_time_falling_flag == 1'b1)begin 
	    bcodebitcnt <= 7'd2; 
   end		 
   else 
	    bcodebitcnt <= bcodebitcnt; 	
end

    cnt的单独拿出来介绍,flag等于1之后开始增长++,2个P标志后变成2,B码错误之后也置成2,形成cnt闭环。

(*noprune*)reg  [1:0]  b_data_rec1;
(*noprune*)reg  [1:0]  b_data_rec2;
(*noprune*)reg  [1:0]  b_data_rec3;
(*noprune*)reg  [1:0]  b_data_rec4;
......
(*noprune*)reg  [1:0]  b_data_rec100;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)begin
		b_data_rec1 <= 2'd0;
		b_data_rec2 <= 2'd0;
		b_data_rec3 <= 2'd0;
        ......
	    b_data_rec100 <= 2'd0; 

		
	end            
	else if (resettime2_falling_flag == 1'b1)begin 
		if(bcodebitcnt == 7'd3) b_data_rec3  <= bcodelevel;
		if(bcodebitcnt == 7'd4) b_data_rec4  <= bcodelevel;
        ......
        if(bcodebitcnt == 7'd100)b_data_rec100 <= bcodelevel;	
   end
end

    根据cnt的值,将B码的值放入100个寄存器中间,中间部分省略了,需要的可以自己加上。当然这个下降沿在2个P标志后才会起作用。

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		TimeOtherStatus <= 16'd0;
	else if (bcodebegin_falling_flag == 1'b1)begin 
			
         if(b_data_rec72 >= 2'd1)TimeOtherStatus[3] <= 1'b0;
		 if(b_data_rec72 >= 2'd2)TimeOtherStatus[3] <= 1'b1;			
         if(b_data_rec73 >= 2'd1)TimeOtherStatus[4] <= 1'b0;
		 if(b_data_rec73 >= 2'd2)TimeOtherStatus[4] <= 1'b1;			
         if(b_data_rec74 >= 2'd1)TimeOtherStatus[5] <= 1'b0;
		 if(b_data_rec74 >= 2'd2)TimeOtherStatus[5] <= 1'b1;			
         if(b_data_rec75 >= 2'd1)TimeOtherStatus[6] <= 1'b0;
		 if(b_data_rec75 >= 2'd2)TimeOtherStatus[6] <= 1'b1;			
         if(b_data_rec76 >= 2'd1)TimeOtherStatus[7] <= 1'b0;
		 if(b_data_rec76 >= 2'd2)TimeOtherStatus[7] <= 1'b1;
			
         if(b_data_rec67 >= 2'd1)TimeOtherStatus[8] <= 1'b0;
		 if(b_data_rec67 >= 2'd2)TimeOtherStatus[8] <= 1'b1; 	   
         if(b_data_rec68 >= 2'd1)TimeOtherStatus[9] <= 1'b0;
		 if(b_data_rec68 >= 2'd2)TimeOtherStatus[9] <= 1'b1;
         if(b_data_rec69 >= 2'd1)TimeOtherStatus[10] <= 1'b0;
		 if(b_data_rec69 >= 2'd2)TimeOtherStatus[10] <= 1'b1;
         if(b_data_rec70 >= 2'd1)TimeOtherStatus[11] <= 1'b0;
		 if(b_data_rec70 >= 2'd2)TimeOtherStatus[11] <= 1'b1;

         if(b_data_rec66 >= 2'd1)TimeOtherStatus[13] <= 1'b0;
		 if(b_data_rec66 >= 2'd2)TimeOtherStatus[13] <= 1'b1;	
         if(b_data_rec63 >= 2'd1)TimeOtherStatus[14] <= 1'b0;
		 if(b_data_rec63 >= 2'd2)TimeOtherStatus[14] <= 1'b1;	
         if(b_data_rec62 >= 2'd1)TimeOtherStatus[15] <= 1'b0;
		 if(b_data_rec62 >= 2'd2)TimeOtherStatus[15] <= 1'b1;			
   end		                  
end

    根据每个位的大小,判断各个标志位的0和1,变为可以输出的01信号。

(*noprune*)reg  [6:0]  utc_sec;
(*noprune*)reg  [6:0]  utc_min;
(*noprune*)reg  [5:0]  utc_hou;
(*noprune*)reg  [9:0]  utc_day;
(*noprune*)reg  [7:0]  utc_yea;

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)begin
		utc_sec <= 7'd0;
		utc_min <= 7'd0;
		utc_hou <= 6'd0;
		utc_day <= 10'd0;
		utc_yea <= 8'd0;
	end
	else if (bcodebegin_falling_flag == 1'b1)begin 
         if(b_data_rec3 >= 2'd1)utc_sec[0] <= 1'b0;
		 if(b_data_rec3 >= 2'd2)utc_sec[0] <= 1'b1;
         if(b_data_rec10 >= 2'd1)utc_sec[6] <= 1'b0;
		 if(b_data_rec10 >= 2'd2)utc_sec[6] <= 1'b1;
			
         if(b_data_rec12 >= 2'd1)utc_min[0] <= 1'b0;
		 if(b_data_rec12 >= 2'd2)utc_min[0] <= 1'b1;		 
         if(b_data_rec19 >= 2'd1)utc_min[6] <= 1'b0;
		 if(b_data_rec19 >= 2'd2)utc_min[6] <= 1'b1;		 		
		
         if(b_data_rec22 >= 2'd1)utc_hou[0] <= 1'b0;
		 if(b_data_rec22 >= 2'd2)utc_hou[0] <= 1'b1;		 			
         if(b_data_rec28 >= 2'd1)utc_hou[5] <= 1'b0;
		 if(b_data_rec28 >= 2'd2)utc_hou[5] <= 1'b1; 
	
         if(b_data_rec32 >= 2'd1)utc_day[0] <= 1'b0;
		 if(b_data_rec32 >= 2'd2)utc_day[0] <= 1'b1;		 
         if(b_data_rec43 >= 2'd1)utc_day[9] <= 1'b0;
		 if(b_data_rec43 >= 2'd2)utc_day[9] <= 1'b1;
		
         if(b_data_rec52 >= 2'd1)utc_yea[0] <= 1'b0;
		 if(b_data_rec52 >= 2'd2)utc_yea[0] <= 1'b1; 
         if(b_data_rec60 >= 2'd1)utc_yea[7] <= 1'b0;
		 if(b_data_rec60 >= 2'd2)utc_yea[7] <= 1'b1;
   end		                  
end

    这部分根据B码的值,判断utc的各个位的01大小,这部分没有什么可以介绍的了。主要是需要主要B码和寄存器对应,对应对了就可以得出每个数据大小。


(*noprune*)reg  [5:0] utc_SecondData;
(*noprune*)reg  [5:0] utc_MinuteData;
(*noprune*)reg  [4:0] utc_HourData;
(*noprune*)reg  [8:0] utc_DayData;
(*noprune*)reg  [5:0] utc_YearData;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)begin
		utc_SecondData <= 6'd0;
		utc_MinuteData <= 6'd0;
		utc_HourData   <= 5'd0;
		utc_DayData    <= 9'd0;
		utc_YearData   <= 6'd0;
	end
		
	else if (resettime3_falling_flag == 1'b1)begin 
		 utc_SecondData <= utc_sec[0] + 2*utc_sec[1] + 4*utc_sec[2] + 8*utc_sec[3] 
						 +(utc_sec[4] + 2*utc_sec[5] + 4*utc_sec[6])*10;	
							 
		 utc_MinuteData <= utc_min[0] + 2*utc_min[1] + 4*utc_min[2] + 8*utc_min[3] 
					     +(utc_min[4] + 2*utc_min[5] + 4*utc_min[6])*10; 

         utc_HourData <= utc_hou[0] + 2*utc_hou[1] + 4*utc_hou[2] + 8*utc_hou[3] 
					   +(utc_hou[4] + 2*utc_hou[5])*10; 
		 	 
	     utc_DayData   <= utc_day[0] + 2*utc_day[1] + 4*utc_day[2] + 8*utc_day[3] 
						+(utc_day[4] + 2*utc_day[5] + 4*utc_day[6] + 8*utc_day[7])*10
					    +(utc_day[8] + 2*utc_day[9])*100; 
 
	     utc_YearData  <= utc_yea[0] + 2*utc_yea[1] + 4*utc_yea[2] + 8*utc_yea[3] 
						+(utc_yea[4] + 2*utc_yea[5] + 4*utc_yea[6] + 8*utc_yea[7])*10;							
							
   end		                  
end

    根据每个位的大小计算具体的时间数据。这部分是B码的定义,怎么计算看看第一篇里面对应的数据就可以了。具体的自己也做了一张表。

     具体的可以自己根据自己存放的寄存器去计算。

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		TimeStamp <= 32'd0;
	else if (resettime5_falling_flag == 1'b1)begin 
      TimeStamp <=  utc_LeapYear   * 31622400 
		          + utc_Year       * 31536000 
	              + utc_DayData    * 86400
	              + utc_HourData   * 3600 
	              + utc_MinuteData * 60 
	              + utc_SecondData
	   		      - 115200;	
   end		                  
end

    然后计算utc时间。具体的也不会占用多少资源,这个计算应该挺方面,直接放进去就行。

    为啥不直接传输各个量呢?计算出来utc的秒数,采用32位就可以传输了,单独每个传输的话,会需要很多,32位是绝对搞不定的,所以计算出来秒数是最好的。

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		TimeStampVerify <= 16'd0;
	else if (resettime3_falling_flag == 1'b1)begin 
      TimeStampVerify <= 16'd0;	
   end		  
	else if (resettime6_falling_flag == 1'b1)begin 
      TimeStampVerify <= TimeStampVerify 
		                 + 8'h5A + 8'hA5 + 8'h55 
					     + TimeStamp[ 7: 0] + TimeStamp[15: 8]
						 + TimeStamp[23:16] + TimeStamp[31:24]
						 + TimeOtherStatus[ 7: 0] + TimeOtherStatus[15: 8];
   end
end

    然后进行校验,当然这里我采用和校验,你自己编写CRC校验也可以,主要是为了传输过程中担心传输错误。接收方只要去解析这个校验码就行了。当然里面也需要闭环,在前面的一个下降沿置0,后面计算。

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		uartsendpluse <= 1'd0;
	else if (resettime6_falling_flag == 1'b1)begin 
      uartsendpluse <= 1'd1;	
   end		  
	else if (resettime7_falling_flag == 1'b1)begin 
      uartsendpluse <= 1'd0;
   end
end

    然后是串口发送使能,当然串口这部分可以自己编写,网上很多这样的例子,将全部的数据放进串口的数据区里,就可以得到文章第一章的数据了。

第四章:总结

    B码的解析程序和以前的串口发送、SPI接收与发送程序感觉步骤是一样的,只要知道自己什么时候干什么事,哪一个时刻需要做什么就行,下面的就是绕来绕去的逻辑问题。整体来说B码的解析程序没有想象的那么麻烦。

    程序最重要的是需要最初的架构做的好点,此时刻不能影响下一个时刻。避免错误时的错误传输,这个逻辑最重要的。

第五章:展望

    B码解析到此已经全部完成。大概需要时间为15天。如果有相关代码的话,差不多一个星期可以完成全部程序。如果有小伙伴需要的请自己关注,至于那里有能关注的图片,请移步之前的文章,有的能显示有的不能显示,所以小伙伴自己去斟酌。关注后请私信,到时候会将编写好的代码放到百度网盘中。采用这种方式的原因前面的文章也有说,所以就不赘述了。如果有什么疑问也可以留言,如果能帮助小伙伴在B码解析和FPGA的编程方面,也请关注下。以后做什么项目或者那个软件也会写文章给大家分享!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值