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