打印

中断扫描键盘:状态机扫描矩阵键盘

[复制链接]
6120|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
cumtnj|  楼主 | 2008-12-26 12:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     首先声明一下,这个程序是一个名为: raosibin 的网友写的,拿到这里来分享一下,前面只见大家讨论状态机扫描,不见详细的程序,所以这个就暂且当个例程吧!

硬件描述:
1 数码管的段选和位选
/************************************************
段选:    P0.0-a          位选:   P0.0-wei1 
        P0.1-b         P0.1-wei2
        P0.2-c        P0.2-wei3
        P0.3-d        P0.3-wei4
        P0.4-e        P0.4-wei5
        P0.5-f        P0.5-wei6
        P0.6-g
        P0.7-dp          使用锁存器 74HC573 
*************************************************/
2 矩阵键盘接口
/**************************************
** ----------------------
** |    P34  P35  P36  P37
** |      |   |   |   |
** |P30   0   1   2   3
** |P31   4   5   6   7
** |P31   8   9   10  11
** |P33   12  13  14  15
** ----------------------
**************************************/

          keyscan.c 程序:

//1、该段程序是4*4键盘扫描程序,功能比较全,根据实际情况可在次基础上进行修改;
//2、已调试通过,但没有优化;

/*********************************************************************
文件:scankey.c
用途:通用4*4按键接口驱动程序
注意:
创建:2008.5.15
修改:2008.5.15                     //cumtnj 移植 2008-12-06
设计:raosibin
版本:ver1.3
功能说明:每隔10MS调用一次 ,实现了组合按键功能;连发功能    
**********************************************************************/
#include "keyscan.h"

uchar flag_2ms;
uchar flag_key_scan_timer_10ms;
uchar key_scan_timer;

uchar key_return = No_key;
uchar key_state = 0;

/************************************************
段选:    P0.0-a          位选:   P0.0-wei1 
        P0.1-b                     P0.1-wei2
        P0.2-c                    P0.2-wei3
        P0.3-d                    P0.3-wei4
        P0.4-e                    P0.4-wei5
        P0.5-f                    P0.5-wei6
        P0.6-g
        P0.7-dp          使用锁存器 74HC573 
*************************************************/

sbit dula=P2^6;   //P0 口复用 ,既当段选 ,又作位选
sbit wela=P2^7;

uchar code table[]=
{0x3F,0x06,0x5B,0x4F,0x66,            //0,1,2,3,4
 0x6D,0x7D,0x07,0x7F,0x6F,            //5,6,7,8,9
 0x77,0x7C,0x39,0x5E,0x79,0x71,0x00}; //a,b,c,d,e,f,No_key

/*************************************
文件:timer0_int.c
用途: 定时器TIMER0,比较匹配中断
创建:2008.5.10
修改:2008.5.10
设计:raosibin
版本:ver1.0
功能说明:定时基本单位为2MS
外部4M晶振;timer0_init()初始化函数;
***************************************/
//TIMER0 initialize 
// desired value: 2mSec

void Time0_init()   //定时器0初始化
{
    TMOD=0x01;     //定时器T0,工作在方式1
    IE =0x82;      //允许定时器T0中断
    TH0=0xF8;
    TL0=0x30;      //12M晶振,定时2ms
}

/**********************************************************
在定时中断涵数内置各种与时间有关的标志位;
flag_2ms: 2ms定时时间,每位数显时间;
flag_key_scan_timer_10ms:每隔10ms扫描键盘;
flag_timer_1s: 秒时间标志;
key_scan_timer:
timer_counter:
**********************************************************/

void Time0_int() interrupt 1  //定时器0中断子函数
{
      TH0=0xF8;
     TL0=0x30;      //12M晶振,定时2ms    
     flag_2ms = 1;
     if (++key_scan_timer >= 5)
     {
        key_scan_timer = 0;
        flag_key_scan_timer_10ms = 1;
     }
}

void display_key( uchar key )
{
    dula=0;
    P0=table[key];  //显示千位
    dula=1;         //从0到1,有个上升沿,解除锁存,显示相应段 
    dula=0;         //从1到0再次锁存
         
    wela=0;
    P0=0xfe;
    wela=1;
    wela=0;
}

