头文件 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); // 注册按键



