stm32F407按键例程安富莱

stm32F407按键例程安富莱/************************************************************************************************************ 模块名称:独立按键驱动模块(外部输入IO)* 文件名称:bsp_key.c* 版本:V1.3* 说明:扫描独立按键,具有软件滤波机制,…

stm32F407按键例程安富莱"

/* ********************************************************************************************************* * * 模块名称 : 独立按键驱动模块 (外部输入IO) * 文件名称 : bsp_key.c * 版 本 : V1.3 * 说 明 : 扫描独立按键,具有软件滤波机制,具有按键FIFO。可以检测如下事件: * (1) 按键按下 * (2) 按键弹起 * (3) 长按键 * (4) 长按时自动连发 * * 修改记录 : * 版本号 日期 作者 说明 * V1.0 2020.02.27 armfly KEY0、KEY1 和 KEY2 是低电平有效的, * 而 KEY_UP 是高电平有效的,并且外部 * 都没有上下拉电阻,所以,需要在 STM32F4 内部设置上下拉。 * * * ********************************************************************************************************* */

#include "bsp.h"


/* 正点原子探索者 按键口线分配: KEY0 键 : PE4 (低电平表示按下) KEY1 键 : PE3 (低电平表示按下) KEY2 键 : PE2 (低电平表示按下) KEY_UP 键 : PA0 (高电平表示按下) */
	
#define HARD_KEY_NUM 4 /* 实体按键个数 */
#define KEY_COUNT (HARD_KEY_NUM + 2) /* 4个独立建 + 2个组合按键 */

/* 使能GPIO时钟 */
#define ALL_KEY_GPIO_CLK_ENABLE() { \ __HAL_RCC_GPIOA_CLK_ENABLE(); \ __HAL_RCC_GPIOE_CLK_ENABLE(); \ };

/* 依次定义GPIO */
typedef struct
{ 
   
	GPIO_TypeDef* gpio;
	uint16_t pin;
	uint8_t ActiveLevel;	/* 激活电平 */
}X_GPIO_T;

/* GPIO和PIN定义 */
static const X_GPIO_T s_gpio_list[HARD_KEY_NUM] = { 
   
	{ 
   GPIOE, GPIO_PIN_4, 0},	/* KEY0 */
	{ 
   GPIOE, GPIO_PIN_3, 0},	/* KEY1 */
	{ 
   GPIOE, GPIO_PIN_2, 0},	/* KEY2 */
	{ 
   GPIOA, GPIO_PIN_0, 1},	/* KEY_UP */
};	

/* 定义一个宏函数简化后续代码 判断GPIO引脚是否有效按下 */
static KEY_T s_tBtn[KEY_COUNT] = { 
   0};  /* 按键对应的全局变量结构体 */
static KEY_FIFO_T s_tKey;		/* 按键FIFO变量,结构体 */

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

#define KEY_PIN_ACTIVE(id) 

/* ********************************************************************************************************* * 函 数 名: KeyPinActive * 功能说明: 判断按键是否按下 * 形 参: 无 * 返 回 值: 返回值1 表示按下(导通),0表示未按下(释放) ********************************************************************************************************* */
static uint8_t KeyPinActive(uint8_t _id)
{ 
   
	uint8_t level;
	
	if ((s_gpio_list[_id].gpio->IDR & s_gpio_list[_id].pin) == 0)
	{ 
   
		level = 0;
	}
	else
	{ 
   
		level = 1;
	}

	if (level == s_gpio_list[_id].ActiveLevel)
	{ 
   
		return 1;
	}
	else
	{ 
   
		return 0;
	}
}

