当前位置:   article > 正文

FPGA基本实验之数码管的动态显示_fpga数码管动态显示

fpga数码管动态显示

        关于数码管的基本知识大家可以参考我上一篇文章数码管的静态显示,

动态数码管的驱动方式

        使用 1ms 的刷新时间让六个数码管轮 流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类推依次点亮六个数 码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮 1ms,这样就能让人眼感觉到数码 管一直在亮了。点亮相应数码管的时候给其显示相应的值,这样就可以使六个数码管显示 不同的值了,这就是驱动数码管动态显示的方法。

实验目的

        让六位数码管显示从十进制数 0 开始计数,每 0.1s 加 1,一直到加到十进制数 999999。到达 999999 之后回到 0 开始重新计数。

程序设计

整体框架设计

        我们可以先设计出系统的整体框架结构,以便我们明白我们的程序可以分为那几部分进行,明确系统的构成。

        根据系统框架结构我们可以分析出该系统可以分为六个子模块,现在我们依次来介绍这六个子模块:

模块名称功能描述
data_gen数据生成模块
seg_danamic动态数码管显示驱动模块
bcd_8421二进制转bcd码模块
hc595_crtl74HC595控制模块
seg_595_dynamic数码管动态显示模块
top_seg_595顶层模块

接下来我们根据各子模块来完成整个实验:

数据生成模块

模块框图:

数据生成模块输入输出信号描述
信号位宽类型功能描述
sys_clk1bitInput系统时钟信号,频率50Mhz
sys_rst_n1bitInput复位信号低电平有效
point6bitOutput输出小数点
data20bitOutput输出数据
sign1bitOutput输出符号
seg_en1bitOutput数码管使能信号

