# XxxSwitchScan_Driver
## 介绍
### 简介
> XxxSwitchScan_Driver可以简单的看作为一个C语言按键驱动,使用简单、灵活且解耦,基于时间片轮询架构编写,同时适用于裸机与操作系统。
>
> 一开始仅为了实现按键驱动。后面把按键结合如高低电平的传感器、开关量的限位等进一步抽象为 ***开关量的输入设备*** 。最终实现响应事件有:**短按/短按抬起/长按/持续长按/长按抬起/连击/单边沿触发**。由此我常会把项目中的*开关量的输入设备*通过该驱动统一管理,使得项目架构合理简化,也让应用层逻辑清晰明了。
### 交流
邮箱:[email protected]
CSDN:https://blog.csdn.net/Lstatxxx
### 特性
> + 基于链表,可动态注册新增设备与删除设备(只要内存足够大,设备数量不限量);
> + 完全隔离硬件,对输入条件进行抽象,独立/矩阵/组合按键等均可适用;
> + 使用了回调机制,驱动逻辑与业务逻辑分离,调用者专心实现事件响应的业务逻辑即可;
> + 隔离内存管理,内存分配与回收由调用者主导;
> + tick由外部主导,适用于裸机与操作系统提供的任意tick环境(定时器中断/阻塞延时/时间片/操作系统下的任务或定时器任务等);
> + 每个设备的按下消抖与抬起消抖均可独立配置,数值配置为零即为**不消抖**(可应用于限位/传感器/硬件滤波良好的按键等);
> + 每个设备的每个响应事件均可独立配置开启与关闭,最基础版本仅响应**短按**与**短按抬起**事件;
> + 可利用`XxxSwitchDevice_Reset()`复位函数对设备进行互斥或屏蔽后续事件;
### 改进点
> + 由于自由度高且功能支持多,代码量会比其他纯按键驱动大。后续会推出精简版;
### 本人实际应用场景
1. 多个独立按键,组合按键;
2. 矩阵键盘;
3. 高低电平信号传感器(电机限位(槽型光电开关等)/霍尔开关/液位传感器等);
4. 气压传感器(别疑惑,通过数值范围的比较条件判断即可变成true/false,一样视为开关量);
5. 异步/事件驱动,如自制队列、GUI按键事件、变量条件(协议通信方式)、IPC(非阻塞方式队列)等;
6. 以上多种任意场景的混合应用;
***期待别的小伙伴能应用到其他的场景中,最大化开发驱动潜力***
### 格式
编码格式:UTF-8
注释格式:doxyfile
-----------------------------------------------------------------------------------------------------------------------------
## 文件介绍
### Components
提供部件有:"矩阵键盘扫描驱动"、"位操作";
### Example
提供"多个独立按键"、"组合按键"、"矩阵键盘"、"高低电平信号传感器"应用场景例程;
### README_Picture
README文档内用到的图片
### LICENSE
许可证,遵循MIT协议
### README.md
说明文档
### XxxSwitchScan_Driver.c 和 XxxSwitchScan_Driver.h
核心驱动代码
-----------------------------------------------------------------------------------------------------------------------------
## 注意事项
+ 首次触发的连击事件即为双击,此时`XxxSwitchDevice_ReadComboHitNum()`函数读取到的连击次数返回即为2;
+ 连击一旦没续上则连击次数会被清零,所以在触发连击事件时按需求是否利用或保存连击次数;
+ 边沿模式仅能单边沿触发;
+ 在操作系统中使用时,如果其中一个设备使用到的IPC是阻塞方式,会导致其他设备的扫描被阻塞;
+ tick是由调用者外部决定的,以1~20ms为一次tick均可。tick越小,功耗与cpu占用越高;
+ 消抖时长(计数数值*tick),一般为10~50ms,由实际抖动程度决定;
-----------------------------------------------------------------------------------------------------------------------------
## 使用流程
### 移植
把XxxSwitchScan_Driver.c与XxxSwitchScan_Driver.h两个文件复制粘贴到自己工程内即可。
### 应用步骤
1. 相关硬件初始化由调用者自己完成
2. 定义一个设备对象的指针
3. 提供状态读取函数
4. 提供事件处理函数
5. 为对象申请内存
6. 注册
7. 调用扫描函数
#### 基于rtos的应用基础模版
```C
#include "XxxSwitchScan_Driver.h" /* 本驱动头文件 */
#include "stm32f10x.h" /* stm32f10x标准库 */
#include "cmsis_os.h" /* rtos标准接口(CMSIS-RTOS2) */
#include <stdlib.h> /* malloc函数的头文件 */
#define ReadValid_KEY1 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4) /**< 按键1状态读取函数 */
/*2.定义按键对象指针*/
STR_XxxSwitchDevice* m_pKEY1;
/*3.提供读取设备1状态的函数*/
unsigned char ReadSwitchDevice1(void)
{
return ReadValid_KEY1();
}
/*4.提供设备1的事件处理函数(假设是基础按键仅开启短按与短按抬起事件)*/
void SwitchDevice1_Handle(enum_XxxSwitchCheckState state)
{
switch(state)
{
case SwitchCheckState_Click:
printf("设备1短按\n");
break;
case SwitchCheckState_Click2Up:
printf("设备1短按抬起\n");
break;
}
}
osThreadId_t m_task1Info; /**< 任务句柄 */
const osThreadAttr_t m_task1Config = {
.name = "Task_1",
.stack_size = 128,
.priority = 10,
};
/*任务1*/
void Task_1(void* p)
{
/*5.申请空间*/
m_pKEY1 = (STR_XxxSwitchDevice*)malloc(XxxSwitchDevice_GetSize());
if(NULL == m_pKEY1)
{
printf("allocation error\n");
return;
}
/*6.调用基础注册,假设是基础按键仅开启短按与短按抬起事件*/
XxxSwitchDevice_BaseRegister(m_pKEY1, SwitchDeviceType_Common, 1, \ /* m_pKEY1对象,SwitchDeviceType_Common为普通类型,有效电平为1 */
2, 2, \ /* 按下消抖为2*tick,抬起消抖为2*tick */
ReadSwitchDevice1, \ /* 第3步的读取设备1状态的函数 */
SwitchDevice1_Handle); /* 第4步的设备1的事件处理函数 */
while(1)
{
/*7.调用开关设备扫描函数*/
XxxSwitchDevice_Scan();
osDelay(10); /* 10ms为一次tick */
}
}
int main(void)
{
...
/*1.相关硬件初始化配置*/
...
osKernelInitialize(); /* 内核初始化 */
osThreadNew(Task_1, NULL, &m_task1Config); /* 任务创建 */
osKernelStart(); /* 内核启动 */
while(1);
}
```
#### 其他形式的应用参考
1. 相关硬件初始化由调用者自己完成
2. 定义一个设备对象的指针
```C
STR_XxxSwitchDevice* m_pKEY1; /**< 按键1对象指针 */
```
3. 提供状态读取函数
```C
/*读取按键1状态的函数(以stm32标准库为例)*/
#define ReadValid_KEY1 GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_4)
/*读取设备1状态的函数*/
unsigned char ReadSwitchDevice1(void)
{
return ReadValid_KEY1();
}
```
4. 提供事件处理函数
```C
/*设备1的事件处理函数(假设该按键开启了长按与持续长按功能)*/
void SwitchDevice1_Handle(enum_XxxSwitchCheckState state)
{
switch(state)
{
case SwitchCheckState_Click:
printf("设备1短按\n");
break;
case SwitchCheckState_Click2Up:
printf("设备1短按抬起\n");
break;
case SwitchCheckState_Long:
printf("设备1长按\n");
break;
case