谢谢楼主! mark一下 mark回来实践下 mark一下感谢马老师 学习学习。。。。。。 老贴标记下 标记以消除浮躁的心! 顶起,正在学习4x4的键盘程序,打算用状态机实现连发长按功能。 好帖留印 学习了,好资料 nice thought about key process 本帖最后由 oszp1688com 于 2013-2-22 14:24 编辑
马老师:你这个按键 低层按键(I/0)扫描函数 中的那个 return key_return; 这一句不要程序中只是多出了两个警告对最终 结果无影响
在抄写你的程序漏写了这句 才发现的开始以为是变量 key_retun与下面那段中的变量相同造成的后面我把其中的一个变量换成大写发现还是和上面一样的现像
本人不才,请马老师 帮忙解释下原因 谢谢
uchar key_drive(void) // 读键盘
{
static uchar key_state=key_state_0,key_time=0; // 状态、按键计时
uchar key_press,Key_return=N_Key; // 按键值、返回事件
key_press=key_input; //读按键IO 电平 :松开1,按下为0。
switch(key_state)
{
case key_state_0: // 按键初始态
if(!key_press) key_state=key_state_1; // 键被按下,状态转换到按键消抖和确认状态
break;
case key_state_1:// 按键消抖与确认态
if(!key_press)
{
key_time=0;
key_state=key_state_2;// 按键仍然处于按下,消抖完成,状态转换到按下键时间的计时状态
}
else key_state=key_state_0;// 按键已抬起,转换到按键初始态。此处完成和实现软件消抖,其实按键的按下和释放都在此消抖的。
break;
case key_state_2:
if(key_press) // 按键释放
{
Key_return=S_Key;// 返回单击事件
key_state=key_state_0;// 转换到按键初始态
}
else if(++key_time>=250) // 继续按下,计时加40ms(40ms为本函数循环执行间隔)
{
Key_return=L_Key;// 按下时间>2000ms,此按键为长按操作,返回长键事件
key_state=key_state_3; // 转换到等待按键释放状态
}
break;
case key_state_3:// 等待按键释放
if(key_press) key_state=key_state_0;//按键已释放,转换到按键初始态
break;
}
return Key_return; //?????????没有这一句,程序只是多了两个警告 对最终函数效果并没有产生影响 百思不知其解???????????????
}
uchar key_read(void)
{
static uchar key_m=key_state_0,key_time_1=0;// 状态、双击时限
uchar key_return=N_Key,key_temp;//反回事件,读键码
key_temp=key_drive();//读键码
switch(key_m)
{
case key_state_0:
if(key_temp==S_Key) // 第1次单击,不返回,到下个状态判断后面是否出现双击
{
key_time_1=0;// 初始化双击计时
key_m=key_state_1;// 转下一步
}
else key_return=key_temp;// 对于无键、长键,返回原事件
break;
case key_state_1:
if(key_temp==S_Key) // 又一次单击(间隔肯定<500ms)
{
key_return=D_Key;// 返回双击键事件,回初始状态
key_m=key_state_0; // 返回初始状态
}
else
{ // 这里500ms内肯定读到的都是无键事件,因为长键>1000ms,在1s前低层返回的都是无键
if(++key_time_1>=150)
{
key_return=S_Key; // 500ms内没有再次出现单键事件,返回上一次的单键事件
key_m=key_state_0; // 返回初始状态
}
}
break;
}
return key_return;//返回按键事件
} 学习记号!!!!!! 不错,看了一个小时还没看完 学习了 受教了!! 收益非浅,学习了 马老师 用状态机算是新手级别,写了3个小时。手边没有硬件验证,先贴出来犒劳下辛苦{:smile:}
uint ms1000,ms500;
uchar s_m=0,ms10,keyval=0,short=0,long=0;
void key()
{
if(s_m==0)
{
if(~(PINC&0X01))s_m=1;
}
else if(s_m==1)//按下
{
ms10=0;
ms500=0;
ms1000=0;
s_m=2;
}
else if(s_m==2)
{
if(ms10==10)
{
ms10=0;
if(~(PINC&0X01))s_m=3;//确认按下
else s_m=0;//没有按下
}
}
else if(s_m==3)
{
if((PINC&0X01))
{
s_m=4;//松手
ms10=0;
}
}
else if(s_m==4)
{
if(ms10==10)
{
ms10=0;
if((ms1000>=1000)&&(PINC&0X01))
{
if((short==0)&&(long==0))
{
keyval=1;//长按
long=1;
}
else if(long==1)
{
long=0;
keyval=0;//本次无效
}
else if(short==1)
{
short=0;
keyval=0;//本次无效
}
s_m=0;
}
if((ms1000<1000)&&(PINC&0X01))
{
if((short==0)&&(long==0))
{
keyval=2;//短按
short=1;
ms500=0;
s_m=5;
}
else if(long==1)
{
long=0;
keyval=0;//本次无效
s_m=0;
}
else if(short==1)
{
short=2;
ms500=0;
s_m=5;
}
}
}
}
else//(s_m==5)
{
if((~(PINC&0X01))&&(ms500<500))
{
if(short==2)
{
keyval=4;//双击
}
s_m=0;
}
if(ms500>500)
{
if(short==1)
{
short=0;
keyval=3;//单击
}
s_m=0;
}
}
}
SIG(SIG_OVERFLOW0)//1ms定时中断
{
ms10++;
ms500++;
ms1000++;
key();
} 记录一下 标记,很好的帖子 不错的设计,值得借鉴! 最实用的操作啊 好东西 本帖最后由 sqmm 于 2013-6-17 10:50 编辑
我老早实现过单个按键实现导航的程序
能够单击双击,3击....9击...N击
能够识别长按,单击后长按,双击后长按....N击后长按
直接用button_get()返回不同键码.
可以很方便实现导航.
用一个定时器,主循环保证150ms内有查询就OK
#include<avr/interrupt.h>
#include<cpu/cpu.h>
#include"avrbutton.h"
static dwordVal_t tsKeyDown;
static dwordVal_t tsKeyUp;
static dwordVal_t* ptsKeyLast = &tsKeyDown; // 最近按键动作计时
static u8 ButtonAction; // 为主循环提供按钮事件信号
static u8 KeyIsMulti; // 连击标识,1:有可能连击
static u8 KeyCount; // 连击次数
#define MULTI_CLIK 150
#define LONG_PRESS 1000
#define KEY_REPEAT 300
u16 ms_diff(dwordVal_t* more, dwordVal_t* less)
{
u16 ret = more->Val - less->Val;
return ( ret & 0x8000 )?0xFFFF:ret;
}
SIGNAL ( MutiFunction_ISR )
{
if( !ms_passed(ptsKeyLast,21) )// 消抖
return;
u8 state = PIN_H(MutiFunction_KEY);
if(!state)
{//按键弹起
if(ButtonAction>=3)//重复后驱动复位
{
KeyCount = 0;
ButtonAction = 0;
KeyIsMulti = 0;
return;
}
ButtonAction = 2;
tsKeyUp.Val = SysClock.Val;
ptsKeyLast = &tsKeyUp;
if(KeyIsMulti)
{
KeyCount ++;
}
}
else
{//按键按下
ButtonAction = 1;
tsKeyDown.Val = SysClock.Val;
ptsKeyLast = &tsKeyDown;
if( ms_diff(&tsKeyDown, &tsKeyUp)<MULTI_CLIK )// 有可能是连击
{
KeyIsMulti = 1;
if(KeyCount==0)
KeyCount = 1;
}
}
}
u8 button_get(void)
{
u8 ret = 0;
if(ButtonAction==0)
{
return ret;
}
else if(ButtonAction==2)
{
if( ms_passed(ptsKeyLast, MULTI_CLIK) )
{
if(KeyIsMulti)
{
ret = KeyCount;
}
else// 要么单击要么长按
{
ret = 1;// 单击
}
KeyCount = 0;
ButtonAction = 0;
KeyIsMulti = 0;
}
}
else if(ButtonAction==1)
{// 很可能产生连击事件.
if( ms_passed(ptsKeyLast, LONG_PRESS) )
{
ButtonAction = 3;
ret = KeyCount?KeyCount:0xFF;
}
}
else if(ButtonAction==3)
{
if( ms_passed(ptsKeyLast, KEY_REPEAT) )
{
if( PIN_L(MutiFunction_KEY) )//按键弹起确认
{
KeyCount = 0;
ButtonAction = 0;
KeyIsMulti = 0;
return ret;
}
//ptsKeyLast->Val = SysClock.Val;
//ret = KeyCount?KeyCount:0xFF;
}
}
return ret;
}
// 初始化按钮端口,打开按钮电平跳变中断
void button_init(u8 id)
{
DRIVER(MutiFunction_KEY, IN);
CLR( MutiFunction_KEY );
MASK_1(EIMSK, MutiFunction_INT);
MASK(EICRB, 0x1/*0b01*/<<((MutiFunction_INT-4)<<1));
ButtonAction = 0;
}
今天才看到这么火帖子,学习! mark,实在想睡觉了,不能熬夜。 先标记一下,回头细看 标记一下 MARK
学习应用中…… {:handshake:},很好的帖子, 下班了回去在看 mark........... m a r k 看看留个标记 多谢大家,学习了 诶好贴啊多看多谢 多领悟马老师应该多开点这种贴啊思想上的真正提高啊。。 mark!!{:lol:} 马老师 我这个长短按键程序对吗?
#define KEY_OFF_ON RB5
unsigned int uiTimer1_Count;
unsigned char ucKey_Count_Start;
unsigned char ucSet_Down_Short;
unsigned char ucSet_Down_Long;
unsigned char ucSet_Short_Flag;
unsigned char ucSet_Long_Flag;
void Key_Long_Short(void)
{
if(KEY_OFF_ON == 0) //检测按键按下
{
ucKey_Count_Start = 1;
if((KEY_OFF_ON == 0) && (uiTimer1_Count >= 8)) //按键500MS为短按
{
ucSet_Down_Short = 1;
ucSet_Down_Long = 0;
}
if((KEY_OFF_ON == 0) && (uiTimer1_Count >= 77)) //按键5S为长按
{
ucSet_Down_Short = 0;
ucSet_Down_Long = 1;
}
}
else
{
if((KEY_OFF_ON == 1) && (ucSet_Down_Short == 1))
{
ucSet_Down_Short = 0;
ucSet_Short_Flag = 1; //一个完整的短按键操作完成
}
if((KEY_OFF_ON == 1) && (ucSet_Down_Long == 1))
{
ucSet_Down_Long = 0;
ucSet_Long_Flag = 1; //一个完整的长按键操作完成
}
ucKey_Count_Start = 0;
}
if(ucKey_Count_Start == 1) //按键开始定时器计数
{
TMR1ON = 1;
}
else if(ucKey_Count_Start == 0)
{
TMR1ON = 0;
}
}
void Power_On_Off(void)
{
if(ucSet_Long_Flag == 1) //是长按键
{
ucSet_Long_Flag = 0;
PCA9635LedoutSet(0x2a, 0xaa, 0xaa, 0x0a, 0x00); //开机
return;
}
else if(ucSet_Short_Flag == 1) //是短按键
{
ucSet_Short_Flag = 0;
PCA9635LedoutSet(0x2a, 0x00, 0x00, 0x00, 0x00); //关机
return;
}
} 讨论太好了,有意思。 谢谢 老师 希望再出点好书 好帖子 顶起 哈哈
学习了! 看起来容易,真正自己独立整理思路去写确为不易 呵呵,学习了!! 呵呵!谢谢分析! 耐心看了一遍,讨论的太好了。 以后还得再看
技术贴,mark mark 好贴学习了 mark 手机mark下,改天细看
先收藏,回头看
mark。。。。。。。 缺少面向对象的思想,小众编程。 谢谢马老师,受教了!
Mark... 看完这个帖子,自己增加了自信,马老师帖子中一直强调的“编程思想和方法是编程的核心”这个观点,我严重同意。 八错{:lol:} 好帖,收藏 这个一个我看过的最简单&最复杂的帖子,琢磨很长时间啦 学习了! 谢谢分享! 很好的状态机按键设计,目前很多按键设计都得益于这篇帖。 mark!!!! 好思路! Mark,谢谢分享 其实不是不想做的好,而是根本没有能力做好!这句话在理,顶楼主~ 很好的东西,谢了 谢谢,马克一下 好贴子,学习了 再简单的功能要是想写好了,写通用了,写稳定了,还真不是一件容易的事 不mark了,果断打印珍藏 多谢分享。 好资料,收藏! 非常有用记一下 好东西一定要顶 mark,很不错 好帖,收藏一下。 MARK一下好了 耐心看完,感慨!向马潮老师学习,将细节做到极致! mark,谢谢楼主的分享 马老师,这一个按键要是接在中断上要如何处理?中断发生后如何处理长按,短按??
主程序执行过程中想从主程序初始化重头执行,gai
马老师,我向您请教一个关于C语言的问题:主程序执行过程中想从主程序初始化重头执行,该怎么编写指令或用什么其他办法实现这个功能呢?敬请您的回复,感谢! 鹰凖明 发表于 2015-9-3 16:49马老师,我向您请教一个关于C语言的问题:主程序执行过程中想从主程序初始化重头执行,该怎么编写指令或用 ...
简单的方法是嵌入一句汇编代码:jmp 0000h lnso 发表于 2015-8-29 08:50
马老师,这一个按键要是接在中断上要如何处理?中断发生后如何处理长按,短按?? ...
不建议用外部中断去处理 machao 发表于 2015-9-8 21:49
不建议用外部中断去处理
项目只有一个按键,而且要做低功耗,逼不得已啊 machao 发表于 2015-9-8 21:45
简单的方法是嵌入一句汇编代码:jmp 0000h
感谢指导!马老师,你说的这个跳转指令后面的数值是main函数的起始地址值吗? 好复杂,如果用AD输入那种,一根线,多简单。 学习下状态机。。。 这个不错,曾经用过有些产品,按键程序就写的不好让人感觉按键坏了一样 受益匪浅,mark Mark,以后可以多一个解决的思路了 受教了,感谢楼主 按键基于状态机算法,确实是一种非常好的思路,也不容易乱。 学习学习,谢谢分享 学习谢谢老师 收藏了,感觉特别好 这个可以节省一些IO点啊,呵呵,学习了,谢谢 代码好,说的也好,支持。