栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

原创支持单击、双击、弹起、长按的按键驱动程序

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

原创支持单击、双击、弹起、长按的按键驱动程序

头文件  KeyDriver.h

#ifndef __KEY_DRIVER_H_
#define __KEY_DRIVER_H_

#include 
#include 
#include 

#include "driver/gpio.h"


typedef signed int s32_t;
typedef unsigned int u32_t;
typedef signed short s16_t;
typedef unsigned short u16_t;
typedef signed char s8_t;
typedef unsigned char u8_t;


#define MAX_KEY_SIZE            (32)        // 最多支持按键数量
#define DEFAULT_RND_TIM         (30)        // 默认循环调用时间 30ms
#define DEFAULT_KEY_INTERVAL    (20)        // 默认连击间隔 20x30=600ms
#define DEFAULT_CB_PRESS_CNT    (100)       // 默认长按回调计数 100x30=3000ms

#define GET_PIN_STATUS(k)       gpio_get_level(k)        // 读取单一按键函数, 不同平台读取函数不同,
                                                         //  返回值要求为bool, 按下返回0

typedef struct KeyStruct {
    struct sta {
        u8_t press    : 1;      // 按下标记
        u8_t click    : 1;      // 单击标记
        u8_t upClick  : 1;      // 弹起标记
        u8_t pressExe : 1;      // 长按执行标记        
    } sta;
    u16_t keyNumber;            // 按键号(和硬件相关)
    u16_t clickCnt;             // 按键次数
    u16_t clickCntDown;         // 按键间隔时间
    u16_t pressCnt;             // 长按时间(执行次数)
    u16_t pressCBCnt;           // 长按回调时间
    void (*clickCallback)(u16_t, u16_t);   // 单击回调 传入参数为键号和点击次数
    void (*upClickCallback)(u16_t, u16_t); // 弹起回调 传入参数为键号和点击次数
    void (*pressCallback)(u16_t, u16_t);   // 长按回调 传入参数为键号和长按时间
} KeyStruct;

void ScanKeyboard(void) ;
void SetKeyInterval(u16_t tim) ;
void SetKeyboardRoundTime(u16_t tim) ;
s16_t GetKeyPressTime(u16_t num) ;
int KeyInsert(struct KeyStruct *k) ;
int KeyAdd(u16_t num, u16_t tim, void (*clickCB)(u16_t, u16_t), void (*uplickCB)(u16_t, u16_t), void (*lpressCB)(u16_t, u16_t)) ;

#endif

按键驱动程序源码


#include "KeyDriver.h"


struct KeyStruct *_gKeyBuffer[MAX_KEY_SIZE];    // 按键buffer,采用数组方式
u16_t _gRoundTime = DEFAULT_RND_TIM;            // 循环时间
u16_t _gInterval = DEFAULT_KEY_INTERVAL;        // 间隔时间
u16_t _gBtnSize = 0;                            // 按键数量


void ScanKeyboard(void) {
    u8_t tssta = 0;
    for(int i=0; i<_gBtnSize; i++) {
        tssta = !GET_PIN_STATUS(_gKeyBuffer[i]->keyNumber);
        _gKeyBuffer[i]->sta.click = tssta & (tssta ^ _gKeyBuffer[i]->sta.press);
        _gKeyBuffer[i]->clickCntDown = _gKeyBuffer[i]->clickCntDown ? _gKeyBuffer[i]->clickCntDown-1 : 0;
        if(_gKeyBuffer[i]->clickCntDown == 0)
            _gKeyBuffer[i]->clickCnt = 0;
        if(_gKeyBuffer[i]->sta.press != tssta) {
            _gKeyBuffer[i]->sta.upClick = tssta ^ _gKeyBuffer[i]->sta.press ^ _gKeyBuffer[i]->sta.click;
            if(_gKeyBuffer[i]->sta.click != 0) {
                _gKeyBuffer[i]->clickCnt += 1;
                _gKeyBuffer[i]->clickCntDown = _gInterval;
            }
        } else 
            _gKeyBuffer[i]->sta.upClick = 0;
        _gKeyBuffer[i]->sta.press = tssta;
        _gKeyBuffer[i]->pressCnt = (tssta ? _gKeyBuffer[i]->pressCnt+1 : 0);
        
        if(_gKeyBuffer[i]->sta.click == 1 && _gKeyBuffer[i]->clickCallback != NULL) {
            _gKeyBuffer[i]->clickCallback(i, _gKeyBuffer[i]->clickCnt);
        } else if(_gKeyBuffer[i]->sta.upClick == 1 && _gKeyBuffer[i]->upClickCallback != NULL) {
            _gKeyBuffer[i]->upClickCallback(i, _gKeyBuffer[i]->clickCnt);
        }
        if((_gKeyBuffer[i]->pressCallback != NULL) && (_gKeyBuffer[i]->pressCnt >= _gKeyBuffer[i]->pressCBCnt)) {
            if(_gKeyBuffer[i]->sta.pressExe == 0) {
                _gKeyBuffer[i]->sta.pressExe = 1;
                _gKeyBuffer[i]->pressCallback(i, _gKeyBuffer[i]->pressCnt*_gRoundTime);
            }
        } else 
            _gKeyBuffer[i]->sta.pressExe = 0;
    }
}


void SetKeyInterval(u16_t tim) {
    _gInterval = tim/_gRoundTime;
}


