新手学习中,感觉原来按键延时去抖和定时器去抖动都不太理想,就想了新的方法
具体思路如下:
1,依旧循环推挽排,当有按键按下时,下拉输入不为0,这跟经典矩阵键盘程序无异
2,设置一个按下累加变量,和一个松手递减变量,每次扫描有按下则累加变量自加,并刷新递减变量的初值比如500。同时,分辨键值。
3,如果松手或者抖动,则递减变量自减,到0则刷新按下第加变量为0,重新来过
4,如果累加变量超过一定值,比如8000,并且递减变量低于一定值,则表示按键有效并已经松手,则输出值
5,如果一直按下则递减变量不会低于一定值,而第加变量达到一个更大值则表示长按,则输出长按键值
现在的问题是,去抖效果很好,但4*4键盘中输出结果,列正确,排则可能是1、2、3、4排任意结果,而貌似晶振速度越快,错误率越大,就像推挽输出在排上有高电平残留一样,百思不得其解啊。怀疑过IO反转速度跟不上,但加了延时也没用。
虽然用定时器的方法写了另外一个用了,但这段程序是个心结,求高手帮分析,得知缘由方位安心吃饭
u8 Key_Row[]={0x01,0x02,0x04,0x08};
u8 Key_ValueTable[4] [4]={
{1,2,3,12},
{4,5,6,13},
{7,8,9,14},
{10,0,11,15}
};
u16 Key_Rank =0;
u8 Key_Row_Num;
u8 Key_Value;
u8 Key_Value2;
u16 Key_GrandTotal = 0;
u16 Key_Decline=500;
void MM_GPIO_Config(void);
int main(void)
{
MM_RCC_HSI64();
MM_GPIO_Config();
MM_USART1_Config();
while(1)
{
GPIOA->ODR =(GPIOA->ODR&0xfff0)|(u16)Key_Row[Key_Row_Num];//打开排,用数组循环
Key_Rank = GPIOA->IDR;//读取端口状态
Key_Rank &= 0x00f0;//只保留PA4~PA7
if(Key_Rank != 0x0000)//如果不等于0,则表示有端口被拉高了
{
switch (Key_Rank)//判断那个端口被拉高,并结合数组判断键值
{
case 0x0010 :
Key_Value = Key_ValueTable[Key_Row_Num][0];
break;
case 0x0020 :
Key_Value = Key_ValueTable[Key_Row_Num][1];
break;
case 0x0040 :
Key_Value = Key_ValueTable[Key_Row_Num][2];
break;
case 0x0080 :
Key_Value = Key_ValueTable[Key_Row_Num][3];
break;
}
if(Key_GrandTotal < 65535)//防止变量溢出循环
Key_GrandTotal ++ ;//计数被拉高轮询的次数
Key_Decline =400;//按键按下时,每次都给松手抖动刷新初值
}
else if(Key_Decline != 0) //松手减值,
Key_Decline -- ;
else //if(Key_GrandTotal<8000)
Key_GrandTotal =0;//如果减值到0了,加值也被清零,重新来过
if((Key_GrandTotal > 4000)&&(Key_Decline < 300))//如果记录超过规定次数,表示按键确实按下,且在松手有段时间后执行输出
{
//Key_ValueJudge(Key_Value);
printf("%d \r\n",Key_Value);
Key_GrandTotal = 0;//清零计数,避免下次错误进入
}
if(Key_GrandTotal > 60000)//如果大于规定值,则表示长按
{
//Key_ValueJudge(Key_Value+100);
printf("%d \r\n",Key_Value+100);
Key_GrandTotal = 0;//清零计数,避免下次错误进入
}
Key_Row_Num ++ ;//排累加
if(Key_Row_Num == 4)//到头循环
Key_Row_Num = 0;
}
} |