上篇完结篇介绍了程序的整体架构和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时间解析,这篇比较多,流程图也比较复杂,需要比较长的时间制作,所以请先关注下,写完后会立刻发出来。