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


    前言

        上篇完结篇介绍了程序的整体架构和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码锁存程序:

    1. //脉宽的上升沿和下降沿检测
    2. reg bpluse_en_d0;
    3. reg bpluse_en_d1;
    4. wire bpluse_falling_flag;
    5. wire bpluse_rasing_flag;
    6. assign bpluse_falling_flag = (~bpluse_en_d0) & bpluse_en_d1;
    7. assign bpluse_rasing_flag = (~bpluse_en_d1) & bpluse_en_d0;
    8. always @(posedge clk or negedge rst_n) begin
    9. if (!rst_n) begin
    10. bpluse_en_d0 <= 1'b0;
    11. bpluse_en_d1 <= 1'b0;
    12. end
    13. else begin
    14. bpluse_en_d0 <= bcodein;
    15. bpluse_en_d1 <= bpluse_en_d0;
    16. end
    17. end

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

    1. //置位time是否开启
    2. (*noprune*)reg timebeginflag;
    3. always@(posedge clk or negedge rst_n)
    4. begin
    5. if (rst_n == 1'b0)
    6. timebeginflag <= 1'd0;
    7. else if (bpluse_rasing_flag == 1'b1)
    8. timebeginflag <= 1'd1;
    9. else if (bpluse_falling_flag == 1'b1)
    10. timebeginflag <= 1'd0;
    11. end
    12. //上升沿之后计数器工作,下降沿之后计数器停止
    13. (*noprune*)reg [31:0] timer;
    14. always@(posedge clk or negedge rst_n)
    15. begin
    16. if (rst_n == 1'b0)
    17. timer <= 32'd0;
    18. else if (timebeginflag == 1'b1)
    19. timer <= timer + 32'd1;
    20. else if (timebeginflag == 1'b0)
    21. timer <= 0;
    22. end

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

    1. (*noprune*)reg [1:0] bcodelevel; /*synthesis noprune*/
    2. always@(posedge clk or negedge rst_n)
    3. begin
    4. if (rst_n == 1'b0)begin
    5. bcodelevel <= 0;
    6. end
    7. else if (bpluse_falling_flag == 1'b1)begin
    8. if((timer >= 32'd350000)&&(timer <= 32'd450000))begin
    9. bcodelevel <= 2'd3; //P电平
    10. end
    11. else if(timer >= 32'd200000)begin
    12. bcodelevel <= 2'd2; //1电平
    13. end
    14. else if(timer >= 32'd50000 )begin
    15. bcodelevel <= 2'd1; //0电平
    16. end
    17. else begin
    18. bcodelevel <= 0; //0电平
    19. end
    20. end
    21. else begin
    22. bcodelevel <= bcodelevel; //0电平
    23. end
    24. end

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

    1. reg resettime_en_d0;
    2. reg resettime_en_d1;
    3. wire resettime_falling_flag;
    4. wire resettime_rasing_flag;
    5. assign resettime_falling_flag = (~resettime_en_d0) & resettime_en_d1;
    6. assign resettime_rasing_flag = (~resettime_en_d1) & resettime_en_d0;
    7. always @(posedge clk or negedge rst_n) begin
    8. if (!rst_n) begin
    9. resettime_en_d0 <= 1'b0;
    10. resettime_en_d1 <= 1'b0;
    11. end
    12. else begin
    13. resettime_en_d0 <= timebeginflag;
    14. resettime_en_d1 <= resettime_en_d0;
    15. end
    16. end
    17. (*noprune*)reg [1:0] bcodelevellatch1;
    18. (*noprune*)reg [1:0] bcodelevellatch2;
    19. always@(posedge clk or negedge rst_n)
    20. begin
    21. if (rst_n == 1'b0)begin
    22. bcodelevellatch1 <= 2'd0;
    23. bcodelevellatch2 <= 2'd0;
    24. end
    25. else if (resettime_falling_flag == 1'b1)begin
    26. bcodelevellatch1 <= bcodelevel;
    27. bcodelevellatch2 <= bcodelevellatch1;
    28. end
    29. else if (ppstimer == 32'd49050000)begin
    30. bcodelevellatch1 <= 2'd0;
    31. bcodelevellatch2 <= 2'd0;
    32. end
    33. end

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

    1. reg resettime1_en_d0;
    2. reg resettime1_en_d1;
    3. wire resettime1_falling_flag;
    4. wire resettime1_rasing_flag;
    5. assign resettime1_falling_flag = (~resettime1_en_d0) & resettime1_en_d1;
    6. assign resettime1_rasing_flag = (~resettime1_en_d1) & resettime1_en_d0;
    7. always @(posedge clk or negedge rst_n) begin
    8. if (!rst_n) begin
    9. resettime1_en_d0 <= 1'b0;
    10. resettime1_en_d1 <= 1'b0;
    11. end
    12. else begin
    13. resettime1_en_d0 <= resettime_falling_flag;
    14. resettime1_en_d1 <= resettime1_en_d0;
    15. end
    16. end
    17. //1PPS计算
    18. (*noprune*)reg ppsreadyflag;
    19. always@(posedge clk or negedge rst_n)
    20. begin
    21. if (rst_n == 1'b0)
    22. ppsreadyflag <= 1'd0;
    23. else if (resettime1_falling_flag == 1'b1)begin
    24. if((bcodelevellatch1 == 2'd3)&&(bcodelevellatch2 == 2'd3))
    25. ppsreadyflag <= 1'd1;
    26. end
    27. else if (ppstime_falling_flag == 1'b1)begin
    28. ppsreadyflag <= 1'd0;
    29. end
    30. else if (code10ms_time_falling_flag == 1'b1)begin
    31. ppsreadyflag <= 1'd0;
    32. end
    33. end

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

    1. (*noprune*)reg [31:0] ppstimer;
    2. always@(posedge clk or negedge rst_n)
    3. begin
    4. if (rst_n == 1'b0)
    5. ppstimer <= 32'd0;
    6. else if (ppsreadyflag == 1'b1)
    7. ppstimer <= ppstimer + 32'd1;
    8. else if (ppsreadyflag == 1'b0)
    9. ppstimer <= 32'd0;
    10. else if (code10ms_time_falling_flag == 1'b1)begin
    11. ppstimer <= 32'd0;
    12. end
    13. end
    14. reg ppshighlevel;
    15. always@(posedge clk or negedge rst_n)
    16. begin
    17. if (rst_n == 1'b0)
    18. ppshighlevel <= 1'd0;
    19. else if (ppstimer == 32'd49550000) // 991ms 置高,994ms置低
    20. ppshighlevel <= 1'b1;
    21. else if (ppstimer == 32'd49700000)
    22. ppshighlevel <= 1'b0;
    23. end

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

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

    1. reg ppstime_en_d0;
    2. reg ppstime_en_d1;
    3. wire ppstime_falling_flag;
    4. wire ppstime_rasing_flag;
    5. assign ppstime_falling_flag = (~ppstime_en_d0) & ppstime_en_d1;
    6. assign ppstime_rasing_flag = (~ppstime_en_d1) & ppstime_en_d0;
    7. always @(posedge clk or negedge rst_n) begin
    8. if (!rst_n) begin
    9. ppstime_en_d0 <= 1'b0;
    10. ppstime_en_d1 <= 1'b0;
    11. end
    12. else begin
    13. ppstime_en_d0 <= ppshighlevel;
    14. ppstime_en_d1 <= ppstime_en_d0;
    15. end
    16. end

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

    1. always @(posedge clk or negedge rst_n) begin
    2. if (!rst_n) begin
    3. ppspluse <= 1'b0;
    4. end
    5. else begin
    6. ppspluse <= (ppshighlevel & bcodein);
    7. end
    8. end

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

    第四章:最后和展望

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

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

  • 相关阅读:
    【教学类-20-01】20221203《世界杯16强国旗》(大班)
    vue判断路由是否相等
    导航守卫的使用记录和beforeEach( )死循环的问题
    自学黑客(网络安全)
    Flink_CDC搭建及简单使用
    一文教你如何快速备考云计算HCIE 3.0 !
    L848字母位移
    【MDP】①二次规划问题MATLAB求解器quadprog
    Windows右键没有新建Word、PPT与Excel的解决方法
    huggingface.co 下载模型文件,死活找不到文件,也没报其他错误。原来是多了个%号
  • 原文地址:https://blog.csdn.net/weixin_45426095/article/details/126385262