调试昨天编写的程序,主要分成3个部分。
第一部分:按键产生8ms,5ms,2ms的脉宽信号;
第二部分:脉宽采集;
第三部分:脉宽采集值变换电平信号,也就是0,1,标志电平(2);
此部分采用STM32产生的,按键由FPGA产生,按一下产生一个脉冲,3个按键,分别产生3冲脉宽波形。



这部分今天调试之后发现几个问题,更新程序如下:
- //脉宽的上升沿和下降沿检测
- 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
先是上升沿下降沿检测。
- //置位time是否开启
- 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
然后time标志位的开启,这点昨天编写有点问题,改过来了,因为上升沿和下降沿标志仅仅是一个脉冲信号,执行一个机器周期就没了,所以去掉了else。
- //上升沿之后计数器工作,下降沿之后计数器停止
- 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
上升沿开启计数器,下降沿将计数器归0。这个地方后面再说,为啥能直接归0。这个地方非常重要,关乎到FPGA的运行方式问题。
- reg [3: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)begin
- bcodelevel <= 4'd3; //P电平
- end
- else if(timer >= 32'd200000)begin
- bcodelevel <= 4'd2; //1电平
- end
- else if(timer >= 32'd50000 )begin
- bcodelevel <= 4'd1; //0电平
- end
- else begin
- bcodelevel <= bcodelevel; //0电平
- end
- end
- else begin
- bcodelevel <= bcodelevel; //0电平
- 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后启动另一个定时器,我将这个定时器称为扫尾定时器。
- (*noprune*)reg resettimebeginflag;
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- resettimebeginflag <= 1'd0;
- else if (bpluse_falling_flag == 1'b1)
- resettimebeginflag <= 1'd1;
- else if (resettimer == 8'd10)
- resettimebeginflag <= 1'd0;
- end
先是:bpluse_falling_flag脉宽下降沿时启动定时器flag,在resettime等于10时关闭定时器flag。
- (*noprune*)reg [7:0] resettimer;
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- resettimer <= 0;
- else if (resettimebeginflag == 1'b1)
- resettimer <= resettimer + 8'd1;
- else if (resettimebeginflag == 1'b0)
- resettimer <= 0;
- end
再是:flag启动定时器,flag等于0时,resettime置0。
这里的FPGA的特殊之处就体现出来了:一个模块只能改变一个变量的值,不能在两个模块里改变一个变量的值,会报错。所以只能两个套在一起。这样就会出现一个问题:怎么执行?按照机器周期执行,里面time等于10的时候会停止,其实是等于11的时候停止。这个是因为下个周期才会改变值的问题,这点非常重要,如果对clk要求非常重要的话,这个自己必须弄好。
然后:捕捉下降沿。
- 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 <= resettimebeginflag;
- resettime_en_d1 <= resettime_en_d0;
-
- end
- end
就会在开始的下降沿之后再出现一个下降沿,这样就能处理其他置位或者没有解决的问题了。当然也可以用其他办法,这个办法是延迟多少个周期再处理,可以灵活应用。
解决了脉宽产生和采集方面的问题,并且第一次观察到FPGA周期的并行机制。希望小伙伴也能解决类似的问题。程序是昨天调试的,今天早上来了之后才刚刚解决这个问题。下面继续编写之后的程序,解决捕捉两个标志电平的问题。需要程序的小伙伴等到程序编写完之后会放上百度网盘链接。
需要的话可以关注公众号,里面的历史消息中只有QT的程序,等编写完B码的FPGA采集后会发布对应的程序,需要的小伙伴可以关注下。
