搜索

[FPGA] 至简设计系列_BCD译码实现

[复制链接]
14|0
 楼主 | 2020-9-11 12:46 | 显示全部楼层 |阅读模式
至简设计系列_BCD译码实现
本文为明德扬原创及录用文章,转载请注明出处

1.1 总体设计1.1.1 概述
BCD码(Binary-Coded Decimal‎),用4位二进制数来表示1位十进制数中的0~910个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。BCD码这种编码形式利用了四个位元来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷的进行。

1.1.2 设计目标
实现BCD译码并显示十进制结果的程序,具体功能要求如下:
1.       串口发送8位十六进制数给FPGA;
2.       FPGA接收串口数据并对其进行BCD译码;
3.       在数码管上以十进制显示串口发送的数值。

1.1.3 系统结构框图
系统结构框图如下图一所示:
215308q2rax2lbqbxvzqtq.jpg

图一
1.1.4模块功能Ø  串口接收模块实现功能
1、  接收上位机PC发来的位宽为8的十六进制数据。
Ø  BCD译码模块实现功能
1、  对接收到的8位十六进制数据进行BCD译码。
Ø  数码管显示模块实现功能
1、  显示BCD译码后的十进制数值。
1.1.5顶层信号
  
信号名
  
I/O
位宽
定义
clk
I
1
系统工作时钟 50M
rst_n
I
1
系统复位信号,低电平有效
rx_uart
I
1
1bit串口接收数据
Segment
O
8
8位数码管段选信号
Seg_sel
O
3
3位数码管位选信号


1.1.6参考代码
下面是使用工程的顶层代码:
  • module top(
  •     clk     ,
  •     rst_n   ,
  •     rx_uart ,
  •     seg_sel ,
  •     segment
  •     );
  •     parameter      DATA_W         =         8;
  •     parameter      SEG_WID        =         8;
  •     parameter      SEL_WID        =         3;
  •     parameter      BCD_OUT        =        12;
  •     input                clk      ;
  •     input                rst_n    ;
  •     input                rx_uart  ;
  •     output[SEL_WID-1:0]  seg_sel  ;
  •     output[SEG_WID-1:0]  segment  ;
  •     wire   [SEL_WID-1:0]  seg_sel  ;
  •     wire   [SEG_WID-1:0]  segment  ;
  •     wire  [DATA_W-1 :0]  rx_dout        ;
  •     wire                 rx_dout_vld    ;
  •     wire  [BCD_OUT-1:0]  bcd_dout       ;
  •     wire                 bcd_dout_vld   ;
  •     uart_rx     u1(
  •         .clk          (  clk             ),
  •         .rst_n        (  rst_n           ),
  •         .din          (  rx_uart         ),
  •         .dout         (  rx_dout         ),
  •         .dout_vld     (  rx_dout_vld     )
  •     );
  •     bcd_water   u2(
  •         .clk          (  clk             ),
  •         .rst_n        (  rst_n           ),
  •         .din          (  rx_dout         ),
  •         .din_vld      (  rx_dout_vld     ),
  •         .dout         (  bcd_dout        ),
  •         .dout_vld     (  bcd_dout_vld    )
  •     );
  •     seg_disp#(.SEG_NUM(SEL_WID))    u3(
  •         .clk          (  clk             ),
  •         .rst_n        (  rst_n           ),
  •         .din          (  bcd_dout        ),
  •         .din_vld      (  bcd_dout_vld    ),
  •         .seg_sel      (  seg_sel         ),
  •         .segment      (  segment         )
  •     );
  • endmodule

[color=rgb(51, 102, 153) !important]复制代码




1.2 串口接收模块设计1.2.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
系统工作时钟 50M
rst_n
I
1
系统复位信号,低电平有效
din
I
1
1bit串口接收数据
dout
O
8
8bit的输出数据
dout_vld
O
1
输出数据有效指示信号


1.2.2 设计思路
在前面的案例中已经有串口接收模块的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=1074&fromuid=100105

