两位按键控制LED数码管加减计数实验

这是一篇拖了一个多月的文章,主要是基于FPGA利用按键消抖原理与动态数码管驱动原理相结合,来实现一个利用两位按键来控制数码管实现0-99的加法计数或者减法计数功能。

1.1 简介

本文使用的开发板的LED数码管是采用共阳极连接,关于如何进行驱动,可以搜索相关动态数码管扫描实验,这边不进行过多的复述了。

1.2 实验任务

本章的实验任务是设计一个两位数码管显示0-99的加减法计数,主要功能是数码管显示数值范围0~99,按下KEY0增1;按下KEY1减1;长按KEY0计数不断增加;长按KEY1计数不断减少。

1.3 软件设计

根据实验任务我们需要画出本次实验的系统模块框图,如下图所示:

如上图所示,可知主要由按键消抖模块,计数器模块以及动态数码管驱动模块组成。

程序中各个模块端口及信号连接如图所示:

顶层模块(top_key_seg)例化了以下三个模块:按键消抖模块(key_debounce)、按键计数器模块(key_cnt)以及数码管动态驱动模块(seg_led)。

顶层代码如下:

1  module top_seg_led(
2      input            sys_clk  ,         //时钟
3      input            sys_rst_n,         //复位(低电平有效)
4      input    [1:0]   key      ,         //两个按键key[0]: add  key[1]:sub
5
6
7      output   [1:0]   seg_sel   ,        // 数码管位选信号
8      output   [7:0]   seg_led            // 数码管段选信号
9  );
10
11 //wire define
12 wire            key_value0;             //按键0有效
13 wire            key_value1;             //按键1有效
14 wire            key_flag0;              //按键0有效的标志位
15 wire            key_flag1;              //按键1有效的标志位
16
17 wire    [19:0]  data;                   //数码管显示的数值
18 wire            en;                     //数码管使能
19
20
21 //*****************************************************
22 //**                    main code
23 //*****************************************************
24
25 //按键消抖加法模块
26 key_debounce u_add_key_debounce(
27     .sys_clk        (sys_clk),
28     .sys_rst_n      (sys_rst_n),
29
30     .key            (key[0]),
31     .key_flag       (key_flag0),
32     .key_value      (key_value0)
33 );
34
35 //按键消抖减法模块
36 key_debounce u_sub_key_debounce1(
37     .sys_clk        (sys_clk),
38     .sys_rst_n      (sys_rst_n),
39
40     .key            (key[1]),
41     .key_flag       (key_flag1),
42     .key_value      (key_value1)
43 );
44
45 //计数器模块,产生数码管需要显示的数据
46 key_cnt u_key_cnt(
47     .sys_clk       (sys_clk  ),
48     .sys_rst_n     (sys_rst_n),
49  .key_flag0     (key_flag0),
50     .key_value0    (key_value0),
51  .key_flag1     (key_flag1),
52     .key_value1    (key_value1),
53
54     .data          (data     ),
55     .en            (en       )
56
57 );
58
59 //数码管动态显示驱动模块
60 seg_led u_seg_led(
61     .sys_clk       (sys_clk  ),
62     .sys_rst_n     (sys_rst_n),
63
64     .data          (data     ),
65     .en            (en       ),
66
67     .seg_sel       (seg_sel  ),
68     .seg_led       (seg_led  )
69 );
70
71 endmodule

顶层模块主要完成对子模块的例化,并且实现各模块之间的信号的交互。按键消抖模块的输出连接计数器模块,计数器模块的输出data和en传递给动态数码管驱动模块,最后将数据从数码管中显示出来。

按键消抖模块的代码如下所示:

1  module key_debounce(
2      input            sys_clk ,           //时钟
3      input            sys_rst_n,          //复位信号,低电平有效
4
5      input            key     ,           //外部按键输入
6      output reg       key_flag    ,       //按键数据有效信号标志位
7      output reg       key_value           //按键有效值
8      );
9
10 //parameter define
11 //10ms计数最大值 19'd500_000
12 parameter CNT_10MS_MAX = 19'd500_000;
13
14 //reg define
15 reg [18:0] cnt_10ms;     //计数器
16 reg        key_reg0 ;        //按键寄存器0
17 reg        key_reg1 ;        //按键寄存器1
18
19 //*****************************************************
20 //**                    main code
21 //*****************************************************
22
23 //检测按键状态
24 always @(posedge sys_clk or negedge sys_rst_n) begin
25     if (!sys_rst_n) begin
26         key_reg0 <= 1'b1;                //按键寄存器低电平有效
27      key_reg1 <= 1'b1;
28         cnt_10ms <= 19'd0;
29     end
30     else begin
31      key_reg0 <= key;                    //按键信号给寄存器0
32         key_reg1 <= key_reg0;            //寄存器0的值传送给寄存器1
33      if(key_reg1 != key_reg0)            //寄存器key0和key1不相等,说明有按键被按下或释放
34             cnt_10ms <= CNT_10MS_MAX;    //10ms延迟计数器
35         else if(cnt_10ms > 19'd0)
36             cnt_10ms <= cnt_10ms - 19'd1;
37     end
38 end
39
40 //给按键消抖后的数据赋值
41 always @(posedge sys_clk or negedge sys_rst_n) begin
42     if (!sys_rst_n) begin
43         key_flag  <= 1'b0;
44         key_value <= 1'b1;
45     end
46     else begin
47         if(cnt_10ms == 19'd1) begin   //当计数器递减到1时,说明按键稳定状态维持了10ms
48             key_flag  <= 1'b1;        //此时消抖过程结束,给出一个时钟周期的标志信号
49             key_value <= key_reg1;    //并寄存此时按键的值
50         end
51         else begin
52             key_flag  <= 1'b0;
53             key_value <= key_value;
54         end
55     end
56 end
57
58 endmodule 

按键消抖模块从第33行开始不断检测按键有没有被按下,当检测到按键被按下赋给10ms寄存器一个10ms的最大值,然后进行逐一递减,当10ms计数器减到1时,说明按键已经进入稳定状态,按键消抖完成,然后输出一个按键消抖标志位信号同时将按键的有效值记录下来。

按键计数器模块代码如下:

1  module key_cnt(
2      input               sys_clk      ,   // 时钟信号
3      input               sys_rst_n    ,   // 复位信号
4   input               key_flag0   ,   //按键数据有效信号标志位0
5   input               key_value0  ,   //按键消抖后的数据 key0: add
6   input               key_flag1   ,   //按键数据有效信号标志位1
7   input               key_value1  ,   //按键消抖后的数据 key1:sub
8
9      output   reg [19:0] data         ,   // 6个数码管要显示的数值
10     output   reg        en               // 数码管使能信号
11 );
12
13 //parameter define
14 //100ms计数最大值 23'd5_000_000
15 parameter CNT_1S_MAX = 26'd50_000_000;
16 parameter CNT_100MS_MAX = 25'd5_000_000;
17
18 //wire define
19 reg  [25:0]  cnt_1s      ;               //1s长按计数器
20 reg          down_flag   ;               //长按标志
21 reg  [24:0]  down_100ms  ;               //100ms长按累加/减
22 reg          flag        ;               //长按累加/减标志位
23
24 //*****************************************************
25 //**                    main code
26 //*****************************************************
27
28 //判断按键是否处于长按状态
29 always @ (posedge sys_clk or negedge sys_rst_n) begin
30  if (!sys_rst_n) begin
31      cnt_1s <= 26'd0;
32      down_flag <= 1'd0;
33  end
34  else if((key_value0 == 1'd1) && (key_value1 == 1'd1)) begin           //按键未被按下时延时计数器和长按标志赋初值
35      cnt_1s <= 26'd0;
36      down_flag <= 1'd0;
37  end
38  else if( (key_value0 == 1'd0) || (key_value1 == 1'd0) ) begin     //按键按下有效时
39      if(cnt_1s < CNT_1S_MAX) begin
40          cnt_1s <= cnt_1s + 1'd1;
41          down_flag <= 1'd0;
42      end
43      else begin                                                                      //延时计数器计数1s时,判定为长按状态,并将长按标志拉高
44          cnt_1s <= cnt_1s;
45          down_flag <= 1'd1;
46      end
47  end
48 end
49
50 //长按状态下每100ms,输出一个时钟周期的脉冲信号
51 always @(posedge sys_clk or negedge sys_rst_n) begin
52  if (!sys_rst_n) begin
53      down_100ms <= 25'd0;
54      flag <= 1'd0;
55  end
56  else if(down_flag) begin
57      if(down_100ms < CNT_100MS_MAX) begin
58          down_100ms <= down_100ms + 1'd1;
59          flag    <=  1'd0;
60      end
61      else begin
62          down_100ms <= 25'd0;
63          flag <= 1'd1;
64      end
65  end
66 end
67
68 //数码管需要显示的数据,从0到99进行累加/减计算
69 always @(posedge sys_clk or negedge sys_rst_n) begin
70  if (!sys_rst_n) begin
71      data <= 20'b0;
72      en <= 1'b0;
73  end
74  else begin
75      en <= 1'b1;
76      if ((key_value0 == 1'd0) && (key_flag0 || flag) ) begin      //按键0按下时显示数值按下时累加一次,或长按状态时每隔0.1s累加一次
77          if(data < 20'd99)
78              data <= data + 1'b1;
79          else if (data == 20'd99)
80                 data <= 20'd0;
81             else
82              data <= data;
83      end
84      else if((key_value1 == 1'd0) && (key_flag1 || flag)) begin   //按键1按下时显示数值按下时累减一次,或长按状态时每隔0.1s累减一次
85          if(data > 20'd0)
86              data <= data - 1'b1;
87          else if(data == 20'd0)
88                 data <= 20'd99;
89             else
90              data <= data;
91      end
92  end
93 end
94
95 endmodule 

按键计数器模块主要实现将消抖后的按键信号,进行进一步判定。在39行之中增加了一个按下超过1s判定为长按的判定,如果超过1s以上的将按键信号判定为长按,并且增加一个长按按键标志位。在长按之后的每0.1s进行对数码管计数累加/减的判定;当按下key0时数码管累加等于99的时候,让数码管数据归零然后继续进行相应的累加,当按下key1进行递减运算的时候,在等于0时,进行一个判定让数码管下一个数据等于99,就完成了本次实验所需求的功能拓展。按键计数模块最后输出一个使能信号以及一个数据信号传递给下一层的数码管驱动模块。

数码管驱动模块:

1   module seg_led(
2           input               sys_clk     ,   //时钟
3           input               sys_rst_n   ,   //复位
4
5           input     [19:0]    data        ,   //数据
6           input               en          ,   //使能
7
8           output reg [7:0]    seg_led     ,   //段选
9           output reg [1:0]    seg_sel         //位选
10      );
11
12  //parameter define
13  //时钟分频 4'd10
14  parameter CLK_DIV     = 4'd10;
15
16  //对数码管驱动分频计数最大值 13'd5_000
17  parameter CNT_NUM_MAX = 13'd5_000;
18
19  //reg define
20  reg     [3:0]   clk_cnt ;                   //时钟分频计数器
21  reg             dri_clk ;                   //时钟分频5MHz
22  reg     [7:0]  num     ;                    //BCD数码位数
23  reg     [15:0]  cnt     ;                   //数码管驱动时钟计数器
24  reg             flag    ;                   //计数器标志位
25  reg     [2:0]   cnt_sel ;                   //数码管位选计数器
26  reg     [3:0]   num_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  assign data0 = data % 4'd10             ;   //个位
41  assign data1 = data / 4'd10 % 4'd10     ;   //十位
42  assign data2 = data / 7'd100 % 4'd10    ;   //百位
43  assign data3 = data / 10'd1_000 % 4'd10 ;   //千位
44  assign data4 = data / 14'd10_000 % 4'd10;   //万位
45  assign data5 = data / 17'd100_000       ;   //十万位
46
47  //对系统时钟进行10分频
48  always @(posedge sys_clk or negedge sys_rst_n) begin
49     if(!sys_rst_n)begin
50         clk_cnt <= 4'd0;
51         dri_clk <= 1'b1;
52     end
53     else if(clk_cnt == CLK_DIV/2 - 1'd1) begin   //0~4为1/2个周期
54         clk_cnt <= 4'd0;
55         dri_clk <= ~dri_clk;
56     end
57     else begin
58         clk_cnt <= clk_cnt + 1'b1;
59         dri_clk <= dri_clk;
60     end
61  end
62
63  //BCD数码显示
64  always @ (posedge dri_clk or negedge sys_rst_n) begin
65      if (!sys_rst_n)
66          num <= 8'b0;
67      else begin
68          if(data1)
69              num[7:0]   <= {data1, data0};
70          else
71              if(data0)
72                  num[3:0]   <= data0;
73              else
74                  num[7:0] <= {2{4'd10}}; //0、1不显示
75      end
76  end
77
78  //数码管1ms计数器
79  always @(posedge dri_clk or negedge sys_rst_n) begin
80      if(!sys_rst_n)begin
81          cnt  <= 13'd0;
82          flag <= 1'd0;
83      end
84      else if(cnt < CNT_NUM_MAX - 13'd1) begin
85          cnt  <= cnt + 13'd1;
86          flag <= 1'b0;
87      end
88      else begin
89          cnt <= 13'd0;
90          flag<= 1'b1;
91      end
92  end
93
94  //位选计数器
95  always @(posedge dri_clk or negedge sys_rst_n)
96      if (!sys_rst_n)
97          cnt_sel <= 2'b0;
98      else if(flag)
99          if(cnt_sel < 2'd2)
100             cnt_sel <= cnt_sel + 1'b1;
101         else
102             cnt_sel <= 2'b0;
103     else
104         cnt_sel <= cnt_sel;
105
106 //数码管的位选判断
107 always @(posedge dri_clk or negedge sys_rst_n) begin
108     if (!sys_rst_n) begin
109         seg_sel  <= 2'b11;           //位选
110         num_disp <= 4'b0;           //数据显示
111     end
112     else begin
113         if(en)begin
114             case (cnt_sel)
115                 2'd0 :begin
116                     seg_sel  <= 2'b10;
117                     num_disp <= num[3:0] ;
118                 end
119                 2'd1 :begin
120                     seg_sel  <= 2'b01;
121                     num_disp <= num[7:4] ;
122                 end
123                 default :begin
124                     seg_sel  <= 2'b0;
125                     num_disp <= 4'b0;
126                 end
127             endcase
128         end
129         else begin
130             seg_sel  <= 2'b0;
131             num_disp <= 4'b0;
132         end
133     end
134 end
135
136 //数码管的数据显示
137 always @(posedge dri_clk or negedge sys_rst_n) begin
138     if(!sys_rst_n)
139         seg_led <= 8'hff;
140     else begin
141         case(num_disp)
142             4'd0 :seg_led <= 8'b1100_0000;            //0
143             4'd1 :seg_led <= 8'b1111_1001;            //1
144             4'd2 :seg_led <= 8'b1010_0100;            //2
145             4'd3 :seg_led <= 8'b1011_0000;            //3
146             4'd4 :seg_led <= 8'b1001_1001;            //4
147             4'd5 :seg_led <= 8'b1001_0010;            //5
148             4'd6 :seg_led <= 8'b1000_0010;            //6
149             4'd7 :seg_led <= 8'b1111_1000;            //7
150             4'd8 :seg_led <= 8'b1000_0000;            //8
151             4'd9 :seg_led <= 8'b1001_0000;            //9
152         default:
153                   seg_led <= 8'b1111_1111;            //关闭
154         endcase
155     end
156 end
157
158 endmodule  

数码管显示驱动部分,位选只选择了两位,如果想要进行更多位数的显示可以增加位选。其次将符号位以及小数点位省略掉了,如果需要显示小数或者负数可以将符号位以及小数点位进行添加。上面数码管的显示方式同样可以采用BCD译码器来完成,思路大致相同仅供各位参考。

基于FPGA的两位按键控制LED数码管加减计数实验相关推荐

  1. 单片机按键控制数码管c语言程序,基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文).doc...

    基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文) 物理与电子工程学院2014级课程设计 PAGE IV 物理与电子工程学院 <单片机原理与接口技术> 课程设计报告书 ...

  2. FPGA:基础入门按键控制LED灯

    题目概述: 使用按键控制LED灯亮灭. 无按键按下--LED全灭 按下KEYO--从右向左的流水灯效果 按下KEY1--从左向右的流水灯效果 按下KEY2--LED闪烁 按下KEY3--LED全亮 编 ...

  3. 基于STM32的TM1638的按键控制以及数码管和LED灯的动态扫描

    目录 前言 关于按键控制的困惑及解决方案 关于按键控制判断只按下一次 数码管和LED动态扫描 关于驱动代码(HAL库加寄存器位端控制GPIO) 效果展示 数码管和led展示 按键按一次自增减展示 前言 ...

  4. 按键控制led灯亮灭c语言实验报告,通过按键控制LED灯的实验

    通过查看开发板的核心电路原理图,加上查阅SUMSANG 2440的datasheed得知四个按键K1,K2,K3,K4对应GPF中的GPF1,GPF4,GPF2,GPF0.将这四个引脚配置为输入模式, ...

  5. FPGA之按键控制LED

    一.按键开关 1.按键开关(轻触开关):主要是指轻触式按键开关,属于电子元器件类,使用时以满足操作力的条件向开关操作方向施压开关功能闭合接通,当撤销压力时开关即断开,其内部结构是靠金属弹片受力变化来实 ...

  6. 奋斗的小孩系列 FPGA学习altera系列: 第十篇 按键控制LED

    奋斗的小孩系列 FPGA学习altera系列: 第十篇 按键控制LED 作者:奋斗的小孩 郝旭帅(转载请注明出处) 大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术江 ...

  7. 51单片机实现独立按键控制LED灯

    本节博客主要实现以下效果: 3-1独立按键控制LED亮灭 本段代码主要使用了if...else...语句实现按下按下灯亮,松开按键时灯灭 #include <REGX51.H>void m ...

  8. 二:FPGA导航按键控制LED

    FPGA按键控制LED 软件 芯片 功能 代码 代码解释 软件 软件使用的是ISE14.6(因为穷没买7系列,劝大家买个7系列的板子,这个软件装着还挺费事,不如Vivado好用,且6和7软件不通用) ...

  9. led计数电路实验报告_「正点原子FPGA连载」第八章 按键控制LED灯实验

    1)实验平台:正点原子开拓者FPGA开发板 2)本实例源码下载:请移步正点原子官网 第八章 按键控制LED灯实验 按键是常用的一种控制器件.生活中我们可以见到各种形式的按键,由于其结构简单,成本低廉等 ...

最新文章

  1. 修复mysql数据库供应商_修复MYSQL数据库
  2. 2008年上半年程序员考试试题分析
  3. nyoj7——街区最短问题
  4. Web前端经典面试试题(一)
  5. 数学图形(1.10) 双曲线
  6. CentOS中的中文乱码问题的解决
  7. 【Python基础】5个Pandas技巧
  8. 登录业务介绍(单点登录)
  9. ThinkPHP6项目基操(20.实战部分 数据库操作返回值总结)
  10. android自定义进度条_Android中的自定义进度栏
  11. css中好看常用的中文字体
  12. TSP问题—Hopfield神经网络算法实现
  13. 基于格的 Hash 函数(SWIFFT) BKW 算法
  14. 下一个20年全球开发者将过亿?Tesra超算网络与开发者一起迎接AI时代的到来!
  15. 实现气泡效果的聊天框
  16. 魅族flashfire_高通平台所有黑砖(不开机)手机通用救砖方法
  17. ADO、DAO、ODBC、RAO和OLE DB等概念及异同
  18. 亚马逊短视频制作需要注意什么
  19. 【调剂】浙江师范大学机械工程需要调剂部分机械类、自动化类、材料加工类等学术研究生...
  20. 【dcdc】AP2813 DCDC降压恒流芯片 两路输出 一路恒流 一路瀑闪 电动摩托汽车灯方案

热门文章

  1. Lambda 架构:强大的数据工程方法
  2. Pico Neo3 通过unity实现VR手柄瞬移功能
  3. 君正主控CPU芯片,X2000多场景应用案例
  4. 特南鲍姆编著经典教材《COMPUTER NERWORKS》翻译
  5. 中国剩余定理 及 拓展中国剩余定理模板
  6. 【合辑】点云基础知识及点云催化剂软件功能介绍
  7. Python爬取豆瓣裤袜视界高清大图
  8. docker启动失败
  9. Solidity开发之 Ganache 安装
  10. 软件开发团队建设思路谈