/* ********************************************************************************************************* * 函 数 名: IsKeyDownFunc * 功能说明: 判断按键是否按下。单键和组合键区分。单键事件不允许有其他键按下。 * 形 参: 无 * 返 回 值: 返回值1 表示按下(导通),0表示未按下(释放) ********************************************************************************************************* */
static uint8_t IsKeyDownFunc(uint8_t _id)
{ 
   
	/* 实体单键 */
	if (_id < HARD_KEY_NUM)
	{ 
   
		uint8_t i;
		uint8_t count = 0;
		uint8_t save = 255;
		
		/* 判断有几个键按下 */
		for (i = 0; i < HARD_KEY_NUM; i++)
		{ 
   
			if (KeyPinActive(i)) 
			{ 
   
				count++;
				save = i;
			}
		}
		
		if (count == 1 && save == _id)
		{ 
   
			return 1;	/* 只有1个键按下时才有效 */
		}		
		return 0;
	}
	
	/* 组合键 K0K1 */
	if (_id == HARD_KEY_NUM + 0)
	{ 
   
		if (KeyPinActive(KID_K0) && KeyPinActive(KID_K1))
		{ 
   
			return 1;
		}
		else
		{ 
   
			return 0;
		}
	}

	/* 组合键 K1KU */
	if (_id == HARD_KEY_NUM + 1)
	{ 
   
		if (KeyPinActive(KID_K1) && KeyPinActive(KID_KU))
		{ 
   
			return 1;
		}
		else
		{ 
   
			return 0;
		}
	}

	return 0;
}

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

/* ********************************************************************************************************* * 函 数 名: bsp_InitKeyHard * 功能说明: 配置按键对应的GPIO * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */
static void bsp_InitKeyHard(void)
{ 
   	
	GPIO_InitTypeDef gpio_init;
	uint8_t i;

	/* 第1步:打开GPIO时钟 */
	ALL_KEY_GPIO_CLK_ENABLE();
	
	/* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */
	gpio_init.Mode = GPIO_MODE_INPUT;   			/* 设置输入 */
	gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  /* GPIO速度等级 */
	
	for (i = 0; i < HARD_KEY_NUM; i++)
	{ 
   
		if(i<3)												/* KEY0 KEY1 KEY2 */
		{ 
   
			gpio_init.Pin = s_gpio_list[i].pin;
			gpio_init.Pull = GPIO_PULLUP;                 /* 上拉电阻 */
			HAL_GPIO_Init(s_gpio_list[i].gpio, &gpio_init);	
		}
		else												/* KEY_UP */
		{ 
   
			gpio_init.Pin = s_gpio_list[i].pin;
			gpio_init.Pull = GPIO_PULLDOWN;                 /* 下拉电阻 */
			HAL_GPIO_Init(s_gpio_list[i].gpio, &gpio_init);			
		}
	}
}

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

	/* 对按键FIFO读写指针清零 */
	s_tKey.Read = 0;
	s_tKey.Write = 0;
	s_tKey.Read2 = 0;

	/* 给每个按键结构体成员变量赋一组缺省值 */
	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].RepeatSpeed = 0;						/* 按键连发的速度,0表示不支持连发 */
		s_tBtn[i].RepeatCount = 0;						/* 连发计数器 */
	}

	/* 如果需要单独更改某个按键的参数,可以在此单独重新赋值 */

}

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

	if (++s_tKey.Write  >= KEY_FIFO_SIZE)
	{ 
   
		s_tKey.Write = 0;
	}
}

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

	if (s_tKey.Read == s_tKey.Write)
	{ 
   
		return KEY_NONE;
	}
	else
	{ 
   
		ret = s_tKey.Buf[s_tKey.Read];

		if (++s_tKey.Read >= KEY_FIFO_SIZE)
		{ 
   
			s_tKey.Read = 0;
		}
		return ret;
	}
}

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

	if (s_tKey.Read2 == s_tKey.Write)
	{ 
   
		return KEY_NONE;
	}
	else
	{ 
   
		ret = s_tKey.Buf[s_tKey.Read2];

		if (++s_tKey.Read2 >= KEY_FIFO_SIZE)
		{ 
   
			s_tKey.Read2 = 0;
		}
		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.Read = s_tKey.Write;
}

