基于FPGA的四位数码管显示按键计数器设计(verilog编程)

软件: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次归零。

                                               共阳极数码管显示(七位和八位)

共阳七位(不包括小数点)八位
0100000011000000
1111100111111001
2010010010100100
3011000010110000
4001100110011001
5001001010010010
6000001010000010
7111100011111000
8000000010000000
9001000010010000

代码示例:

按键消抖模块:

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的数字计数功能。这种设计可以在许多数字电路应用中使用,例如测量仪器、时钟和闹钟等。

        这里只采用了四个一位记数,后期可以改成多位记数,但是掌握了一位记数,就可以

        通过设计这个计数器,我获得了以下收获:

  1. 深入了解数字电路的工作原理和设计方法。
  2. 掌握计数器的原理和实现方式,以及如何将其集成到更大的数字系统中。
  3. 学习如何使用Verilog硬件描述语言来编写数字电路设计。
  4. 加强了对硬件设计工具(Vivado)的运用能力。

        总之,BASYS 3开发板设计的四位记数器是一项有意义的项目,它帮助我进一步深化对数字电路设计的理解,并提高我的技能水平。

  • 9
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值