- /*---------------------------------------------------------
- 零耗时键盘各种事件及消抖处理模板裸奔程序详解
- 雁塔菜农HotPower 2006.12.22 于菜地www.HotPower.21ic.org/
- ---------------------------------------------------------*/
- #include <LPC213xdef.h>//包含213x自定义结构指针写法头文件
- //注意:可将LPC213XDEF.H拷贝到Keil\ARM\INC\PHILIPS目录下
- /*---------------------------------------------------------
- 时钟参数定义
- ---------------------------------------------------------*/
- #define Fosc 11059200
- #define Fcclk (Fosc * 5)
- #define Fcco (Fcclk * 5)
- #define Fpclk (Fcclk / 5) * 1
- /*---------------------------------------------------------
- 键盘接口定义
- ---------------------------------------------------------*/
- #define KEYPORT P1//KEY在P1口
- #define KEY0 P1_16//
- #define KEY1 P1_17//
- #define KEY2 P1_18//
- #define KEY3 P1_19//
- /*---------------------------------------------------------
- 申请系统变量
- ---------------------------------------------------------*/
- volatile signed int KeyPressCount[4];//申请压键20mS计数器数组
- volatile signed int KeyDblCount[4];//申请键值计数器数组
- /*---------------------------------------------------------
- 系统函数声明
- ---------------------------------------------------------*/
- void SystemInit(void);//系统初始化
- void VicInit(void);//向量中断初始化
- void PortInit(void);//IO初始化
- void Timer0Init(void);//定时器初始化
- void KeyInit(void);//键盘初始化
- void VicIntSetup(void);//向量中断设置
- void KeyCommandExec(unsigned int, unsigned int);//执行键盘命令
- void IRQ_Timer0 (void) __irq;//定时器0中断
- /*---------------------------------------------------------
- 键盘事件处理函数声明
- ---------------------------------------------------------*/
- void Key00(void);//放键事件
- void Key01(void);
- void Key02(void);
- void Key03(void);
- void Key10(void);//短压键事件
- void Key11(void);
- void Key12(void);
- void Key13(void);
- void Key20(void);//双击键事件
- void Key21(void);
- void Key22(void);
- void Key23(void);
- void Key30(void);//长键事件
- void Key31(void);
- void Key32(void);
- void Key33(void);
- void Key0_1(void);//组合键事件
- void Key1_2(void);
- void Key2_3(void);
- void Key3_0(void);
- /*----------------------------------------------------------------------
- “零耗时键盘”介绍:
- “零耗时”并非不耗时。它主要是将原本需要延时消除键盘抖动的时间转化为
- 对定时器的计数来替代。这样就可将节约的时间用于对其他事件的处理。
- “零耗时”键盘程序的编写很简单,首先要做到:
- 1.用总键盘个数除消除键盘抖动的时间20mS.本例有4个键,即20mS/4=5mS.
- 所以,定时器0中断时间常数应该定义为5mS.
- 2.设置1个压键20mS计数器数组KeyPressCount[]。用于对各键盘的压键次数计数。
- 由于全部键盘扫描需要20mS,故KeyPressCount[]内的值为20mS的倍数。
- 3.设置1个键扫描位置计数器KeyCount,用于记录当前键扫描的位置。
- 注意键扫描函数KeyScan()每次只扫描1个键(本例即为IRQ_Timer0())。
- 4.设置1个键扫描键值计数器数组KeyDblCount[],用于记录键值以处理双击状态。
- 本例主要讲解“零耗时”键盘程序的编写,一般不主张在MCU系统下用双击键。
- 多建议采用长压键来替代双击键。
- 特别注意:
- “零耗时”键盘程序属于“扫而不描”类型,即每次只扫描1个键而不管其他键
- 的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。
- 键扫描位置计数器KeyCount的值就是键盘扫描结果的键值。故也改进了经典的
- 键扫描函数KeyScan()需要逐次扫描的缺点
- “零耗时”键盘程序只区分键释放,单击键,双击键 和长压键4种基本事件。
- 区分只简单地判别KeyPressCount[]的个数即可。
- 1.当无键压下且KeyPressCount[]减到0时,可判别为键释放事件发生。
- 2.当有键压下且KeyPressCount[]=2时,可认为键已经经过20mS消抖处理,
- 即单击键事件发生。
- 如果需要双击键处理,则需要附加KeyDblCount[]双击键计数器数组。
- 3.当有键压下且KeyPressCount[]=3*50时,即3*50*20mS=3S时,认为3S长压键事件发生。
- 对“零耗时键盘”的个人应用总结:
- 在MCU的裸奔中,“零耗时键盘”很容易构成一个基于时间片小型的操作系统。
- 它可以“并行地”处理多个键盘事件及任务。
- 它的节拍不是OS常用的10mS,而是20mS消抖时间的1/N份。
- 由于20mS也做为视觉暂留的时间基准,故在常用的LED+KEY系统中裸奔表现很不错。
- 如果每个任务都能保证在20mS/N内完成,那么后台程序可以废除,即主程序只是个
- 死循环。这在低功耗系统中应用很广。
- ----------------------------------------------------------------------*/
- /*----------------------------------------------------------------------
- 定时器0中断作为键盘扫描和键盘消抖处理及命令执行
- ----------------------------------------------------------------------*/
- void IRQ_Timer0 (void) __irq
- {
- const static unsigned int KeyTab[4] = {//定义FLASH数组
- KEY0, KEY1, KEY2, KEY3
- };
- static unsigned int KeyCount = 0;
- unsigned int i;
- KeyCount &= 0x03;//只有4个键KEY0~KEY3(注意每次只扫描1个键)
- if (KEYPORT->IOPIN & (1 << KeyTab[KeyCount])) {//高电平压键无效
- if (KeyPressCount[KeyCount] > 0) {
- KeyPressCount[KeyCount] = -2;//键释放也需消除键盘抖动至少20mS
- }
- else if (KeyPressCount[KeyCount] < 0) {
- KeyPressCount[KeyCount] ++;
- if (KeyPressCount[KeyCount] == 0) {//键释放
- KeyCommandExec(0, KeyCount);//键释放
- }
- }
- }
- else {
- KeyPressCount[KeyCount] ++;//
- if (KeyPressCount[KeyCount] == 2) {//单击键刚满20mS
- if (KeyDblCount[KeyCount] != KeyCount) {
- KeyCommandExec(1, KeyCount);//单击压键
- for (i = 0; i < 4; i ++ ) {
- if (i == KeyCount) {
- KeyDblCount[i] = KeyCount;//设置单击标志
- }
- else {
- KeyDblCount[i] = -1;//摧毁其他键单击标志
- }
- }
- }
- else {
- KeyCommandExec(2, KeyCount);//双击压键
- for (i = 0; i < 4; i ++ ) {
- if (i == KeyCount) {
- KeyDblCount[i] = 0x80 + KeyCount;//设置双击标志
- }
- else {
- KeyDblCount[i] = -1;//摧毁其他键双击标志
- }
- }
- }
- }
- else if (KeyPressCount[KeyCount] >= 3 * 50) {//3S长压键
- KeyCommandExec(3, KeyCount);//长压键
- KeyDblCount[KeyCount] = -1;//清除单击压键
- KeyPressCount[KeyCount] = 3;//避开单击键以实现多次长压键事件处理
- }
- }
- KeyCount ++;
- T0->TIMER_IR = 0x01; //清除中断标志
- VIC->VectAddr = 0x00; //通知VIC中断处理结束
- }
- /*----------------------------------------------------------------------
- 系统初始化函数
- ----------------------------------------------------------------------*/
- void SystemInit(void)
- {
- VicInit();
- PortInit();
- KeyInit();
- Timer0Init();
- VicIntSetup();
- }
- /*----------------------------------------------------------------------
- 向量中断初始化函数
- ----------------------------------------------------------------------*/
- void VicInit(void)
- {
- volatile unsigned int start;
- VIC->IntEnable = 0;//关闭全部中断
- VIC->SoftIntClr = 0xffffffff;//清除所有软中断标志
- VIC->IntSelect = 0;//全部中断为IRQ中断或默认中断
- for (start = 0; start < 10000; start ++);//系统延时等待接口稳定
- }
- /*----------------------------------------------------------------------
- IO初始化函数
- ----------------------------------------------------------------------*/
- void PortInit(void)
- {
- PINSEL->PIN_SEL0 = 0x00000000;//设置管脚连接GPIO
- PINSEL->PIN_SEL1 = 0x00000000;//设置管脚连接GPIO
- P0->IODIR = 0x00000000;//设置P0口为输入
- P1->IODIR = 0x00000000;//设置P1口为输入
- }
- /*----------------------------------------------------------------------
- 定时器初始化函数
- ----------------------------------------------------------------------*/
- void Timer0Init(void)
- {
- T0->TIMER_TC = 0; //时器设置为0
- T0->TIMER_PR = 0; //时钟不分频
- T0->TIMER_MCR = 0x03; //设置T0MR0匹配后复位T0TC,并产生中断标志
- T0->TIMER_MR0 = Fpclk / 200; //5mS定时
- T0->TIMER_TCR = 0x01; //启动定时器
- T0->TIMER_IR = 0x01; //清除中断标志
- }
- /*----------------------------------------------------------------------
- 向量中断设置函数
- ----------------------------------------------------------------------*/
- void VicIntSetup(void)
- {
- /* 设置定时器0中断IRQ */
- VIC->VectCntls[0] = VICIntSel_Enable | VICIntSel_Time0;//设置定时器0中断通道分配最高优先级
- VIC->VectAddrs[0] = (unsigned int)IRQ_Timer0;//设置中断服务程序地址
- VIC->IntEnable |= (1 << VICIntSel_Time0);//使能定时器0中断
- }
- /*----------------------------------------------------------------------
- 键盘初始化函数
- ----------------------------------------------------------------------*/
- void KeyInit(void)
- {
- unsigned int i;
- KEYPORT->IODIR &= ~((1 << KEY0) | (1 << KEY1) | (1 << KEY2) | (1 << KEY3));// 设置KEY控制口为输入
- for(i = 0; i < 4;i ++) {//4个键KEY0~KEY3
- KeyPressCount[i] = 0;//清除压键20mS计数器数组,默认无键压下
- KeyDblCount[i] = -1;//清除单击压键
- }
- }
- /*----------------------------------------------------------------------
- 执行键盘命令函数
- ----------------------------------------------------------------------*/
- void KeyCommandExec(unsigned int CommMode, unsigned int CommTask)
- {
- typedef void (* PV)(void);//函数指针
- const static PV KeyCommandArray[4][4] = {//二维函数数组指针阵列表(散转命令地址表)
- {Key00, Key01, Key02, Key03},
- {Key10, Key11, Key12, Key13},
- {Key20, Key21, Key22, Key23},
- {Key30, Key31, Key32, Key33}
- };
- PV func;//声明函数指针
- func = KeyCommandArray[CommMode][CommTask];//从FLASH中取出键盘放事件处理表
- func();//运行KeyX0()~KeyX3()
- }
- int main(void)
- {
- SystemInit();//系统初始化
- while(1) {
- POWER->P_CON = 1;//待机
- __nop();
- __nop();
- __nop();
- }
- }
- void Key00(void)//键释放事件
- {
- if (KeyDblCount[0] == 0x80) {
- //在此添加双击键释放事件处理
- __nop();
- }
- else {
- //在此添加单击键释放事件处理
- __nop();
- }
- }
- void Key01(void)
- {
- //在此添加释放事件处理(不管单双击键)
- __nop();
- }
- void Key02(void)
- {
- }
- void Key03(void)
- {
- }
- void Key10(void)//单击键事件
- {
- if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
- Key0_1();
- }
- else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件
- Key3_0();
- }
- }
- void Key11(void)
- {
- if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
- Key0_1();
- }
- else if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件
- Key1_2();
- }
- }
- void Key12(void)
- {
- if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
- Key1_2();
- }
- else if (KeyPressCount[3] >= 2) {//在KEY3也压下时执行组合键事件
- Key2_3();
- }
- }
- void Key13(void)
- {
- if (KeyPressCount[2] >= 2) {//在KEY2也压下时执行组合键事件
- Key2_3();
- }
- else if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
- Key3_0();
- }
- }
- void Key20(void)//双击键事件
- {
- }
- void Key21(void)
- {
- }
- void Key22(void)
- {
- }
- void Key23(void)
- {
- }
- void Key30(void)//长压键事件
- {
- }
- void Key31(void)
- {
- }
- void Key32(void)
- {
- }
- void Key33(void)
- {
- }
- /*----------------------------------------------------------------------
- “零耗时键盘”对组合键的处理方法:
- “零耗时键盘”的一个重要的方法是对键盘“扫而不描”。即每次只扫描1个键而不
- 管其他键的状态。这样就可在一定的时间范围内“并行”地处理多个键盘事件。
- 所谓”组合键”即为在1个键压下后再发生其他键压下的键盘事件。最简单的是
- 有序组合键即有压键顺序的组合键。
- 例先压KEY0再压KEY1,这样我们就可只对单击键事件Key11()做出相应处理即可。
- void Key11(void)
- {
- if (KeyPressCount[0] >= 2) {//在KEY0也压下时执行组合键事件
- Key0_1();//KEY0KEY1组合键事件
- }
- }
- KeyPressCount[0]内存放的是KEY0的20mS压键次数。若>=2表示KEY0已经压下。
- 如果是无序组合键,则如例中示例在Key10()中也要做相应处理。
- void Key10(void)//单击键事件
- {
- if (KeyPressCount[1] >= 2) {//在KEY1也压下时执行组合键事件
- Key0_1();//KEY0KEY1组合键事件
- }
- }
- 零耗时键盘”对组合键的处理方法看上去“很烦琐”,但却带来了更大的灵活性。
- 而且也减轻了键扫描程序的负担。
- ----------------------------------------------------------------------*/
- void Key0_1(void)//组合键事件KEY0KEY1
- {
- }
- void Key1_2(void)//KEY1KEY2
- {
- }
- void Key2_3(void)//KEY2KEY3
- {
- }
- void Key3_0(void)//KEY3KEY0
- {
- }
|