image.png

一、前置知识

数码管是将若干发光二极管按一定图形排列并封装在一起的一种数码显示器件。比较常见的是八段数码管,即内部为8个二极管按共阳极或共阴极的方式排列制作的。
另外,生产商为了统一封装,会将8段数码管引出10个引脚,其中有两个引脚是公共脚 com。
image.png
静态显示是指将多个数码管的段选相连,位选也相同。这样多个数码管就可以同时显示相同的内容。
动态显示是指利用视觉暂留,在短时间内控制数码管位选,让多个数码管显示不同的内容。

二、原理图分析

为了增加 FPGA 输出信号的驱动能力,开发板使用 PNP 型三极管驱动数码管的位选段,所以给三极管基
极提供低电平时,三极管导通,位选信号为高电平。另外,开发板的数码管是共阳极数码管。
image.png
实例化两个模块,一个是用于计时0.5秒的定时器(time_count),每到0.5秒产生一个flag脉冲信号。另一个模块(seg_show_static)用来控制数码管显示的具体数值。
image.png
image.png

三、代码编写

3.1 计时模块

期望效果:每隔0.5秒产生一个脉冲信号。
Modelsim仿真效果(每隔20个clk产生一次flag脉冲):
image.png

  1. // ----------------------------------
  2. // 计时模块:每隔0.5秒产生一个flag脉冲信号
  3. // ----------------------------------
  4. module test(
  5. input clk,
  6. input rst_n,
  7. output reg flag
  8. );
  9. //parameter TIME = 25_000_000;
  10. parameter TIME = 20;
  11. reg [4:0] counter;
  12. // 每隔0.5秒产生一个flag脉冲信号
  13. always @(posedge clk or negedge rst_n) begin
  14. if(!rst_n) begin
  15. flag <= 1'b0;
  16. counter <= 5'd0;
  17. end
  18. else begin
  19. if(counter >= TIME) begin
  20. flag <= 1'b1;
  21. counter <= 5'd0;
  22. end
  23. else begin
  24. flag <= 1'b0;
  25. counter <= counter + 1'b1;
  26. end
  27. end
  28. end
  29. endmodule
  1. `timescale 1 ns/ 1 ns
  2. module test_vt();
  3. // test vector input registers
  4. reg clk;
  5. reg rst_n;
  6. // wires
  7. wire flag;
  8. // assign statements (if any)
  9. test i1 (
  10. // port map - connection between master ports and signals/registers
  11. .clk(clk),
  12. .flag(flag),
  13. .rst_n(rst_n)
  14. );
  15. initial begin
  16. clk = 1'b0;
  17. rst_n = 1'b0;
  18. #10 rst_n = 1'b1;
  19. #1000 $stop;
  20. end
  21. always #10 clk = ~clk;
  22. endmodule

3.2 数码管静态显示模块

  1. // -------------------------
  2. // 控制数码管段选位选的模块
  3. // -------------------------
  4. module seg_show_static(
  5. input clk,
  6. input rst_n,
  7. input add_flag,
  8. output reg [5:0] sel,
  9. output reg [6:0] seg_led
  10. );
  11. reg [3:0] num; // 0-F
  12. // 控制位选
  13. always @(posedge clk or negedge rst_n) begin
  14. if(!rst_n)
  15. sel <= 6'b11_1111; // 位选禁用
  16. else
  17. sel <= 6'b00_0000; // 位选全部选中
  18. end
  19. // 控制数码管显示的数据:每来一个flga脉冲就为num加一
  20. always @(posedge clk or negedge rst_n) begin
  21. if(!rst_n) begin
  22. num <= 4'd0;
  23. end
  24. else begin
  25. if(add_flag)
  26. if(num > 4'hf)
  27. num <= 4'd0;
  28. else
  29. num <= num + 1'b1;
  30. else
  31. num <= num;
  32. end
  33. end
  34. // 控制数码管段选
  35. always @(posedge clk or negedge rst_n) begin
  36. if(!rst_n)
  37. seg_led <= 7'b1111111;
  38. else begin
  39. case (num)
  40. 5'h0: seg_led <= 7'b1_000_000;
  41. 5'h1: seg_led <= 7'b1_111_001;
  42. 5'h2: seg_led <= 7'b0_100_100;
  43. 5'h3: seg_led <= 7'b0_110_000;
  44. 5'h4: seg_led <= 7'b0_011_001;
  45. 5'h5: seg_led <= 7'b0_010_010;
  46. 5'h6: seg_led <= 7'b0_000_010;
  47. 5'h7: seg_led <= 7'b1_111_000;
  48. 5'h8: seg_led <= 7'b0_000_000;
  49. 5'h9: seg_led <= 7'b0_010_000;
  50. 5'hA: seg_led <= 7'b0_001_000;
  51. 5'hB: seg_led <= 7'b0_000_011;
  52. 5'hC: seg_led <= 7'b0_100_111;
  53. 5'hD: seg_led <= 7'b0_100_001;
  54. 5'hE: seg_led <= 7'b0_000_110;
  55. 5'hF: seg_led <= 7'b0_001_110;
  56. default: seg_led <= 7'b1111111;
  57. endcase
  58. end
  59. end
  60. endmodule

四、代码仿真

计时器模块的代码已经仿真过了,下面将顶层模块seg_led_static编写完成,并编写相应的激励文件。

  1. //---------------------- Copyright (c) ----------------------
  2. // Copyright(C) ZHJ0125 2022
  3. // All rights reserved
  4. //-----------------------------------------------------------
  5. // File Name : seg_led_static.v
  6. // Created Date : 2022-04-21 13:32
  7. // Modified Date : 2022-04-25 12:40
  8. // Created By : ZHJ0125
  9. // Last Version : V0.1
  10. // Descriptions : 数码管静态显示
  11. // 开发板上电后数码管显示0~F,以此循环。
  12. //-----------------------------------------------------------
  13. module seg_led_static(
  14. input clk,
  15. input rst_n,
  16. output [5:0] sel,
  17. output [6:0] seg_led
  18. );
  19. wire add_flag;
  20. time_count u_time_count(
  21. .clk(clk),
  22. .rst_n(rst_n),
  23. .flag(add_flag)
  24. );
  25. seg_show_static u_seg_show_static(
  26. .clk(clk),
  27. .rst_n(rst_n),
  28. .add_flag(add_flag),
  29. .sel(sel),
  30. .seg_led(seg_led)
  31. );
  32. endmodule
  33. // ----------------------------------
  34. // 计时模块:每隔0.5秒产生一个flag脉冲信号
  35. // ----------------------------------
  36. module time_count(
  37. input clk,
  38. input rst_n,
  39. output reg flag
  40. );
  41. //parameter TIME = 25_000_000; // 0.5s
  42. parameter TIME = 10; // 仿真时使用
  43. reg [24:0] counter;
  44. // 每隔0.5秒产生一个flag脉冲信号
  45. always @(posedge clk or negedge rst_n) begin
  46. if(!rst_n) begin
  47. flag <= 1'b0;
  48. counter <= 25'd0;
  49. end
  50. else begin
  51. if(counter >= TIME) begin
  52. flag <= 1'b1;
  53. counter <= 25'd0;
  54. end
  55. else begin
  56. flag <= 1'b0;
  57. counter <= counter + 1'b1;
  58. end
  59. end
  60. end
  61. endmodule
  62. // -------------------------
  63. // 控制数码管段选位选的模块
  64. // -------------------------
  65. module seg_show_static(
  66. input clk,
  67. input rst_n,
  68. input add_flag,
  69. output reg [5:0] sel,
  70. output reg [6:0] seg_led
  71. );
  72. reg [3:0] num; // 0-F
  73. // 控制位选
  74. always @(posedge clk or negedge rst_n) begin
  75. if(!rst_n)
  76. sel <= 6'b11_1111; // 位选禁用
  77. else
  78. sel <= 6'b00_0000; // 位选全部选中
  79. end
  80. // 控制数码管显示的数据:每来一个flga脉冲就为num加一
  81. always @(posedge clk or negedge rst_n) begin
  82. if(!rst_n) begin
  83. num <= 4'd0;
  84. end
  85. else begin
  86. if(add_flag)
  87. if(num > 4'hf)
  88. num <= 4'd0;
  89. else
  90. num <= num + 1'b1;
  91. else
  92. num <= num;
  93. end
  94. end
  95. // 控制数码管段选
  96. always @(posedge clk or negedge rst_n) begin
  97. if(!rst_n)
  98. seg_led <= 7'b1111111;
  99. else begin
  100. case (num)
  101. 5'h0: seg_led <= 7'b1_000_000;
  102. 5'h1: seg_led <= 7'b1_111_001;
  103. 5'h2: seg_led <= 7'b0_100_100;
  104. 5'h3: seg_led <= 7'b0_110_000;
  105. 5'h4: seg_led <= 7'b0_011_001;
  106. 5'h5: seg_led <= 7'b0_010_010;
  107. 5'h6: seg_led <= 7'b0_000_010;
  108. 5'h7: seg_led <= 7'b1_111_000;
  109. 5'h8: seg_led <= 7'b0_000_000;
  110. 5'h9: seg_led <= 7'b0_010_000;
  111. 5'hA: seg_led <= 7'b0_001_000;
  112. 5'hB: seg_led <= 7'b0_000_011;
  113. 5'hC: seg_led <= 7'b0_100_111;
  114. 5'hD: seg_led <= 7'b0_100_001;
  115. 5'hE: seg_led <= 7'b0_000_110;
  116. 5'hF: seg_led <= 7'b0_001_110;
  117. default: seg_led <= 7'b1111111;
  118. endcase
  119. end
  120. end
  121. endmodule

激励文件也很好写,如下:

  1. // *****************************************************************************
  2. // This file contains a Verilog test bench template that is freely editable to
  3. // suit user's needs .Comments are provided in each section to help the user
  4. // fill out necessary details.
  5. // *****************************************************************************
  6. // Generated on "04/25/2022 09:27:52"
  7. // Verilog Test Bench template for design : seg_led_static
  8. //
  9. // Simulation tool : ModelSim-Altera (Verilog)
  10. //
  11. `timescale 1 ns/ 1 ns
  12. module seg_led_static_vt();
  13. // constants
  14. // general purpose registers
  15. // reg eachvec;
  16. // test vector input registers
  17. reg clk;
  18. reg rst_n;
  19. // wires
  20. wire [6:0] seg_led;
  21. wire [5:0] sel;
  22. parameter T = 20;
  23. // assign statements (if any)
  24. seg_led_static i1 (
  25. // port map - connection between master ports and signals/registers
  26. .clk(clk),
  27. .rst_n(rst_n),
  28. .seg_led(seg_led),
  29. .sel(sel)
  30. );
  31. initial begin
  32. clk = 1'b0;
  33. rst_n = 1'b0;
  34. #(5*T)
  35. rst_n = 1'b1;
  36. #(200*T) $stop;
  37. end
  38. always begin
  39. #(T/2) clk = ~clk;
  40. end
  41. endmodule