/*********************************************************************
       COL0 COL1 COL2 COL3     
* ROW4   0    1    2    3
* ROW5   4    5    6    7
* ROW6   8    9    A    B
* ROW7   C    D    E    F

       P3.4 P3.5 P3.6 P3.7   
* P3.0   0    1    2    3
* P3.1   4    5    6    7
* P3.2   8    9    A    B
* P3.3   C    D    E    F

* 普通按键:
* 2~E:数字按键

* 连发按键:
* 0:
* 1:
*   
* 组合按键
* F  : 引导键
* F+0:
* F+1:
* 状态机思路
* F为组合引导键
* 每10MS扫描一次键盘  

* 状态0:检测是否有键按下;  
* 状态1:消抖处理  
* 状态2:确认按键,同状态1比较,除F引导键外相同则返回键值,
*       F引导键未按下转状态3;F引导键按下转状态5;  
* 状态3:计数1,(按3S,连发判断);  
* 状态4:计数2,连发功能 

* 状态5:500MS内检测是否有F引导键+其他键按下;有转状态6,没有转状态8 
* 状态6:消抖处理 
* 状态7:同状态6比较,确认组合按键,返回组合键值,转状态3 
* 状态8:消抖处理 
* 状态9:先导键为单按键,返键值,转状态3. 
**********************************************************************/
//按键初始化
void key_board_int1(void)
{
//    KEY_DDR_ROW   |= KEY_ROW_ALL;                                 //行输出,对于AVR单片机需要对IO口进行配置,而51的IO都是双向的
    KEY_PORT_ROW &=~ KEY_ROW_ALL;
//    KEY_DDR_COL   &=~ KEY_COL_ALL;                                 //列输入,并使能内部上拉
    KEY_PORT_COL |= KEY_COL_ALL;
}

void key_board_int2(void)                                          //翻转初始化
{
//    KEY_DDR_ROW   &=~ KEY_ROW_ALL;                                 //行输入,并使能内部上拉
    KEY_PORT_ROW |= KEY_ROW_ALL;
//    KEY_DDR_COL   |= KEY_COL_ALL;                                 //列输出;
    KEY_PORT_COL &=~ KEY_COL_ALL;
}
//按键扫描
uchar key_board_scan(void)
{
    uchar key_value_buf;
    key_board_int1();
    KEY_PORT_ROW &=~ KEY_ROW_ALL;                                                        // 必须送2次!!!!!很关键
    key_value_buf = (~KEY_ROW_ALL) & KEY_PIN_COL;                                        // 读列电平
                                
    key_board_int2();                                                                        
    KEY_PORT_COL  &=~ KEY_COL_ALL;
    key_value_buf |= (~KEY_COL_ALL) & KEY_PIN_ROW;                                       // 读行电平,并同翻转前的结果或
    return key_value_buf;
}