/* ********************************************************************************************************* * 函 数 名: bsp_DetectKey * 功能说明: 检测一个按键。非阻塞状态,必须被周期性的调用。 * 形 参: IO的id, 从0开始编码 * 返 回 值: 无 ********************************************************************************************************* */
static void bsp_DetectKey(uint8_t i)
{ 
   
	KEY_T *pBtn;

	pBtn = &s_tBtn[i];
	if (IsKeyDownFunc(i))		/* 是否有按键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;

				/* 发送按钮按下的消息 */
				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_DetectFastIO * 功能说明: 检测高速的输入IO. 1ms刷新一次 * 形 参: IO的id, 从0开始编码 * 返 回 值: 无 ********************************************************************************************************* */
static void bsp_DetectFastIO(uint8_t i)
{ 
   
	KEY_T *pBtn;

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

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

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

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

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

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

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

/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

/* 例程 */

/* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
// uint8_t ucKeyCode; /* 按键代码 */
// while(1)
// { 
   
// ucKeyCode = bsp_GetKey(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
// if (ucKeyCode != KEY_NONE)
// { 
   
// switch (ucKeyCode)
// { 
   
// case KEY_DOWN_K0: /* K0键按下 */
// bsp_LedToggle(0);
// break;
// case KEY_DOWN_K1: /* K0键按下 */
// bsp_LedToggle(1);
// break;
// case SYS_DOWN_K1KU: /* K1KU键按下 */
// bsp_LedOff(1);
// bsp_LedOff(0);
// break; 
// default:
// /* 其它的键值不处理 */
// break;
// }
// 
// }
// }
/* ********************************************************************************************************* * * 模块名称 : 按键驱动模块 * 文件名称 : bsp_key.h * 版 本 : V1.0 * 说 明 : 头文件 * * Copyright (C), 2013-2014, 安富莱电子 www.armfly.com * ********************************************************************************************************* */

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

/* 根据应用程序的功能重命名按键宏 */

#define KEY_DOWN_K0 KEY_1_DOWN /* KEY0 键 */
#define KEY_UP_K0 KEY_1_UP
#define KEY_LONG_K0 KEY_1_LONG

#define KEY_DOWN_K1 KEY_2_DOWN /* KEY1 键 */
#define KEY_UP_K1 KEY_2_UP
#define KEY_LONG_K1 KEY_2_LONG

#define KEY_DOWN_K2 KEY_3_DOWN /* KEY2 键 */
#define KEY_UP_K2 KEY_3_UP
#define KEY_LONG_K2 KEY_3_LONG

#define KEY_DOWN_KU KEY_4_DOWN /* KEY_UP 键 */
#define KEY_UP_KU KEY_4_UP
#define KEY_LONG_KU KEY_4_LONG

#define SYS_DOWN_K0K1 KEY_5_DOWN /* K0 K1 组合键 */
#define SYS_UP_K0K1 KEY_5_UP
#define SYS_LONG_K0K1 KEY_5_LONG

#define SYS_DOWN_K1KU KEY_6_DOWN /* K1 KU 组合键 */
#define SYS_UP_K1KU KEY_6_UP
#define SYS_LONG_K1KU KEY_6_LONG

/* 按键ID, 主要用于bsp_KeyState()函数的入口参数 */
typedef enum
{ 
   
	KID_K0 = 0,
	KID_K1,
	KID_K2,
	KID_KU,
}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_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_ENUM;

/* 按键FIFO用到变量 */
#define KEY_FIFO_SIZE 10
typedef struct
{ 
   
	uint8_t Buf[KEY_FIFO_SIZE];		/* 键值缓冲区 */
	uint8_t Read;					/* 缓冲区读指针1 */
	uint8_t Write;					/* 缓冲区写指针 */
	uint8_t Read2;					/* 缓冲区读指针2 */
}KEY_FIFO_T;

/* 供外部调用的函数声明 */
void bsp_InitKey(void);
void bsp_KeyScan10ms(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

/***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/

今天的文章stm32F407按键例程安富莱分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/28300.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注