FPGA:按键控制数码管加减

一、简介

Key1按下时数码管显示数字加一,Key2按下时数码管显示数字减一。采用4位共阳数码管显示,模块某宝可买到。其中模块接口SLCK与程序中shcp连接,RLCK与stcp相连,DIO与ds相连

二、设计

硬件方面

从商家得到的数码管原理图如下:

通过观察原理图可知,数码管模块采用两片74HC595驱动,第一片74HC595输出用于选中数码管,第二片用于控制数码管内容的显示。

74HC595 是一个 8 位串行输入、并行输出的位移缓存器。其内部具有 8 位移位寄存器
和一个存储器,具有三态输出功能。
引脚功能描述如下:

其中Q0~Q7为并行输出端,当串行输入8位数据时时第一位在Q7端输出。

Q7S(9号引脚)为串行数据输出端,通过观察数码管原理图可以发现第一片74HC595的 串行数据输出端连接到第二片74HC595的数据输入端(DS)。

SHCP为移位寄存器时钟输入端

STCP为存储时钟输入端

DS为串行数据输入端

OE输出使能端,低电平有效

MR为主复引脚,低电平有效。

(完整数据手册可在立创商城或半岛小芯找到)

74HC595的使用流程如下:

1 、 首先把要传输的数据通过引脚 DS 输入到 74HC595 中。
2 、 产生 SHCP 时钟,将 DS 上的数据串行移入移位寄存器。
3 、 产生 STCP 时钟,将移位寄存器里的数据送入存储寄存器。
4 、 将 OE 引脚置为低电平,存储寄存器的数据会在 Q0 Q7 并行输出,同时并行输出
的数据会被锁存起来。
(具体控制流程可参考野火的教程)

根据代码综合出来的RTL视图如下

程序设计如下:

(1)按键消抖程序

 (由于我所使用的开发板复位引脚默认为低电平,当复位按键按下按下时为高电平,因此我这里采用了if(sys_rst==1'b1)来判断是否复位)

module key
(
   input wire sys_clk,
	input wire sys_rst,
	input wire Key_1,
	input wire Key_2,
   output reg Key1,
	output reg Key2
);

parameter CNT_MAX = 20'd999_999; //计数最大值
reg [19:0] cnt1_20ms;//20ms计时
reg [19:0] cnt2_20ms;//20ms计时
/*按键1消抖*/
always@(posedge sys_clk or posedge sys_rst)
  begin
      if(sys_rst==1'b1)
		  cnt1_20ms<=20'd0;
		 else if(Key_1==1'b1)
		  cnt1_20ms<=20'd0;
		  else if((cnt1_20ms==CNT_MAX)&&(Key_1==1'b0))
		     cnt1_20ms<=CNT_MAX;
		    else
			  cnt1_20ms<=cnt1_20ms+20'd1;
  end
always@(posedge sys_clk or posedge  sys_rst)
begin
        if(sys_rst==1'b1)
		  Key1<=1'b0;
		  else if(cnt1_20ms==(CNT_MAX-20'd1))
		    Key1<=1'b1;
			 else 
			    Key1<=1'b0;
end
/*按键2消抖*/
always@(posedge sys_clk or posedge sys_rst)
  begin
      if(sys_rst==1'b1)
		  cnt2_20ms<=20'd0;
		 else if(Key_2==1'b1)
		  cnt2_20ms<=20'd0;
		  else if((cnt2_20ms==CNT_MAX)&&(Key_2==1'b0))
		     cnt2_20ms<=CNT_MAX;
		    else
			  cnt2_20ms<=cnt2_20ms+20'd1;
  end
always@(posedge sys_clk or posedge  sys_rst)
begin
        if(sys_rst==1'b1)
		  Key2<=1'b0;
		  else if(cnt2_20ms==(CNT_MAX-20'd1))
		    Key2<=1'b1;
			 else 
			  Key2<=1'b0;
end

(2) 数码管显示代码

  由于第一位74HC595用于选中数码管,其中Q4~Q7端,为防止数据在输出时产生错误,将sel设位8位有效数据,即sel <=  8'b1111_1111;如需改动选中数码管,只需对高位数字改动即可

module  seg_static
(
    input wire sys_clk,   
    input wire sys_rst,   
    input wire Key1,
	 input wire Key2,
    output reg [7:0] sel,   //数码管位选信号
    output reg [7:0] seg             //数码管段选信号
);

//十六进制数显示编码
parameter   SEG_0 = 8'b1100_0000,   SEG_1 = 8'b1111_1001,
            SEG_2 = 8'b1010_0100,   SEG_3 = 8'b1011_0000,
            SEG_4 = 8'b1001_1001,   SEG_5 = 8'b1001_0010,
            SEG_6 = 8'b1000_0010,   SEG_7 = 8'b1111_1000,
            SEG_8 = 8'b1000_0000,   SEG_9 = 8'b1001_0000;
parameter   IDLE  = 8'b1111_1111;   //不显示状态

reg     [3:0]   num=4'd0;  



always@(posedge sys_clk or posedge  sys_rst)
    if(sys_rst == 1'b1)
        num <= 4'd0;
    else    if((Key1 == 1'd1)&&(num == 4'd9))
        num <= 4'd0;
    else    if((Key2 == 1'd1)&&(num == 4'd0))
        num <= 4'd9;   
    else    if(Key1 == 1'd1)
        num <= num + 1'd1;
    else    if(Key2 == 1'd1)
        num <= num - 1'd1;
    else
        num <= num;


	
//sel:选中4个数码管
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        sel <=  8'b0000_0000;
    else
        sel <=  8'b1111_1111;

//给要显示的值编码
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        seg    <=  IDLE;
    else    case(num)
        4'd0:   seg    <=  SEG_0;
        4'd1:   seg    <=  SEG_1;
        4'd2:   seg    <=  SEG_2;
        4'd3:   seg    <=  SEG_3;
        4'd4:   seg    <=  SEG_4;
        4'd5:   seg    <=  SEG_5;
        4'd6:   seg    <=  SEG_6;
        4'd7:   seg    <=  SEG_7;
        4'd8:   seg    <=  SEG_8;
        4'd9:   seg    <=  SEG_9;
        default:seg    <=  IDLE ;  //闲置状态,不显示
    endcase

endmodule

(3)74HC595控制代码(此代码根据野火的控制代码进行改动,具体可参考野火的教程)

module  hc595_ctrl
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst   ,   
    input   wire    [7:0]   sel         ,   //数码管位选信号
    input   wire    [7:0]   seg         ,   //数码管段选信号
    
    output  reg             stcp        ,   //数据存储器时钟
    output  reg             shcp        ,   //移位寄存器时钟
    output  reg             ds             //串行数据输入
                
);

reg     [1:0]   cnt_4   ;   //分频计数器
reg     [3:0]   cnt_bit ;   //传输位数计数器


wire    [15:0]  data    ;   //数码管信号寄存


//将数码管信号寄存
assign  data = {sel,seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7]};//两片595 8位sel选中数码管



//分频计数器:0~3循环计数
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        cnt_4 <=  2'd0;
    else    if(cnt_4 == 2'd3)
        cnt_4 <=  2'd0;
    else
        cnt_4 <=  cnt_4 + 1'b1;

//cnt_bit:每输入一位数据加一
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        cnt_bit   <=  4'd0;
    else    if(cnt_4 == 2'd3 && cnt_bit == 4'd15)
        cnt_bit   <=  4'd0;
    else    if(cnt_4  ==  2'd3)
        cnt_bit   <=  cnt_bit   +   1'b1;
    else
        cnt_bit   <=  cnt_bit;

//stcp:16个信号传输完成之后产生一个上升沿
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        stcp    <=  1'b0;
    else    if(cnt_bit == 4'd15 && cnt_4 == 2'd3)
        stcp    <=  1'b1;
    else
        stcp    <=  1'b0;

//shcp:产生四分频移位时钟
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        shcp    <=  1'b0;
    else    if(cnt_4 >= 4'd2)
        shcp    <=  1'b1;
    else
        shcp    <=  1'b0;

//ds:将寄存器里存储的数码管信号输入即
always@(posedge sys_clk or  posedge sys_rst)
    if(sys_rst == 1'b1)
        ds  <=  1'b0;
    else    if(cnt_4 == 2'd0)
        ds  <=  data[cnt_bit];
    else
        ds  <=  ds;

endmodule

 (4)顶层模块代码

module key_seg
(
  input wire sys_clk,
  input wire sys_rst,
  input wire Key_1,
  input wire Key_2,
  output wire ds,
  output wire shcp,
  output wire stcp
);

wire Key1;
wire Key2;
wire [7:0] sel;
wire [7:0] seg;
key key_inst
(
   .sys_clk(sys_clk),
	.sys_rst(sys_rst),
	.Key_1(Key_1),
	.Key_2(Key_2),
   .Key1(Key1),
	.Key2(Key2)
);
seg_static seg_static_inst
(
    .sys_clk(sys_clk),   
    .sys_rst(sys_rst),   
    .Key1(Key1),
	 .Key2(Key2),
    .sel(sel),   
    .seg(seg)             
);
hc595_ctrl  hc595_ctrl
(
    .sys_clk(sys_clk),   
    .sys_rst(sys_rst),   
    .sel(sel),   
    .seg(seg),   
    .stcp(stcp),  
    .shcp(shcp),  
    .ds(ds)             
);


endmodule

实现效果如下: 

如有错误,欢迎指出! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值