单片机按键FIFO

本文代码参考安富莱按键FIFO

FIFO数据结构如果不清楚可以参考博文简单的FIFO

一般的单片机系统,按键作为人机交互工具是必不可少的,但是普通的按键需要消抖处理,极大的增加了程序开销,降低系统实时性。

安富莱的FIFO按键,无需延时处理消抖,可以记录按键按下、弹起、长按、组合按,并且移植起来也十分方便。之前在做一个项目时,用到一个矩阵键盘,移植了这个按键FIFO程序,用起来效果很不错。

主要流程就是开启一个10ms的定时器中断,在中断中扫描按键状态,并对按键状态进行分析消抖处理,如果按键动作,将按键动作压入FIFO中,在主循环中读取FIFO,获取按键状态。
在这里插入图片描述

使用时首先要调用初始化函数,此函数有两个子函数,分别完成变量初始化和板子硬件初始化。

/*
*********************************************************************************************************
*	函 数 名: bsp_InitKey
*	功能说明: 初始化按键. 该函数被 bsp_Init() 调用。
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
	bsp_InitKeyVar();		/* 初始化按键变量 */
	bsp_InitKeyHard();		/* 初始化按键硬件 */
}

移植时需要注意对应IO硬件初始化修改成自己的,这里使用的STC8A单片机,3*4矩阵键盘,使用的IO通过宏定义封装起来。
在这里插入图片描述


/* 矩阵键盘 */
#define C1_PIN             1
#define C1_GPIO_PORT       2

#define C2_PIN             2
#define C2_GPIO_PORT       2

#define C3_PIN             3
#define C3_GPIO_PORT       2

#define C4_PIN             4
#define C4_GPIO_PORT       2

 
// Row1, Row2, Row3, Row4
#define R1_PIN             1
#define R1_GPIO_PORT       4

#define R2_PIN             0
#define R2_GPIO_PORT       2

#define R3_PIN             2
#define R3_GPIO_PORT       4


/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyHard
*	功能说明: 配置按键对应的GPIO
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{
	//KEY 初始化
    PIN_InitPushPull(C1_GPIO_PORT, C1_PIN);
	PIN_InitPushPull(C2_GPIO_PORT, C2_PIN);
	PIN_InitPushPull(C3_GPIO_PORT, C3_PIN);
	PIN_InitPushPull(C4_GPIO_PORT, C4_PIN);
	PIN_InitOpenDrain(R1_GPIO_PORT, R1_PIN);
	PIN_InitOpenDrain(R2_GPIO_PORT, R2_PIN);
	PIN_InitOpenDrain(R3_GPIO_PORT, R3_PIN);
	
}

按键参数初始化需要注意设置连发速度来确定对应按键是否支持连按。还要自行修改判断按键按下函数和按键个数

/*
	按键滤波时间50ms, 单位10ms。
	只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
	即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100		     	/* 单位10ms, 持续1秒,认为长按事件 */
#define KEY_COUNT    13	   					/* 按键个数, 12个独立建 + 1 个组合键 */
static KEY_T xdata s_tBtn[KEY_COUNT];
static KEY_FIFO_T xdata s_tKey;		/* 按键FIFO变量,结构体 */