uchar read_keyboard()
{        
static uchar key_value1,key_value2,key_value3,key_value4,key_value5;
static uchar key_value6,key_value7,key_value8,key_value9;
static uint  key_time = 0;
       
     switch (key_state)
    {
         case 0:                                                                         // 判断是否有键按下
                if (key_board_scan() != No_key)
                {
                        key_state = 1;                                           // 有按键
                        break;                                                 // 转消抖确认状态
                }
                break;
                        
         case 1:                                                                         // 消抖处理
                if (++key_time >= 1)                                                  // 改变判断条件可改变键盘灵敏度
                 {           
                    key_value1 = key_board_scan();
                    if (key_value1 != No_key)
                    {
                        key_state = 2;
                        break;
                    }
                    key_state = 0;
                    break;
                }
                break;
                    
          case 2:                                                                              // 确认按键,同状态1比较
                  key_value2 = key_board_scan();                                                         
                if (key_value2 == key_value1)                                                  // 再次扫描,
                {        
                            if (key_value2 !=0x77)                          // 是否为引导键F,是则转状态5
                            {
                                switch (key_value2)                          // 与状态1的相同,确认按键
                                {                                             // 键盘编码,返回编码值 
                                        case  0xee:
                                                key_return = K1_1;                // 0;
                                                break;
                                        case  0xde:
                                                key_return = K1_2;                // 1
                                                break;
                                         case 0xbe:
                                                key_return = K1_3;
                                                break;
                                         case 0x7e:
                                                key_return = K1_4;
                                                break;
                                         case 0xed:
                                                key_return = K2_1;
                                                break;
                                        case  0xdd:
                                                key_return = K2_2;
                                                break;
                                         case 0xbd:
                                                key_return = K2_3;
                                                break;
                                         case 0x7d:
                                                key_return = K2_4;
                                                break;
                                        case  0xeb:
                                                key_return = K3_1;
                                                break;
                                         case 0xdb:
                                                key_return = K3_2;
                                                break;
                                         case 0xbb:
                                                key_return = K3_3;
                                                break;
                                        case  0x7b:
                                                key_return = K3_4;
                                                break;
                                        case  0xe7:
                                                key_return = K4_1;
                                                break;
                                         case 0xd7:
                                                key_return = K4_2;
                                                break;
                                         case 0xb7:
                                                key_return = K4_3;
                                                break;
                                           default:
                                                   break;
                                 }
                                 key_state = 3;                                     // 转入等待按键释放状态
                                 key_time = 0;                                      // 清0按键时间计数器
                                     break;
                            }
                            else
                            {
                                    key_state = 5;
                                 key_time = 0;
                                 break;
                            }
                }                
                else
                    key_state = 0;                                                // 两次列电平不同返回状态0,(消抖处理)
                    break;        
                                                        
        case 3:                                                                                   // 计数1,(按3S,连发判断)                  
                key_value3 = key_board_scan();                
                if ( key_value3 == No_key)
                        key_state=0;                                                    // 按键已释放,转换到按键初始态
                else if ((key_value3 == 0x7e)||(key_value3 == 0xed))          // 改变此判断条件,可以确定
                {                                                                       // 哪些键具备加速功能,3,4加速
                    if (++key_time >= 300)                                               // 按键时间计数
                      {
                            key_state = 4;                                                  // 按下时间>3s,状态转换到计时2
                            key_time = 0;                                                 // 清按键计数器
                            key_return = 18;                                             // 输出'18'
                      }
                        break;
                 }
                break;
                        
        case 4:                                                                                         // 计数2,连发功能           
                   key_value4 = key_board_scan();                   
                   if ( key_value4 == No_key)
                    key_state=0;                                                     // 按键已释放,转换到按键初始态
                else if (++key_time >= 20)                                                // 按键时间计数
                {
                    key_time = 0;                                                 // 清按键计数器
                    key_return = 18;                                             // 输出'18'
                    break;
                }
                  break;
          
        case 5:                                                               // 检测是否有F引导键+其他键按下;有转状态6,没有转状态8
                key_value5 = key_board_scan();           
                if (key_value5 == key_value2)
                {
                      if (++key_time >= 50)                                               // 500MS到了吗?未到,状态5,不断扫描,到了,状态8
                    {
                      key_state = 8;
                      key_time = 0;
                      break;
                    }
                        break;
                }
                else
                {
                    key_state = 6;
                    break;
                }
                break;
        case 6:                                                               // 消抖处理
                key_value6 = key_board_scan();
                key_state = 7;
                break;
        case 7:                                                              // 同状态6比较,确认组合按键,返回组合键值,转状态3
                key_value7 = key_board_scan();
                if (key_value7 == key_value6)
                {
                    switch (key_value7)
                    {
                        case 0x66:
                                    key_return = KF_0;                   // 组合按键F+0
                                    break;
                        case 0x56:
                                      key_return = KF_1;                     // 组合按键F+1
                                    break;                
                    }
                    key_state = 3;
                  }
                break;
        case 8:                                                            //  消抖处理
                key_value8 = key_board_scan();
                key_state = 9;
                break;        
        case 9:                                                             // 同状态8比较,确认单按键,返键值,转状态3
                key_value9 = key_board_scan();
                if (key_value9 == key_value8)
                {
                    key_return = K4_4;
                    key_state = 3;
                    break;
                }
                key_state = 0;
                  break;
          default:
                break;
    }
    return key_return;
}

int main()
{
  uchar dis_num;
  dula=0;
  wela=0;
  Time0_init();          // 定时器0初始化
  TR0=1;
  P1=0xff;
  while(1)
   {         
        if (flag_key_scan_timer_10ms == 1)
         {
             flag_key_scan_timer_10ms = 0;
            switch (read_keyboard())
             {
                 case 0xff:
                display_key(16);
                     break;
                 case 0x0f:
                         display_key(15);
                     break;
                 default:
                dis_num=read_keyboard();
                display_key(dis_num);
                     break;
            }
         }
   }
}

        头文件  keyscan.h

