/*
**************************************************************************************************
* 深圳新元电子工作室
*
* 文 件:stm32_key.h 模板
* 描 述:1.按键消抖
* 2.按键按下键码
* 3.按键弹起键码
* 4.按键长按键码
* 5.复合按键
*
* 版 本:V0.0
* 实验平台: 野火 iSO STM32 开发板
* 备 注:通过修改“移植修改区” 适合开发用 方便移植
* 编 辑:雨海
* 日 期:2017-03-15
***************************************************************************************************
*/
#ifndef _STM32_KEY_H
#define _STM32_KEY_H
#include "stm32f10x.h"
/*移植可修改区=======================================================*/
#define KEY_ActiveLevel 0 /* 0:低电平按键执行 1:高电平按键执行*/
#define KEY_NUM 2 /* 按键个数 */
/*按键接口定义 */
#define KEY_Pin_1 GPIO_Pin_0
#define KEY_Port_1 GPIOA
#define KEY_CLK_1 RCC_APB2Periph_GPIOA
#define KEY_Pin_2 GPIO_Pin_13
#define KEY_Port_2 GPIOC
#define KEY_CLK_2 RCC_APB2Periph_GPIOC
/* 按键名字ID */
typedef enum
{
KID_1 = 0,
KID_2,
} KEY_ID;
/*定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件*/
typedef enum
{
KEY_NONE = 0, /* 0 表示按键事件 */
KEY_1_DOWN,
KEY_1_UP,
KEY_1_LONG,
KEY_2_DOWN,
KEY_2_UP,
KEY_2_LONG,
} KEY_ENUM;
/*移植禁止修改区=======================================================*/
/*
按键滤波时间50ms, 单位10ms。
只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME 5 /*按键滤波时间 单位10MS */
#define KEY_LONG_TIME 100 /* 单位10ms, 持续1秒,认为长按事件 */
#define KEY_FIFO_SIZE 6 /* 按键FIFO用到变量 */
/*每个按键属性对应1个全局的结构体变量*/
typedef struct
{
uint8_t (*IsKeyDownFunc)(KEY_ID); /* 函数指针,指向判断按键手否按下的函数,1表示按下 */
uint8_t FilterCount; /* 滤波器计数器 */
uint16_t LongFilterCount; /* 长按计数器 */
uint16_t LongTime; /* 按键按下持续时间, 0表示不检测长按 */
uint8_t ButtonState; /* 按键当前状态(按下还是弹起) */
uint8_t RepeatSpeed; /* 连续按键周期 */
uint8_t RepeatCount; /* 连续按键计数器 */
} KEY_typeDef;
/*按键缓冲*/
typedef struct
{
uint8_t Buf[KEY_FIFO_SIZE]; /* 键值缓冲区 */
uint8_t Read; /* 缓冲区读指针 */
uint8_t Write; /* 缓冲区写指针 */
} KeyBuf_typeDef;
/*供外部调用的函数声明*/
void Key_Init(void);
void Key_Scan(void);//共节拍调用
void Key_Put(uint8_t _KeyCode);
uint8_t Key_Get(void);
uint8_t Key_State(KEY_ID _ucKeyID);
void Key_SetParam(uint8_t KeyID, uint16_t LongTime, uint8_t Speed);
void Key_Clear(void);
#endif
/***************************** END STM32_KEY.H*********************************/
/*
**************************************************************************************************
* 深圳新元电子工作室
*
* 文 件:stm32_key.c 模板
* 描 述:1.按键消抖
* 2.按键按下键码
* 3.按键弹起键码
* 4.按键长按键码
* 5.复合按键
*
* 版 本:V0.0
* 实验平台: 野火 iSO STM32 开发板
* 备 注:通过修改“移植修改区” 适合开发用 方便移植
* 编 辑:雨海
* 日 期:2017-03-15
***************************************************************************************************
*/
#include "stm32_key.h"
/*移植可修改区=======================================================*/
const uint16_t KEY_Pin[KEY_NUM] ={ KEY_Pin_1, KEY_Pin_2 };
GPIO_TypeDef* KEY_Port[KEY_NUM] ={ KEY_Port_1,KEY_Port_2};
const uint32_t KEY_CLK[KEY_NUM] ={ KEY_CLK_1, KEY_CLK_2 };
/*移植禁止修改区=======================================================*/
static KEY_typeDef BUTTON[KEY_NUM]; //按键结构体类型的结构体变量
static KeyBuf_typeDef key; //FIFO变量定义
/**
* @brief 配置按键对应的GPIO
* @param None
* @retval None
*/
static void Key_IOConfig(void)
{
uint8_t i=0;
GPIO_InitTypeDef GPIO_InitStructure;
for(i=0;i<KEY_NUM;i++)
{
RCC_APB2PeriphClockCmd(KEY_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = KEY_Pin;
if(KEY_ActiveLevel==1)
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
else
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(KEY_Port, &GPIO_InitStructure);
}
}
/**
* @brief 判断按键是否按下
* @param keyName:按键名字
* @retval 返回值1 表示按下,0表示未按下
*/
static uint8_t IsKeyDown(KEY_ID keyName)
{
if(KEY_ActiveLevel==1)
{
if((KEY_Port[keyName]->IDR & KEY_Pin[keyName]) != 0)
return 1; else return 0;
}
else
{
if((KEY_Port[keyName]->IDR & KEY_Pin[keyName]) == 0)
return 1; else return 0;
}
}
/**
* @brief 初始化按键
* @param None
* @retval None
*/
void Key_Init(void)
{
uint8_t i;
/* 对按键FIFO读写指针清零 */
key.Read = 0;
key.Write = 0;
Key_IOConfig();
/* 给每个按键结构体成员变量赋一组缺省值 */
for (i = 0; i < KEY_NUM; i++)
{
BUTTON.LongTime = KEY_LONG_TIME; /* 长按时间 0 表示不检测长按键事件 */
BUTTON.FilterCount = KEY_FILTER_TIME / 2; /* 计数器设置为滤波时间的一半 */
BUTTON.ButtonState = 0; /* 按键缺省状态,0为未按下 */
BUTTON.RepeatSpeed = 0; /* 按键连发的速度,0表示不支持连发 */
BUTTON.RepeatCount = 0; /* 连发计数器 */
BUTTON.IsKeyDownFunc = IsKeyDown; /* 判断按键按下的函数 */
}
}
/**
* @brief 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
* @param KeyCode : 按键代码
* @retval None
*/
void Key_Put(uint8_t _KeyCode)
{
key.Buf[key.Write] = _KeyCode;
if (++key.Write >= KEY_FIFO_SIZE)
{
key.Write = 0;
}
}
/**
* @brief 从按键FIFO缓冲区读取一个键值。
* @param None
* @retval 按键代码
*/
uint8_t Key_Get(void)
{
uint8_t ret;
if (key.Read == key.Write)
{
return KEY_NONE;
}
else
{
ret = key.Buf[key.Read];
if (++key.Read >= KEY_FIFO_SIZE)
{
key.Read = 0;
}
return ret;
}
}
/**
* @brief 读取按键的状态
* @param _ucKeyID : 按键ID,从0开始
* @retval 1 表示按下, 0 表示未按下
*/
uint8_t Key_GetButtonState(KEY_ID KeyID)
{
return BUTTON[KeyID].ButtonState;
}
/**
* @brief 设置按键参数
* @param _ucKeyID : 按键ID,从0开始
* _LongTime : 长按事件时间
* _RepeatSpeed : 连发速度
* @retval None
*/
void Key_SetParam(uint8_t KeyID, uint16_t LongTime, uint8_t RepeatSpeed)
{
BUTTON[KeyID].LongTime = LongTime; /* 长按时间 0 表示不检测长按键事件 */
BUTTON[KeyID].RepeatSpeed = RepeatSpeed; /* 按键连发的速度,0表示不支持连发 */
BUTTON[KeyID].RepeatCount = 0; /* 连发计数器 */
}
/**
* @brief 清空按键FIFO缓冲区
* @param None
* @retval None
*/
void Key_Clear(void)
{
key.Read = key.Write;
}
/**
* @brief 检测一个按键。非阻塞状态,必须被周期性的调用。
* @param 按键结构变量指针
* @retval None
*/
static void Key_Detect(uint8_t i) //10MS执行一次
{
KEY_typeDef *pBtn;
/*
如果没有初始化按键函数,则报错
if (BUTTON.IsKeyDownFunc == 0)
{
printf("Fault : DetectButton(), BUTTON.IsKeyDownFunc undefine");
}
*/
pBtn = &BUTTON;
if (pBtn->IsKeyDownFunc((KEY_ID)i))//判断i按键是否按下
{
if (pBtn->FilterCount < KEY_FILTER_TIME)
{
pBtn->FilterCount = KEY_FILTER_TIME;
}
else if(pBtn->FilterCount < 2 * KEY_FILTER_TIME)
{
pBtn->FilterCount++;//10ms自加
}
else
{
if (pBtn->ButtonState == 0)
{
pBtn->ButtonState = 1;
/* 发送按钮按下的消息 */
Key_Put((uint8_t)(3 * i + 1));
}
if (pBtn->LongTime > 0)
{
if (pBtn->LongFilterCount < pBtn->LongTime)
{
/* 发送按钮持续按下的消息 */
if (++pBtn->LongFilterCount == pBtn->LongTime)
{
/* 键值放入按键FIFO */
Key_Put((uint8_t)(3 * i + 3));
}
}
else
{
if (pBtn->RepeatSpeed > 0)
{
if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
{
pBtn->RepeatCount = 0;
/* 常按键后,每隔10ms发送1个按键 */
Key_Put((uint8_t)(3 * i + 1));
}
}
}
}
}
}
else
{
if(pBtn->FilterCount > KEY_FILTER_TIME)
{
pBtn->FilterCount = KEY_FILTER_TIME;
}
else if(pBtn->FilterCount != 0)
{
pBtn->FilterCount--;
}
else
{
if (pBtn->ButtonState == 1)
{
pBtn->ButtonState = 0;
/* 发送按钮弹起的消息 */
Key_Put((uint8_t)(3 * i + 2));
}
}
pBtn->LongFilterCount = 0;
pBtn->RepeatCount = 0;
}
}
/**
* @brief 扫描所有按键。非阻塞,被systick中断周期性的调用
* @param None
* @retval None
*/
void Key_Scan(void)//10MS执行一次
{
uint8_t i;
static uint8_t s_count = 0; //按键扫描定时计数器
/*按键扫描检测程序段*/
if (++s_count >= 10)
{
s_count = 0;
for (i = 0; i < KEY_NUM; i++)
{
Key_Detect(i);//10MS执行一次
}
}
}
/***************************** END STM32_KEY.C**********************************************************/
void SysTick_Handler(void)
{
Key_Scan();
}
|