verilog矩阵键盘模块(粗糙版本,未挂到Avalon总线上)

Posted on 2009-12-02 22:36  Herway  阅读(770)  评论(0编辑  收藏  举报

模块:4x4矩阵键盘

功能:4x4矩阵键盘识别与编码.可以实现双键识别,以及同行的多键(两个以上)识别.不能去除"井字形"的误判断现象.可以实现重复触发,即类似电脑键盘的长按功能,可以用参数控制长按时间与长按后的触发频率.键盘每次键值改变都会产生中断信号给nios,具体处理由软核判断处理.

说明:这个模块是应学长的要求,想做成模块化的东西,前段时间做了一下,这个是比较简单的版本,没有做时序的优化,也没有挂到Avalon总线上去.只是实现了当初提出的要求功能.如果只是应用,是可以了的,但是还有很大的改进空间.最近有别的事情,等忙完了,再改进改进,写个文档挂上来.

1.矩阵键盘电路原理图:

 

2.verilog源程序:

 

 

key4x4
module KEYTEST_01(
    
//input
    clk,        //50M时钟
    rst_n,        //复位
    col,        //列输入4bits
    
//output
    row,        //行扫描4bits
    interrupt,    //中断信号
    value        //键值16bits
    );
    
input clk,rst_n;
input [3:0]col;
output interrupt;
output [3:0]row;
output [15:0]value;

//可调参数列表
parameter 
DUP_PERIOD_CNT
=32'd10000000,    //长按后产生重复中断时间间隔(默认为500x1ms=200ms)
DUP_JUDGE_CNT=32'd300000000;    //长按与否判断阈值(默认为5000x1ms=3s)


reg [5:0]cnt_clk_scan; //400kHz扫描时钟计数器
reg [20:0]cnt_debounce; //20ms去抖计数器
always@(posedge clk)
begin 
    cnt_clk_scan
<=cnt_clk_scan+1;
    cnt_debounce
<=cnt_debounce+1'b1;
end

reg clk_scan; //产生扫描时钟信号
always@(posedge clk)
    
if (cnt_clk_scan==6'h3f) begin clk_scan<=~clk_scan; end
    else clk_scan<=clk_scan;

reg [1:0]state; //状态机状态寄存器
reg [3:0]row,col1,col2,col3; //行列寄存器
reg [15:0]value_reg0; //实时采集的键值
always@(posedge clk_scan or negedge rst_n)
begin     
    
if(!rst_n)
    
begin 
    state
<=0; row<=4'b0111; value_reg0<=16'hffff; 
    
end
    
else
    
case (state)
    
0:  
        
begin
        col1
<=col[3:0];       //填第一行(1-4位)数据
        state<=1; row<=4'b1011;    //扫描第二行
        end 
    
1:
        
begin
        col2
<=col[3:0];       //填第二行(5-8位)数据
        state<=2; row<=4'b1101;    //扫描第三行
        end 
    
2:
        
begin
        col3
<=col[3:0];       //填第三行(9-12位)数据
        state<=3; row<=4'b1110;    //扫描第四行
        end 
    
3:
        
begin
        value_reg0
<={col1,col2,col3,col[3:0]};       //填第四行(13-16位)数据
        state<=0; row<=4'b0111;    //扫描第一行
        end 
    
default :
        
begin state<=0; row<=4'b0111; value_reg0<=value_reg0; end
       endcase    
       
end

reg [15:0]value_reg1; //延时20ms去抖
always@(posedge clk or negedge rst_n)
begin
    
if (!rst_n)    value_reg1<=16'hffff;
    else
    
if (cnt_debounce==20'hfffff) value_reg1<=value_reg0;
    else value_reg1<=value_reg1; 
end

reg [15:0]value; //去抖后的键值
always@(posedge clk or negedge rst_n)
begin
    
if (!rst_n) value<=16'hffff;
    else
    
if (value_reg1==value_reg0) value<=value_reg1;
    
else value<=value;
end

reg [15:0]value_pre; //前一键值寄存器
reg interrupt_state; //状态改变中断信号
reg [31:0]dup_judge_cnt;
reg dup_inte_en;
always@(posedge clk or negedge rst_n)
begin
    
if(!rst_n) 
    
begin value_pre<=16'hffff; interrupt_state<=1'b0; dup_judge_cnt<=32'd0; dup_inte_en<=1'b0;end 
    
else
    
begin
    
    value_pre
<=value;
    
if (value_pre!=value) 
    
begin interrupt_state<=1'b1; dup_inte_en<=1'b0; dup_judge_cnt<=0end
    
else if ((value==value_pre)&&(value==16'hffff))
    begin interrupt_state<=1'b0; dup_inte_en<=1'b0; dup_judge_cnt<=0end
    
else if (dup_judge_cnt>=DUP_JUDGE_CNT)
    
begin interrupt_state<=1'b0; dup_inte_en<=1'b1; dup_judge_cnt<=dup_judge_cnt; end
    
else 
    
begin interrupt_state<=1'b0; dup_inte_en<=1'b0; dup_judge_cnt<=dup_judge_cnt+1'b1; end
    
    
end
end

//长按后的重复中断产生模块
reg [31:0]dup_period_cnt;
reg interrupt_dup;
always@(posedge clk or negedge rst_n)            
begin
    
if (rst_n==1'b0)    
    begin     
        dup_period_cnt
<=32'd0;    //重复时间间隔计数器
        interrupt_dup<=1'b0;    //长按重复中断
    end
    
    
else if (dup_inte_en==1'b1)
        if (dup_period_cnt<=DUP_PERIOD_CNT) 
        
begin dup_period_cnt<=dup_period_cnt+1'b1; interrupt_dup<=1'b0; end
        
else 
        
begin dup_period_cnt<=0; interrupt_dup<=1'b1; end
    else begin dup_period_cnt<=0; interrupt_dup<=1'b0; end
end

assign interrupt=interrupt_state||interrupt_dup;


endmodule

 

3.nios软核c程序(根据中断读键值):

 

key4x4_c
#include <stdio.h>
#include 
"system.h"
#include 
"altera_avalon_pio_regs.h"
#include 
"alt_types.h"
#include 
"sys/alt_irq.h"
#include 
"priv/alt_busy_sleep.h"

volatile alt_u32 done = 0;                   // 信号量:通知外部中断

事件发生

static void KeyDown_interrupts(void* context, alt_u32 id)

   
/* 清中断捕获寄存器 */
   IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_INTE_BASE, 
0);
   
/* 通知外部有中断事件发生 */
   done
++;
}

void InitPIO(void)

/* 初始化LED_PIO为输出,KEY为输入 */
IOWR_ALTERA_AVALON_PIO_DIRECTION(PIO_INTE_BASE, 
0x00);
/* 开KEY的中断 */
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_INTE_BASE, 
0xff);
/* 清边沿捕获寄存器 */ 
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_INTE_BASE, 
0x00);
/* 注册中断服务子程序 */
alt_irq_register(PIO_INTE_IRQ, NULL, KeyDown_interrupts); 
}

   
int main()
{
    
int value1,value2,i;
    
    InitPIO();
    
       
while(1
    { 
       
if(done != 0)
       {
          
/* 中断事件数量减1 */
       done
--;
       value1
=IORD_16DIRECT(PIO_VALUE_BASE,0);  
       printf(
"%d   ",value1);
       }
          
          
else;
       
    }
}

 

 

先记这么多,以后再完善...

Copyright © 2024 Herway
Powered by .NET 8.0 on Kubernetes