激励实现的功能是:

  1. 初始化时钟和复位信号
  2. 5个周期后复位拉高,开始执行程序
  3. 每个周期让时钟信号clk以50%占空比产生脉冲信号
  4. 200个周期后停止仿真过程

仿真得到的时序图如下:
image.png
image.png
在仿真波形图中可以看到:

  1. 数码管段选seg_led在16个状态之间切换 (显示0~F)
  2. 数码管位选sel在复位拉高后,状态变为000000,表示选中全部6个数码管。

    五、程序烧录

    配置引脚的TCL脚本: ```verilog

package require ::quartus::project

system clock:50Mhz

set_location_assignment PIN_M2 -to sys_clk

system reset

set_location_assignment PIN_M1 -to sys_rst_n

digit display

set_location_assignment PIN_N16 -to sel[0] set_location_assignment PIN_N15 -to sel[1] set_location_assignment PIN_P16 -to sel[2] set_location_assignment PIN_P15 -to sel[3] set_location_assignment PIN_R16 -to sel[4] set_location_assignment PIN_T15 -to sel[5]

set_location_assignment PIN_M11 -to seg_led[0] set_location_assignment PIN_N12 -to seg_led[1] set_location_assignment PIN_C9 -to seg_led[2] set_location_assignment PIN_N13 -to seg_led[3] set_location_assignment PIN_M10 -to seg_led[4] set_location_assignment PIN_N11 -to seg_led[5] set_location_assignment PIN_P11 -to seg_led[6]

set_location_assignment PIN_D9 -to seg_led[7]

``` 烧录后的效果: 06_seg_led_static_error.mp4 (4.72MB)没想到居然会出问题,仿真的效果符合预期,到了板子上居然不能正常运行。
检查一下引脚配置,系统时钟和系统复位引脚没有被正确配置。因为TCL脚本里的引脚名称跟我代码里的引脚名称不一致。
image.png
修改TCL脚本,重新运行,引脚配置正常了。。
image.png
重新编译烧录,发现只显示数字8: 06_seg_led_static_error_2.mp4 (4.9MB)再检查一下代码,是因为为了方便仿真,把产生flag脉冲的计时时间改成10了,这样的话变化间隔时间太短。重新改正后再次烧录。
image.png 06_seg_led_static.mp4 (9.2MB)