当前位置: 首页 > article >正文

单片机按键扫描程序,可以单击、双击、长按,使用状态机,无延时,不阻塞。

  • 根据按下时的时长、间隔来判断是否是连按或者长按。
  • 当连按间隔很短时,计录连按次数
  • 超过连接间隔时,回报按下次数
  • 根据按键次数自行判断是单击、双击、三击、四击。。。最多记录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;
            }
        }
    }
}


http://www.kler.cn/a/466216.html

相关文章:

  • Transformer知识梳理
  • redis源码系列--(四)--redis cluster
  • windows11安装minikube
  • PDF文件提示-文档无法打印-的解决办法
  • web漏洞之文件包含漏洞
  • lenovo联想IdeaPad 15sIML 2020款(81WB)笔记本电脑原装出厂OEM预装系统Windows10镜像下载
  • JavaScript中的“==”和“===”有什么区别
  • Docker 容器技术与 K8s
  • 七、Hadoop环境搭建之安装JDK
  • 基于RNN模型的心脏病预测,提供tensorflow和pytorch实现
  • 单元测试3.0+ @RunWith(JMockit.class)+mock+injectable+Expectations
  • 【工具进阶】使用 Nmap 进行有效的服务和漏洞扫描
  • 报考重庆大学计算机研究生有哪些要求?
  • 弧形导轨如何避免生锈?
  • 学AI编程的Prompt工程,豆包Marscode
  • 扩展正则表达式
  • Python提取目标Json键值:包含子嵌套列表和字典
  • DAY178内网渗透之内网对抗:横向移动篇入口差异切换上线IPC管道ATSC任务Impacket套件UI插件
  • 机器学习和深度学习
  • IDEA自带插件禁用,减少内存占用
  • 快速理解MIMO技术
  • 讲解一下$.ajax
  • 如何向您的网站添加 SSL 证书?
  • QT------------------串口编程
  • 计算机网络 (21)网络层的几个重要概念
  • 【机器学习:一、机器学习简介】