当前位置:   article > 正文

小梅哥Xilinx FPGA学习笔记13——动态数码管显示_数码管显示12fpga

数码管显示12fpga

目录

一、数码管简介

二、静态显示实验目标

2.1 设计文件

2.2 仿真激励文件

2.3 仿真图

三、 动态显示实验

3.1 设计文件

3.2 仿真激励文件

3.3 仿真图


一、数码管简介

       数码管是一种半导体发光器件,其基本单元是发光二极管。数码管按段数一般分为七段数码管和八段数码管,八段数码管比七段数码管多一个发光二极管(多一个小数点显示)。当然也还有一些其他类型的数码管如“N”形管、“米”字管以及工业科研领域用的 16 段管、24 段管等,在此就不详细介绍。下面将为大家详细介绍本次实验使用的八段 数码管。
        
    八段数码管是一个八字型数码管,分为八段: a b c d e f g dp ,其中 dp 为小数点,每一段即为一个发光二极管,这样的八段我们称之为段选信号。 数码管分为 共阳极数码管 共阴极数码管 。共阳极数码管就是把发光二极管的正极连接在一起作为一个引脚,负极分开。相反的,共阴极数码管就是把发光二极管的阴极连接在一起作为一个引脚,正极分开。这两者的区别在于,公共端是接地还是接高电平。如果接地则为共阴极数码管,则段选信号为高电平时对应段才会被点亮;如果接高电平则为共阳极数码管,则段选信号为低电平时对应段才会被点亮。 下表给出不同的段点亮可显示 0~f 的值,如下表 所示。
       段式数码管工作方式有两种: 静态显示和动态显示 静态显示的特点是每个数码管的 段选必须接一个 8 位数据线来显示字形,显示字形可一直保持,直到送入新字形码为止。那么如果点亮 8 个码管是不是需要 64 位数据线去分别控制每一个码管的段选?当然这种方 法也可以,但是其占用的 I/O 口较多,因此硬件电路比较复杂,成本较高,很少使用。 那么如何节约资源呢?以六位数码管为例,如下图 所示:
       由上图可以看到,我们将六个数码管的段选信号连接在一起,而位选( sel )独立控制,这样六个数码管接在一起就少了 8 × 5 I/O 口。这里对位选信号特别说明一下:由上 图可以看到每一个数码管都有一个位选信号,而这个位选信号就控制着数码管的亮灭。这 样我们就可以通过位选信号去控制数码管亮,而在同一时刻,位选选通的数码管上显示的 字形是一样的,因为我们将 6 个数码管相对应的段选连在了一起,数码管的显示自然就相 同了,数码管的这种显示方式即为静态显示。而如果要让每个数码管显示的值不同,我们 要用到另外一种显示方式,即动态显示,将在下一章节给大家介绍。本章节先讲述 6 位共 阳极数码管的静态显示,为下个章节的动态显示做准备。

二、静态显示实验目标

       根据上一小节的理论学习,我们可以设计一个这样的 6 位数码管静态显示:控制八位 数码管让其以 000000 111111 222222 一直到 FFFFFF 循环显示。每个字符显示 0.5s 后变 化。系统框图如下图所示:

