企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
【102.1 “异行输入”“同行输入”“有序”。】 ![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png) 上图102.1.1 有源蜂鸣器电路 ![](https://img.kancloud.cn/68/91/6891d9a9e89ee7345b1505221de5c26b_252x282.png) 上图102.1.2 LED电路 ![](https://img.kancloud.cn/c1/8a/c18ad9232965b2a0699e388df49ac7b9_341x221.png) 上图102.1.3 3\*3矩阵按键的电路 “任意行输入”是指能兼容“异行输入”与“同行输入”这两种按键状态。 何谓“异行输入”何谓“同行输入”?如上图矩阵按键,P2.2,P2.1,P2.0是输入行,P2.5,P2.4,P2.3是输出列。以S1按键为例,很明显,S2和S3都是属于S1的“同行输入”,都是属于P2.2的输入行。除了S2和S3以外,其它所有的按键都是S1的“异行输入”,比如S5按键就是S1的“异行输入”,因为S1是属于P2.2的输入行,而S5是属于P2.1的输入行。 何谓“有序”组合触发?就是两个按键的触发必须遵守“先后顺序”才能构成“组合触发”。比如,像电脑的复制快捷键(Ctrl+C),你必须先按住Ctrl再按住C此时“复制快捷键”才有效,如果你先按住C再按住Ctrl此时“复制快捷键”无效。 “异行输入”与“同行输入”,相比之下,“同行输入”更难更有代表性,如果把“同行输入”的程序写出来了,那么完全按“同行输入”的思路,就可以把“异行输入”的程序写出来。因此,只要把“同行输入”的程序写出来了,也就意味着“任意行输入”的程序也就实现了。本节以S1和S2的“同行输入”按键为例,S1是主键,类似复制快捷键的Ctrl键;S2是从键,类似复制快捷键的C键。要触发组合键(S1+S2),必须先按住S1再按S2才有效。功能如下:(1)S1每单击一次,LED要么从“灭”变成“亮”,要么从“亮”变成“灭”,在两种状态之间切换。(2)如果先按住S1再按S2,就认为构造了“有序”组合键,蜂鸣器发出“嘀”的一声。 \#include "REG52.H" \#define KEY\_VOICE\_TIME 50 \#define KEY\_SHORT\_TIME 20 void T0\_time(); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); void BeepClose(void); void LedOpen(void); void LedClose(void); void VoiceScan(void); void KeyScan(void); void SingleKeyTask(void); void DoubleKeyTask(void); sbit P3\_4=P3^4; sbit P1\_4=P1^4; sbit ROW\_INPUT1=P2^2; //第1行输入口。 sbit ROW\_INPUT2=P2^1; //第2行输入口。 sbit ROW\_INPUT3=P2^0; //第3行输入口。 sbit COLUMN\_OUTPUT1=P2^5; //第1列输出口。 sbit COLUMN\_OUTPUT2=P2^4; //第2列输出口。 sbit COLUMN\_OUTPUT3=P2^3; //第3列输出口。 volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; unsigned char Gu8LedStatus=0; volatile unsigned char vGu8SingleKeySec=0; volatile unsigned char vGu8DoubleKeySec=0; void main() { SystemInitial(); Delay(10000); PeripheralInitial(); while(1) { SingleKeyTask(); DoubleKeyTask(); } } /\* 注释一: \* 两个“任意行输入”矩阵按键“有序”触发的两个最关键地方: \* (1)当S1按键被按下单击触发之后, “马上更新输出列的信号状态”,然后切换到后面的步骤。 \* (2)在后面的步骤里,进入到S1和S2两个按键的轮番循环监控之中,如果发现S1按键率先 \* 被松开了,就把步骤切换到开始的第一步,重新开始新一轮的按键扫描。 \* (3)按照这个模板,只需“更改不同的列输出,判断不同的行输入”,就可以实现“任意行输入” \* 矩阵按键的“有序”组合触发。 \*/ void KeyScan(void) //此函数放在定时中断里每1ms扫描一次 { static unsigned char Su8KeyLock=0; static unsigned int Su16KeyCnt=0; static unsigned char Su8KeyStep=1; static unsigned char Su8ColumnRecord=0; switch(Su8KeyStep) { case 1: if(0==Su8ColumnRecord) { COLUMN\_OUTPUT1=0; COLUMN\_OUTPUT2=1; COLUMN\_OUTPUT3=1; } else if(1==Su8ColumnRecord) { COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=0; COLUMN\_OUTPUT3=1; } else { COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=1; COLUMN\_OUTPUT3=0; } Su16KeyCnt=0; Su8KeyStep++; break; case 2: //等待列输出稳定,但不是去抖动延时 Su16KeyCnt++; if(Su16KeyCnt>=2) { Su16KeyCnt=0; Su8KeyStep++; } break; case 3: if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) { Su8KeyStep=1; Su8KeyLock=0; Su16KeyCnt=0; Su8ColumnRecord++; if(Su8ColumnRecord>=3) { Su8ColumnRecord=0; } } else if(0==Su8KeyLock) { if(0==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; if(0==Su8ColumnRecord) { vGu8SingleKeySec=1; //单击任务,触发1号键 对应S1键 //“马上更新输出列的信号状态” COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=0; //列2也输出0,非常关键的代码! COLUMN\_OUTPUT3=1; Su16KeyCnt=0; //去抖动延时清零,为下一步计时做准备 Su8KeyStep++; //切换到下一步步骤 } else if(1==Su8ColumnRecord) { vGu8SingleKeySec=2; } else if(2==Su8ColumnRecord) { vGu8SingleKeySec=3; } } } else if(1==ROW\_INPUT1&&0==ROW\_INPUT2&&1==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; if(0==Su8ColumnRecord) { vGu8SingleKeySec=4; } else if(1==Su8ColumnRecord) { vGu8SingleKeySec=5; } else if(2==Su8ColumnRecord) { vGu8SingleKeySec=6; } } } else if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&0==ROW\_INPUT3) { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; if(0==Su8ColumnRecord) { vGu8SingleKeySec=7; } else if(1==Su8ColumnRecord) { vGu8SingleKeySec=8; } else if(2==Su8ColumnRecord) { vGu8SingleKeySec=9; } } } } break; case 4: //等待列输出稳定,但不是去抖动延时 Su16KeyCnt++; if(Su16KeyCnt>=2) { Su16KeyCnt=0; Su8KeyLock=0; //关键语句!自锁清零,为下一步自锁组合按键做准备 Su8KeyStep++; } break; case 5: //判断S2按键 if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) //S2按键没有被按下 { Su8KeyLock=0; Su16KeyCnt=0; //“马上更新输出列的信号状态” COLUMN\_OUTPUT1=0; //列1输出0,非常关键的代码! COLUMN\_OUTPUT2=1; COLUMN\_OUTPUT3=1; Su8KeyStep++; //切换到下一个步骤,监控S1是否率先已经松开 } else if(0==Su8KeyLock) { if(0==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) //S2按键被按下 { Su16KeyCnt++; if(Su16KeyCnt>=KEY\_SHORT\_TIME) { Su8KeyLock=1; //组合按键的自锁 vGu8DoubleKeySec=1; //触发组合按键(S1+S2) } } } break; case 6: //等待列输出稳定,但不是去抖动延时 Su16KeyCnt++; if(Su16KeyCnt>=2) { Su16KeyCnt=0; Su8KeyLock=0; //关键语句!自锁清零,为下一步自锁组合按键做准备 Su8KeyStep++; } break; case 7: //监控S1按键是否率先已经松开 if(1==ROW\_INPUT1&&1==ROW\_INPUT2&&1==ROW\_INPUT3) { Su16KeyCnt=0; Su8KeyLock=0; Su8KeyStep=1; //如果S1按键已经松开,返回到第一个运行步骤重新开始扫描 Su8ColumnRecord++; if(Su8ColumnRecord>=3) { Su8ColumnRecord=0; } } else { //“马上更新输出列的信号状态” COLUMN\_OUTPUT1=1; COLUMN\_OUTPUT2=0; //列2输出0,非常关键的代码! COLUMN\_OUTPUT3=1; Su8KeyStep=4; //如果S1按键没有松开,继续返回判断S2是否已按下 } break; } } void SingleKeyTask(void) { if(0==vGu8SingleKeySec) { return; } switch(vGu8SingleKeySec) { case 1: //S1按键的单击任务,更改LED灯的显示状态 if(0==Gu8LedStatus) { Gu8LedStatus=1; LedOpen(); } else { Gu8LedStatus=0; LedClose(); } vGu8SingleKeySec=0; break; default: vGu8SingleKeySec=0; break; } } void DoubleKeyTask(void) { if(0==vGu8DoubleKeySec) { return; } switch(vGu8DoubleKeySec) { case 1: //S1与S2的组合按键触发,发出“嘀”一声 vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=KEY\_VOICE\_TIME; vGu8BeepTimerFlag=1; vGu8DoubleKeySec=0; break; } } void T0\_time() interrupt 1 { VoiceScan(); KeyScan(); TH0=0xfc; TL0=0x66; } void SystemInitial(void) { TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; } void Delay(unsigned long u32DelayTime) { for(;u32DelayTime>0;u32DelayTime--); } void PeripheralInitial(void) { if(0==Gu8LedStatus) { LedClose(); } else { LedOpen(); } } void BeepOpen(void) { P3\_4=0; } void BeepClose(void) { P3\_4=1; } void LedOpen(void) { P1\_4=0; } void LedClose(void) { P1\_4=1; } void VoiceScan(void) { static unsigned char Su8Lock=0; if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0) { if(0==Su8Lock) { Su8Lock=1; BeepOpen(); } else { vGu16BeepTimerCnt--; if(0==vGu16BeepTimerCnt) { Su8Lock=0; BeepClose(); } } } }