找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11119|回复: 2
收起左侧

verilog学习笔记二:用状态机实现按键的消抖及按键按下次数计数

[复制链接]
ID:51025 发表于 2014-8-29 17:34 | 显示全部楼层 |阅读模式
module keytest (clk, key, ledout, weiout, key_count);

input clk;
input  [4:0] key;
output reg [7:0] ledout;
output reg [7:0] weiout;


parameter key_init  = 0;
parameter key_delay = 1;
parameter key_check = 2;

reg [15:0] state_clk_count;
output reg [3:0]  key_count ;
reg       state_clk;
reg [1:0]  key_state;
reg [4:0]  key_temp;


initial
begin
key_state = key_init;
state_clk = 0;
state_clk_count = 4'b0000;
key_count = 4'b0000;
key_temp = 4'b1111;
ledout = 7'b11000000;
weiout = ~8'b1000_0000;
end

//状态机时钟分频
always @(posedge clk)
begin
state_clk_count <= state_clk_count + 1;
if(state_clk_count == 16'hffff)
state_clk <= ~ state_clk;
end   

//状态机
always @(posedge state_clk )
begin
//key_temp  <= key;    //把键码的值赋给寄存器
case (key_state)
key_init :  
if(key == 5'b11111)
key_state <= key_init;
else
   key_state <=key_delay;
key_delay :
if(key == 5'b11111)
key_state <= key_init;
else
   key_state <=key_check;
key_check :
if(key == 5'b11111)    //释放按键后,按键计数+1
begin
key_count <= key_count +1;
key_state <= key_init;
end
else                    //按键按下时,维持此状态,直到按键释放
begin
key_state <= key_check;
key_temp  <= key;    //把键码的值赋给寄存器
end
default :
key_state <= key_init;
endcase
end


always @(state_clk or key_count)
begin
case (key_count)
4'b0001 : ledout = 7'b11111001; // 1
4'b0010 : ledout = 7'b10100100; // 2
4'b0011 : ledout = 7'b10110000; // 3
4'b0100 : ledout = 7'b10011001; // 4
4'b0101 : ledout = 7'b10010010; // 5
4'b0110 : ledout = 7'b10000010; // 6
4'b0111 : ledout = 7'b11111000; // 7
4'b1000 : ledout = 7'b10000000; // 8
4'b1001 : ledout = 7'b10010000; // 9
4'b1010 : ledout = 7'b10001000; // A
4'b1011 : ledout = 7'b10000011; // B
4'b1100 : ledout = 7'b11000110; // C
4'b1101 : ledout = 7'b10100001; // D
4'b1110 : ledout = 7'b10000110; // E
4'b1111 : ledout = 7'b10001110;
default : ledout = 7'b11000000;
endcase
end
endmodule


  程序如上,主要思想是将按键按下分为几个状态:初始化、延时消抖、按键检测,同时,为了实现按键的计数,在本程序中的key_check状态中,当按键按下时,把键值赋值给键值寄存器,等到按键松开后,才进行键值处理,这样可以较好的实现按键按下计数。当有几个按键时,程序如下所示,思想与上例相同。

module keytest (clk, key, ledout, weiout, key_id,key_count_o);

input clk;
input  [4:0] key;
output reg [7:0] ledout;
output reg [7:0] weiout;
output reg [3:0] key_id;   //键码
output reg [3:0] key_count_o; //连续按键次数


parameter key_init  = 0;    //按键状态
parameter key_delay = 1;
parameter key_check = 2;

reg [15:0] state_clk_count;  //状态机时钟分频计数
reg [3:0]  key_count [4:0];  //5个按键中,每个按键连续按键次数寄存  
reg       state_clk;        //状态机时钟
reg [1:0]  key_state;       //按键状态寄存
reg [4:0]  key_temp;        //键值寄存器


initial
begin
key_state = key_init;
state_clk = 0;
state_clk_count = 4'b0000;
key_id = 4'b0000;
key_temp = 4'b1111;
ledout = 7'b11000000;
weiout = ~8'b1000_0000;
end

//状态机时钟分频
always @(posedge clk)
begin
state_clk_count <= state_clk_count + 1;
if(state_clk_count == 16'hffff)
state_clk <= ~ state_clk;
end   

//状态机
always @(posedge state_clk )
begin
case (key_state)
key_init :  
if(key == 5'b11111)
key_state <= key_init;
else
   key_state <=key_delay;
key_delay :
if(key == 5'b11111)
key_state <= key_init;
else
   key_state <=key_check;
key_check :
if(key == 5'b11111)    //释放按键后,按键计数+1
begin
case (key_temp)
    5'b01111:
begin
key_id <= 1;
key_count_o <= key_count[0];
key_count[0] <= key_count[0] + 1;
key_count[1] <= 0;
key_count[2] <= 0;
key_count[3] <= 0;
key_count[4] <= 0;
end
    5'b10111:
begin
key_id <= 2;
key_count_o <= key_count[1];
key_count[1] <= key_count[1] + 1;
key_count[0] <= 0;
key_count[2] <= 0;
key_count[3] <= 0;
key_count[4] <= 0;
end
    5'b11011:
begin
key_id <= 3;
key_count_o <= key_count[2];
key_count[2] <= key_count[2] + 1;
key_count[0] <= 0;
key_count[1] <= 0;
key_count[3] <= 0;
key_count[4] <= 0;
end
    5'b11101 :
begin
key_id <= 4;
key_count_o <= key_count[3];
key_count[3] <= key_count[3] + 1;
key_count[4] <= 0;
key_count[0] <= 0;
key_count[1] <= 0;
key_count[2] <= 0;
end
    5'b11110 :
begin
key_id <= 5;
key_count_o <= key_count[4];
key_count[4] <= key_count[4] + 1;
key_count[0] <= 0;
key_count[1] <= 0;
key_count[2] <= 0;
key_count[3] <= 0;
end
    default :
begin
key_id <= 0;
key_count[0] <= 0;
key_count[1] <= 0;
key_count[2] <= 0;
key_count[3] <= 0;
key_count[4] <= 0;
key_count_o  <= 0;
end   
  endcase
   key_state <=key_init;
end
else                    //按键按下时,维持此状态,直到按键释放
begin
key_state <= key_check;
key_temp  <= key;    //把键码的值赋给寄存器
end
default :
key_state <= key_init;
endcase
end


always @(state_clk or key_count_o or key_id)
begin
case (key_count_o)  //数码管段选
4'b0001 : ledout = 7'b11111001; // 1
4'b0010 : ledout = 7'b10100100; // 2
4'b0011 : ledout = 7'b10110000; // 3
4'b0100 : ledout = 7'b10011001; // 4
4'b0101 : ledout = 7'b10010010; // 5
4'b0110 : ledout = 7'b10000010; // 6
4'b0111 : ledout = 7'b11111000; // 7
4'b1000 : ledout = 7'b10000000; // 8
4'b1001 : ledout = 7'b10010000; // 9
4'b1010 : ledout = 7'b10001000; // A
4'b1011 : ledout = 7'b10000011; // B
4'b1100 : ledout = 7'b11000110; // C
4'b1101 : ledout = 7'b10100001; // D
4'b1110 : ledout = 7'b10000110; // E
4'b1111 : ledout = 7'b10001110;
default : ledout = 7'b11000000;
endcase
   
    case (key_id)    //按照键码进行数码管位选
1 : weiout = ~8'b1000_0000;
2 : weiout = ~8'b0100_0000;
3 : weiout = ~8'b0010_0000;
4 : weiout = ~8'b0001_0000;
5 : weiout = ~8'b0000_1000;
default : weiout = ~8'b1000_0000;
endcase
end


回复

使用道具 举报

ID:163409 发表于 2020-1-4 00:14 | 显示全部楼层
您好,感谢您的分享,请问三段式状态机能实现延时检测吗?怎么实现?
回复

使用道具 举报

ID:704585 发表于 2020-3-25 16:59 | 显示全部楼层
感谢楼主分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表