本帖更新内容详见:http://www.51hei.com/bbs/dpj-176186-1.html
#define BaseTime 10 //时间基准10 ms ,如果设计的时基是5ms 则前面的10就用该变成5
#define number_init 0xfffffffd // 初始化相关的utime(unsigned long)变量(如果是unsigned int 就应该是0xfffd),采用这个值的好处是,即使发生时钟计数器溢出,也不影响其他的程序
typedef unsigned long utime; //这个类型是为了方便移植专门给时间相关变量使用
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;
enum ButtonModel{noneClick=0,singalClick,doubleClick,repeatClick,longPress}; //doubleClick目前未定义完整动作 pressDownHold,按下保持
enum ButtonStaus{nonePress=1,pressDown,pressUp,pressDownHold}; /
struct button
{
uchar outPutEn:1; //发送数据使能
uchar lastButton:1; //按键上次变动后的状态 默认 1
uchar init_leavel:1; //设置默认按键电平
uchar longPressFlag:1; //长按释放标志 默认0,一旦开始长按则置1
uchar Gpio_level :3;
uchar applyUseOpenMaxPwm:1; //在关机状态下,若本按键按下启动了系统,则需要申请全局变量标明,禁止其他按键关闭被本按键打开的系统(自己打开自己负责关闭)
uchar ticks; //按键按下次数
enum ButtonStaus lastButtonStaus; //按键上个循环的状态
enum ButtonStaus thisButtonStaus; //按键本循环的状态
enum ButtonModel lastButtonModel; //按键上个循环所处的模式
enum ButtonModel thisButtonModel; //按键本循环应该所处的模式
uint changeModelTime; //10ms基准 //长按时间定义
uint pressLongTime; //10ms基准 //多击时间定义
utime lastPressDownMoment; //上次按键按下所处的时刻
utime thisPressDownMoment; //本次按键按下所处的时刻
uint tempTime; //缓存按键两次按下之间的时长
utime buttonConfir; //按键防抖时长
utime getTimer; //获取时钟精准时刻,用于设定按键扫描周期
utime acquisitionMoment; //获取时钟精准时刻,用于记录相同按键状态持续时长
uchar (*read_gpio)(void); //获取按键状态方法
};
/*================================
outPutEn 是为后面接收按键是单击、多击长按函数准备的参数。比如按键扫描程序10ms运行一次,输出的是单击,如果没有outPutEn这个参数,后面的接收程序会在10ms内一直都接收的是单击指令,这样就会一直执行单击需要进行的操作,本来单击一次档位变化1,结果现在档位变化了n
applyUseOpenMaxPwm 是为多按键且按键操作有优先权做准备,,有优先权的操作不会被其他按键操作打断
===============================*/
struct button button1,button2,button3;
void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime)
{
enOutTime/=BaseTime;
noiseProofTime/=BaseTime;
if(number_init==Key->getTimer) //如果是第一次运行,则更新时间
{
Key->getTimer=timer;
}
else
{
if(timer-Key->getTimer>=enOutTime) //如果时间足够“定义的循环时间” ,则更新时间并允许运行
{
Key->getTimer=timer;
Key->Gpio_level=Key->read_gpio();
if(Key->Gpio_level>1);
else
{
if(Key->lastButton^Key->Gpio_level)//按键有电平变化模块处理开始----------------------------------------------------------------------------//
{
if(number_init==Key->buttonConfir)
Key->buttonConfir=timer;
if(timer-Key->buttonConfir>=noiseProofTime)//按键防抖,必须再确认状态
{
Key->lastButton=Key->Gpio_level;
Key->acquisitionMoment=number_init;
Key->buttonConfir=number_init; //二次确认标志重置
if(Key->init_leavel^Key->Gpio_level)//本次按键状态改变后与定义的电平不一致模块处理开始
{
switch(Key->lastButtonStaus)
{
case nonePress: //上个循环是定义的初始电平
case pressUp:
{
if(number_init==Key->lastPressDownMoment)//是第一次记录按键按下时刻
{
Key->thisPressDownMoment=Key->lastPressDownMoment=timer;
}
else //不第一次记录
{
Key->thisPressDownMoment=timer;
Key->tempTime+=Key->thisPressDownMoment-Key->lastPressDownMoment; //获取两次按键按下之间的时间间隔
Key->lastPressDownMoment=Key->thisPressDownMoment; //更新
}
Key->ticks++;
//不在此处增加pressLongTime判断的原因是,buftime0记录的是两次按键按下之间的时长,在两次按下之间必有弹起,一旦弹起,pressLongTime 就置零
switch(Key->ticks)
{
case 1:
{
if(Key->tempTime>=Key->changeModelTime)//ticks未初始化。
{
Key->ticks=0;
Key->tempTime=0;
}
Key->thisButtonModel=singalClick;
break;
}
case 2:
{
if(Key->tempTime<Key->changeModelTime)//ticks未初始化
Key->thisButtonModel=doubleClick;
else
{
Key->thisButtonModel=singalClick;
Key->ticks=0;
Key->tempTime=0;
}
break;
}
case 3: //三击,想要增加N击自己在后面添加 CASE 4,CASE 5,CASE N
{
if(Key->tempTime<Key->changeModelTime)//第三击的时间也小于定义的改变模式时间
{
Key->thisButtonModel=repeatClick;
}
else //第三击的时间大于定义的改变模式时间
{
Key->thisButtonModel=singalClick;
}
Key->ticks=0;
Key->tempTime=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
break;
}
default :
{
Key->ticks=0;
Key->thisButtonModel=noneClick;
}
}
Key->thisButtonStaus=pressDown; //按键按下
break;
}
}
} //按键状态改变后是与定义的电平模块不同处理结束//
///////////////////////////////////////////////////////////////////////////
else //按键状态改变与定义的电平模块相同开始处理//
{
Key->longPressFlag=0;
switch(Key->lastButtonStaus)
{
case pressDown://上个循环是低电平,机器人手速
case pressDownHold:
{
if(Key->tempTime>=Key->changeModelTime)
{
Key->tempTime=0;
Key->ticks=0;
}
Key->thisButtonStaus=pressUp; //按键弹起
Key->thisButtonModel=noneClick;
break;
}
}
}//本次按键状态改变后是定义的电平处理结束//
}
}//按键有电平变化模块处理结束-------------------------------------------------------------------------------------------------------//
else if(Key->init_leavel==Key->Gpio_level)//按键无改变且是初始电平处理开始//
{
if(number_init==Key->acquisitionMoment)
{
Key->acquisitionMoment=timer;
}
if(timer-Key->acquisitionMoment>Key->pressLongTime||timer-Key->acquisitionMoment>Key->changeModelTime||Key->tempTime>=Key->pressLongTime||Key->tempTime>=Key->changeModelTime)
{
Key->ticks=0;
Key->tempTime=0; //tempTime 一旦置零,记录按键按下时刻的值都应当重置
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
}
if(timer-Key->acquisitionMoment>259200000)//一个月都没有使用了,所以清零
{
Open_timer0; //重置timer0 防止一年后溢出
}
switch(Key->lastButtonStaus)
{
case nonePress:
case pressUp:
{
Key->thisButtonStaus=nonePress;
Key->thisButtonModel=noneClick;
break;
}
/*
case pressDown:
case pressDownHold: //容错处理,理论上若上个循环与本循环电平不一致,应该在电平有变化模块处理不会在本模块
{
Key->thisButtonStaus=pressUp;
if(Key->tempTime+1>=Key->pressLongTime)
{
Key->thisButtonModel=longPress;
}
else
{
Key->thisButtonModel=noneClick;
}
Key->tempTime=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=number_init;
break;
}
*/
}
}//按键状态无改变且是初始电平处理结束//
else//按键状态无改变且是非初始电平处理开始//
{
if(number_init==Key->acquisitionMoment)
{
Key->acquisitionMoment=timer;
}
switch(Key->lastButtonStaus)
{
/*
case nonePress: //容错处理,理论上若上个循环与本循环电平不一致,应该在电平有变化模块处理不会在本模块
case pressUp:
{
if(longPress==Key->lastButtonModel)
{
Key->thisButtonStaus=nonePress;
Key->thisButtonModel=noneClick;
}
else
{
if(timer-Key->acquisitionMoment>Key->pressLongTime||Key->tempTime>=Key->pressLongTime)
{
Key->thisButtonModel=longPress;
Key->lastPressDownMoment=Key->thisPressDownMoment=number_init; //一但超时,所谓的记录按键按下时间间隔就无意义,必须重置
Key->tempTime=0; //按键键缓冲时间不清零,对操作体验影响很不好
Key->ticks=0;
}
else
{
Key->thisButtonModel=singalClick;
}
Key->thisButtonStaus=pressDown;
}
}
*/
case pressDown:
case pressDownHold:
{
if(longPress==Key->lastButtonModel||Key->longPressFlag)
{
Key->thisButtonModel=noneClick; // 改变了模式,下个循环程序进入其它分支,所以必须在上面加入模式判断
Key->thisButtonStaus=pressDownHold;
Key->acquisitionMoment=timer;
Key->lastPressDownMoment=Key->thisPressDownMoment=timer; //PressDownMoment必须在按键按下保持状态下更新时间,否则tempTime会计算总的按键时长,以至于无法进入repeat模式
}
else
{
if(timer-Key->acquisitionMoment>Key->pressLongTime)
{
Key->longPressFlag=1;
Key->thisButtonModel=longPress;
Key->tempTime=0;
Key->ticks=0;
Key->thisPressDownMoment=Key->lastPressDownMoment=Key->acquisitionMoment=number_init;
}
else
{
Key->thisButtonModel=noneClick;
}
Key->thisButtonStaus=pressDownHold;
}
break;
}
}
}
Key->lastButtonStaus=Key->thisButtonStaus; //最后更新
Key->lastButtonModel=Key->thisButtonModel;
Key->outPutEn=1;
}
}
}
}
/*============================
void Scan_key(struct button *Key,utime timer ,uint enOutTime ,uint noiseProofTime) 中,timer 是授时系统,比如每10ms加一位,enOutTime 是循环检测时间,比如允许每50ms建成运行一次,noiseProofTime是按键防抖动时间,程序运行后检测按键的thisButtonModel状态就能知道是单击双击多击还是长按。目前在函数中定义了单击到三击,三击以上可自己添加
================================*/
|