现有:STM32已经编写好B码解析程序,现需要采用FPGA解析B码。
原因:CSDN上找不到编写的B码程序,就算有全部需要下载,穷,没有下载点。
区别:STM32是顺序执行,方便接收B码和解析B码,同时有PWM采集例程,定时器,计数器。
难点:FPGA是并行结构,逻辑比较复杂。
之前使用FPGA仅仅是编写了串口,SPI,double双精度数据的运算。对于1s数据和脉宽采集这种还没有编写过,现在尝试采用自己总结的编写方式和规范一步一步的编写B码解析程序。
写这篇文章的时候,自己还不知道怎么去编写,下面一步一步的将自己的思维方式和程序展示,希望能对学习FPGA的小伙伴和解析B码的小伙伴给与帮助,能节省一定的时间和金钱。也希望能和大家一起学习。
输入:B码;
输出:1PPS信号,UTC时间,B码关键帧信息。

信号时长:1s;
信号种类:2ms:0电平(暗红色);5ms:1电平(橙黄色);8ms:标志电平(绿色);
大类拆解:1PPS,B码信息输出;
小类拆解:脉宽采集,码元存储,时间计算,串口输出。
这部分最好加上,显示自己的程序正在运行,最好用1个LED灯进行显示,然后最后程序定型之后再给去掉,如果空间大的话那就随意了。
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- timer <= 32'd0;
- else if (timer == 32'd9_999_999)
- timer <= 32'd0;
- else
- timer <= timer + 32'd1;
- end
先来个计数器,一般程序就采用这种格式(这个格式是自己总结的,方便,好看,简单,一句一句的来,以后自己阅读着也能看出来自己写的是啥!):
(1)复位,变量赋值为0;
(2)条件1,变量处理1;
(3)条件2,变量处理2;
(4)其他条件,变量处理3。
当然中间也可以再增加其他条件。计数器作用从0加到9999999,然后再回到0。具体最大值多少随便。
然后再来个点灯,到一定数值,flag加1,然后下次再加1,也就是flag在0和1之间变化。
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- flag <= 1'b0;
- else if(timer == 32'd8_999_999)
- flag <= flag + 1'b1;
- else
- flag <= flag;
- end
- always@(posedge clk or negedge rst_n)
- begin
- if(rst_n == 1'b0)
- begin
- led4<=1'b1;
- end
- else begin
- case (flag)
- 1'b0 : led4<=1'b1;
- 1'b1 : led4<=1'b0;
- default : led4<=1'b0;
- endcase
- end
- end
结构还是刚才那个结构,复位置0,time的值等于8999999时flag加1,其他条件flag不变。
复位灯置0,两个条件下LED分别置0和1,代表亮和灭。
这样LED灯就可以按照一定周期亮和灭了。
思路:这部分我想着大概分成4部分,具体还需要怎么弄,一会编程看看!
(1)上升沿检测;(2)下降沿检测;(3)沿检测后的计数器启动和停止;(4)脉宽时间计算。
上升沿检测和下降沿检测程序:
- //脉宽的上升沿和下降沿检测
- 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
检测到上升沿和下降沿时,两个flag分别一个周期的脉冲信号。
- //置位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)
- timebeginflag <= 1'd1;
- else if (bpluse_falling_flag == 1)
- timebeginflag <= 1'd0;
- else
- timebeginflag <= 1'd0;
- end
当上升沿和下降沿时,timebeginflag分别置1和0。
- //上升沿之后计数器工作,下降沿之后计数器停止
- reg [31:0] timer;
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- timer <= 32'd0;
- else if (timebeginflag == 1)
- timer <= timer + 32'd1;
- else if (timebeginflag == 0)
- timer <= timer;
- end
上升沿时,time计数器开始计数,下降沿时,time计数器停止。
这块缺少一个time复位为0的一个条件,这个条件为time计数器中的数据取出后的标志位,当取出time之后,time复位,为下次的脉宽测量做准备。
脉宽计算程序:
- reg bcodelevel;
- always@(posedge clk or negedge rst_n)
- begin
- if (rst_n == 1'b0)
- bcodelevel <= 0;
- else if (bpluse_falling_flag == 1)begin
- if(timer >= 350000)bcodelevel = 3; //P电平
- else if(timer >= 200000)bcodelevel = 2; //1电平
- else if(timer >= 50000 )bcodelevel = 1; //0电平
- else bcodelevel = 0;
- end
- else
- bcodelevel <= 0;
- end
当下降沿来时,time计数器停止计数,同时判断time中的数据,由于time的数据不那么准确,可以判断大范围之后的电平,大于7ms即为标志电平,大于4ms即为1电平,大于1ms即为0电平。中间用了else,所以3个判断并不冲突。这样就能判断每个脉冲的电平了。
程序先编写到此,明天测试编写程序。测试程序直接发送对应的ms数的电平信号,测试能否采集到对应电平。
整体思路就是先采集上升沿和下降沿,启动计数器,下降沿时判断计数器数据大小,根据数据大小输出电平类型。当然下面还得锁存电平,只能明天考虑了