2.1 设计文件

  1. `timescale 1ns / 1ps
  2. module seg_static(
  3. input Clk,
  4. input Reset_n,
  5. output reg [7:0] sel,
  6. output reg [7:0] seg
  7. );
  8. reg [3:0]num;
  9. reg add_flag;//数码管数值+1 标志信号
  10. reg [24:0]cnt_wait;//0.5s计数器
  11. parameter CNT_WAIT_MAX = 25000_000-1; //计数器最大值(0.5s)
  12. //十六进制数显示编码
  13. parameter SEG_0 = 8'b1100_0000, SEG_1 = 8'b1111_1001,
  14. SEG_2 = 8'b1010_0100, SEG_3 = 8'b1011_0000,
  15. SEG_4 = 8'b1001_1001, SEG_5 = 8'b1001_0010,
  16. SEG_6 = 8'b1000_0010, SEG_7 = 8'b1111_1000,
  17. SEG_8 = 8'b1000_0000, SEG_9 = 8'b1001_0000,
  18. SEG_A = 8'b1000_1000, SEG_B = 8'b1000_0011,
  19. SEG_C = 8'b1100_0110, SEG_D = 8'b1010_0001,
  20. SEG_E = 8'b1000_0110, SEG_F = 8'b1000_1110;
  21. parameter IDLE = 8'b1111_1111; //不显示状态
  22. //0.5S计数功能
  23. always@(posedge Clk or negedge Reset_n)
  24. if(!Reset_n)
  25. cnt_wait <= 0;
  26. else if (cnt_wait == CNT_WAIT_MAX)
  27. cnt_wait <= 0;
  28. else
  29. cnt_wait <= cnt_wait +1;
  30. //数码管数值+1 标志信号实现逻辑(一个高脉冲)
  31. always@(posedge Clk or negedge Reset_n)
  32. if(!Reset_n)
  33. add_flag <= 0;
  34. else if (cnt_wait == CNT_WAIT_MAX)
  35. add_flag <= 1;
  36. else
  37. add_flag <= 0;
  38. //num:从 4'h0 加到 4'hf 循环
  39. always@(posedge Clk or negedge Reset_n)
  40. if(!Reset_n)
  41. num <= 0;
  42. else if(add_flag)
  43. num <= num +1;
  44. else
  45. num <= num;
  46. //sel:选中六个数码管(位选信号)
  47. always@(posedge Clk or negedge Reset_n)
  48. if(!Reset_n)
  49. sel <= 8'b0000_0000;
  50. else
  51. sel <= 8'b1111_1111;
  52. //给要显示的值编码
  53. always@(posedge Clk or negedge Reset_n)
  54. if(!Reset_n)
  55. seg <= IDLE;
  56. else begin
  57. case (num)
  58. 4'd0: seg <= SEG_0;
  59. 4'd1: seg <= SEG_1;
  60. 4'd2: seg <= SEG_2;
  61. 4'd3: seg <= SEG_3;
  62. 4'd4: seg <= SEG_4;
  63. 4'd5: seg <= SEG_5;
  64. 4'd6: seg <= SEG_6;
  65. 4'd7: seg <= SEG_7;
  66. 4'd8: seg <= SEG_8;
  67. 4'd9: seg <= SEG_9;
  68. 4'd10: seg <= SEG_A;
  69. 4'd11: seg <= SEG_B;
  70. 4'd12: seg <= SEG_C;
  71. 4'd13: seg <= SEG_D;
  72. 4'd14: seg <= SEG_E;
  73. 4'd15: seg <= SEG_F;
  74. default:seg <= IDLE ; //闲置状态,不显示
  75. endcase
  76. end
  77. endmodule

2.2 仿真激励文件

  1. `timescale 1ns / 1ps
  2. module seg_static_tb();
  3. reg Clk;
  4. reg Reset_n;
  5. wire [7:0]sel;
  6. wire [7:0]seg;
  7. seg_static seg_static(
  8. .Clk(Clk),
  9. .Reset_n(Reset_n),
  10. .sel(sel),
  11. .seg (seg)
  12. );
  13. defparam seg_static.CNT_WAIT_MAX = 25000 - 1;
  14. initial Clk <= 1;
  15. always #10 Clk <= ~Clk;
  16. initial
  17. begin
  18. Reset_n <= 1'b0;
  19. #201
  20. Reset_n <= 1'b1;
  21. #8000000;
  22. $stop;
  23. end
  24. endmodule

2.3 仿真图

从仿真图中可以看出 数码管上的数据每0.5s从 000000~FFFFFF 之间循环显示,试验成功。 以上是是师从野火教程。

三、 动态显示实验

       在上一章节我们知道静态显示是让六个数码管的 8 位段选信号连在 8 根线上且六个数码管的位选信号同时选中点亮。但是如果我们每次只选中一个数码管点亮呢? 这样我们段选信号点亮的就只是我们选中数码管的值了,那是不是就可以给每个数码管显 示不一样的值了?但是这样我们又会发现一个新的问题:每次只点亮一个数码管,那么同 一时间六个数码管就只能看到一个数码管在亮,那不是同时显示不了六个不同的字符了 吗?针对这个问题先为大家介绍 两种现象:
首先 是人眼视觉暂留:人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的 时间,光的作用结束后,视觉影像并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”。 其次 是数码管的余晖效应:当停止向发光二极管供电时发光二极管亮度仍能维持一段 时间。
       根据这两种现象我们可以想到,如果让数码管轮流显示,而且轮流显示速度很快,这样会不会看起来六个数码管都在显示呢?事实证明是可以的,这种方式称为动态扫描。为 帮助大家理解,打个比方:若一个数码管在 1s 内点亮两次,那么我们可以很明显的看到其 亮了两次,若 1s 内点亮 10 次呢?我们可能只能看到其在快速的闪烁,若点亮 100 1000次呢?总有一个速度我们人眼是分辨不出来在闪烁的。所以说一个数码管让我们人眼感觉 一直在亮并不用一直给其点亮,只要我们让其亮的间隔足够短就行。这样我们就可以用不 在点亮的时间去点亮其他数码管,让其他数码管也达到这样的效果,这样就可以让我们人 眼感觉所有数码管都在同时点亮了。
系统框图如下图所示:

3.1 设计文件

  1. module hex8(
  2. input Clk,
  3. input Reset_n,
  4. input [31:0]Disp_Data,
  5. output reg [7:0]SEL,
  6. output reg [7:0]SEG
  7. );
  8. reg [29:0]cnt_1ms;
  9. reg Flag_1ms; //1毫秒切换一个数码管
  10. reg [2:0]cnt_sel;//位选信号
  11. parameter MCNT = 50000-1;
  12. //定时一毫秒
  13. always@(posedge Clk or negedge Reset_n)
  14. if(!Reset_n)
  15. cnt_1ms <= 0;
  16. else if(cnt_1ms == MCNT)
  17. cnt_1ms <= 0;
  18. else
  19. cnt_1ms <= cnt_1ms + 1;
  20. //产生一个一毫秒到来的高脉冲信号
  21. always@(posedge Clk or negedge Reset_n)
  22. if(!Reset_n)
  23. Flag_1ms <= 0;
  24. else if(cnt_1ms == MCNT)
  25. Flag_1ms <= 1;
  26. else
  27. Flag_1ms <= 0;
  28. //cnt_sel自加逻辑实现(每1毫秒自加一次切换一个数码管)
  29. always@(posedge Clk or negedge Reset_n)
  30. if(!Reset_n)
  31. cnt_sel <= 0;
  32. else if(Flag_1ms)
  33. cnt_sel <= cnt_sel + 1;
  34. else
  35. cnt_sel <= cnt_sel;
  36. //数码管的位选择逻辑
  37. always@(posedge Clk)begin
  38. case(cnt_sel)
  39. 0: SEL <= 8'b0000_0001;
  40. 1: SEL <= 8'b0000_0010;
  41. 2: SEL <= 8'b0000_0100;
  42. 3: SEL <= 8'b0000_1000;
  43. 4: SEL <= 8'b0001_0000;
  44. 5: SEL <= 8'b0010_0000;
  45. 6: SEL <= 8'b0100_0000;
  46. 7: SEL <= 8'b1000_0000;
  47. default:SEL <= 8'b0000_0000;
  48. endcase
  49. end
  50. reg [3:0]data_temp;//显示16种数值8 + 4 + 2 + 1+1 = 16,
  51. //建立一个查找表,表中列出SEG段选信号的各种情况。
  52. always@(posedge Clk)begin
  53. case(data_temp)
  54. 0: SEG <= 8'b1100_0000; //0
  55. 1: SEG <= 8'b1111_1001; //1
  56. 2: SEG <= 8'b1010_0100; //2
  57. 3: SEG <= 8'b1011_0000; //3
  58. 4: SEG <= 8'b1001_1001; //4
  59. 5: SEG <= 8'b1001_0010; //5
  60. 6: SEG <= 8'b1000_0010; //6
  61. 7: SEG <= 8'b1111_1000; //7
  62. 8: SEG <= 8'b1000_0000; //8
  63. 9: SEG <= 8'b1001_0000; //9
  64. 10: SEG <= 8'b1000_1000;//A
  65. 11: SEG <= 8'b1000_0011;//B
  66. 12: SEG <= 8'b1100_0110;//C
  67. 13: SEG <= 8'b1010_0001;//D
  68. 14: SEG <= 8'b1000_0110;//E
  69. 15: SEG <= 8'b1000_1110;//F
  70. default:SEL <= 8'b1111_1111;//全灭
  71. endcase
  72. end
  73. //8路选择器实现逻辑(使用组合逻辑时<==无区别)
  74. always@(*)
  75. case(cnt_sel)
  76. 0:data_temp <= Disp_Data[3:0];
  77. 1:data_temp <= Disp_Data[7:4];
  78. 2:data_temp <= Disp_Data[11:8];
  79. 3:data_temp <= Disp_Data[15:12];
  80. 4:data_temp <= Disp_Data[19:16];
  81. 5:data_temp <= Disp_Data[23:20];
  82. 6:data_temp <= Disp_Data[27:24];
  83. 7:data_temp <= Disp_Data[31:28];
  84. default:data_temp = 4'b0000;
  85. endcase
  86. endmodule

3.2 仿真激励文件

  1. //只需要对输入信号赋值
  2. `timescale 1ns / 1ps
  3. module hex8_tb();
  4. reg Clk;
  5. reg Reset_n;
  6. reg [31:0]Disp_Data;
  7. wire [7:0]SEL;
  8. wire [7:0]SEG;
  9. hex8 hex8(
  10. .Clk(Clk),
  11. .Reset_n(Reset_n),
  12. .Disp_Data(Disp_Data),
  13. .SEL(SEL),
  14. .SEG(SEG)
  15. );
  16. initial Clk = 1;
  17. always #10 Clk = ~Clk;
  18. initial
  19. begin
  20. Reset_n = 0;
  21. Disp_Data = 32'h12345678;
  22. #201;
  23. Reset_n = 1;
  24. #20000000;//延时20ms
  25. Disp_Data = 32'h9abcdef0;
  26. #20000000;//延时20ms
  27. $stop;
  28. end
  29. endmodule

3.3 仿真图

由此,动态扫描得以实现,把想要显示的值放到32位的Disp_Data上即可,但是由于需要连接8个位选信号,8个段选信号,共16个信号线,说多不多说少也不少,所以ZYNQ使用74HC595芯片进行并转串将其变为三路输出,即将16路变为3路的芯片,岂不是更节约资源,这就要在下一章节阐述74HC595的驱动代码了。让我们转战下一小节。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/464991
推荐阅读
相关标签
  

闽ICP备14008679号