绘制数据波形图

        cnt_100ms:我们实验的要求是每 0.1s 让显示的数据加 1,所以我们需要用到一个间隔 为 0.1s 的循环计数器。计数器其从 0 开始计数,计到 0.1s(4999999)时归 0 开始下一个 0.1s 计数。因为计数器的时钟为 50MHz,一个时钟为 1/50MHz(s),也就是 20ns,所 以   0.1s=4999_999*20ns。

        cnt_flag:当计数器计到 0.1s 时拉高一个标志信号,让这个标志信号去控制数据的加 1。         data:输出数据。当检测到 0.1s 到来的标志信号为高时让其加 1,当加到 999999 并检 测到 0.1s 到来的标志信号时让其归 0,开始下一轮的数据显示。

        point:输出小数点。在这里我们让其高电平有效(以本次使用的数码管为例,即当小 数点对应段选信号为低,位选信号为高时点亮有效),本次实验不需要显示小数点,让每 一位都为 0 即可。

         seg_en:数码管使能信号。因为我们一直在显示,所以一直给其拉高就行

         sign:负号显示,高电平有效。因本次实验不显示负号,给其一直为低电平即可。

  1. module data_gen
  2. #(
  3. parameter CNT_MAX = 23'd4999_999,
  4. parameter DATA_MAX = 20'd999_999
  5. )
  6. (
  7. input wire sys_clk,
  8. input wire sys_rst_n,
  9. output reg [19:0] data,
  10. output wire[5:0] point,
  11. output reg seg_en,
  12. output wire sign
  13. );
  14. reg [22:0] cnt_100ms;
  15. reg cnt_flag;
  16. //不显示小数点和负数
  17. assign point = 6'b000_000;
  18. assign sign = 1'b0;
  19. //cnt_100ms循环计数计数计到100ms
  20. always@(posedge sys_clk or negedge sys_rst_n)
  21. if(sys_rst_n == 1'b0)
  22. cnt_100ms <= 23'd0;
  23. else if(cnt_100ms == CNT_MAX)
  24. cnt_100ms <= 23'd0;
  25. else
  26. cnt_100ms <= cnt_100ms + 1'b1;
  27. //cnt_flag:每100ms产生一个标志信号
  28. always@(posedge sys_clk or negedge sys_rst_n)
  29. if(sys_rst_n == 1'b0)
  30. cnt_flag <= 1'b0;
  31. else if(cnt_100ms == CNT_MAX - 1'b1)
  32. cnt_flag <= 1'b1;
  33. else
  34. cnt_flag <= 1'b0;
  35. //数码管显示数据0~999——999
  36. always@(posedge sys_clk or negedge sys_rst_n)
  37. if(sys_rst_n == 1'b0)
  38. data <= 20'd0;
  39. else if((data == DATA_MAX) && (cnt_flag == 1'b1))
  40. data <= 20'd0;
  41. else if(cnt_flag == 1'b1)
  42. data <= data + 1'b1;
  43. else
  44. data <= data;
  45. //数码管使能信号拉高
  46. always@(posedge sys_clk or negedge sys_rst_n)
  47. if(sys_rst_n == 1'b0)
  48. seg_en <= 1'b0;
  49. else
  50. seg_en <= 1'b1;
  51. endmodule

 二进制转BCD码模块

        根据实验目标可知我们要让数码管显示的数是十进制数,而我们数码管是通过段选信 号和位选信号去控制每个数码管进行显示的,一位数码管显示的是一个十进制数,而我们 的十进制数是以二进制进行编码的,如果我们直接使用二进制编码进行显示,那么每个数 码管显示的数就不是十进制数而是十六进制数了。所以我们需要将二进制编码的十进制数 转换为 BCD 编码的十进制数进行显示。

        BCD 码(Binary-Coded Decimal),又称二 - 十进制码,使用 4 位二进制数来表示 1 位 十进制数中的 0~9 这 10 个数码,是一种二进制的数字编码形式,用二进制编码的十进制代 码。

        BCD 码根据权值的有无可分为“有权码”和“无权码”。其中的权字表示的是权值, 有权码的四位二进制数中的每一位都有一个固定的权值,而无权码是没有权值的。常见的 有权码有 8421 码、5421 码和 2421 码,8421 码它的权值从左到右是 8421,5421 码的权值 从左到右是 5421,2421 码的权值是 2421,其中 8421 码是最为常用的 BCD 编码,本实验 中使用的也是这种编码。常用的无权码有余 3 码、余 3 循环码。

        以本次实验我们使用的 8421BCD 编码 为例,比如说十进制数 5,它的 8421BCD 码为 0101,那么我们怎么通过 0101 得到我们的 数字 5 呢,这里需要用到一个算法,就是将其每位二进制数乘以它的权值然后相加。十进 制 5 的 8421BCD 码为 0101,即:1×1 + 0×2 + 1×4+ 0×8 = 5。其它有权码也是这种计算方 式,

        为什么转换为 BCD 码就能进行十 进制的显示了呢?这里我们举个例子,例如我们需要显示十进制数据 234,根据我们前面讲的动态显示原理,在第一个显示周期我们点亮数码管 1,并让其显示值 4;在第二个显示 周期我们点亮数码管 2,并让其显示值 3;在第三个显示周期我们点亮数码管 3,并让其显 示值 2;第四、五、六个周期我们不点亮其余数码管即可,以此循环就完成了十进制数 234 的显示。那个我们如何才能在点亮相应的值时给其显示相应的值呢?我们先看看 234 的十 进制数的二进制表示为:1110_1010;234 的 8421BCD 码为:0010_0011_0100;可以发现 如果我们使用二进制数赋值我们并不能准确的给到 2、3、4 值,而如果我们用 8421BCD 码 给其赋值的话,每 4 位 8421BCD 码代表一个十进制数,那么就能完美的进行显示了。

绘制本模块的实验框图

        

二进制转BCD码模块输入输出信号描述
信号位宽类型功能描述
sys_clk1bitInput系统时钟频率为50MHZ
sys_rst_n1bitInput复位信号,低电平有效
data20bitInput输入以二进制表示的十进制数
unit1bitOutput个位BCD码
ten1bitOutput十位BCD码
hun1bitOutput百位BCD码
tho1bitOutput千位BCD码
t_tho1bitOutput万位BCD码
h_hun1bitOutput十万位BCD码

绘制波形图:

        cnt_shift:移位判断计数器,前面我们说到我们输入转换的二进制码有多少位我们就需 要进行多少次判断移位操作,这里我们 data 数据的位宽为 20 位,所以这里我们声明移位 判断计数器对移位 20 次进行判断控制。

        data_shift:移位判断数据寄存器,该寄存器用于存储移位判断操作过程中的数据,这 里我们输入的二进制位宽为 20 位,待转换成的 BCD 码位宽为 24 位,所以这里我们声明该 寄存器的位宽为输入的二进制位宽和待转换完成的 BCD 码位宽之和,即 44 位。根据波形 图可知,这里我们设计当移位计数器等于 0 时寄存器的低 20 位即为待转换数据,而由于还 没开始进行转换,高 24 位的 BCD 码我们补 0 即可。

        shift_flag:移位判断操作标志信号。前面说到我们需要对数据进行移位和判断,判断 在前移位在后,所以这里我们声明一个标志信号,用于控制判断和移位的先后顺序,当 shift_flag 为低时对数据进行判断,当 shift_flag 为高时对数据进行移位。需要注意的是无论 是移位操作和判断操作都是在单个系统时钟下完成的,故我们判断 20 次移位 20 次在 40 个 系统时钟内就能完成。         unit、ten、hum、tho、t_tho、h_hun:六路 BCD 码,前面我们说到我们开发板上有六 位数码管,故可以显示的最大值是六位十进制数,随意这里我们声明了六路 BCD 码,一个 BCD 码代表十进制的一个位数,其中 unit 代表个位、ten 代表十位、hun 代表百位、tho 代 表千位、t_tho 代表万位、h_hun 代表十万位。当我们的移位判断计数器等于 21 是说明 20 位二进制转 BCD 码的移位判断操作已经完成,此时 data_shift 里寄存的数据就是转换的寄 存数据,该数据的高 24 位即转换完成后的 BCD 码。所以当 cnt_shift 等于 20 是就将寄存的 BCD 码值赋值为相对应的各个位数。

编写二进制转BCD码

  1. module bcd_8421
  2. (
  3. input wire sys_clk ,
  4. input wire sys_rst_n ,
  5. input wire [19:0] data ,
  6. output reg [3:0] unit ,
  7. output reg [3:0] ten ,
  8. output reg [3:0] hun ,
  9. output reg [3:0] tho ,
  10. output reg [3:0] t_tho ,
  11. output reg [3:0] h_hun
  12. );
  13. reg [4:0] cnt_shift ;
  14. reg [43:0] data_shift ;
  15. reg shift_flag ;
  16. //cnt_shift:从0~21循环计数
  17. always@(posedge sys_clk or negedge sys_rst_n)
  18. if(sys_rst_n == 1'b0)
  19. cnt_shift <= 5'd0;
  20. else if((cnt_shift == 5'd21) && (shift_flag == 1'b1))
  21. cnt_shift <= 5'd0;
  22. else if(shift_flag == 1'b1)
  23. cnt_shift <= cnt_shift + 1'b1;
  24. else
  25. cnt_shift <= cnt_shift;
  26. //data_shift:计数器为0时赋值,计数器为1~20时进行移位判断操作
  27. always@(posedge sys_clk or negedge sys_rst_n)
  28. if(sys_rst_n == 1'b0)
  29. data_shift <= 44'b0;
  30. else if(cnt_shift == 5'd0)
  31. data_shift <= {24'b0,data};
  32. else if((cnt_shift <= 20) && (shift_flag == 1'b0))
  33. begin
  34. data_shift[23:20] <= (data_shift[23:20] > 4) ?
  35. (data_shift[23:20] + 2'd3) : (data_shift[23:20]);
  36. data_shift[27:24] <= (data_shift[27:24] > 4) ?
  37. (data_shift[27:24] + 2'd3) : (data_shift[27:24]);
  38. data_shift[31:28] <= (data_shift[31:28] > 4) ?
  39. (data_shift[31:28] + 2'd3) : (data_shift[31:28]);
  40. data_shift[35:32] <= (data_shift[35:32] > 4) ?
  41. (data_shift[35:32] + 2'd3) : (data_shift[35:32]);
  42. data_shift[39:36] <= (data_shift[39:36] > 4) ?
  43. (data_shift[39:36] + 2'd3) : (data_shift[39:36]);
  44. data_shift[43:40] <= (data_shift[43:40] > 4) ?
  45. (data_shift[43:40] + 2'd3) : (data_shift[43:40]);
  46. end
  47. else if((cnt_shift <= 20) && (shift_flag == 1'b1))
  48. data_shift <= data_shift << 1;
  49. else
  50. data_shift <= data_shift;
  51. //shift_flag:移位判断标志信号,用于判断移位控制的先后顺序
  52. always@(posedge sys_clk or negedge sys_rst_n)
  53. if(sys_rst_n == 1'b0)
  54. shift_flag <= 1'b0;
  55. else
  56. shift_flag <= ~shift_flag;
  57. //当计数器等于二十时,移位判断操作完成,对各个位数的BCD码进行赋值
  58. always@(posedge sys_clk or negedge sys_rst_n)
  59. if(sys_rst_n == 1'b0)
  60. begin
  61. unit <= 4'b0;
  62. ten <= 4'b0;
  63. hun <= 4'b0;
  64. tho <= 4'b0;
  65. t_tho <= 4'b0;
  66. h_hun <= 4'b0;
  67. end
  68. else if(cnt_shift == 5'd21)
  69. begin
  70. unit <= data_shift[23:20];
  71. ten <= data_shift[27:24];
  72. hun <= data_shift[31:28];
  73. tho <= data_shift[35:32];
  74. t_tho <= data_shift[39:36];
  75. h_hun <= data_shift[43:40];
  76. end
  77. endmodule

 数码管动态显示驱动模块

模块框图:

数码管动态显示驱动模块输入输出信号描述
信号位宽类型功能描述
sys_clk1bitInput系统时钟,频率50MHZ
sys_rst_n1bitInput复位信号,低电平有效
point6bitInput输入小数点
data20bitInput输入数据
seg_en1bitInput数码管使能信号
sign1bitInput输入符号
sel6bitOutput数码管位选信号
seg8bitOutput数码管段选信号

 波形图绘制:

根据波形图对信号进行逐一说明:

        point:输入小数点控制信号,高电平有效,这里我们假设要让第二个数码管显示小数 点,其余数码管不显示小数点,那么此时 point 的输入的值就应该是 6’b000010。

        seg_en:数码管使能信号,这里一直让其拉高即可。

        data:输入的十进制数据,假设这里我们输入的十进制数为 9876。

         sign:符号位控制信号,高电平有效。假设我们需要显示的是负数,那么这里就让符 号位控制信号为高即可。 unit、ten、hun、tho、t_tho、h_hun:这六个信号就是我们例化的 bcd_8421 模块转化的 的 8421BCD 码,也就是说这六个 BCD 码就是输入十进制数 9876 各个位的 BCD 码。所以 这里个位(unit)是 6,十位(ten)是 7,百位(hun)是 8,千位(tho)是 9,万位和十 万位都为 0。

         data_reg:数码管待显示内容寄存器,因为这里我们假设输入要显示的十进制数为 9876,并且显示负号,所以前五个数码管就会显示-9876 的数值,此时最高位数码管什么都 不显示,我们用 X 表示,所以这里六个数码管显示的内容就是:X-9876。

        cnt_1ms:前面讲到要让显示的数码管不会有闪烁感,我们需要使用 1ms 的扫描时间去 扫描各个数码管。所以这里我们需要一个 1ms 的计数器对 1ms 进行循环计数。

        flag_1ms:1ms 计数标志信号,当 1ms 计数器计到 1ms 时拉高该标志信号,我们使用 该标志信号去控制位选数码管计数器的计数。 

        cnt_sel:位选数码管计数器。我们在理论学习中说到动态扫描方式是用 1ms 的刷新时 间让六个数码管轮流显示:第 1ms 点亮第一个数码管,第 2ms 点亮第二个数码管,以此类 推依次点亮六个数码管,6ms 一个轮回,也就是说每个数码管每 6ms 点亮一次。那问题是 我们怎么去选中这个要显示的数码管并且给其要显示的值呢?这个时候我们就引入了一个 cnt_sel 信号,让其从 0~5 循环计数,1 个数代表一个数码管,可以看做是给数码管编号。 这样的话我们只要选择计数器的值就相当于选中了其中对应的数码管。特别要说明的是我 们的 cnt_sel 计数器必须与数码管的刷新状态一致,也就是 1ms 计 1 个数。

        sel_reg:数码管位选信号寄存器,为了让数码管位选信号和段选信号同步,这里我们 先将位选信号进行寄存。刷新到哪个数码管就将 sel 中对应位(6 个位宽,每一位对应一个 数码管)给高点亮即可。选中点亮的数码管后我们需要给其要显示的值,所以我们引入一 个新的信号。

        data_disp:当前点亮数码管显示的值。若我们此时点亮的是第一个数码管,那么我们 就需要给第一个数码管显示值 6,若刷新到第二个数码管,那么我们就需要给第二个数码 管显示值 7,以此类推;当刷新到第五个数码管时,此时显示的是负号,那么我们该如何 表示呢?这里我们让该信号的值为 10 来表示,也就是说当 data_disp 的值为 10 时就让数码 管显示负号,同理这里我们定义 data_disp 的值为 11 时让数码管什么也不显示,即不点亮 数码管。 

        dot_disp:当前数码管显示的小数点,我们输入的 point 信号是点亮第二个数码管的小 数点,而我们的数码管是低电平点亮,所以这里当扫描到第二个数码管时让 dot_disp 信号 为低即可。         seg:数码管段选信号,我们根据数码管编码译码表当扫描到哪个数码管显示需要显示 的值时,我们将对于的段点亮即可。         

        sel:数码管位选信号。将数码管位选信号寄存器打一拍即可,这样就能实现数码管段 选信号和位选信号的同步。

  1. module seg_dynamic
  2. (
  3. input wire sys_clk,
  4. input wire sys_rst_n,
  5. input wire [19:0] data,
  6. input wire [5:0] point,
  7. input wire seg_en,
  8. input wire sign,
  9. output reg [5:0] sel,
  10. output reg [7:0] seg
  11. );
  12. (
  13. parameter CNT_MAX = 16'd49_999
  14. )
  15. wire [3:0] unit ;
  16. wire [3:0] ten ;
  17. wire [3:0] hun ;
  18. wire [3:0] tho ;
  19. wire [3:0] t_tho ;
  20. wire [3:0] h_hun ;
  21. reg [23:0] data_reg;
  22. reg [15:0] cnt_1ms ;
  23. reg [2:0] cnt_sel ;
  24. reg [5:0] sel_reg ;
  25. reg [3:0] data_disp;
  26. reg dot_disp;
  27. reg flag_1ms;
  28. //主程序
  29. //data_reg:控制数码管显示数据
  30. always@(posedge sys_clk or negedge sys_rst_n)
  31. if(sys_rst_n == 1'b0)
  32. data_reg <= 24'd0;
  33. //若显示十进制的十万位为非零数据或需要显示小数点,则六个数码管全部显示
  34. else if((h_hun) || (point[5]))
  35. data_reg <= {h_hun,t_tho,tho,hun,ten,unit};
  36. else if((t_tho) || (point[4]) && (sign == 1'b1))
  37. data_reg <= {4'd10,t_tho,tho,hun,ten,unit};
  38. else if(((t_tho) || (point[4])) && (sign == 1'b0))
  39. data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11 我们定义为不显示
  40. //若显示的十进制数的千位为非零数据或需显示小数点,则值显示 4 个数码管
  41. else if(((tho) || (point[3])) && (sign == 1'b1))
  42. data_reg <= {4'd11,4'd10,tho,hun,ten,unit};
  43. else if(((tho) || (point[3])) && (sign == 1'b0))
  44. data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
  45. //若显示的十进制数的百位为非零数据或需显示小数点,则值显示 3 个数码管
  46. else if(((hun) || (point[2])) && (sign == 1'b1))
  47. data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};
  48. else if(((hun) || (point[2])) && (sign == 1'b0))
  49. data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
  50. //若显示的十进制数的十位为非零数据或需显示小数点,则值显示 2 个数码管
  51. else if(((ten) || (point[1])) && (sign == 1'b1))
  52. data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};
  53. else if(((ten) || (point[1])) && (sign == 1'b0))
  54. data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
  55. //若显示的十进制数的个位且需显示负号
  56. else if(((unit) || (point[0])) && (sign == 1'b1))
  57. data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
  58. //若上面都不满足都只显示一位数码管
  59. else
  60. data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
  61. //cnt_1ms:1ms循环计数
  62. always@(posedge sys_clk or negedge sys_rst_n)
  63. if(sys_rst_n == 1'b0)
  64. cnt_1ms <= 16'd0;
  65. else if(cnt_1ms == CNT_MAX)
  66. cnt_1ms <= 16'd0;
  67. else
  68. cnt_1ms <= cnt_1ms +1'b1;
  69. //flag_1ms:1ms标志信号
  70. always@(posedge sys_clk or negedge sys_rst_n)
  71. if(sys_rst_n == 1'b0)
  72. flag_1ms <= 1'b0;
  73. else if(cnt_1ms == CNT_MAX -1'b1)
  74. flag_1ms <= 1'b1;
  75. else
  76. flag_1ms <= 1'b0;
  77. //cnt_sel:从0~5循环数,用于选择当前显示的数码管
  78. always@(posedge sys_clk or negedge sys_rst_n)
  79. if(sys_rst_n == 1'b0)
  80. cnt_sel <= 3'd0;
  81. else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
  82. cnt_sel <= 3'd0;
  83. else if(flag_1ms == 1'b1)
  84. cnt_sel <= cnt_sel + 1'b1;
  85. else
  86. cnt_sel <= cnt_sel;
  87. //数码管位选信号寄存器
  88. always@(posedge sys_clk or negedge sys_rst_n)
  89. if(sys_rst_n == 1'b0)
  90. sel_reg <= 6'b000_000;
  91. else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
  92. sel_reg <= 6'b000_001;
  93. else if(flag_1ms == 1'b1)
  94. sel_reg <= sel_reg << 1;
  95. else
  96. sel_reg <= sel_reg;
  97. //控制数码管的位选信号,使六个数码管轮流显示
  98. always@(posedge sys_clk or negedge sys_rst_n)
  99. if(sys_rst_n == 1'b0)
  100. data_disp <= 4'b0;
  101. else if((seg_en == 1'b1) && (flag_1ms == 1'b1))
  102. case(cnt_sel)
  103. 3'd0: data_disp <= data_reg[3:0] ;
  104. 3'd1: data_disp <= data_reg[7:4] ;
  105. 3'd2: data_disp <= data_reg[11:8] ;
  106. 3'd3: data_disp <= data_reg[15:12];
  107. 3'd4: data_disp <= data_reg[19:16];
  108. 3'd5: data_disp <= data_reg[23:20];
  109. default:data_disp <= 4'b0;
  110. endcase
  111. else
  112. data_disp <= data_disp;
  113. //dot_disp:小数点低电平点亮,需对小数点有效信号取反
  114. always@(posedge sys_clk or negedge sys_rst_n)
  115. if(sys_rst_n == 1'b0)
  116. dot_disp <= 1'b1;
  117. else if(flag_1ms == 1'b1)
  118. dot_disp <= ~point[cnt_sel];
  119. else
  120. dot_disp <= dot_disp;
  121. //控制数码管段选信号,显示数字
  122. always@(posedge sys_clk or negedge sys_rst_n)
  123. if(sys_rst_n == 1'b0)
  124. seg <= 8'b1111_1111;
  125. else
  126. case(data_disp)
  127. 4'd0 : seg <= {dot_disp,7'b100_0000}; //显示数字 0
  128. 4'd1 : seg <= {dot_disp,7'b111_1001}; //显示数字 1
  129. 4'd2 : seg <= {dot_disp,7'b010_0100}; //显示数字 2
  130. 4'd3 : seg <= {dot_disp,7'b011_0000}; //显示数字 3
  131. 4'd4 : seg <= {dot_disp,7'b001_1001}; //显示数字 4
  132. 4'd5 : seg <= {dot_disp,7'b001_0010}; //显示数字 5
  133. 4'd6 : seg <= {dot_disp,7'b000_0010}; //显示数字 6
  134. 4'd7 : seg <= {dot_disp,7'b111_1000}; //显示数字 7
  135. 4'd8 : seg <= {dot_disp,7'b000_0000}; //显示数字 8
  136. 4'd9 : seg <= {dot_disp,7'b001_0000}; //显示数字 9
  137. 4'd10 : seg <= 8'b1011_1111 ; //显示负号
  138. 4'd11 : seg <= 8'b1111_1111 ; //不显示任何字符
  139. default:seg <= 8'b1100_0000;
  140. endcase
  141. //sel:数码管位选信号赋值
  142. always@(posedge sys_clk or negedge sys_rst_n)
  143. if(sys_rst_n == 1'b0)
  144. sel <= 6'b000_000;
  145. else
  146. sel <= sel_reg;
  147. bcd_8421 bcd_8421_inst
  148. (
  149. .sys_clk (sys_clk ) , //系统时钟,频率 50MHz
  150. .sys_rst_n (sys_rst_n) , //复位信号,低电平有效
  151. .data (data ) , //输入需要转换的数据
  152. .unit (unit ) , //个位 BCD 码
  153. .ten (ten ) , //十位 BCD 码
  154. .hun (hun ) , //百位 BCD 码
  155. .tho (tho ) , //千位 BCD 码
  156. .t_tho (t_tho ) , //万位 BCD 码
  157. .h_hun (h_hun ) //十万位 BCD 码
  158. );
  159. endmodule

数码管动态显示模块

        我们将数码管动态显示驱动模块和 74HC595 控制模块整合到一个模块之中,后面我们 工程要用到数码管动态显示时我们直接例化这一个模块即可,较为方便。其模块框图如图所示:

        该模块主要是对数码管动态显示驱动模块和 74HC595 控制模块的实例化,以及对应信 号的连接,

  1. module seg_595_dynamic
  2. (
  3. input wire sys_clk ,
  4. input wire sys_rst_n ,
  5. input wire [19:0] data ,
  6. input wire [5:0] point ,
  7. input wire seg_en ,
  8. input wire sign ,
  9. output wire stcp ,
  10. output wire shcp ,
  11. output wire ds ,
  12. output wire oe
  13. );
  14. //wire define
  15. wire [5:0] sel;
  16. wire [7:0] seg;
  17. seg_dynamic seg_dynamic_inst
  18. (
  19. .sys_clk (sys_clk ) ,
  20. .sys_rst_n (sys_rst_n) ,
  21. .data (data ) ,
  22. .point (point ) ,
  23. .seg_en (seg_en ) ,
  24. .sign (sign ) ,
  25. .sel (sel ) ,
  26. .seg (seg )
  27. );
  28. hc595_crtl hc595_crtl_inst
  29. (
  30. .sys_clk (sys_clk ) ,
  31. .sys_rst_n (sys_rst_n) ,
  32. .sel (sel ) ,
  33. .seg (seg ) ,
  34. .stcp (stcp ) ,
  35. .shcp (shcp ) ,
  36. .ds (ds ) ,
  37. .oe (oe )
  38. );
  39. endmodule

595控制模块

  1. module hc595_crtl
  2. (
  3. input wire sys_clk ,
  4. input wire sys_rst_n ,
  5. input wire [5:0] sel ,
  6. input wire [7:0] seg ,
  7. output reg stcp ,
  8. output reg shcp ,
  9. output reg ds ,
  10. output wire oe
  11. );
  12. reg [1:0] cnt_4;
  13. reg [3:0] cnt_bit;
  14. wire [13:0] data;
  15. assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};
  16. assign oe = 1'b0;
  17. always@(posedge sys_clk or negedge sys_rst_n)
  18. if(sys_rst_n == 1'b0)
  19. cnt_4 <= 2'd0;
  20. else if(cnt_4 == 2'd3)
  21. cnt_4 <= 2'd0;
  22. else
  23. cnt_4 <= cnt_4 + 1'b1;
  24. always@(posedge sys_clk or negedge sys_rst_n)
  25. if(sys_rst_n == 1'b0)
  26. cnt_bit <= 4'd0;
  27. else if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
  28. cnt_bit <= 4'd0;
  29. else if(cnt_4 == 2'd3)
  30. cnt_bit <= cnt_bit + 1'b1;
  31. else
  32. cnt_bit <= cnt_bit;
  33. always@(posedge sys_clk or negedge sys_rst_n)
  34. if(sys_rst_n == 1'b0)
  35. stcp <= 1'b0;
  36. else if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
  37. stcp <= 1'b1;
  38. else
  39. stcp <= 1'b0;
  40. always@(posedge sys_clk or negedge sys_rst_n)
  41. if(sys_rst_n == 1'b0)
  42. shcp <= 1'b0;
  43. else if (cnt_4 >= 4'd2)
  44. shcp <= 1'b1;
  45. else
  46. shcp <= 1'b0;
  47. always@(posedge sys_clk or negedge sys_rst_n)
  48. if(sys_rst_n == 1'b0)
  49. ds <= 1'b0;
  50. else if (cnt_4 == 2'd0)
  51. ds <= data[cnt_bit];
  52. else
  53. ds <= ds;
  54. endmodule

 顶层模块设计

模块框图

顶层模块主要是对各个子功能模块的实例化,以及对应信号的连接。

  1. module top_seg_595
  2. (
  3. input wire sys_clk,
  4. input wire sys_rst_n,
  5. output wire stcp,
  6. output wire shcp,
  7. output wire ds,
  8. output wire oe
  9. );
  10. wire [19:0] data;
  11. wire [5:0] point;
  12. wire seg_en;
  13. wire sign;
  14. data_gen
  15. #(
  16. .CNT_MAX (23'd4999_999),
  17. .DATA_MAX (20'd999_999)
  18. )
  19. data_gen_inst
  20. (
  21. .sys_clk (sys_clk ) ,
  22. .sys_rst_n (sys_rst_n) ,
  23. .data (data ) ,
  24. .point (point ) ,
  25. .seg_en (seg_en ) ,
  26. .sign (sign )
  27. );
  28. seg_595_dynamic seg_595_dynamic_inst
  29. (
  30. .sys_clk (sys_clk ) ,
  31. .sys_rst_n (sys_rst_n ) ,
  32. .data (data ) ,
  33. .point (point ) ,
  34. .seg_en (seg_en ) ,
  35. .sign (sign ) ,
  36. .stcp (stcp ) ,
  37. .shcp (shcp ) ,
  38. .ds (ds ) ,
  39. .oe (oe )
  40. );
  41. endmodule

 仿真代码编写:

  1. `timescale 1ns/1ns
  2. module tb_top_seg_595();
  3. reg sys_clk;
  4. reg sys_rst_n;
  5. wire stcp;
  6. wire shcp;
  7. wire ds;
  8. wire oe;
  9. initial
  10. begin
  11. sys_clk = 1'b1;
  12. sys_rst_n = 1'b0;
  13. #30
  14. sys_rst_n = 1'b1;
  15. end
  16. always #10 sys_clk <= ~sys_clk;
  17. defparam top_seg_595_inst.seg_595_dynamic_inst.seg_dynamic_inst.CNT_MAX=16'd19;
  18. defparam top_seg_595_inst.data_gen_inst.CNT_MAX = 23'd49;
  19. top_seg_595 top_seg_595_inst
  20. (
  21. .sys_clk (sys_clk ) ,
  22. .sys_rst_n (sys_rst_n ) ,
  23. .stcp (stcp ) ,
  24. .shcp (shcp ) ,
  25. .ds (ds ) ,
  26. .oe (oe )
  27. );
  28. endmodule

 仿真波形:

本人亲测结果是正确的,需要自己仿真后慢慢分析波形

绑定FPGA管脚上板验证

上板验证结果为:

由于时间关系没有跑到999_999.

创作不易有用请点赞,后续我还会继续更新相关实验。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/704370
推荐阅读
相关标签
  

闽ICP备14008679号