单片机按键扫描程序,可以单击、双击、长按,使用状态机,无延时,不阻塞。
- 根据按下时的时长、间隔来判断是否是连按或者长按。
- 当连按间隔很短时,计录连按次数
- 超过连接间隔时,回报按下次数
- 根据按键次数自行判断是单击、双击、三击、四击。。。最多记录15击。
结构体版:
#define KEY_CHANNEL_COUNT (6 + 8 + 8)
struct keyInfo
{
uint8_t act : 4; // 按了多少次,最多连按15次
uint8_t down : 1; // 按下了
uint8_t up : 1; // 松开了
uint8_t longPress : 1; // 长按了
uint8_t io : 1; // 按键IO状态
uint8_t intervalTime; // 连按间隔时间
uint8_t holdTime; // 长按时间
uint8_t duration; // 按键次数保持时间,超过后,act清零
};
struct keyInfo keyValues[KEY_CHANNEL_COUNT] = {0};
void button_trace_handle(void *p)
{
static uint32_t lastTime = 0;
const uint8_t KEYSCAN_INTERVAL_TIME = 10; // 按键扫描间隔时间
const uint8_t LONG_PRESS_TIME = 100; // 长按多久生效, 实际时间为,下面同理 LONG_PRESS_TIME * KEYSCAN_INTERVAL_TIME
const uint8_t INTERVAL_TIME_SET = 20; // 两次按键检测超时
const uint8_t ANTI_SHAKE_TIME = 2; // 按键防抖检测超时
/**
* @brief 需要实现millis()函数,系统毫秒计时器。
*
*/
if (millis()> lastTime + KEYSCAN_INTERVAL_TIME)
{
lastTime = millis();
}
else
{
return;
}
/**给按键IO赋值, 有多个按键就传多少个, 自己实现ic_read函数 */
for (uint8_t i = 0; i < KEY_CHANNEL_COUNT; i++)
{
keyValues[i].io = io_read(i);
}
for (uint8_t i = 0; i < KEY_CHANNEL_COUNT; i++)
{
if (keyValues[i].io)//按下了
{
if (keyValues[i].holdTime < LONG_PRESS_TIME)
{
keyValues[i].holdTime++;
if (keyValues[i].holdTime >= ANTI_SHAKE_TIME)//防抖
{
keyValues[i].down = 1;
keyValues[i].intervalTime = INTERVAL_TIME_SET;
}
}
else //长按了,会一直标记,直到松开
{
keyValues[i].longPress = 1;
}
}
else
{
//松开了
keyValues[i].holdTime = 0;
keyValues[i].longPress = 0;
if (keyValues[i].down)//按下过了
{
if (!keyValues[i].longPress)//不是长按
keyValues[i].act++;//按下次数+1
keyValues[i].down = 0;
}
if (keyValues[i].intervalTime)//连按超时
{
keyValues[i].intervalTime--;
if (keyValues[i].intervalTime == 1)
{
LOG_D("key[%d] act:%d", i, keyValues[i].act);//打印哪个按键按了多少次
keyValues[i].duration = 10;
}
}
}
if (keyValues[i].duration >= 1)
{
keyValues[i].duration--;
if (keyValues[i].duration == 1)//按键次数保持时间到
{
keyValues[i].act = 0;
}
}
}
}
无结构体版,更方便移到51单片机上
#define KEY_DOWN_MASK 0X80/**按下标记*/
#define KEY_LONG_PRESS_MASK 0X40/**长按标记 */
#define KEY_TIMEOUT_MASK 0X10/**超时标志,此时返回按键值*/
#define KEY_TIEMES_MASK 0X0F/**按了多少次 */
#define KEY_VALUE(x) (0x0001<<(x))
#define KEY_COUNTS 5
void keyScanPro()
{
const uint8_t SHORT_PRESS_TIME = 25;
const uint16_t LONG_PRESS_TIME = 150;
const uint8_t IS_KEY_DOWN = 0X80;/**按下了 */
const uint8_t IS_LONG_PRESS = 0X40;/**长按了 */
const uint8_t IS_TIME_OUT = 0X10;/**退好久没按 */
static uint8_t keyActionHold = 0;
static uint8_t pressTimesRecord[KEY_COUNTS] = { 0 };
static uint8_t pressTime[KEY_COUNTS] = { 0 };
static uint16_t longPressTime[KEY_COUNTS] = { 0 };
const uint16_t channel_keyScan_map[KEY_COUNTS] = { DEF_SET_BIT0,DEF_SET_BIT1,DEF_SET_BIT2,DEF_SET_BIT3,DEF_SET_BIT4 };/**A,B,C,D,E,F,G,H,I,J,K,L对就的键值*/
uint8_t i;
keyValue = KP; keyValue <<= 1;
keyValue |= !K1; keyValue <<= 1;
keyValue |= !K2; keyValue <<= 1;
keyValue |= !K3; keyValue <<= 1;
keyValue |= !K4;
if (keyValue != keyValuePre)
{
ResetSystemShutdownCountdown();
keyValuePre = keyValue;
}
// LOG("keyValue:%d\n",(int)keyValue);
if (keyAction)
{
if (keyActionHold++ > 100)
{
keyActionHold = 0;
keyAction = 0;
}
}
for (i = 0; i < KEY_COUNTS; i++)
{
if (keyValue & channel_keyScan_map[i])
{
//按下了
if (longPressTime[i] < LONG_PRESS_TIME)
{
longPressTime[i]++;
pressTime[i] = SHORT_PRESS_TIME;
pressTimesRecord[i] |= IS_KEY_DOWN;
}
else
{
pressTimesRecord[i] |= IS_LONG_PRESS;
pressTimesRecord[i] |= IS_TIME_OUT;
pressTimesRecord[i] &= ~IS_KEY_DOWN; //取消标记高位
keyLongPress |= 1 << i;
keyAction |= ((i + 1) << 8);
if (keyValue & PWR_KEY_VALUE)
{
LOG("System shutting down ...");
SYS_PWR_SHUTDOWN();
while (1);
}
LOG("long press:%d\n", (int)keyLongPress);
// if (pwrKeyLongPressCb) pwrKeyLongPressCb();
// else pwrKeyLongPressCbDefault();
}
}
else
{
//松开了
longPressTime[i] = 0;
keyLongPress &= ~(1 << i);
if (pressTimesRecord[i] & IS_KEY_DOWN)
{
//高位标记过,即按下过
keyActionHold = 0;
pressTimesRecord[i] &= ~IS_KEY_DOWN; //取消标记高位
if ((pressTimesRecord[i] & KEY_TIEMES_MASK) < 15)
{
uint8_t ptc = 0;
pressTimesRecord[i]++;
ptc = pressTimesRecord[i] & KEY_TIEMES_MASK;
ptc = ptc > 7 ? 7 : ptc;
// speaker_out(music_note_freq[ptc], 100);
}
}
if (pressTime[i] > 0)
{
if (pressTime[i] == 1)
{
pressTimesRecord[i] |= IS_TIME_OUT;//BIT4 为检测时间到
}
pressTime[i]--;
}
if (pressTimesRecord[i] & IS_TIME_OUT)
{
if (pressTimesRecord[i] & IS_LONG_PRESS)
{
// rt_kprintf("Long press:%d \n", i);
pressTimesRecord[i] &= ~IS_LONG_PRESS;
}
else if (pressTimesRecord[i] & KEY_TIEMES_MASK)
{
uint8_t ptc = pressTimesRecord[i] & KEY_TIEMES_MASK;
keyAction |= (i + 1) << 4 | ptc;
LOG("keyAction:%x\n", (int)keyAction);
// struct pwrKeyActList* p;
// p = &pwrKeyActListHead;
// do {
// // LOG_D("P:0x%08X", p);
// if (p->cb)
// {
// p->cb(ptc);
// }
// p = p->next;
// } while (p);
}
// LOG("Press:%d - %d\n", (int)i, (int)(pressTimesRecord[i] & KEY_TIEMES_MASK));;
pressTimesRecord[i] = 0;
}
}
}
}