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

本文详细介绍了如何在FPGA中实现1PPS信号的产生,包括从B码检测、1PPS脉冲生成到B码错误保护的完整流程。通过检测B码的上升沿和下降沿,锁定B码并解析UTC时间,最终在特定时刻产生2ms宽度的1PPS脉冲。同时,文章讨论了软件和芯片国产化的趋势,并提出了应对可能的封锁措施。在B码错误发生时,设计的错误保护程序能及时关闭1PPS输出,确保信号的准确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

    上篇完结篇介绍了程序的整体架构和B码错误保护程序,这篇主要介绍下1PPS的产生。

    写到这里想先聊聊现在的软件,用的是QII,不知道这个软件还能免费用多久。现在国外的软件慢慢的都不能用了,只能用国产的软件。芯片也慢慢的不能用了,只能用国产的芯片了。上段时间matlab对几所大学封闭,想着如果对所有的国内用户都不能用了呢?现在EDA软件不能用了,当然只是高端用户,如果全部用户也不能用了呢?所以呢,还是老老实实的电脑硬盘下载几个现在可以破解的软件,等将来都封锁了,还能断网裸机跑几个像样的这样的软件。也是挺无奈的吧!

第一章:波形介绍

    先看最后的1PPS波形吧,这个是最终的目的和最关心的。

     这个是产生的高电平和B码的波形,图中可以得出:波形在2个P标志位中间位置产生高电平,在第2个P标志位之后2ms处变为低电平。

     这个是产生的1PPS脉宽和B码的波形,图中可以得出:B码的1PPS处和FPGA产生的1PPS脉宽可以完全对应,并且1PPS脉宽为2ms。

     这个是产生的1PPS和B码的1PPS处放大图,图中可以得出:由于FPGA的时钟问题,产生的1PPS落后于真实的B码1PPS一个时钟周期,即20ns(FPGA采用的50M的时钟)。如果有严格需要1PPS对齐的小伙伴,这个地方需要特殊考虑。

     这个是串口发送数据和产生的1PPS波形,图中可以得出:串口提前1PPS10ms的时间发送,即1s的B码开始P标志帧处发送。这个时间是自己规定的,后面会做出介绍。

    当B码出现问题时,即有B码单元丢失时,1PPS不输出,没有时间做视频,所以就只能说说观察到的现象:1s中间拔掉并再插上B码线,1PPS不产生,1s后1PPS继续产生。所以B码错误保护代码是正确的。

第二章:流程介绍

    流程分成2个部分:(1)B码锁定;(2)1PPS产生。

    其中B码锁定部分牵涉到UTC时间的解析,也就在这里详细介绍下,下一篇介绍UTC时间解析的时候,这部分就省略了。1PPS的产生进行详细介绍。

     B码锁定流程介绍:

    (1)每次检测B码的上升沿和下降沿。上升沿timebeginflag置1,下降沿置0,同时计数器启动和复位。

    (2)下降沿时,获取计数器的值。这个时间采集问题以前的文章的有介绍,可以正确获得计数器的值,这个没啥问题。

    (3)timebeginflag下降沿(已获得time的值),每次都进行锁存,其中锁存flag的信号为下降沿resettimefallingflag,这个下降沿是每10ms产生一次,也即一个B码单元就产生一次,而不是一个机器周期产生一次。

    (4)滞后的下一个下降沿比较两个电平。

    此时,如果都是P标志的话,就代表锁存成功。如果两个不同时是P标志的话,就不成功,继续执行前面代码,直至锁存到两个P标志。

    这部分是1PPS和UTC时间共同使用的。

    1PPS产生流程介绍: 

    (1)当比较两个电平都为P标志时,将flag置1。同时启动time计数器。

    (2)time运行到下一个B码起始帧中间时,level置1,经过3ms后置0。当然这个时间看自己需要,也不是固定的。考虑到一定问题,最好是这样设置。

    (3)level的下降沿,flag复位,同时time复位。这样三个参数闭环。

    注意:闭环定义:变量开始值后,结束值和错误值都能恢复到开始值,这个过程叫闭环

    经过1PPS流程,也即锁存到两个P标志位后,经过一定时间置高,然后拉低。此时与B码信号相与,就能产生对应的1PPS信号。这样做的好处:每次检测到2个标志位才启动定时器,才在对应时间产生高电平,才能输出1PPS。每次都需要锁存2个P标志位,这样这1s的错误就不会延伸到下1s。

    前面正常的B码流程叙述完毕,如果中间B码消失了怎么办?这个还不是最重要的,如果在高电平时出现错误高电平怎么办,就需要加入B码错误保护程序。此时需要加入前篇文章介绍的B码错误保护程序了。当产生B码丢失时,也就是10ms之后没有接收到新的B码,即falling_flag产生(具体参看上篇)。

    保护流程:

    falling_flag产生时,flag置0,time置0,这样time就不会增加到产生高电平的值,这样就避免了由于B码出现问题带来的错误1PPS。这点在1PPS中并不明显,主要是UTC时间产生的时候比较重要。由于两个都置0,这样就达到了错误B码的闭环。当出现错误时,可以将变量恢复到原来的状态,等待下一次正确状态的到来。

