"新型的按键扫描程序"的改进版,欢迎大家继续改进
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999以下代码为在上面的文章和my_avr代码的基础上进行改进:
static volatile unsigned long g_PreKey; //前次按键值
static volatile unsigned long g_NowKey; //当前按键值
volatile unsigned long g_ShortKeyCode;//短按键,单次触发
volatile unsigned long g_LongKeyCode; //长按键,持续触发
void KeyRead(void)
{
//g_NowKey ^ g_PreKey :边缘触发
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
//触发方式 ^g_KeyCode :仅更新变化部分按键值
static unsigned char CntPlus=0;
g_PreKey= g_NowKey;
g_NowKey= KeyScan();
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;
if (g_NowKey^g_PreKey^g_NowKey^g_LongKeyCode)
{
if(CntPlus++>LONG_KEY_TIME)//是否符合长按键要求
{
g_LongKeyCode|= g_NowKey;//添加长按键值
}
}
else
{
CntPlus=0;//无新增按键出现,计数归零
g_LongKeyCode &= g_NowKey; //剔除已释放的按键
}
}
核心算法:
//g_NowKey ^ g_PreKey :边缘触发
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
//触发方式 ^g_KeyCode :仅更新变化部分按键值
特点: 1.支持长短按键同时并行触发。
2.将短按键触发判据改为下降沿触发,即增加了检测短按键释放功能
欢迎各位大侠指点和改进,期望着更高效和强大的代码出现 mark HAO
TKS mark mark 强大 mark 回复【楼主位】mojinpan
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
以下代码为在上面的文章和my_avr代码的基础上进行改进:
static volatile unsigned long g_prekey; //前次按键值
static volatile unsigned long g_nowkey; //当前按键值
volatile unsigned long g_shortkeycode;//短按键,单次触发
volatile unsigned long g_longkeycode; //长按键,持续触发
void keyread(void)
{
//g_nowkey ^ g_prekey :边缘触发
//g_nowkey & (g_nowkey ^......
-----------------------------------------------------------------------
发现一个bug:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;
这个语句末尾的"^g_LongKeyCode"本来是解决长按释放时会同时出现一个短按键判断成功的问题,但是加上去以后又引入了新问题,那就是当,长按键未松手时: g_PreKey & (g_NowKey ^ g_PreKey)=0,g_LongKeyCode=1,相与为1,结果某个短按键被误判.
目前只能将:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey)^g_LongKeyCode;
改为:
g_ShortKeyCode = g_PreKey & (g_NowKey ^ g_PreKey);
那么遗留问题就是:长按键松手是会同时出现一个短按键,需另外构建一个判据:同时出现长按键和短按键时,短按键无效.(目前还没有想好比较好的方法) http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
【3楼】 kevintang 同学添加的释放功能比较有用
unsigned char Trg;
unsigned char Cont;
unsigned char Release;
void KeyRead( void )
{
unsigned char ReadData = PINB^0xff; // 1读键值
Trg = ReadData & (ReadData ^ Cont); // 2得到按下触发值
Release=(ReadData ^ Trg ^ Cont); // 3得到释放触发值
Cont = ReadData; // 4得到所有未释放的键值
}
这东西的实用性是不可置疑的,我在多个项目上使用了,效果非常好
我比较期待的是有没有同学能证明这个怎么推导出来的.....(布尔代数是否可以?) mark mark 回复【8楼】Etual
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=9999
【3楼】 kevintang 同学添加的释放功能比较有用
unsigned char trg;
unsigned char cont;
unsigned char release;
void keyread( void )
{
unsigned char readdata = pinb^0xff; // 1读键值
trg = readdata & (readdata ^ cont); // 2得到按下触发值
release=(readdata ^ trg ^ cont); // 3得到释放触发值
cont = readdata; ......
-----------------------------------------------------------------------
请看我开头部分的描述:
核心算法:
//g_NowKey ^ g_PreKey :边缘触发
//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
//g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
你给出的函数中变量和我的函数中变量的关系如下:
ReadData=g_NowKey;
trg=g_shortkeycode;
Releas=g_longkeycode;
对比看,我上面的写法和你提供的代码是完全等效的,那么下面我来"推导"一下:
这些运算是根据状态机的思想来设计出来的,注意几个关键词:现态,次态(过去的状态),即我程序里的g_NowKey 和g_PreKey
那么再来分析按键的过程,g_NowKey ^ g_PreKey即表示,那么按键的边缘触发就是g_NowKey !=g_PreKey,也就是g_NowKey ^ g_PreKey,但是g_NowKey ^ g_PreKey比g_NowKey !=g_PreKey的表示方法要好,及g_NowKey ^ g_PreKey能按位来表示按键状态,即能实现并发的表示g_NowKey !=g_PreKey
再看//g_NowKey & (g_NowKey ^ g_PreKey):上升沿触发
实际上翻译过来就是:前一次状态是高电平的边缘触发,即上升沿触发
//g_PreKey & (g_NowKey ^ g_PreKey):下降沿触发
实际上翻译过来就是:前一次状态是低电平的边缘触发,即上升沿触发
剩下的就是当前按键的判据了:
你的代码是:Cont = ReadData; // 4得到所有未释放的键值
我的代码是://g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
按照上面的将我的代码来翻译的话就是 当前:电平状态 ^ 边缘触发,结果分成4种:
电平状态 ^ 边缘触发结果
1. 0 0 0 //一直没有按键
2. 1 0 1 //一直没有松开按键
3. 0 1 1 //突然松开按键
4. 1 1 0 //突然按下按键
以上状态中的2和3合并:按下按键至到释放,注意我的代码是带消抖的,你的代码是不带消抖,因为你没有判边缘触发 学无止境 mark 对这个比较感兴趣! http://cache.amobbs.com/bbs_upload782111/files_40/ourdev_648093WDZIPV.jpg
(原文件名:真值表.jpg)
点击此处下载 ourdev_648094FKFGTM.doc(文件大小:30K) (原文件名:真值表.doc) 先顶后下! 我一般不喜欢只有一个按键有效的按键扫描程序
我来抛砖引玉
#define LittleSound 18
#define testbit(var,bit) ((var)&(1<<(bit)))//位测试
#define testbit(var,bit) ((var)&(1<<(bit)))//位测试
#define setbit(var,bit)((var)|=(1<<(bit)))//位置1
#define clrbit(var,bit)((var)&=~(1<<(bit)))//位清零clrbit(p3,1);就是了
#define nop (__no_operation()) //将后者指令行缩写为nop;
#define di(__disable_interrupt()) //将后者指令行缩写为di;
#define ei(__enable_interrupt()) //将后者指令行缩写为ei;
#define wdtasm("LD BTCON,#02H");
void CheckKeys()
{
uchar tmp;
uchar tmp2 = 0;
for(tmp=0; tmp<3; tmp++)
{
tmp2 =0;
if(tmp==0)
{
if(testbit(P0,0))
{
tmp2 = 0x55;
}
}
if(tmp==1)
{
if(testbit(P0,2))
{
tmp2 = 0x55;
}
}
if(tmp==2)
{
if(testbit(P0,1))
{
tmp2 = 0x55;
}
}
/* 判断 */
if(tmp2 == 0x55)
{
InputCnt = 0;
if(tmp == 0)
{
Input.Solo.KeyAdd =0;
KeyTreated.Solo.KeyAdd =0;
}
if(tmp == 1)
{
Input.Solo.KeySub =0;
KeyTreated.Solo.KeySub =0;
}
if(tmp == 2)
{
Input.Solo.KeyEnter =0;
KeyTreated.Solo.KeyEnter =0;
}
}
InputCnt++;
if(InputCnt >15)
{
if(tmp == 0)
{
Input.Solo.KeyAdd =1;
}
if(tmp == 1)
{
Input.Solo.KeySub =1;
}
if(tmp == 2)
{
Input.Solo.KeyEnter =1;
}
}
}
}
void KeyDecide()
{
//<<<<<<<<<<<<<<<连加连减判断>>>>>>>>>>>>>>>>>
if((Input.Solo.KeyAdd^Input.Solo.KeySub) )/* 异或 */
{
if (RepeatTimer>=RepeatTime)
{
RepeatTimer=0;
if(Input.Solo.KeyAdd==1)
{
KeyTreated.Solo.KeyAdd=0; //按键有效
RunFlag.Solo.ToFive = 1;
}
if(Input.Solo.KeySub==1)
{
KeyTreated.Solo.KeySub=0; //按键有效
RunFlag.Solo.ToFive = 1;
}
if(RepeatTime>80)
{
RepeatTime = 80;
}
if(RepeatTime >60) //加速
{
RepeatTime -= 5;
}
}
}
else
{
RepeatTime = 150; //复位连击计时
RepeatTimer = 0;
}
}
void KeyAction_KeyEnter()
{
if((Input.Solo.KeyEnter) && (!KeyTreated.Solo.KeyEnter ))
{
KeyTreated.Solo.KeyEnter =1;
AutoOffTimer = 0;
test = 20;
if(EeFlag != RD24)
{
EeFlag = WR24;
}
BellTimer = LittleSound;
if(SetStatus ==1)
{
RunFlag.Solo.SetType = 0;
SetStatus = 0;
}
else if(SetStatus ==0)
{
SetStatus = 1;
AutoOffTimer = 8;
}
else if(SetStatus > 1)
{
ShowSpecialTimer = 10;
AutoOffTimer = AutoOffTimerMAX;
RunFlag.Solo.SetType = 0;
if(++SetStatus >3)
{
SetStatus = 0;
RunFlag.Solo.SetType = 0;
ShowSpecialTimer = 0;
AutoOffTimer = 0;
}
}
}
}
void KeyAction_KeySub()
{
uchar tmp;
if ((Input.Solo.KeySub) && (!KeyTreated.Solo.KeySub ))
{
KeyTreated.Solo.KeySub =1;
AutoOffTimer = AutoOffTimerMAX;
if(SetStatus == 1)
{
AutoOffTimer = 8;
}
if(SetStatus>0)
{
BellTimer = LittleSound;
ShowSpecialTimer = 0;
if(SetStatus !=1)
if(!RunFlag.Solo.SetType)
{
RunFlag.Solo.SetType = 1;
return;
}
tmp=SetStatus-1; /* SetStatus大于1时,为参数设置状态 */
/* 设置中参数减1 */
ToFive:
if(--Para.intPara>ParaMax || Para.intPara< ParaMin)
{
Para.intPara=ParaMin;/* 并判断是否大最大值与最小值之间 */
}
if(RunFlag.Solo.ToFive)
{
if(Para.intPara%5 !=0)
{
goto ToFive;
}
}
RunFlag.Solo.ToFive = 0;
}
}
}
void KeyAction_KeyAdd()
{
uchar tmp;
if ((Input.Solo.KeyAdd) && (!KeyTreated.Solo.KeyAdd ))
{
KeyTreated.Solo.KeyAdd =1;
AutoOffTimer = AutoOffTimerMAX;
if(SetStatus == 1)
{
AutoOffTimer = 8;
}
if(SetStatus>0)
{
BellTimer = LittleSound;
ShowSpecialTimer = 0;
if(SetStatus !=1)
if(!RunFlag.Solo.SetType)
{
RunFlag.Solo.SetType = 1;
return;
}
tmp=SetStatus-1;
/* 设置中参数加1 */
ToFive:
if(++Para.intPara>ParaMax || Para.intPara< ParaMin)
{
Para.intPara=ParaMax; /* 并判断是否大最大值与最小值之间 */
}
if(RunFlag.Solo.ToFive)
{
if(Para.intPara%5 !=0)
{
goto ToFive;
}
}
RunFlag.Solo.ToFive = 0;
}
}
}
void KeyActions()
{
if(KeyTreated.Solo.KeyEnter)
{
if((KeyTimer==0))
{
ShowSpecialTimer = 10;
SetStatus =2;
BellTimer = LittleSound + 10;
AutoOffTimer =AutoOffTimerMAX;
}
return;
}
else
{
KeyTimer =10;
}
KeyAction_KeyEnter();
KeyAction_KeyAdd();
KeyAction_KeySub();
if(Error > 0)
{
SetStatus = 0;
}
} mark,很强大 都是高手啊,学习··· //g_NowKey ^ (g_NowKey ^ g_PreKey):高电平触发(带消抖)
感觉这句没带消抖
来一毛刺干扰 1^(1^0)=0;正常
毛刺消失后 0^(0^1)=0; ???? 出了键值了
是不是我理解错了????????????? mark mark mark了慢慢看 学习! 先记号,我按键还一直是很老的方法做的,有时候用起来还真不怎么好用 路过收藏 学习了,我一直按键处理做得不好 MARK 按键处理 先 mark ,再细看下。。。 精益求精,值得学习呀。 生命不息,学习不止 {:lol:}。。。改天我用9212试试看。。。。 先记号,再慢慢看 值得学习借鉴下! 学习。。。。 下周抽时间玩一下 实践证明,很好用 很精彩! 高潮,期待高潮 coyool 发表于 2011-6-13 11:51 static/image/common/back.gif
我一般不喜欢只有一个按键有效的按键扫描程序
我来抛砖引玉
能把你的按键程序完整的给我发一份吗?阿莫论坛上你发的应该是不完整的。hongqi1029@126.com,qq:35039828 mojinpan 发表于 2011-3-6 22:32
回复【楼主位】mojinpan
原文链接:http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3941614&bbs_id=999 ...
手机上的,做个记号! 学习一下,顺便问下,这中方法检测的应该是独立式按键吧,它能用于检测矩阵键盘码 好老的帖子了,当年很受启发呢 mark 组合按键好弄吗 数学推理加真值表用在按键上,思路真心好 强大 MARK 谢谢分享.. 有想法,有思路,不错 按键的处理觉得还是有算法可循的,但是主要是按键的判断时间(消抖时间5-10MS),怎么样处理不占用MCU的时间对于我的使用来说才是关键!一个项目不可能只用来键盘扫描,中断处理也是有限的。
页:
[1]