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


    前言

        调试昨天编写的程序,主要分成3个部分。

        第一部分:按键产生8ms,5ms,2ms的脉宽信号;

        第二部分:脉宽采集;

        第三部分:脉宽采集值变换电平信号,也就是0,1,标志电平(2);

    第一部分:脉宽产生

        此部分采用STM32产生的,按键由FPGA产生,按一下产生一个脉冲,3个按键,分别产生3冲脉宽波形。

     第二部分:脉宽采集和电平信号变换

         这部分今天调试之后发现几个问题,更新程序如下:

    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

        先是上升沿下降沿检测。

    1. //置位time是否开启
    2. 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

        然后time标志位的开启,这点昨天编写有点问题,改过来了,因为上升沿和下降沿标志仅仅是一个脉冲信号,执行一个机器周期就没了,所以去掉了else。

    1. //上升沿之后计数器工作,下降沿之后计数器停止
    2. 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

        上升沿开启计数器,下降沿将计数器归0。这个地方后面再说,为啥能直接归0。这个地方非常重要,关乎到FPGA的运行方式问题。

    1. 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)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 <= bcodelevel; //0电平
    19. end
    20. end
    21. else begin
    22. bcodelevel <= bcodelevel; //0电平
    23. end

        下降沿来的时候将time转换成电平,为了后面编程方便,0 1 标志电平对应数值1 2 3,没有脉宽的时候对应数值0。这个程序else里面也做了修改,昨天编写的是等于0,今天是数值不变。

        教训:今天调试结果一直等于0,找了半天原因,原因出来了两个:

        (1)硬件连线没有连接好,插错了管脚,以至于脉冲信号进不来;

        (2)else里面给置0了,下降沿和上升沿标志仅仅是个脉冲,仅仅执行一次,如果else置0,结果就是仅仅有1次time的数据是对的,其他时间都是0。所以修改成了赋值不变。

    第三部分:锁存问题

        来源:FPGA每个模块都是并行设计,也就是不存在先执行哪个,后执行哪个。所以改变变量的时候,必须考虑到时序问题。这点也是编写while(1)不适应FPGA的方面。思维方式必须变回来。

        bpluse_falling_flag来的时候,将time锁存,但是同时又将time置0。那么会不会锁存到错误的time的值。bpluse_falling_flag先改变timebeginflag的值,这步非常关键,这样就将锁存的时间提前置0一个周期。

         观察每个周期的图可以发现,锁存的值并且判断高低电平时,先由3变成1,也就是标志电平变为0电平,而time延后了一个周期,就是因为timebeginflag的问题。如果没有这个标志的话,两个同时进行,会出现不定值,这点非常重要。但是如果没有这个标志位怎么办?

        解决办法:加入bpluse_falling_flag后启动另一个定时器,我将这个定时器称为扫尾定时器。

    1. (*noprune*)reg resettimebeginflag;
    2. always@(posedge clk or negedge rst_n)
    3. begin
    4. if (rst_n == 1'b0)
    5. resettimebeginflag <= 1'd0;
    6. else if (bpluse_falling_flag == 1'b1)
    7. resettimebeginflag <= 1'd1;
    8. else if (resettimer == 8'd10)
    9. resettimebeginflag <= 1'd0;
    10. end

        先是:bpluse_falling_flag脉宽下降沿时启动定时器flag,在resettime等于10时关闭定时器flag。

    1. (*noprune*)reg [7:0] resettimer;
    2. always@(posedge clk or negedge rst_n)
    3. begin
    4. if (rst_n == 1'b0)
    5. resettimer <= 0;
    6. else if (resettimebeginflag == 1'b1)
    7. resettimer <= resettimer + 8'd1;
    8. else if (resettimebeginflag == 1'b0)
    9. resettimer <= 0;
    10. end

        再是:flag启动定时器,flag等于0时,resettime置0。

        这里的FPGA的特殊之处就体现出来了:一个模块只能改变一个变量的值,不能在两个模块里改变一个变量的值,会报错。所以只能两个套在一起。这样就会出现一个问题:怎么执行?按照机器周期执行,里面time等于10的时候会停止,其实是等于11的时候停止。这个是因为下个周期才会改变值的问题,这点非常重要,如果对clk要求非常重要的话,这个自己必须弄好。

        然后:捕捉下降沿。

    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 <= resettimebeginflag;
    14. resettime_en_d1 <= resettime_en_d0;
    15. end
    16. end

        就会在开始的下降沿之后再出现一个下降沿,这样就能处理其他置位或者没有解决的问题了。当然也可以用其他办法,这个办法是延迟多少个周期再处理,可以灵活应用。

    第四部分:总结

        解决了脉宽产生和采集方面的问题,并且第一次观察到FPGA周期的并行机制。希望小伙伴也能解决类似的问题。程序是昨天调试的,今天早上来了之后才刚刚解决这个问题。下面继续编写之后的程序,解决捕捉两个标志电平的问题。需要程序的小伙伴等到程序编写完之后会放上百度网盘链接。

        需要的话可以关注公众号,里面的历史消息中只有QT的程序,等编写完B码的FPGA采集后会发布对应的程序,需要的小伙伴可以关注下。

  • 相关阅读:
    靠这一份面试文档,我花了2个通宵看完,最终拿到阿里offer
    隐马尔可夫模型(HMM)
    Apache Spark 的基本概念和在大数据分析中的应用
    MAVSDK示例takeoff_and_land编译与使用
    element-ui踩坑之表格选中toggleRowSelection问题
    R语言条件判断语句编程:使用if/else语句实现条件逻辑判断、使用any函数判断向量中的值是否部分满足条件
    Java 如何使用Matcher类验证Email地址呢?
    【关于Java:认识异常】
    SpringBoot整合Redis - @Cacheable 和 RedisTemplate
    Handler的message分为三种
  • 原文地址:https://blog.csdn.net/weixin_45426095/article/details/126037673