第三章:程序介绍

    前面文章已经贴出主要的代码,当然之后写程序的过程中,又加入了新的变量,所以代码还是有所改变的。

    B码锁存程序:

//脉宽的上升沿和下降沿检测
reg        bpluse_en_d0; 
reg        bpluse_en_d1; 
wire       bpluse_falling_flag;
wire       bpluse_rasing_flag;
assign bpluse_falling_flag = (~bpluse_en_d0) & bpluse_en_d1;
assign bpluse_rasing_flag  = (~bpluse_en_d1) & bpluse_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        bpluse_en_d0 <= 1'b0;                                  
        bpluse_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        bpluse_en_d0 <= bcodein;                               
        bpluse_en_d1 <= bpluse_en_d0;
        		  
    end
end

    B码上升沿、下降沿判断。

//置位time是否开启
(*noprune*)reg        timebeginflag; 
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		timebeginflag <= 1'd0;                     
	else if (bpluse_rasing_flag == 1'b1)      
    	timebeginflag <= 1'd1;                      
	else if (bpluse_falling_flag == 1'b1)
		timebeginflag <= 1'd0; 
end

//上升沿之后计数器工作,下降沿之后计数器停止
(*noprune*)reg [31:0]      timer;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		timer <= 32'd0;                     
	else if (timebeginflag == 1'b1)      
    	timer <= timer + 32'd1;                     
	else if (timebeginflag == 1'b0)
		timer <= 0;    		
end

    上升沿和下降沿,启动定时器和复位计数器。

(*noprune*)reg  [1:0]  bcodelevel;  /*synthesis noprune*/
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)begin
		bcodelevel <= 0; 
   end		
	else if (bpluse_falling_flag == 1'b1)begin
		if((timer >= 32'd350000)&&(timer <= 32'd450000))begin
		   bcodelevel <= 2'd3; //P电平
	   end
      else if(timer >= 32'd200000)begin
		   bcodelevel <= 2'd2; //1电平
		end	
		else if(timer >= 32'd50000 )begin
		   bcodelevel <= 2'd1; //0电平
		end
	   else begin
		   bcodelevel <= 0; //0电平
		end
	end
	else begin
		bcodelevel <= bcodelevel; //0电平
	end
end

    下降沿时,判断time值,由此得出取得是的B码的什么帧。


reg        resettime_en_d0; 
reg        resettime_en_d1; 
wire       resettime_falling_flag;
wire       resettime_rasing_flag;
assign resettime_falling_flag = (~resettime_en_d0) & resettime_en_d1;
assign resettime_rasing_flag  = (~resettime_en_d1) & resettime_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        resettime_en_d0 <= 1'b0;                                  
        resettime_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        resettime_en_d0 <= timebeginflag;                               
        resettime_en_d1 <= resettime_en_d0;
        		  
    end
end

(*noprune*)reg  [1:0]  bcodelevellatch1;
(*noprune*)reg  [1:0]  bcodelevellatch2;

always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)begin
		bcodelevellatch1 <= 2'd0;
		bcodelevellatch2 <= 2'd0;
	end
	else if (resettime_falling_flag == 1'b1)begin      
		bcodelevellatch1 <= bcodelevel;
		bcodelevellatch2 <= bcodelevellatch1;                     
   end
	else if (ppstimer == 32'd49050000)begin    
		bcodelevellatch1 <= 2'd0;
		bcodelevellatch2 <= 2'd0;
	end
end

    flag结束后的下降沿,锁存B码的值。

reg        resettime1_en_d0; 
reg        resettime1_en_d1; 
wire       resettime1_falling_flag;
wire       resettime1_rasing_flag;
assign resettime1_falling_flag = (~resettime1_en_d0) & resettime1_en_d1;
assign resettime1_rasing_flag  = (~resettime1_en_d1) & resettime1_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        resettime1_en_d0 <= 1'b0;                                  
        resettime1_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        resettime1_en_d0 <= resettime_falling_flag;                               
        resettime1_en_d1 <= resettime1_en_d0;
        		  
    end
end

//1PPS计算
(*noprune*)reg    ppsreadyflag;
always@(posedge clk or negedge rst_n)
begin
	if (rst_n == 1'b0)
		ppsreadyflag <= 1'd0;
	else if (resettime1_falling_flag == 1'b1)begin 
      if((bcodelevellatch1 == 2'd3)&&(bcodelevellatch2 == 2'd3))	
	       ppsreadyflag <= 1'd1; 
   end
	else if (ppstime_falling_flag == 1'b1)begin 
	   ppsreadyflag <= 1'd0; 
   end	
	else if (code10ms_time_falling_flag == 1'b1)begin 
	   ppsreadyflag <= 1'd0; 
   end	
end

    再延迟一个周期,判断是否2个都为P标志位,是的话flag置1。还有两个条件,一个是结束复位,一个是错误复位,正好两个闭环。

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

    flag置1的时候开启计数器,另两个复位条件,1个是产生高电平之后的下降沿置0,1个是错误        B码置0,也是两个闭环。

    后面时间是对应2个P标志位之后,产生高电平的位置,正好对应波形中的下1s的第2个P标志位的中间位置和2ms之后的位置。

reg        ppstime_en_d0; 
reg        ppstime_en_d1; 
wire       ppstime_falling_flag;
wire       ppstime_rasing_flag;
assign ppstime_falling_flag = (~ppstime_en_d0) & ppstime_en_d1;
assign ppstime_rasing_flag  = (~ppstime_en_d1) & ppstime_en_d0;
always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        ppstime_en_d0 <= 1'b0;                                  
        ppstime_en_d1 <= 1'b0; 
    end                                                      
    else begin                                               
        ppstime_en_d0 <= ppshighlevel;                               
        ppstime_en_d1 <= ppstime_en_d0;     		  
    end
end

    正确产生高电平之后的延迟下降沿。用来闭环参数的。

always @(posedge clk or negedge rst_n) begin         
    if (!rst_n) begin
        ppspluse <= 1'b0;                                  
    end                                                      
    else begin                                               
        ppspluse <= (ppshighlevel & bcodein);                                   		  
    end
end

    最后输出1PPS的代码。高电平和B码相与,产生1PPS脉宽。由于采用clk的输出,所以会在波形中延迟1个clk周期,即20ns。如果需要更加准确的1PPS精度,则需要外部硬件电路做,FPGA是没有办法直接输出0ms的延迟的,如果有知道的小伙伴可以告诉我下,我给试试。

第四章:最后和展望

    至此1PPS的输出就全部完成,上面给出了完整的流程图和代码,能做的小伙伴可以直接放入代码,应该是可以运行的。如果需要源代码的请移步其他文章,具体哪篇有公众号的图片可以找找,现在贴不上去,“电力电子嵌入式”,有需要的小伙伴可以关注下。具体代码可以里面私信,到时候会给出百度网盘的链接。由于公众号没有办法上传大的文件,所以也只能给出网盘连接,没办法。

    下一篇会具体写B码的UTC时间解析,这篇比较多,流程图也比较复杂,需要比较长的时间制作,所以请先关注下,写完后会立刻发出来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值