349 lines
7.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//按键 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, 50, 6);
KEY_SetKeyParam(JOY_D, 50, 6);
KEY_SetKeyParam(JOY_L, 50, 6);
KEY_SetKeyParam(JOY_R, 50, 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);
}