1.2.3参考代码
  • module uart_rx(
  •     clk      ,
  •     rst_n    ,
  •     din      ,
  •     dout     ,
  •     dout_vld
  •     );
  • parameter          DATA_W = 8    ;
  • parameter          NUM_W  = 4    ;
  • parameter          CNT_W  = 14   ;
  • parameter               BPS          =        5208 ;
  • parameter                BPS_P  = BPS/2;
  • input               clk          ;
  • input               rst_n        ;
  • input               din          ;
  • output[DATA_W-1:0]  dout         ;
  • output              dout_vld     ;
  • reg   [DATA_W-1:0]  dout         ;
  • reg                 dout_vld     ;
  • reg   [NUM_W-1 :0]  data_num     ;
  • reg   [DATA_W-1:0]  rx_temp_data ;
  • reg                 din_ff0      ;
  • reg                 din_ff1      ;
  • reg                 din_ff2      ;
  • reg                 flag_add     ;
  • wire                end_cnt      ;
  • wire                end_cnt_p    ;
  • wire                add_data_num ;
  • wire                end_data_num ;
  • reg   [CNT_W-1:0]   cnt          ;
  • wire                add_cnt      ;
  • always @ (posedge clk or negedge rst_n) begin
  •         if(!rst_n) begin
  •                 din_ff0 <= 1'b1;
  •         din_ff1 <= 1'b1;
  •         din_ff2 <= 1'b1;
  •         end
  •         else begin
  •                 din_ff0 <= din;
  •         din_ff1 <= din_ff0;
  •         din_ff2 <= din_ff1;
  •         end
  • end
  • always @ (posedge clk or negedge rst_n)begin
  •         if(!rst_n) begin
  •                 flag_add <= 1'b0;
  •         end
  •         else if(din_ff2 & ~din_ff1) begin
  •                 flag_add <= 1'b1;
  •         end
  •         else if(data_num==4'd8&&end_cnt) begin
  •                 flag_add <= 1'b0;
  •         end
  • end
  • always @ (posedge clk or negedge rst_n)begin
  •     if(!rst_n)begin
  •         cnt <= 0;
  •     end
  •     else if(add_cnt)begin
  •         if(end_cnt)begin
  •             cnt <= 0;
  •         end
  •         else begin
  •             cnt <= cnt+1'b1;
  •         end
  •     end
  •     else begin
  •         cnt <= 0;
  •     end
  • end
  • assign add_cnt = flag_add;
  • assign end_cnt = add_cnt && cnt == BPS-1;
  • always @(posedge clk or negedge rst_n) begin
  •     if (rst_n==0) begin
  •         data_num <= 0;
  •     end
  •     else if(add_data_num) begin
  •         if(end_data_num)
  •             data_num <= 0;
  •         else
  •             data_num <= data_num+1 ;
  •    end
  • end
  • assign add_data_num = end_cnt;
  • assign end_data_num = add_data_num  && data_num == 9-1 ;
  • always @ (posedge clk or negedge rst_n)begin
  •         if(!rst_n) begin
  •                 dout <= 8'd0;
  •         end
  •         else if(add_cnt && cnt==BPS_P-1 && data_num!=0) begin
  •             dout<={din,{dout[7:1]}};
  •         end
  •     else begin
  •         dout<=dout;
  •     end
  • end
  • always @ (posedge clk or negedge rst_n)begin
  •         if(!rst_n) begin
  •                 dout_vld <= 1'b0;
  •         end
  •     else if(add_data_num && data_num == 4'd8) begin
  •                 dout_vld <= 1'b1;
  •         end
  •         else begin
  •         dout_vld <= 1'b0;
  •         end
  • end
  • endmodule

[color=rgb(51, 102, 153) !important]复制代码



1.3 BCD译码模块设计1.3.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
系统工作时钟 50M
rst_n
I
1
系统复位信号,低电平有效
din
I
8
8bit的输入数据
din_vld
I
1
输入数据有效指示信号
dout
O
12
12bit的输出BCD译码数据,每 4 bit一
  
组,分别表示百、十、个位的值
dout_vld
O
1
输出数据有效指示信号


1.3.2设计思路
Ø  左移加3算法
此处二进制转 BCD 码的硬件实现,采用左移加 3 的算法,具体描述如下:(此处以 8-bit 二进制码为例)
1、左移要转换的二进制码 1
2、左移之后,BCD 码分别置于百位、十位、个位
3、如果移位后所在的 BCD 码列大于或等于 5,则对该值加 3
4、继续左移的过程直至全部移位完成

举例:将十六进制码 0xFF 转换成 BCD
215308apxwkrr5p5xhyxxp.jpg

1.3.3参考代码

  • module bcd_water(
  •         clk     ,
  •         rst_n   ,
  •         din     ,
  •         din_vld ,
  •         dout    ,
  •         dout_vld
  •     );
  • input                   clk                 ;
  • input                   rst_n               ;
  • input       [ 7:0]      din                 ;
  • input                   din_vld             ;
  • output      [11:0]      dout                ;
  • wire        [11:0]      dout                ;
  • output                  dout_vld            ;
  • wire                    dout_vld            ;
  • reg         [19:0]      din_temp            ;
  • reg         [19:0]      din_temp_ff0        ;
  • reg         [19:0]      din_temp_ff1        ;
  • reg         [19:0]      din_temp_ff2        ;
  • reg         [19:0]      din_temp_ff3        ;
  • reg         [19:0]      din_temp_ff4        ;
  • wire        [20:0]      din_shift_temp      ;
  • wire        [20:0]      din_shift_temp_ff0  ;
  • wire        [20:0]      din_shift_temp_ff1  ;
  • wire        [20:0]      din_shift_temp_ff2  ;
  • wire        [20:0]      din_shift_temp_ff3  ;
  • wire        [ 7:0]      din_a_temp          ;
  • wire        [ 3:0]      din_b_temp          ;
  • wire        [ 3:0]      din_c_temp          ;
  • wire        [ 3:0]      din_d_temp          ;
  • wire        [ 7:0]      din_add_a_temp      ;
  • wire        [ 3:0]      din_add_b_temp      ;
  • wire        [ 3:0]      din_add_c_temp      ;
  • wire        [ 3:0]      din_add_d_temp      ;
  • wire        [ 7:0]      din_a_temp_ff0      ;
  • wire        [ 3:0]      din_b_temp_ff0      ;
  • wire        [ 3:0]      din_c_temp_ff0      ;
  • wire        [ 3:0]      din_d_temp_ff0      ;
  • wire        [ 7:0]      din_a_temp_ff1      ;
  • wire        [ 3:0]      din_b_temp_ff1      ;
  • wire        [ 3:0]      din_c_temp_ff1      ;
  • wire        [ 3:0]      din_d_temp_ff1      ;
  • wire        [ 7:0]      din_a_temp_ff2      ;
  • wire        [ 3:0]      din_b_temp_ff2      ;
  • wire        [ 3:0]      din_c_temp_ff2      ;
  • wire        [ 3:0]      din_d_temp_ff2      ;
  • wire        [ 7:0]      din_a_temp_ff3      ;
  • wire        [ 3:0]      din_b_temp_ff3      ;
  • wire        [ 3:0]      din_c_temp_ff3      ;
  • wire        [ 3:0]      din_d_temp_ff3      ;
  • wire        [ 7:0]      din_add_a_temp_ff0  ;
  • wire        [ 3:0]      din_add_b_temp_ff0  ;
  • wire        [ 3:0]      din_add_c_temp_ff0  ;
  • wire        [ 3:0]      din_add_d_temp_ff0  ;
  • wire        [ 7:0]      din_add_a_temp_ff1  ;
  • wire        [ 3:0]      din_add_b_temp_ff1  ;
  • wire        [ 3:0]      din_add_c_temp_ff1  ;
  • wire        [ 3:0]      din_add_d_temp_ff1  ;
  • wire        [ 7:0]      din_add_a_temp_ff2  ;
  • wire        [ 3:0]      din_add_b_temp_ff2  ;
  • wire        [ 3:0]      din_add_c_temp_ff2  ;
  • wire        [ 3:0]      din_add_d_temp_ff2  ;
  • wire        [ 7:0]      din_add_a_temp_ff3  ;
  • wire        [ 3:0]      din_add_b_temp_ff3  ;
  • wire        [ 3:0]      din_add_c_temp_ff3  ;
  • wire        [ 3:0]      din_add_d_temp_ff3  ;
  • reg                     dout_vld_temp       ;
  • reg                     dout_vld_temp_ff0   ;
  • reg                     dout_vld_temp_ff1   ;
  • reg                     dout_vld_temp_ff2   ;
  • reg                     dout_vld_temp_ff3   ;
  • reg                     dout_vld_temp_ff4   ;
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         din_temp <= 20'b0;
  •     end
  •     else if(din_vld)begin
  •         din_temp <= {9'b0,din,3'b0};
  •     end
  • end
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         dout_vld_temp <= 1'b0;
  •     end
  •     else if(din_vld)begin
  •         dout_vld_temp <= 1'b1;
  •     end
  •     else begin
  •         dout_vld_temp <= 1'b0;
  •     end
  • end
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         din_temp_ff0 <= 20'b0;
  •     end
  •     else begin
  •         din_temp_ff0 <= din_shift_temp[19:0];
  •     end
  • end
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         dout_vld_temp_ff0 <= 1'b0 ;
  •     end
  •     else begin
  •         dout_vld_temp_ff0 <= dout_vld_temp;
  •     end
  • end
  • assign  din_a_temp_ff0     = din_temp_ff0[ 7: 0]                                             ;
  • assign  din_b_temp_ff0     = din_temp_ff0[11: 8]                                             ;
  • assign  din_c_temp_ff0     = din_temp_ff0[15:12]                                             ;
  • assign  din_d_temp_ff0     = din_temp_ff0[19:16]                                             ;
  • assign  din_add_a_temp_ff0 = din_a_temp_ff0                                                  ;
  • assign  din_add_b_temp_ff0 = din_b_temp_ff0 + ((din_b_temp_ff0>=5)?4'd3:4'd0)                ;
  • assign  din_add_c_temp_ff0 = din_c_temp_ff0 + ((din_c_temp_ff0>=5)?4'd3:4'd0)                ;
  • assign  din_add_d_temp_ff0 = din_d_temp_ff0 + ((din_d_temp_ff0>=5)?4'd3:4'd0)                ;
  • assign  din_shift_temp_ff0 = {din_add_d_temp_ff0,din_add_c_temp_ff0,din_add_b_temp_ff0,din_add_a_temp_ff0,1'b0};
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         din_temp_ff4 <= 20'b0;
  •     end
  •     else begin
  •         din_temp_ff4 <= din_shift_temp_ff3[19:0];
  •     end
  • end
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         dout_vld_temp_ff4 <= 1'b0;
  •     end
  •     else begin
  •         dout_vld_temp_ff4 <= dout_vld_temp_ff3;
  •     end
  • end
  • assign  dout     = din_temp_ff4[19:8];
  • assign  dout_vld = dout_vld_temp_ff4 ;
  • endmodule

[color=rgb(51, 102, 153) !important]复制代码


1.4 数码管显示模块设计1.4.1接口信号
  
信号名
  
I/O
位宽
定义
clk
I
1
系统工作时钟 50M
rst_n
I
1
系统复位信号,低电平有效
din
I
12
12位的输入BCD译码数据
din_vld
I
1
输入数据有效指示信号
segment
O
8
8位数码管段选信号
seg_sel
O
3
3位数码管位选信号


1.4.2设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=399

1.4.3参考代码
  • module  seg_disp(
  •                  rst_n       ,
  •                  clk         ,
  •                  din         ,
  •                  din_vld     ,
  •                  seg_sel     ,
  •                  segment
  •              );
  • parameter  DATA_IN        =       12          ;
  • parameter  TIME_30MS      =       15000        ;
  • parameter  SEG_WID        =       8           ;
  • parameter  SEG_NUM        =       8           ;
  • parameter  CNT_WID        =       10          ;
  • parameter  NUM_0          =       8'b1100_0000;
  • parameter  NUM_1          =       8'b1111_1001;
  • parameter  NUM_2          =       8'b1010_0100;
  • parameter  NUM_3          =       8'b1011_0000;
  • parameter  NUM_4          =       8'b1001_1001;
  • parameter  NUM_5          =       8'b1001_0010;
  • parameter  NUM_6          =       8'b1000_0010;
  • parameter  NUM_7          =       8'b1111_1000;
  • parameter  NUM_8          =       8'b1000_0000;
  • parameter  NUM_9          =       8'b1001_0000;
  • parameter  NUM_ERR        =       8'b1111_1111;
  • input                             clk         ;
  • input                             rst_n       ;
  • input  [DATA_IN - 1:0]            din         ;
  • input                             din_vld     ;
  • output [SEG_NUM - 1:0]            seg_sel     ;
  • output [SEG_WID - 1:0]            segment     ;
  • reg    [SEG_NUM - 1:0]            seg_sel     ;
  • reg    [SEG_WID - 1:0]            segment     ;
  • reg    [ 31    :    0]            cnt_30us      ;
  • reg    [SEG_NUM - 1:0]            sel_cnt     ;
  • reg    [ 4 - 1 :    0]            seg_tmp     ;
  • wire                              add_cnt_30us  ;
  • wire                              end_cnt_30us  ;
  • wire                              add_sel_cnt ;
  • wire                              end_sel_cnt ;
  • always @(posedge clk or negedge rst_n) begin
  •     if (rst_n==0) begin
  •         cnt_30us <= 0;
  •     end
  •     else if(add_cnt_30us) begin
  •         if(end_cnt_30us)
  •             cnt_30us <= 0;
  •         else
  •             cnt_30us <= cnt_30us+1 ;
  •    end
  • end
  • assign add_cnt_30us = 1;
  • assign end_cnt_30us = add_cnt_30us  && cnt_30us == TIME_30MS-1 ;
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         sel_cnt <= 0;
  •     end
  •     else if(add_sel_cnt)begin
  •         if(end_sel_cnt)
  •             sel_cnt <= 0;
  •         else
  •             sel_cnt <= sel_cnt + 1;
  •     end
  • end
  • assign add_sel_cnt = end_cnt_30us;
  • assign end_sel_cnt = add_sel_cnt && sel_cnt == SEG_NUM-1;
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         seg_sel <= {SEG_NUM{1'b1}};
  •     end
  •     else begin
  •         seg_sel <= ~(1'b1 << sel_cnt);
  •     end
  • end
  • always  @(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         seg_tmp <= 0;
  •     end
  •     else begin
  •         seg_tmp <= din[4*(sel_cnt+1)-1 -:4];
  •     end
  • end
  • always@(posedge clk or negedge rst_n)begin
  •     if(rst_n==1'b0)begin
  •         segment<=NUM_0;
  •     end
  •     else  begin
  •         case (seg_tmp)
  •             0 : segment <= NUM_0;
  •             1 : segment <= NUM_1;
  •             2 : segment <= NUM_2;
  •             3 : segment <= NUM_3;
  •             4 : segment <= NUM_4;
  •             5 : segment <= NUM_5;
  •             6 : segment <= NUM_6;
  •             7 : segment <= NUM_7;
  •             8 : segment <= NUM_8;
  •             9 : segment <= NUM_9;
  •             default : segment <= NUM_ERR;
  •         endcase
  •     end
  • end
  • endmodule

[color=rgb(51, 102, 153) !important]复制代码

1.5 效果和总结
Ø  下图是该工程在db603开发板上的现象——串口发送数据8’h93,数码管显示12’d147
215309x0l6q2d2dsv7dqw2.jpg

215309qrqaar50tz544nq4.jpg

下图是该工程在mp801试验箱上的现象——串口发送数据8’he9,数码管显示12’d233

215309n3acrej5j2uoai1f.jpg

215920rqp7pqjpjpojpoj7.jpg

下图是该工程在ms980试验箱上的现象——串口发送数据8’h52,数码管显示12’d082
215309r4cccx58m8ummi08.jpg

215309g5jw17rq8roeryq7.jpg
由于该项目的上板现象是串口发送8位的十六进制数经过BCD译码后,在数码管上显示对应的十进制数值,想观看完整现象的朋友可以看一下上板演示的视频。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:

至简设计系列案例_BCD译码实现.pdf

1.49 MB, 下载次数: 1

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 我要提问 投诉建议 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

关闭

热门推荐上一条 /5 下一条

在线客服 快速回复 返回顶部 返回列表