349 lines
7.2 KiB
C
349 lines
7.2 KiB
C
//按键 FIFO 库
|
||
|
||
#include "adc.h"
|
||
|
||
#include "key.h"
|
||
|
||
#define DIGITAL_KEY_NUM 3 //数字信号按键个数
|
||
#define ADC_KEY_NUM 4 //模拟信号按键个数
|
||
#define KEY_COUNT (DIGITAL_KEY_NUM + ADC_KEY_NUM)
|
||
|
||
//使能 GPIO 时钟
|
||
#define ALL_KEY_GPIO_CLK_ENABLE() \
|
||
{ \
|
||
__HAL_RCC_GPIOE_CLK_ENABLE(); \
|
||
__HAL_RCC_GPIOC_CLK_ENABLE(); \
|
||
__HAL_RCC_GPIOD_CLK_ENABLE(); \
|
||
};
|
||
|
||
typedef struct
|
||
{
|
||
GPIO_TypeDef *gpio;
|
||
uint16_t pin;
|
||
} X_GPIO_T;
|
||
|
||
static const X_GPIO_T s_gpio_list[DIGITAL_KEY_NUM] = {
|
||
{GPIOE, GPIO_PIN_3}, //KEY1
|
||
{GPIOC, GPIO_PIN_5}, //KEY2
|
||
{GPIOD, GPIO_PIN_10} //JOY_OK
|
||
};
|
||
|
||
static volatile KEY_T s_tBtn[KEY_COUNT] = {0};
|
||
static volatile KEY_FIFO_T s_tKey; //按键 FIFO 变量
|
||
static volatile is_key_clear = 1;
|
||
|
||
static void InitKeyVar(void);
|
||
static void InitKeyDigital(void);
|
||
static void DetectKey(uint8_t i);
|
||
|
||
static uint8_t KeyPinActive(uint8_t _id)
|
||
{
|
||
if (_id < DIGITAL_KEY_NUM)
|
||
{
|
||
uint8_t level;
|
||
|
||
if ((s_gpio_list[_id].gpio->IDR & s_gpio_list[_id].pin) == 0)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
else if (_id < DIGITAL_KEY_NUM + ADC_KEY_NUM)
|
||
{
|
||
switch (_id)
|
||
{
|
||
case JOY_U:
|
||
{
|
||
if (ADC_Get_PC0() < 0.01)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
break;
|
||
|
||
case JOY_D:
|
||
{
|
||
if (ADC_Get_PC0() > 3.20)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
break;
|
||
|
||
case JOY_L:
|
||
{
|
||
if (ADC_Get_PC1() < 0.01)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
break;
|
||
|
||
case JOY_R:
|
||
{
|
||
if (ADC_Get_PC1() > 3.20)
|
||
return 1;
|
||
else
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static uint8_t IsKeyDownFunc(uint8_t _id)
|
||
{
|
||
uint8_t count = 0;
|
||
uint8_t save = UINT8_MAX;
|
||
|
||
//判断有几个键按下
|
||
for (uint8_t i = 0; i < KEY_COUNT; i++)
|
||
{
|
||
if (KeyPinActive(i))
|
||
{
|
||
count++;
|
||
save = i;
|
||
}
|
||
}
|
||
|
||
if (count == 1 && save == _id)
|
||
return 1; //只有单个键按下时才有效
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 初始化按键
|
||
*/
|
||
void KEY_Init(void)
|
||
{
|
||
InitKeyVar(); //初始化按键变量
|
||
InitKeyDigital(); //初始化数字按键
|
||
}
|
||
|
||
static void InitKeyDigital(void)
|
||
{
|
||
GPIO_InitTypeDef gpio_init;
|
||
|
||
ALL_KEY_GPIO_CLK_ENABLE();
|
||
|
||
gpio_init.Mode = GPIO_MODE_INPUT;
|
||
gpio_init.Pull = GPIO_PULLUP;
|
||
gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||
|
||
for (uint8_t i = 0; i < DIGITAL_KEY_NUM; i++)
|
||
{
|
||
gpio_init.Pin = s_gpio_list[i].pin;
|
||
HAL_GPIO_Init(s_gpio_list[i].gpio, &gpio_init);
|
||
}
|
||
}
|
||
|
||
static void InitKeyVar(void)
|
||
{
|
||
s_tKey.Read = 0;
|
||
s_tKey.Write = 0;
|
||
|
||
//缺省值
|
||
for (uint8_t i = 0; i < KEY_COUNT; i++)
|
||
{
|
||
s_tBtn[i].LongTime = KEY_LONG_TIME; //若设置为 0 表示不检测长按键事件
|
||
s_tBtn[i].Count = KEY_FILTER_TIME / 2; //计数器设置为滤波时间的一半
|
||
s_tBtn[i].State = 0; //按键缺省状态,0 为未按下
|
||
s_tBtn[i].RepeatSpeed = 0; //按键连发的速度,0 表示不支持连发
|
||
s_tBtn[i].RepeatCount = 0; //连发计数器
|
||
}
|
||
|
||
//摇杆上下左右,支持长按 1 秒后,自动连发
|
||
KEY_SetKeyParam(JOY_U, 100, 6);
|
||
KEY_SetKeyParam(JOY_D, 100, 6);
|
||
KEY_SetKeyParam(JOY_L, 100, 6);
|
||
KEY_SetKeyParam(JOY_R, 100, 6);
|
||
}
|
||
|
||
/**
|
||
* @brief 将键值压入按键 FIFO。可用于模拟一个按键事件
|
||
* @param _KeyCode: 键值
|
||
*/
|
||
void KEY_PutKey(uint8_t _KeyCode)
|
||
{
|
||
s_tKey.Buf[s_tKey.Write] = _KeyCode;
|
||
|
||
if (++s_tKey.Write >= KEY_FIFO_SIZE)
|
||
{
|
||
s_tKey.Write = 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 从按键 FIFO 读取一个键值
|
||
* @retval 键值
|
||
*/
|
||
uint8_t KEY_GetKey(void)
|
||
{
|
||
if (s_tKey.Read == s_tKey.Write)
|
||
{
|
||
return KEY_NONE;
|
||
}
|
||
else
|
||
{
|
||
uint8_t ret = s_tKey.Buf[s_tKey.Read];
|
||
|
||
if (++s_tKey.Read >= KEY_FIFO_SIZE)
|
||
s_tKey.Read = 0;
|
||
|
||
if (
|
||
ret == KEY1_UP ||
|
||
ret == KEY2_UP ||
|
||
ret == JOY_OK_UP ||
|
||
ret == JOY_U_UP ||
|
||
ret == JOY_D_UP ||
|
||
ret == JOY_L_UP ||
|
||
ret == JOY_R_UP)
|
||
{
|
||
if (is_key_clear)
|
||
{
|
||
is_key_clear = 0;
|
||
return KEY_NONE;
|
||
}
|
||
}
|
||
|
||
is_key_clear = 0;
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 从按键 FIFO 读取一个键值,空值时阻塞
|
||
* @retval 键值
|
||
*/
|
||
uint8_t KEY_GetKeyWait(void)
|
||
{
|
||
uint8_t key = KEY_NONE;
|
||
|
||
while (key == KEY_NONE)
|
||
key = KEY_GetKey();
|
||
|
||
return key;
|
||
}
|
||
|
||
/**
|
||
* @brief 读取某键状态
|
||
* @param _ucKeyID: 按键 ID
|
||
* @retval 1 表示按下,0 表示未按下
|
||
*/
|
||
uint8_t KEY_GetKeyState(KEY_ID_E _ucKeyID)
|
||
{
|
||
return s_tBtn[_ucKeyID].State;
|
||
}
|
||
|
||
/**
|
||
* @brief 等待特定键按下
|
||
* @param _ucKeyID: 按键 ID
|
||
*/
|
||
void KEY_WaitKey(KEY_ID_E _ucKeyID)
|
||
{
|
||
while (KEY_GetKeyState(_ucKeyID) != 1)
|
||
;
|
||
}
|
||
|
||
/**
|
||
* @brief 设置按键参数
|
||
* @param _ucKeyID: 按键 ID
|
||
* @param _LongTime: 长按事件时间
|
||
* @param _RepeatSpeed: 连发速度
|
||
*/
|
||
void KEY_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t _RepeatSpeed)
|
||
{
|
||
s_tBtn[_ucKeyID].LongTime = _LongTime; //长按时间 0 表示不检测长按键事件
|
||
s_tBtn[_ucKeyID].RepeatSpeed = _RepeatSpeed; //按键连发的速度,0 表示不支持连发
|
||
s_tBtn[_ucKeyID].RepeatCount = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief 清空按键 FIFO
|
||
*/
|
||
void KEY_ClearKey(void)
|
||
{
|
||
s_tKey.Read = s_tKey.Write;
|
||
is_key_clear = 1;
|
||
}
|
||
|
||
static void DetectKey(uint8_t i)
|
||
{
|
||
KEY_T *pBtn;
|
||
|
||
pBtn = &s_tBtn[i];
|
||
if (IsKeyDownFunc(i))
|
||
{
|
||
if (pBtn->Count < KEY_FILTER_TIME)
|
||
{
|
||
pBtn->Count = KEY_FILTER_TIME;
|
||
}
|
||
else if (pBtn->Count < 2 * KEY_FILTER_TIME)
|
||
{
|
||
pBtn->Count++;
|
||
}
|
||
else
|
||
{
|
||
if (pBtn->State == 0)
|
||
{
|
||
pBtn->State = 1;
|
||
|
||
KEY_PutKey((uint8_t)(3 * i + 1));
|
||
}
|
||
|
||
if (pBtn->LongTime > 0)
|
||
{
|
||
if (pBtn->LongCount < pBtn->LongTime)
|
||
{
|
||
if (++pBtn->LongCount == pBtn->LongTime)
|
||
{
|
||
KEY_PutKey((uint8_t)(3 * i + 3));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (pBtn->RepeatSpeed > 0)
|
||
{
|
||
if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
|
||
{
|
||
pBtn->RepeatCount = 0;
|
||
KEY_PutKey((uint8_t)(3 * i + 1));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (pBtn->Count > KEY_FILTER_TIME)
|
||
{
|
||
pBtn->Count = KEY_FILTER_TIME;
|
||
}
|
||
else if (pBtn->Count != 0)
|
||
{
|
||
pBtn->Count--;
|
||
}
|
||
else
|
||
{
|
||
if (pBtn->State == 1)
|
||
{
|
||
pBtn->State = 0;
|
||
|
||
KEY_PutKey((uint8_t)(3 * i + 2));
|
||
}
|
||
}
|
||
|
||
pBtn->LongCount = 0;
|
||
pBtn->RepeatCount = 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 扫描所有按键。非阻塞,应被 SysTick 中断周期性的调用,10ms 一次
|
||
*/
|
||
void KEY_Scan10ms(void)
|
||
{
|
||
for (uint8_t i = 0; i < KEY_COUNT; i++)
|
||
DetectKey(i);
|
||
}
|