• FPGA解析B码----连载5


    前言

        原则:1s的B码无论错误正确不能影响下1s的B码解析和1PPS输出。

        解决办法:每次锁定2个P标志位,无论B码中间正确与否,都会在下一个2个P标志位的地方输出一个对应第2个P标志位的高电平与之相与。如果锁定2个P标志位之后,下1s没有B码,那么只输出高电平,由于没有B码,那么就不会输出1PPS信号。

        原则适用范围:1PPS,和B码的全部解析。

        需要修改的地方:前面的B码的utc解析没有遵守这个原则,所以需要修改。

        文章解决问题:1PPS产生。

    第一章:1PPS波形

        先放上解析的1PPS的波形:

     

        左边:B码存在时,示波器处于TD状态,能一直锁定到1PPS信号的波形;

        右边:B码不存在时,示波器处于WAIT状态,不输出1PPS信号。

        再次将B码恢复,再次捕捉到输出的1PPS信号。所以程序是没什么问题。 

     第二章:1PPS代码

    (1)捕捉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

    (2)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

    (3)根据标志位,启动计数器和计数器复位

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

    (4)B码下降沿的延后一个下降沿,判断time值和置位B码电平

    1. (*noprune*)reg [3: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 <= 4'd3; //P电平
    10. end
    11. else if(timer >= 32'd200000)begin
    12. bcodelevel <= 4'd2; //1电平
    13. end
    14. else if(timer >= 32'd50000 )begin
    15. bcodelevel <= 4'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

        此处修改:严格了判断区间,防止误判;其他时间对应电平为0;

        此处注意:在B码下降沿时立刻判断time的值,而不是标志位为0时判断,FPGA的工作原理,没办法。直接在timeflag处也试验了下,没什么问题,但是为了严谨,在这个地方判断了!

    (5)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

        此时time已经锁存,并且B码的电平已经锁定。然后下面再判断锁定值。

    (6)锁定2个脉宽信号

    1. (*noprune*)reg [3:0] bcodelevellatch1;
    2. (*noprune*)reg [3:0] bcodelevellatch2;
    3. always@(posedge clk or negedge rst_n)
    4. begin
    5. if (rst_n == 1'b0)begin
    6. bcodelevellatch1 <= 4'd0;
    7. bcodelevellatch2 <= 4'd0;
    8. end
    9. else if (resettime_falling_flag == 1'b1)begin
    10. bcodelevellatch1 <= bcodelevel;
    11. bcodelevellatch2 <= bcodelevellatch1;
    12. end
    13. else if (ppstimer == 32'd49050000)begin
    14. bcodelevellatch1 <= 4'd0;
    15. bcodelevellatch2 <= 4'd0;
    16. end
    17. end

        此处49050000对应981ms,至于这个是为什么,程序完毕之后再解释。这个地方是将锁存的两个值复位。目的是不影响下一次的2个P标志位的采集。也就是将1PPS的产生效果控制在1s之内。

    (7)锁存后,延后一个周期

    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

    (8)判断锁存的两个标志,是否为P标志位

    1. (*noprune*)reg ppsreadyflag;
    2. always@(posedge clk or negedge rst_n)
    3. begin
    4. if (rst_n == 1'b0)
    5. ppsreadyflag <= 1'd0;
    6. else if (resettime1_falling_flag == 1'b1)begin
    7. if((bcodelevellatch1 == 4'd3)&&(bcodelevellatch2 == 4'd3))
    8. ppsreadyflag <= 1'd1;
    9. end
    10. else if (ppstime_falling_flag == 1'b1)begin
    11. ppsreadyflag <= 1'd0;
    12. end
    13. end

        此处:锁存值都为P标志的话,flag置1。等到产生1PPS之后的下降沿,flag置0。

        下面的(9)中,flag等于1时,计数器加,flag等于0时,计数器复位。并且是在高电平产生之后计数器归0,保证每次信号的产生时间。

    (9)启动计数器

    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. end

    (10)根据计数器值,输出高电平,然后变为低电平

    1. always@(posedge clk or negedge rst_n)
    2. begin
    3. if (rst_n == 1'b0)
    4. ppshighlevel <= 1'd0;
    5. else if (ppstimer == 32'd49550000) // 991ms 置高,994ms置低
    6. ppshighlevel <= 1'b1;
    7. else if (ppstimer == 32'd49700000)
    8. ppshighlevel <= 1'b0;
    9. end

        此处即为与B码与的电平信号,使得高电平刚好落到下1s的第二个P电平的上升沿。

    (11)高电平变为低电平时,延后一个周期

    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

        这个下降沿就是前面介绍的扫尾脉冲,可以延后一个周期,也可以像前面介绍的定时之后产生的下降沿。

    第三章:编写原则

        编写到这里突然发现编写FPGA程序的原则。

        原则1:每个模块只改变一个变量;

        原则2:适时延迟一个周期;

        原则3:变量闭环。

        解释下:

        原则1,这个是FPGA的规则,要不会报错。当然为了自己的程序好看并且以后能看懂自己编写的程序,一个模块只有一个变量。当然高手是将多个变量放到一个模块里,这个我就不奢求了。

        原则2,这个为了规避FPGA的并行逻辑,单片机while(1)过来的,没有办法,思维方式就是顺序执行,但是由于解析B码这种,必须有顺序执行的逻辑,所以也是没办法的事情。这和嵌入式QT中有点像,虽然是面对对象,但是也是面向过程的。具体对象和过程定义,可以看看以前的文章,里面介绍的很详细。

        原则3,这个是现在刚刚发现的原则。由于FPGA是并行逻辑,但是加入顺序执行之后,像B码这种时间顺序的东西,必须将每个量都在一定时间产生,并且在一定时间关闭,形成一个闭环,然后等待下一个循环。这样此刻不影响下一刻。这样应该比较好,出了任何问题都不会一直出现。

    第四章:交流

        解析B码的基本程序已经告一段落,能正确解析出utc时间和1PPS,这个才是最基本的应用,对于不同状况下的B码解析还没有考虑,这是下一步考虑的事情。当然程序中存在的bug也存在,下面主要根据原则3修改utc时间的程序。

  • 相关阅读:
    MongoDB聚合运算符:$setDifference
    Shior02(身份认证加密)
    haproxy+keepalived集群搭建02
    Java二叉搜索树
    SpringBoot的自动配置原理
    这玩意也太猛了!朋友们,我在此严正呼吁大家:端好饭碗,谨防 AI!
    精通Java事务编程-深入理解事务
    利用FastAPI和OpenAI-Whisper打造高效的语音转录服务
    node编写服务器
    SpringCloud系列(14)--Eureka服务发现(Discovery)
  • 原文地址:https://blog.csdn.net/weixin_45426095/article/details/126172738