软件:Vivado 2022.2
硬件:BASYS 3
设计描述:通过开发板上的四个按键,按下一次记数加一,分别由四个数码管从左到右分别显示四个按键记数情况。
例:
1.初始值为0000,意为无记数
2.当按下第一个按键,记数加一,数码管显示1000
3.再次按下一第一个按键,记数加一,数码管显示2000
4.按下第二个按键9次,数码管显示2900,第二位记满
5.当再次按下第二个按键后,数码管显示2000
应用原理:
1.按键去抖动:
通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时, 由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动 。抖动时间的长短由按键的机械特性决定,一般为5ms~20ms。这是一个 很重要的时间参数,在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几秒至数秒。键抖动会引起一次按键被误读多次。为确保对按键的一次闭合仅作一次处理,必须去除键抖动。
2.数码管显示:
共阳极数码管 :
共阳极数码管的8个发光二极管的阳极(二极管正端)连接在一起。通常,公共阳极接高电平(一般接电源),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为低电平时,则该端所连接的字段导通并点亮。
共阴极数码管:
通常,公共阴极接低电平(一般接地),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为高电平时,则该端所连接的字段导通并点亮。
设计思路:
按键扫描消抖延时:
BASYS 3开发板W5引脚的时钟为100MHZ,设计采用10ms的按键消抖,100MHz(0101_1111_0101_1110_0001_0000_0000)分频记数得100Hz(1111_0100_0010_0100_0000)
即为10ms延时
数码管显示:
BASYS 3 数码管的是共阳极,所以引脚输出为0时亮起。 按键记数每一位从0到9显示,记满9次归零。
共阳极数码管显示(七位和八位)
共阳 | 七位(不包括小数点) | 八位 |
0 | 1000000 | 11000000 |
1 | 1111001 | 11111001 |
2 | 0100100 | 10100100 |
3 | 0110000 | 10110000 |
4 | 0011001 | 10011001 |
5 | 0010010 | 10010010 |
6 | 0000010 | 10000010 |
7 | 1111000 | 11111000 |
8 | 0000000 | 10000000 |
9 | 0010000 | 10010000 |
代码示例:
按键消抖模块:
module delay(clk,key,key_delay);
input clk;//100MHz
input key;//按键输入
output key_delay;//按键消抖判断输出
reg key_delay;
reg[19:0] cnt1;//10ms延时20位记数
always@(posedge clk)//100Mhz
begin
if(key) //假如按键按下,按键按下为高电平
begin
if( cnt1==20'b1111_0100_0010_0100_0000 && key) //10ms
key_delay<=1; //延时后,如果key仍然为1,则key_delay输出1,确认按键按下
else begin
cnt1<= cnt1+1;key_delay<=0; // 否则输出0,
end
end
else begin
cnt1<= 0 ; key_delay<= 0 ; //当key不为1 时,计数器清零,输出0
end
end
endmodule
记数模块:
module cnt_delay(key_delay,cnt);
input key_delay;
output reg [3:0]cnt;
always@(posedge key_delay ) //以按键作为时钟信号,实现计数
begin
if(cnt==4'b1001)
cnt<=0;
else
cnt<=cnt+1;
end
endmodule
主模块:
module but_delay_cnt_4(clk,key1,key2,key3,key4,dig,seg);
input clk; //clk是Basys3板上时钟100 MHz
input key1,key2,key3,key4;//按钮1,2,3,4
output dig;
output seg;
reg[3:0] dig;//位选数码管
reg[6:0] seg;//段选数码管
wire key4_delay,key1_delay,key2_delay,key3_delay;//按钮消抖按下输出
reg [19:0] cnt;//延时计数器,100MHz/3KHz,
reg [15:0] seg_count;//按键扫描计数器
reg clk_ttt;//数码管位选动态扫描频率,3KHz
reg [1:0]selcnt;//数码管位选标志位,0,1,2,3
reg [1:0]ain;//位选0123对应的段选标志位
wire [3:0]cnt1,cnt2,cnt3,cnt4;//段选转换标志位,0-9
//按键消抖,判断按键按下
delay U1(.clk(clk),.key(key1),.key_delay(key1_delay));
delay U2(.clk(clk),.key(key2),.key_delay(key2_delay));
delay U3(.clk(clk),.key(key3),.key_delay(key3_delay));
delay U4(.clk(clk),.key(key4),.key_delay(key4_delay));
//判断按键上调变,即为按键按下,记数加一
cnt_delay K1(.key_delay(key1_delay),.cnt(cnt1));
cnt_delay K2(.key_delay(key2_delay),.cnt(cnt2));
cnt_delay K3(.key_delay(key3_delay),.cnt(cnt3));
cnt_delay K4(.key_delay(key4_delay),.cnt(cnt4));
always @(posedge clk) //100MHz
begin
seg_count <= seg_count + 1;
if(seg_count==16'b1000_0010_0011_0101)//100MHz/33333Hz
begin
seg_count <=0;//清零
clk_ttt <= ~clk_ttt;//3KHz的动态扫描频率
end
end
always @(posedge clk_ttt)//位选标志位随3KHz频率从0-3轮换
begin
selcnt <= selcnt + 1;//标志位+1,11记满后加一回到00
end
always @(selcnt) //位选信号控制
begin
case (selcnt)
2'b00: begin dig <= 4'b0111;ain <= 2'b00;end //1号数码管显示ain对应的段码
2'b01: begin dig <= 4'b1011;ain <= 2'b01;end
2'b10: begin dig <= 4'b1101;ain <= 2'b10;end
2'b11: begin dig <= 4'b1110;ain <= 2'b11;end
default: dig<=4'b1111;
endcase
end
always @(ain or cnt1 or cnt2 or cnt3 or cnt4)
begin
if (ain==2'b00)begin //1号数码管
case({ain,cnt1})
6'b000000:seg <= 7'b1000000;
6'b000001:seg <= 7'b1111001;
6'b000010:seg <= 7'b0100100;
6'b000011:seg <= 7'b0110000;
6'b000100:seg <= 7'b0011001;
6'b000101:seg <= 7'b0010010;
6'b000110:seg <= 7'b0000010;
6'b000111:seg <= 7'b1111000;
6'b001000:seg <= 7'b0000000;
6'b001001:seg <= 7'b0010000;
default :seg <= 7'b1100000;
endcase
end
if (ain==2'b01)begin //2号数码管
case({ain,cnt2})
6'b010000:seg <= 7'b1000000;
6'b010001:seg <= 7'b1111001;
6'b010010:seg <= 7'b0100100;
6'b010011:seg <= 7'b0110000;
6'b010100:seg <= 7'b0011001;
6'b010101:seg <= 7'b0010010;
6'b010110:seg <= 7'b0000010;
6'b010111:seg <= 7'b1111000;
6'b011000:seg <= 7'b0000000;
6'b011001:seg <= 7'b0010000;
default :seg <= 7'b1010000;
endcase
end
if (ain==2'b10)begin //3号数码管
case({ain,cnt3})
6'b100000:seg <= 7'b1000000;
6'b100001:seg <= 7'b1111001;
6'b100010:seg <= 7'b0100100;
6'b100011:seg <= 7'b0110000;
6'b100100:seg <= 7'b0011001;
6'b100101:seg <= 7'b0010010;
6'b100110:seg <= 7'b0000010;
6'b100111:seg <= 7'b1111000;
6'b101000:seg <= 7'b0000000;
6'b101001:seg <= 7'b0010000;
default :seg <= 7'b1001000;
endcase
end
if (ain==2'b11)begin //4号数码管
case({ain,cnt4})
6'b110000:seg <= 7'b1000000;
6'b110001:seg <= 7'b1111001;
6'b110010:seg <= 7'b0100100;
6'b110011:seg <= 7'b0110000;
6'b110100:seg <= 7'b0011001;
6'b110101:seg <= 7'b0010010;
6'b110110:seg <= 7'b0000010;
6'b110111:seg <= 7'b1111000;
6'b111000:seg <= 7'b0000000;
6'b111001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
end
endmodule
创建工程:
调用四个按键消抖模块和四个记数模块
引脚定义:
数码管位选
数码管段选
100MHz输入,四个按键
综合出的电路:
可以看到八个我们调用的模块
设计障碍以及解决方法:
错误代码示例:
这里与主模块的代码做一个比较,这个always模块下的思路是ain位选到数码管,然后通过cntx选择对应的段选,以为真正记数的时候是0-9,
但cntx记数是0-15(1111),为了避cntx跳到10-15(1010-1111)非法范围,所以在case语句中加如default语句,使其归零
但此处就会遇到一个综合问题,因为{ain,cntx}中的ain值在同一时间只有一种情况,但是cntx有cnt1,cnt2,cnt3,cnt4,不论四个一位计数器是否跳变,seg就会被四个case语句赋值,并由于“<="且是同时赋值。这里就会产生竞争。
always @(ain or cnt1 or cnt2 or cnt3 or cnt4)
begin
//1号数码管
case({ain,cnt1})
6'b000000:seg <= 7'b1000000;
6'b000001:seg <= 7'b1111001;
6'b000010:seg <= 7'b0100100;
6'b000011:seg <= 7'b0110000;
6'b000100:seg <= 7'b0011001;
6'b000101:seg <= 7'b0010010;
6'b000110:seg <= 7'b0000010;
6'b000111:seg <= 7'b1111000;
6'b001000:seg <= 7'b0000000;
6'b001001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
//2号数码管
case({ain,cnt2})
6'b010000:seg <= 7'b1000000;
6'b010001:seg <= 7'b1111001;
6'b010010:seg <= 7'b0100100;
6'b010011:seg <= 7'b0110000;
6'b010100:seg <= 7'b0011001;
6'b010101:seg <= 7'b0010010;
6'b010110:seg <= 7'b0000010;
6'b010111:seg <= 7'b1111000;
6'b011000:seg <= 7'b0000000;
6'b011001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
//3号数码管
case({ain,cnt3})
6'b100000:seg <= 7'b1000000;
6'b100001:seg <= 7'b1111001;
6'b100010:seg <= 7'b0100100;
6'b100011:seg <= 7'b0110000;
6'b100100:seg <= 7'b0011001;
6'b100101:seg <= 7'b0010010;
6'b100110:seg <= 7'b0000010;
6'b100111:seg <= 7'b1111000;
6'b101000:seg <= 7'b0000000;
6'b101001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
//4号数码管
case({ain,cnt4})
6'b110000:seg <= 7'b1000000;
6'b110001:seg <= 7'b1111001;
6'b110010:seg <= 7'b0100100;
6'b110011:seg <= 7'b0110000;
6'b110100:seg <= 7'b0011001;
6'b110101:seg <= 7'b0010010;
6'b110110:seg <= 7'b0000010;
6'b110111:seg <= 7'b1111000;
6'b111000:seg <= 7'b0000000;
6'b111001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
硬件综合故障演示:
可以看到,四个位数,只能综合处一个按键记数,另外三个key1,key2,key3,没有连接。
解决方法:
所以我们需要判断,ain的情况,在同一时间,seg只由一个{ain,cntx}六位组合值case语句赋值。
故这里加了四个if判断ain约束进入哪个case语句。
always @(ain or cnt1 or cnt2 or cnt3 or cnt4)
begin
if (ain==2'b00)begin //1号数码管
case({ain,cnt1})
6'b000000:seg <= 7'b1000000;
6'b000001:seg <= 7'b1111001;
6'b000010:seg <= 7'b0100100;
6'b000011:seg <= 7'b0110000;
6'b000100:seg <= 7'b0011001;
6'b000101:seg <= 7'b0010010;
6'b000110:seg <= 7'b0000010;
6'b000111:seg <= 7'b1111000;
6'b001000:seg <= 7'b0000000;
6'b001001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
if (ain==2'b01)begin //2号数码管
case({ain,cnt2})
6'b010000:seg <= 7'b1000000;
6'b010001:seg <= 7'b1111001;
6'b010010:seg <= 7'b0100100;
6'b010011:seg <= 7'b0110000;
6'b010100:seg <= 7'b0011001;
6'b010101:seg <= 7'b0010010;
6'b010110:seg <= 7'b0000010;
6'b010111:seg <= 7'b1111000;
6'b011000:seg <= 7'b0000000;
6'b011001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
if (ain==2'b10)begin //3号数码管
case({ain,cnt3})
6'b100000:seg <= 7'b1000000;
6'b100001:seg <= 7'b1111001;
6'b100010:seg <= 7'b0100100;
6'b100011:seg <= 7'b0110000;
6'b100100:seg <= 7'b0011001;
6'b100101:seg <= 7'b0010010;
6'b100110:seg <= 7'b0000010;
6'b100111:seg <= 7'b1111000;
6'b101000:seg <= 7'b0000000;
6'b101001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
if (ain==2'b11)begin //4号数码管
case({ain,cnt4})
6'b110000:seg <= 7'b1000000;
6'b110001:seg <= 7'b1111001;
6'b110010:seg <= 7'b0100100;
6'b110011:seg <= 7'b0110000;
6'b110100:seg <= 7'b0011001;
6'b110101:seg <= 7'b0010010;
6'b110110:seg <= 7'b0000010;
6'b110111:seg <= 7'b1111000;
6'b111000:seg <= 7'b0000000;
6'b111001:seg <= 7'b0010000;
default :seg <= 7'b1000000;
endcase
end
end
设计总结:
BASYS 3开发板设计的四位记数器可以实现从0到9999的数字计数功能。这种设计可以在许多数字电路应用中使用,例如测量仪器、时钟和闹钟等。
这里只采用了四个一位记数,后期可以改成多位记数,但是掌握了一位记数,就可以
通过设计这个计数器,我获得了以下收获:文章来源:https://www.toymoban.com/news/detail-476923.html
- 深入了解数字电路的工作原理和设计方法。
- 掌握计数器的原理和实现方式,以及如何将其集成到更大的数字系统中。
- 学习如何使用Verilog硬件描述语言来编写数字电路设计。
- 加强了对硬件设计工具(Vivado)的运用能力。
总之,BASYS 3开发板设计的四位记数器是一项有意义的项目,它帮助我进一步深化对数字电路设计的理解,并提高我的技能水平。文章来源地址https://www.toymoban.com/news/detail-476923.html
到了这里,关于基于FPGA的四位数码管显示按键计数器设计(verilog编程)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!