/************************************************
文件:scankey.h
用途:4*4按键接口驱动程序,可在此基础上裁剪
注意:
创建:2008.5.8
修改:2008.5.8
设计:raosibin
版本:ver1.0
功能说明:每隔10MS调用一次
************************************************/
#include "reg52.h"

/**************************************
** ----------------------
** |    P34  P35  P36  P37
** |      |   |   |   |
** |P30   0   1   2   3
** |P31   4   5   6   7
** |P31   8   9   10  11
** |P33   12  13  14  15
** ----------------------
**************************************/

#ifndef __KEYSCAN_h__
#define __KEYSCAN_h__   1

#define uchar unsigned char
#define uint unsigned int


//按键端口定义
//#define KEY_DDR_COL   DDRA
#define KEY_PORT_COL   P3
#define KEY_PIN_COL    P3

//#define KEY_DDR_ROW   DDRA
#define KEY_PORT_ROW   P3
#define KEY_PIN_ROW    P3

/*
#define WAIT_KEY_UP     1
//每行每列的按键数
#define PER_POW         4
#define PER_COL         4

//按键声
#define BEEP_EN      1 //使用蜂鸣器
#define BEEP_BIT     0 //PD0接蜂鸣器
//#define BEEP_DDR     DDRD
#define BEEP_PORT    PORTD*/


//行连接所对应的IO口
#define KEY_ROW_0     0
#define KEY_ROW_1     1
#define KEY_ROW_2     2
#define KEY_ROW_3     3
#define KEY_ROW_ALL   ((1<<KEY_ROW_0)|(1<<KEY_ROW_1)|(1<<KEY_ROW_2)|(1<<KEY_ROW_3))

//列连接所对应的IO口
#define KEY_COL_0     4
#define KEY_COL_1     5
#define KEY_COL_2     6
#define KEY_COL_3     7
#define KEY_COL_ALL   ((1<<KEY_COL_0)|(1<<KEY_COL_1)|(1<<KEY_COL

相关帖子

沙发
sz_kd| | 2008-12-26 12:54 | 只看该作者

路过看看

使用特权

评论回复
板凳
hab2000| | 2008-12-26 12:54 | 只看该作者

真长

使用特权

评论回复
地板
ljm810010| | 2008-12-26 18:41 | 只看该作者

这么长够做整个项目了

使用特权

评论回复
5
ayb_ice| | 2008-12-26 19:36 | 只看该作者

真长

使用特权

评论回复
6
xin289379297| | 2011-4-7 15:27 | 只看该作者
08年的帖子,不知道现在问还有人回答么?

使用特权

评论回复
7
xin289379297| | 2011-4-7 15:28 | 只看该作者
#define KEY_COL_ALL   ((1<<KEY_COL_0)|(1<<KEY_COL_1)|(1<<KEY_COL这是什么意思?

使用特权

评论回复
8
老鱼探戈| | 2011-5-28 17:16 | 只看该作者
有点长...

使用特权

评论回复
9
fm0597| | 2011-5-28 17:25 | 只看该作者

使用特权

评论回复
10
流行音乐| | 2011-5-29 00:20 | 只看该作者
看明白了,这是一种比较少见的扫描方法,不是逐行或逐列扫描的方法。我更喜欢用逐行或逐列扫描的方法。

使用特权

评论回复
11
klyzh2003| | 2011-5-29 10:10 | 只看该作者
有一定借鉴价值!

使用特权

评论回复
12
ICwater| | 2011-6-1 12:56 | 只看该作者
不算很长吧~~

使用特权

评论回复
13
红金龙吸味| | 2011-6-1 18:28 | 只看该作者
反转法。

使用特权

评论回复
14
ouluolin| | 2011-6-18 21:43 | 只看该作者
红金龙吸味也来了?顶一个

使用特权

评论回复
15
wangkangming| | 2012-6-4 13:54 | 只看该作者
很好。我来的晚不晚啊,哈哈

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

78

帖子

0

粉丝