首先声明一下,这个程序是一个名为: 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 |
|