【FPGA实验】数码管动态显示

数码管动态/静态显示区别

静态显示:

每一个管脚都用固定的一个电平去控制。

优点:能够做到”同时”

缺点:管脚太多

【FPGA实验】数码管动态显示 - 第1张

动态显示:

每一个数码管共用一套电路,显示时只需控制哪一个数码管进行显示。

优点:大大减小了管脚的数量

缺点:一次只能控制单独一个数码管进行显示,但可以快速切换数码管显示,利用人眼的”视觉暂留"来“同步”进行显示。

【FPGA实验】数码管动态显示 - 第2张

功能描述

动态控制数码管,使其每1ms从0开始累加1,按下复位键后重新开始计数。

计数模块

先通过分频,每0.1s输出一个脉冲信号,数码管每接收到一个脉冲信号时,数值累加一次。

  1. module count(
  2. //mudule clock
  3. input clk , // 时钟信号
  4. input rst_n, // 复位信号
  5. //user interface
  6. output reg [19:0] data , // 6个数码管要显示的数值
  7. output reg [ 5:0] point, // 小数点的位置,高电平点亮对应数码管位上的小数点
  8. output reg en , // 数码管使能信号
  9. output reg sign // 符号位,高电平时显示负号,低电平不显示负号
  10. );
  11.  
  12. //parameter define
  13. parameter MAX_NUM = 23'd5000_000; // 计数器计数的最大值
  14.  
  15. //reg define
  16. reg [22:0] cnt ; // 计数器,用于计时100ms
  17. reg flag; // 标志信号
  18.  
  19. //*****************************************************
  20. //** main code
  21. //*****************************************************
  22.  
  23. //计数器对系统时钟计数达100ms时,输出一个时钟周期的脉冲信号
  24. always @ (posedge clk or negedge rst_n) begin
  25. if (!rst_n) begin
  26. cnt <= 23'b0;
  27. flag<= 1'b0;
  28. end
  29. else if (cnt < MAX_NUM - 1'b1) begin
  30. cnt <= cnt + 1'b1;
  31. flag<= 1'b0;
  32. end
  33. else begin
  34. cnt <= 23'b0;
  35. flag <= 1'b1;
  36. end
  37. end
  38.  
  39. //数码管需要显示的数据,从0累加到999999
  40. always @ (posedge clk or negedge rst_n) begin
  41. if (!rst_n)begin
  42. data <= 20'b0;
  43. point <=6'b000000;
  44. en <= 1'b0;
  45. sign <= 1'b0;
  46. end
  47. else begin
  48. point <= 6'b000000; //不显示小数点
  49. en <= 1'b1; //打开数码管使能信号
  50. sign <= 1'b0; //不显示负号
  51. if (flag) begin //显示数值每隔0.1s累加一次
  52. if(data < 20'd999999)
  53. data <= data +1'b1;
  54. else
  55. data <= 20'b0;

数码管显示模块

和静态不同的是,需要通过一个额外的寄存器来控制数码管的选择计数,这里用的是 cnt_sel。

  1. module seg_led(
  2. input clk , // 时钟信号
  3. input rst_n , // 复位信号
  4.  
  5. input [19:0] data , // 6位数码管要显示的数值
  6. input [5:0] point , // 小数点具体显示的位置,从高到低,高电平有效
  7. input en , // 数码管使能信号
  8. input sign , // 符号位(高电平显示“-”号)
  9.  
  10. output reg [5:0] seg_sel, // 数码管位选,最左侧数码管为最高位
  11. output reg [7:0] seg_led // 数码管段选
  12. );
  13.  
  14. //parameter define
  15. localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数
  16. localparam MAX_NUM = 13'd5000 ; // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
  17.  
  18. //reg define
  19. reg [ 3:0] clk_cnt ; // 时钟分频计数器
  20. reg dri_clk ; // 数码管的驱动时钟,5MHz
  21. reg [23:0] num ; // 24位bcd码寄存器
  22. reg [12:0] cnt0 ; // 数码管驱动时钟计数器
  23. reg flag ; // 标志信号(标志着cnt0计数达1ms)
  24. reg [2:0] cnt_sel ; // 数码管位选计数器
  25. reg [3:0] num_disp ; // 当前数码管显示的数据
  26. reg dot_disp ; // 当前数码管显示的小数点
  27.  
  28. //wire define
  29. wire [3:0] data0 ; // 个位数
  30. wire [3:0] data1 ; // 十位数
  31. wire [3:0] data2 ; // 百位数
  32. wire [3:0] data3 ; // 千位数
  33. wire [3:0] data4 ; // 万位数
  34. wire [3:0] data5 ; // 十万位数
  35.  
  36. //*****************************************************
  37. //** main code
  38. //*****************************************************
  39.  
  40. //提取显示数值所对应的十进制数的各个位
  41. assign data0 = data % 4'd10; // 个位数
  42. assign data1 = data / 4'd10 % 4'd10 ; // 十位数
  43. assign data2 = data / 7'd100 % 4'd10 ; // 百位数
  44. assign data3 = data / 10'd1000 % 4'd10 ; // 千位数
  45. assign data4 = data / 14'd10000 % 4'd10; // 万位数
  46. assign data5 = data / 17'd100000; // 十万位数
  47.  
  48. //对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
  49. always @(posedge clk or negedge rst_n) begin
  50. if(!rst_n) begin
  51. clk_cnt <= 4'd0;
  52. dri_clk <= 1'b1;
  53. end
  54. else if(clk_cnt == CLK_DIVIDE/2 - 1'd1) begin
  55. clk_cnt <= 4'd0;
  56. dri_clk <= ~dri_clk;
  57. end
  58. else begin
  59. clk_cnt <= clk_cnt + 1'b1;
  60. dri_clk <= dri_clk;
  61. end
  62. end
  63.  
  64. //将20位2进制数转换为8421bcd码(即使用4位二进制数表示1位十进制数)
  65. always @ (posedge dri_clk or negedge rst_n) begin
  66. if (!rst_n)
  67. num <= 24'b0;
  68. else begin
  69. if (data5 || point[5]) begin //如果显示数据为6位十进制数,
  70. num[23:20] <= data5; //则依次给6位数码管赋值
  71. num[19:16] <= data4;
  72. num[15:12] <= data3;
  73. num[11:8] <= data2;
  74. num[ 7:4] <= data1;
  75. num[ 3:0] <= data0;
  76. end
  77. else begin
  78. if (data4 || point[4]) begin //如果显示数据为5位十进制数,则给低5位数码管赋值
  79. num[19:0] <= {data4,data3,data2,data1,data0};
  80. if(sign)
  81. num[23:20] <= 4'd11; //如果需要显示负号,则最高位(第6位)为符号位
  82. else
  83. num[23:20] <= 4'd10; //不需要显示负号时,则第6位不显示任何字符
  84. end
  85. else begin //如果显示数据为4位十进制数,则给低4位数码管赋值
  86. if (data3 || point[3]) begin
  87. num[15: 0] <= {data3,data2,data1,data0};
  88. num[23:20] <= 4'd10; //第6位不显示任何字符
  89. if(sign) //如果需要显示负号,则最高位(第5位)为符号位
  90. num[19:16] <= 4'd11;
  91. else //不需要显示负号时,则第5位不显示任何字符
  92. num[19:16] <= 4'd10;
  93. end
  94. else begin //如果显示数据为3位十进制数,则给低3位数码管赋值
  95. if (data2 || point[2]) begin
  96. num[11: 0] <= {data2,data1,data0};
  97. //第6、5位不显示任何字符
  98. num[23:16] <= {2{4'd10}};
  99. if(sign) //如果需要显示负号,则最高位(第4位)为符号位
  100. num[15:12] <= 4'd11;
  101. else //不需要显示负号时,则第4位不显示任何字符
  102. num[15:12] <= 4'd10;
  103. end
  104. else begin //如果显示数据为2位十进制数,则给低2位数码管赋值
  105. if (data1 || point[1]) begin
  106. num[ 7: 0] <= {data1,data0};
  107. //第6、5、4位不显示任何字符
  108. num[23:12] <= {3{4'd10}};
  109. if(sign) //如果需要显示负号,则最高位(第3位)为符号位
  110. num[11:8] <= 4'd11;
  111. else //不需要显示负号时,则第3位不显示任何字符
  112. num[11:8] <= 4'd10;
  113. end
  114. else begin //如果显示数据为1位十进制数,则给最低位数码管赋值
  115. num[3:0] <= data0;
  116. //第6、5位不显示任何字符
  117. num[23:8] <= {4{4'd10}};
  118. if(sign) //如果需要显示负号,则最高位(第2位)为符号位
  119. num[7:4] <= 4'd11;
  120. else //不需要显示负号时,则第2位不显示任何字符
  121. num[7:4] <= 4'd10;
  122. end
  123. end
  124. end
  125. end
  126. end
  127. end
  128. end
  129.  
  130. //每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
  131. always @ (posedge dri_clk or negedge rst_n) begin
  132. if (rst_n == 1'b0) begin
  133. cnt0 <= 13'b0;
  134. flag <= 1'b0;
  135. end
  136. else if (cnt0 < MAX_NUM - 1'b1) begin
  137. cnt0 <= cnt0 + 1'b1;
  138. flag <= 1'b0;
  139. end
  140. else begin
  141. cnt0 <= 13'b0;
  142. flag <= 1'b1;
  143. end
  144. end
  145.  
  146. //cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
  147. always @ (posedge dri_clk or negedge rst_n) begin
  148. if (rst_n == 1'b0)
  149. cnt_sel <= 3'b0;
  150. else if(flag) begin
  151. if(cnt_sel < 3'd5)
  152. cnt_sel <= cnt_sel + 1'b1;
  153. else
  154. cnt_sel <= 3'b0;
  155. end
  156. else
  157. cnt_sel <= cnt_sel;
  158. end
  159.  
  160. //控制数码管位选信号,使6位数码管轮流显示
  161. always @ (posedge dri_clk or negedge rst_n) begin
  162. if(!rst_n) begin
  163. seg_sel <= 6'b111111; //位选信号低电平有效
  164. num_disp <= 4'b0;
  165. dot_disp <= 1'b1; //共阳极数码管,低电平导通
  166. end
  167. else begin
  168. if(en) begin
  169. case (cnt_sel)
  170. 3'd0 :begin
  171. seg_sel <= 6'b111110; //显示数码管最低位
  172. num_disp <= num[3:0] ; //显示的数据
  173. dot_disp <= ~point[0]; //显示的小数点
  174. end
  175. 3'd1 :begin
  176. seg_sel <= 6'b111101; //显示数码管第1位
  177. num_disp <= num[7:4] ;
  178. dot_disp <= ~point[1];
  179. end
  180. 3'd2 :begin
  181. seg_sel <= 6'b111011; //显示数码管第2位
  182. num_disp <= num[11:8];
  183. dot_disp <= ~point[2];
  184. end
  185. 3'd3 :begin
  186. seg_sel <= 6'b110111; //显示数码管第3位
  187. num_disp <= num[15:12];
  188. dot_disp <= ~point[3];
  189. end
  190. 3'd4 :begin
  191. seg_sel <= 6'b101111; //显示数码管第4位
  192. num_disp <= num[19:16];
  193. dot_disp <= ~point[4];
  194. end
  195. 3'd5 :begin
  196. seg_sel <= 6'b011111; //显示数码管最高位
  197. num_disp <= num[23:20];
  198. dot_disp <= ~point[5];
  199. end
  200. default :begin
  201. seg_sel <= 6'b111111;
  202. num_disp <= 4'b0;
  203. dot_disp <= 1'b1;
  204. end
  205. endcase
  206. end
  207. else begin
  208. seg_sel <= 6'b111111; //使能信号为0时,所有数码管均不显示
  209. num_disp <= 4'b0;
  210. dot_disp <= 1'b1;
  211. end
  212. end
  213. end
  214.  
  215. //控制数码管段选信号,显示字符
  216. always @ (posedge dri_clk or negedge rst_n) begin
  217. if (!rst_n)
  218. seg_led <= 8'hc0;
  219. else begin
  220. case (num_disp)
  221. 4'd0 : seg_led <= {dot_disp,7'b1000000}; //显示数字 0
  222. 4'd1 : seg_led <= {dot_disp,7'b1111001}; //显示数字 1
  223. 4'd2 : seg_led <= {dot_disp,7'b0100100}; //显示数字 2
  224. 4'd3 : seg_led <= {dot_disp,7'b0110000}; //显示数字 3
  225. 4'd4 : seg_led <= {dot_disp,7'b0011001}; //显示数字 4
  226. 4'd5 : seg_led <= {dot_disp,7'b0010010}; //显示数字 5
  227. 4'd6 : seg_led <= {dot_disp,7'b0000010}; //显示数字 6
  228. 4'd7 : seg_led <= {dot_disp,7'b1111000}; //显示数字 7
  229. 4'd8 : seg_led <= {dot_disp,7'b0000000}; //显示数字 8
  230. 4'd9 : seg_led <= {dot_disp,7'b0010000}; //显示数字 9
  231. 4'd10: seg_led <= 8'b11111111; //不显示任何字符
  232. 4'd11: seg_led <= 8'b10111111; //显示负号(-)
  233. default:
  234. seg_led <= {dot_disp,7'b1000000};

顶层模块

  1. module top_seg_led(
  2. //global clock
  3. input sys_clk , // 全局时钟信号
  4. input sys_rst_n, // 复位信号(低有效)
  5.  
  6. //seg_led interface
  7. output [5:0] seg_sel , // 数码管位选信号
  8. output [7:0] seg_led // 数码管段选信号
  9. );
  10.  
  11. //wire define
  12. wire [19:0] data; // 数码管显示的数值
  13. wire [ 5:0] point; // 数码管小数点的位置
  14. wire en; // 数码管显示使能信号
  15. wire sign; // 数码管显示数据的符号位
  16.  
  17. //*****************************************************
  18. //** main code
  19. //*****************************************************
  20.  
  21. //计数器模块,产生数码管需要显示的数据
  22. count u_count(
  23. .clk (sys_clk ), // 时钟信号
  24. .rst_n (sys_rst_n), // 复位信号
  25.  
  26. .data (data ), // 6位数码管要显示的数值
  27. .point (point ), // 小数点具体显示的位置,高电平有效
  28. .en (en ), // 数码管使能信号
  29. .sign (sign ) // 符号位
  30. );
  31.  
  32. //数码管动态显示模块
  33. seg_led u_seg_led(
  34. .clk (sys_clk ), // 时钟信号
  35. .rst_n (sys_rst_n), // 复位信号
  36.  
  37. .data (data ), // 显示的数值
  38. .point (point ), // 小数点具体显示的位置,高电平有效
  39. .en (en ), // 数码管使能信号
  40. .sign (sign ), // 符号位,高电平显示负号(-)
  41. .seg_sel (seg_sel ), // 位选
  42. .seg_led (seg_led ) // 段选
  43. );

管脚分配

管脚分配和静态显示完全一样。

【FPGA实验】数码管动态显示 - 第3张
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/9559.html

"愿我的文字能带给您一丝美好"

还没有人赞赏,支持一下

评论

A 为本文作者,G 为游客总数:0
加载中…

提交评论

游客,您好,欢迎参与讨论。

我的购物车

购物车为空

优惠券

没有优惠券