void SetKeyboardRoundTime(u16_t tim) {
    u16_t tsInterval = _gRoundTime * _gInterval;
    _gRoundTime = tim;
    _gInterval = tsInterval / tim;  // 重新计算连击间隔时间
}


s16_t GetKeyPressTime(u16_t num) {
    if(num < _gBtnSize) {
        return _gKeyBuffer[num]->pressCnt * _gRoundTime;
    }
    return -1;
}


int KeyInsert(struct KeyStruct *k) {
    if(_gBtnSize >= MAX_KEY_SIZE)
        return -1;
    if(k->pressCBCnt < _gInterval)
        k->pressCBCnt = DEFAULT_CB_PRESS_CNT;
    _gKeyBuffer[_gBtnSize] = k;
    _gBtnSize += 1;
    return 0;
}


int KeyAdd(u16_t num, u16_t tim, void (*clickCB)(u16_t, u16_t), void (*uplickCB)(u16_t, u16_t), void (*lpressCB)(u16_t, u16_t)) {
    if(_gBtnSize >= MAX_KEY_SIZE)
        return -1;
    struct KeyStruct *k = malloc(sizeof(struct KeyStruct));
    if(k == NULL)
        return -2;
    memset(k,0,sizeof(struct KeyStruct));
    k->keyNumber = num;
    k->pressCBCnt = tim/_gRoundTime;
    k->clickCallback = clickCB;
    k->upClickCallback = uplickCB;
    k->pressCallback = lpressCB;
    if(k->pressCBCnt < _gInterval)
        k->pressCBCnt = DEFAULT_CB_PRESS_CNT;
    _gKeyBuffer[_gBtnSize] = k;
    _gBtnSize += 1;
    return 0;
}

使用说明看源代码顶端的说明部分

示例

#define GPIO_INPUT_SW1 11


void keyLpress(u16_t k, u16_t t) {  // 长按1秒回调
    printf("Key %d Long press  %d", k,t);
}

void keyClick(u16_t k, u16_t c) { // 按键回调    
    printf("Click %d  cnt=%d",k,c);
}

void keyUpClick(u16_t k, u16_t c) { // 按键回调    
    printf("UpClick %d  cnt=%d",k,c);
}


SetKeyboardRoundTime(100);
KeyAdd(GPIO_INPUT_SW1,1000,keyClick,keyUpClick,keyLpress);  // 注册按键


// 其中这部分是ESP32中的定义
#define GET_PIN_STATUS(k)       gpio_get_level(k) 

STM32使用的话需要修改一下单按键读取的部分

也就是  #define GET_PIN_STATUS(k)

这里的定义

我一般采用下面的这种定义方式,里面的地址是STM32F10X的地址

其他系列需要修改

//-另外一种定义方式
#define nPA(n)     (0x20+n)
#define nPB(n)     (0x30+n)
#define nPC(n)     (0x40+n)
#define nPD(n)     (0x50+n)
#define nPE(n)     (0x60+n)
#define nPF(n)     (0x70+n)
#define nPG(n)     (0x80+n)

#define nRCC(PIN)      (1<<(PIN>>4))                                            // 转换为端口时钟
#define nPIN(PIN)      (1<<(PIN&0x0f))                                          // 转换为管脚号
#define nGIO(PIN)      ((GPIO_TypeDef *)(((PIN&0xF0)<<6) + APB2PERIPH_BASE))    // 转换为端口号


#define GPIO_PinOutHL(pin,sta)      (nGIO(pin)->BSRR = (nPIN(pin)<<(((sta)==0)<<4)))   // GPIO按位输出指定电平
#define GPIO_PinOutHigh(pin)        (nGIO(pin)->BSRR = nPIN(pin))                      // GPIO按位输出高
#define GPIO_PinOutLow(pin)         (nGIO(pin)->BRR  = nPIN(pin))                      // GPIO按位输出低
#define GPIO_PinOutRev(pin)         (nGIO(pin)->ODR ^= nPIN(pin))                      // GPIO按位输出反
#define GPIO_PortOutHigh(port,pin)  (nGIO(port)->BSRR = pin)                           // GPIO端口输出高  暂时有问题
#define GPIO_PortOutLow(port,pin)   (nGIO(port)->BRR  = pin)                           // GPIO端口输出低  暂时有问题
#define GPIO_PortOutRev(port,pin)   (nGIO(port)->ODR ^= pin)                           // GPIO端口输出反  暂时有问题

#define GPIO_PinIn(pin)             ((nGIO(pin)->IDR & nPIN(pin)) != 0)      // 读取单一端口状态
#define GPIO_PortIn(pin)            (nGIO(pin)->IDR )                        // 读取端口状态(输入该端口任何一个管脚即可)

#define GPIO_PinOutSta(pin)         ((nGIO(pin)->ODR & nPIN(pin)) != 0)      // 读取单一端口输出状态
#define GPIO_PortOutSta(port)       (nGIO(pin)->ODR )                        // 读取端口输出状态(输入该端口任何一个管脚即可)

使用方式如下

// 修改读取IO的接口
#define GET_PIN_STATUS(k)    GPIO_PinIn(k)



#define GPIO_INPUT_SW1  nPA(1)


SetKeyboardRoundTime(100);
KeyAdd(GPIO_INPUT_SW1,800,keyClick,NULL,keyLpress);  // 注册按键

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/934310.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号