/* 检测按键按下函数 */
static uint8_t IsKeyDown0(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown1(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; NOP(50); if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown2(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown3(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown4(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown5(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown6(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown7(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown8(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown9(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown10(void) {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; NOP(50); if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown11(void) {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}

/* 组合按键 key1 && key2 */
static uint8_t IsKeyDown12(void)  { return IsKeyDown1() && IsKeyDown2();}
/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyVar
*	功能说明: 初始化按键变量
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
	uint8_t xdata i;

	/* 对按键FIFO读写指针清零 */
	fifo_init(&s_tKey);
	
	/* 给每个按键结构体成员变量赋一组缺省值 */
	for (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].KeyCodeDown = 3 * i + 1;				/* 按键按下的键值代码 */
		//s_tBtn[i].KeyCodeUp   = 3 * i + 2;				/* 按键弹起的键值代码 */
		//s_tBtn[i].KeyCodeLong = 3 * i + 3;				/* 按键被持续按下的键值代码 */
		s_tBtn[i].RepeatSpeed = 0;						/* 按键连发的速度,0表示不支持连发 */
		s_tBtn[i].RepeatCount = 0;						/* 连发计数器 */
	}


	/* 判断按键按下的函数 */
	s_tBtn[0].IsKeyDownFunc = IsKeyDown0;
	s_tBtn[1].IsKeyDownFunc = IsKeyDown1;
	s_tBtn[2].IsKeyDownFunc = IsKeyDown2;
	s_tBtn[3].IsKeyDownFunc = IsKeyDown3;
	s_tBtn[4].IsKeyDownFunc = IsKeyDown4;
	s_tBtn[5].IsKeyDownFunc = IsKeyDown5;
	s_tBtn[6].IsKeyDownFunc = IsKeyDown6;
	s_tBtn[7].IsKeyDownFunc = IsKeyDown7;
	s_tBtn[8].IsKeyDownFunc = IsKeyDown8;
	s_tBtn[9].IsKeyDownFunc = IsKeyDown9;
	s_tBtn[10].IsKeyDownFunc = IsKeyDown10;
	s_tBtn[11].IsKeyDownFunc = IsKeyDown11;
	
	/* 组合按键 */
	s_tBtn[12].IsKeyDownFunc = IsKeyDown12;
}

按键扫描函数,需要开启一个10ms的定时器中断,在中断中对按键状态进行扫描。


/*
*********************************************************************************************************
*	函 数 名: bsp_DetectKey
*	功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*	形    参:  按键结构变量指针
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
	KEY_T xdata *pBtn;

	/*
		如果没有初始化按键函数,则报错
		if (s_tBtn[i].IsKeyDownFunc == 0)
		{
			printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
		}
	*/

	pBtn = &s_tBtn[i];
	if (pBtn->IsKeyDownFunc())
	{
		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;

				/* 发送按钮按下的消息 */
				bsp_PutKey((uint8_t)(3 * i + 1));
			}

			if (pBtn->LongTime > 0)
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 发送按钮持续按下的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 键值放入按键FIFO */
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
				else
				{
					if (pBtn->RepeatSpeed > 0)
					{
						if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							/* 常按键后,每隔10ms发送1个按键 */
							bsp_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;

				/* 发送按钮弹起的消息 */
				bsp_PutKey((uint8_t)(3 * i + 2));
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}
/*
*********************************************************************************************************
*	函 数 名: bsp_KeyScan
*	功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan(void)
{
	uint8_t xdata i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

在主函数中,非堵塞方式获取按键键值

/*
*********************************************************************************************************
*	函 数 名: bsp_GetKey
*	功能说明: 从按键FIFO缓冲区读取一个键值。
*	形    参:  无
*	返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
	uint8_t xdata ret;

	if (fifo_pop(&s_tKey, &ret) == 1)
	{
		return KEY_NONE;
	}
	else
	{
		return ret;
	}
}

完整代码如下
bsp_key.h

/*!
  * @file     BSP_KEY.h
  *
  * @brief    按键驱动文件
  *
  * @company  
  *
  * @author   不咸不要钱
  *
  * @note     无
  *
  * @version  
  *
  * @date     2019/10/18 星期五
  */ 
#ifndef __LQ_KEY_H
#define __LQ_KEY_H

#include "LQ_GPIO.h"



/* 矩阵键盘 */
#define C1_PIN             1
#define C1_GPIO_PORT       2

#define C2_PIN             2
#define C2_GPIO_PORT       2

#define C3_PIN             3
#define C3_GPIO_PORT       2

#define C4_PIN             4
#define C4_GPIO_PORT       2

 
// Row1, Row2, Row3, Row4
#define R1_PIN             1
#define R1_GPIO_PORT       4

#define R2_PIN             0
#define R2_GPIO_PORT       2

#define R3_PIN             2
#define R3_GPIO_PORT       4




#define KEY_COUNT    13	   					/* 按键个数, 12个独立建 + 1 个组合键 */



/* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
typedef enum
{
	KID_K0,
	KID_K1, 
	KID_K2,
	KID_K3,
	KID_K4,
	KID_K5,
	KID_K6,
	KID_K7,
	KID_K8,
	KID_K9,
	KID_K10,
	KID_K11,
	KID_K12,
	
}KEY_ID_E;

/*
	按键滤波时间50ms, 单位10ms。
	只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件
	即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
*/
#define KEY_FILTER_TIME   5
#define KEY_LONG_TIME     100			/* 单位10ms, 持续1秒,认为长按事件 */

/*
	每个按键对应1个全局的结构体变量。
*/
typedef struct
{
	/* 下面是一个函数指针,指向判断按键手否按下的函数 */
	uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */

	uint8_t  Count;			/* 滤波器计数器 */
	uint16_t LongCount;		/* 长按计数器 */
	uint16_t LongTime;		/* 按键按下持续时间, 0表示不检测长按 */
	uint8_t  State;			/* 按键当前状态(按下还是弹起) */
	uint8_t  RepeatSpeed;	/* 连续按键周期 */
	uint8_t  RepeatCount;	/* 连续按键计数器 */
}KEY_T;

/*
	定义键值代码, 必须按如下次序定时每个键的按下、弹起和长按事件

	推荐使用enum, 不用#define,原因:
	(1) 便于新增键值,方便调整顺序,使代码看起来舒服点
	(2) 编译器可帮我们避免键值重复。
*/
typedef enum
{
	KEY_NONE = 0,			/* 0 表示按键事件 */

	KEY_0_DOWN,			    /* 0键按下 */
	KEY_0_UP,				/* 0键弹起 */
	KEY_0_LONG,			    /* 0键长按 */
	
	KEY_1_DOWN,				/* 1键按下 */
	KEY_1_UP,				/* 1键弹起 */
	KEY_1_LONG,				/* 1键长按 */

	KEY_2_DOWN,				/* 2键按下 */
	KEY_2_UP,				/* 2键弹起 */
	KEY_2_LONG,				/* 2键长按 */

	KEY_3_DOWN,				/* 3键按下 */
	KEY_3_UP,				/* 3键弹起 */
	KEY_3_LONG,				/* 3键长按 */

	KEY_4_DOWN,				/* 4键按下 */
	KEY_4_UP,				/* 4键弹起 */
	KEY_4_LONG,				/* 4键长按 */

	KEY_5_DOWN,				/* 5键按下 */
	KEY_5_UP,				/* 5键弹起 */
	KEY_5_LONG,				/* 5键长按 */

	KEY_6_DOWN,				/* 6键按下 */
	KEY_6_UP,				/* 6键弹起 */
	KEY_6_LONG,				/* 6键长按 */

	KEY_7_DOWN,				/* 7键按下 */
	KEY_7_UP,				/* 7键弹起 */
	KEY_7_LONG,				/* 7键长按 */

	KEY_8_DOWN,				/* 8键按下 */
	KEY_8_UP,				/* 8键弹起 */
	KEY_8_LONG,				/* 8键长按 */

	KEY_9_DOWN,				/* 9键按下 */
	KEY_9_UP,				/* 9键弹起 */
	KEY_9_LONG,				/* 9键长按 */

	KEY_10_DOWN,			/* 10键按下 */
	KEY_10_UP,				/* 10键弹起 */
	KEY_10_LONG,			/* 10键长按 */
	
	KEY_11_DOWN,			/* 11键按下 */
	KEY_11_UP,				/* 11键弹起 */
	KEY_11_LONG,			/* 11键长按 */
	
	KEY_12_DOWN,			/* 12键按下 */
	KEY_12_UP,				/* 12键弹起 */
	KEY_12_LONG,			/* 12键长按 */
}KEY_ENUM;

/* 按键FIFO用到变量 */
#define FIFO_SIZE	15

/*! fifo缓冲区类型 */
#define FIFO_TYPE    uint8_t

/*! fifo缓冲区满后 是否覆盖旧数据 0进行覆盖  1报错入栈失败*/
#define FIFO_COVER   0
typedef struct
{
    FIFO_TYPE  buff[FIFO_SIZE];       /* FIFO 缓冲区 */

    uint32_t   fifoLen;               /* FIFO 缓冲区有效数据长度 */

    uint32_t   fifoWrite;             /* 缓冲区写指针 */
    uint32_t   fifoRead;              /* 缓冲区读指针 */

}fifo_t;

typedef fifo_t KEY_FIFO_T;

/* 供外部调用的函数声明 */
void bsp_InitKey(void);
void bsp_KeyScan(void);
void bsp_PutKey(uint8_t _KeyCode);
uint8_t bsp_GetKey(void);
uint8_t bsp_GetKey2(void);
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID);
void bsp_SetKeyParam(uint8_t _ucKeyID, uint16_t _LongTime, uint8_t  _RepeatSpeed);
void bsp_ClearKey(void);




#endif

bsp_key.c

/*!
  * @file     BSP_KEY.c
  *
  * @brief    按键驱动文件
  *
  * @company  
  *
  * @author   不咸不要钱
  *
  * @note     无
  *
  * @version  
  *
  * @date     2019/10/18 星期五
  */ 
#include "bsp_key.h"
#include "lq_gpio.h"
#include "stdio.h"



static KEY_T xdata s_tBtn[KEY_COUNT];
static KEY_FIFO_T xdata s_tKey;		/* 按键FIFO变量,结构体 */

static void bsp_InitKeyVar(void);
static void bsp_InitKeyHard(void);
static void bsp_DetectKey(uint8_t i);

static uint8_t IsKeyDown0(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown1(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; NOP(50); if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown2(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown3(void)  {P(C1_GPIO_PORT, C1_PIN) = 0;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown4(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown5(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown6(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 0;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown7(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown8(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R2_GPIO_PORT, R2_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown9(void)  {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 0; P(C4_GPIO_PORT, C4_PIN) = 1; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown10(void) {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; NOP(50); if (P(R1_GPIO_PORT, R1_PIN) == 0) return 1;else return 0;}
static uint8_t IsKeyDown11(void) {P(C1_GPIO_PORT, C1_PIN) = 1;  P(C2_GPIO_PORT, C2_PIN) = 1;  P(C3_GPIO_PORT, C3_PIN) = 1; P(C4_GPIO_PORT, C4_PIN) = 0; if (P(R3_GPIO_PORT, R3_PIN) == 0) return 1;else return 0;}

/* 组合按键 key1 && key2 */
static uint8_t IsKeyDown12(void)  { return IsKeyDown1() && IsKeyDown2();}

/*!
 * @brief    fifo初始化
 *
 * @param    fifo_t    :  FIFO
 *
 * @return   无
 *
 * @note     无
 *
 * @see      fifo_t  tempFifo;
 *           fifo_init(tempFifo);  //fifo初始化
 *
 * @date     2020/5/21
 */
void fifo_init(fifo_t *fifo)
{
    fifo->fifoLen   = 0;
    fifo->fifoRead  = 0;
    fifo->fifoWrite = 0;
}


/*!
 * @brief    fifo压入数据
 *
 * @param    fifo_t    :  FIFO
 * @param    data      :  入栈数据
 *
 * @return   0 :成功   1 :失败
 *
 * @note     FIFO_COVER 宏定义进行判断缓冲区满后的操作
 *
 * @see      fifo_t  tempFifo;
 *           fifo_push(tempFifo, 120);  //fifo中压入一个数据
 *
 * @date     2020/5/21
 */
uint8_t fifo_push(fifo_t *fifo, FIFO_TYPE dat)
{
    fifo->fifoLen++;

    /* 判断缓冲区是否已满 */
    if(fifo->fifoLen > FIFO_SIZE)
    {
        fifo->fifoLen = FIFO_SIZE;

#if FIFO_COVER
        return 1;
#else
        if(++fifo->fifoRead >= FIFO_SIZE)
        {
            fifo->fifoRead = 0;
        }
#endif
    }

    fifo->buff[fifo->fifoWrite] = dat;

    if(++fifo->fifoWrite >= FIFO_SIZE)
    {
        fifo->fifoWrite = 0;
    }

    return 0;

}

/*!
 * @brief    fifo弹出数据
 *
 * @param    fifo_t    :  FIFO
 * @param    data      :  出栈数据
 *
 * @return   0 :成功   1 :失败
 *
 * @note     无
 *
 * @see      fifo_t  tempFifo;
 *           FIFO_TYPE tempData;
 *           fifo_push(tempFifo, 120);       //fifo中压入一个数据
 *           fifo_pop(tempFifo, &tempData);  //fifo中弹出一个数据
 *
 * @date     2020/5/21
 */
uint8_t fifo_pop(fifo_t *fifo, FIFO_TYPE *dat)
{
    /* 缓冲区为空 */
    if(fifo->fifoLen == 0)
    {
        return 1;
    }

    fifo->fifoLen--;

    *dat = fifo->buff[fifo->fifoRead];

    if(++fifo->fifoRead >= FIFO_SIZE)
    {
        fifo->fifoRead = 0;
    }

    return 0;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_InitKey
*	功能说明: 初始化按键. 该函数被 bsp_Init() 调用。
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitKey(void)
{
	bsp_InitKeyVar();		/* 初始化按键变量 */
	bsp_InitKeyHard();		/* 初始化按键硬件 */
}


/*
*********************************************************************************************************
*	函 数 名: bsp_PutKey
*	功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
*	形    参:  _KeyCode : 按键代码
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_PutKey(uint8_t _KeyCode)
{
	fifo_push(&s_tKey, _KeyCode);
}


/*
*********************************************************************************************************
*	函 数 名: bsp_GetKey
*	功能说明: 从按键FIFO缓冲区读取一个键值。
*	形    参:  无
*	返 回 值: 按键代码
*********************************************************************************************************
*/
uint8_t bsp_GetKey(void)
{
	uint8_t xdata ret;

	if (fifo_pop(&s_tKey, &ret) == 1)
	{
		return KEY_NONE;
	}
	else
	{
		return ret;
	}
}


/*
*********************************************************************************************************
*	函 数 名: bsp_GetKeyState
*	功能说明: 读取按键的状态
*	形    参:  _ucKeyID : 按键ID,从0开始
*	返 回 值: 1 表示按下, 0 表示未按下
*********************************************************************************************************
*/
uint8_t bsp_GetKeyState(KEY_ID_E _ucKeyID)
{
	return s_tBtn[_ucKeyID].State;
}


/*
*********************************************************************************************************
*	函 数 名: bsp_SetKeyParam
*	功能说明: 设置按键参数
*	形    参:_ucKeyID : 按键ID,从0开始
*			_LongTime : 长按事件时间
*			 _RepeatSpeed : 连发速度
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_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;						/* 连发计数器 */
}

/*
*********************************************************************************************************
*	函 数 名: bsp_ClearKey
*	功能说明: 清空按键FIFO缓冲区
*	形    参:无
*	返 回 值: 按键代码
*********************************************************************************************************
*/
void bsp_ClearKey(void)
{
	s_tKey.fifoRead = s_tKey.fifoWrite;
}


/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyHard
*	功能说明: 配置按键对应的GPIO
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyHard(void)
{
	//KEY 初始化
    PIN_InitPushPull(C1_GPIO_PORT, C1_PIN);
	PIN_InitPushPull(C2_GPIO_PORT, C2_PIN);
	PIN_InitPushPull(C3_GPIO_PORT, C3_PIN);
	PIN_InitPushPull(C4_GPIO_PORT, C4_PIN);
	PIN_InitOpenDrain(R1_GPIO_PORT, R1_PIN);
	PIN_InitOpenDrain(R2_GPIO_PORT, R2_PIN);
	PIN_InitOpenDrain(R3_GPIO_PORT, R3_PIN);
	
}


/*
*********************************************************************************************************
*	函 数 名: bsp_InitKeyVar
*	功能说明: 初始化按键变量
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_InitKeyVar(void)
{
	uint8_t xdata i;

	/* 对按键FIFO读写指针清零 */
	fifo_init(&s_tKey);
	
	/* 给每个按键结构体成员变量赋一组缺省值 */
	for (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].KeyCodeDown = 3 * i + 1;				/* 按键按下的键值代码 */
		//s_tBtn[i].KeyCodeUp   = 3 * i + 2;				/* 按键弹起的键值代码 */
		//s_tBtn[i].KeyCodeLong = 3 * i + 3;				/* 按键被持续按下的键值代码 */
		s_tBtn[i].RepeatSpeed = 0;						/* 按键连发的速度,0表示不支持连发 */
		s_tBtn[i].RepeatCount = 0;						/* 连发计数器 */
	}


	/* 判断按键按下的函数 */
	s_tBtn[0].IsKeyDownFunc = IsKeyDown0;
	s_tBtn[1].IsKeyDownFunc = IsKeyDown1;
	s_tBtn[2].IsKeyDownFunc = IsKeyDown2;
	s_tBtn[3].IsKeyDownFunc = IsKeyDown3;
	s_tBtn[4].IsKeyDownFunc = IsKeyDown4;
	s_tBtn[5].IsKeyDownFunc = IsKeyDown5;
	s_tBtn[6].IsKeyDownFunc = IsKeyDown6;
	s_tBtn[7].IsKeyDownFunc = IsKeyDown7;
	s_tBtn[8].IsKeyDownFunc = IsKeyDown8;
	s_tBtn[9].IsKeyDownFunc = IsKeyDown9;
	s_tBtn[10].IsKeyDownFunc = IsKeyDown10;
	s_tBtn[11].IsKeyDownFunc = IsKeyDown11;
	
	/* 组合按键 */
	s_tBtn[12].IsKeyDownFunc = IsKeyDown12;
}

/*
*********************************************************************************************************
*	函 数 名: bsp_DetectKey
*	功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。
*	形    参:  按键结构变量指针
*	返 回 值: 无
*********************************************************************************************************
*/
static void bsp_DetectKey(uint8_t i)
{
	KEY_T xdata *pBtn;

	/*
		如果没有初始化按键函数,则报错
		if (s_tBtn[i].IsKeyDownFunc == 0)
		{
			printf("Fault : DetectButton(), s_tBtn[i].IsKeyDownFunc undefine");
		}
	*/

	pBtn = &s_tBtn[i];
	if (pBtn->IsKeyDownFunc())
	{
		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;

				/* 发送按钮按下的消息 */
				bsp_PutKey((uint8_t)(3 * i + 1));
				P27 = 1;                   //开启蜂鸣器
			}

			if (pBtn->LongTime > 0)
			{
				if (pBtn->LongCount < pBtn->LongTime)
				{
					/* 发送按钮持续按下的消息 */
					if (++pBtn->LongCount == pBtn->LongTime)
					{
						/* 键值放入按键FIFO */
						bsp_PutKey((uint8_t)(3 * i + 3));
					}
				}
				else
				{
					if (pBtn->RepeatSpeed > 0)
					{
						if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
						{
							pBtn->RepeatCount = 0;
							/* 常按键后,每隔10ms发送1个按键 */
							bsp_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;

				/* 发送按钮弹起的消息 */
				bsp_PutKey((uint8_t)(3 * i + 2));
				P27 = 0;                   //关闭蜂鸣器
			}
		}

		pBtn->LongCount = 0;
		pBtn->RepeatCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: bsp_KeyScan
*	功能说明: 扫描所有按键。非阻塞,被systick中断周期性的调用
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void bsp_KeyScan(void)
{
	uint8_t xdata i;

	for (i = 0; i < KEY_COUNT; i++)
	{
		bsp_DetectKey(i);
	}
}

思考

这个按键底层驱动可以检测到底层按下弹起,是否支持双击?
双击实现可以基于此底层驱动,不需要修改代码,只需要在修改应用层,比如检测到某按键按下,开启一个软件定时器,在定时器定时结束前,再次检测到按下,则为双击。

  • 9
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值