基于FPGA的两位按键控制LED数码管加减计数实验
两位按键控制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数码管加减计数实验相关推荐
- 单片机按键控制数码管c语言程序,基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文).doc...
基于单片机的按键控制LED数码管共阴极动态显示电路设计报告(毕业论文) 物理与电子工程学院2014级课程设计 PAGE IV 物理与电子工程学院 <单片机原理与接口技术> 课程设计报告书 ...
- FPGA:基础入门按键控制LED灯
题目概述: 使用按键控制LED灯亮灭. 无按键按下--LED全灭 按下KEYO--从右向左的流水灯效果 按下KEY1--从左向右的流水灯效果 按下KEY2--LED闪烁 按下KEY3--LED全亮 编 ...
- 基于STM32的TM1638的按键控制以及数码管和LED灯的动态扫描
目录 前言 关于按键控制的困惑及解决方案 关于按键控制判断只按下一次 数码管和LED动态扫描 关于驱动代码(HAL库加寄存器位端控制GPIO) 效果展示 数码管和led展示 按键按一次自增减展示 前言 ...
- 按键控制led灯亮灭c语言实验报告,通过按键控制LED灯的实验
通过查看开发板的核心电路原理图,加上查阅SUMSANG 2440的datasheed得知四个按键K1,K2,K3,K4对应GPF中的GPF1,GPF4,GPF2,GPF0.将这四个引脚配置为输入模式, ...
- FPGA之按键控制LED
一.按键开关 1.按键开关(轻触开关):主要是指轻触式按键开关,属于电子元器件类,使用时以满足操作力的条件向开关操作方向施压开关功能闭合接通,当撤销压力时开关即断开,其内部结构是靠金属弹片受力变化来实 ...
- 奋斗的小孩系列 FPGA学习altera系列: 第十篇 按键控制LED
奋斗的小孩系列 FPGA学习altera系列: 第十篇 按键控制LED 作者:奋斗的小孩 郝旭帅(转载请注明出处) 大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术江 ...
- 51单片机实现独立按键控制LED灯
本节博客主要实现以下效果: 3-1独立按键控制LED亮灭 本段代码主要使用了if...else...语句实现按下按下灯亮,松开按键时灯灭 #include <REGX51.H>void m ...
- 二:FPGA导航按键控制LED
FPGA按键控制LED 软件 芯片 功能 代码 代码解释 软件 软件使用的是ISE14.6(因为穷没买7系列,劝大家买个7系列的板子,这个软件装着还挺费事,不如Vivado好用,且6和7软件不通用) ...
- led计数电路实验报告_「正点原子FPGA连载」第八章 按键控制LED灯实验
1)实验平台:正点原子开拓者FPGA开发板 2)本实例源码下载:请移步正点原子官网 第八章 按键控制LED灯实验 按键是常用的一种控制器件.生活中我们可以见到各种形式的按键,由于其结构简单,成本低廉等 ...
最新文章
- 修复mysql数据库供应商_修复MYSQL数据库
- 2008年上半年程序员考试试题分析
- nyoj7——街区最短问题
- Web前端经典面试试题(一)
- 数学图形(1.10) 双曲线
- CentOS中的中文乱码问题的解决
- 【Python基础】5个Pandas技巧
- 登录业务介绍(单点登录)
- ThinkPHP6项目基操(20.实战部分 数据库操作返回值总结)
- android自定义进度条_Android中的自定义进度栏
- css中好看常用的中文字体
- TSP问题—Hopfield神经网络算法实现
- 基于格的 Hash 函数(SWIFFT) BKW 算法
- 下一个20年全球开发者将过亿?Tesra超算网络与开发者一起迎接AI时代的到来!
- 实现气泡效果的聊天框
- 魅族flashfire_高通平台所有黑砖(不开机)手机通用救砖方法
- ADO、DAO、ODBC、RAO和OLE DB等概念及异同
- 亚马逊短视频制作需要注意什么
- 【调剂】浙江师范大学机械工程需要调剂部分机械类、自动化类、材料加工类等学术研究生...
- 【dcdc】AP2813 DCDC降压恒流芯片 两路输出 一路恒流 一路瀑闪 电动摩托汽车灯方案