freertos开发空气检测仪之按键输入事件管理系统设计与实现

freertos开发空气检测仪之按键输入事件管理系统设计与实现

在本次项目中,我的设计思路如下,从输入事件得到数据,将数据放入对应的buffer进行管理,方便供上层代码进行调用管理。

1. 系统架构

按键输入事件管理系统采用分层设计,主要包括以下几个层次:

  1. 底层驱动层:负责硬件按键的扫描和状态检测
  2. 设备抽象层:将硬件按键抽象为输入设备
  3. 输入系统层:管理所有输入设备,提供统一的事件获取接口
  4. 应用层:从输入系统获取事件并进行处理

2. 核心组件

2.1 输入事件结构体

input_system.h 中定义了 InputEvent 结构体,用于表示各种输入事件:

c 复制代码
typedef struct InputEvent {
    TIME_T time;                /* 事件时间戳 */
    INPUT_EVENT_TYPE eType;     /* 事件类型 */
    
    /* 通用事件数据 */
    union {
        /* 按键事件数据 */
        struct {
            int iKey;           /* 按键代码 */
            KEY_STATE eState;   /* 按键状态 */
            int iTime;          /* 时间ms */
            int iClickCount;    /* 点击次数,用于多击判断 */
        } key;
        
        /* 触摸事件数据 */
        struct {
            int iX;
            int iY;
            int iPressure;
        } touch;
        
        /* 网络事件数据 */
        struct {
            int iEventCode;
            char strData[INPUT_BUF_LEN];
        } net;
        
        /* 标准输入事件数据 */
        struct {
            char strInput[INPUT_BUF_LEN];
        } stdio;
    } data;
} InputEvent, *PInputEvent;

2.2 输入设备结构体

input_system.h 中定义了 InputDevice 结构体,用于表示输入设备:

c 复制代码
typedef struct InputDevice {
    char *name;                /* 设备名称 */
    int (*GetInputEvent)(PInputEvent ptInputEvent);  /* 获取输入事件的函数 */
    int (*DeviceInit)(void);   /* 设备初始化函数 */
    int (*DeviceExit)(void);   /* 设备退出函数 */
    struct InputDevice *pNext; /* 指向下一个设备的指针 */
} InputDevice, *PInputDevice;

2.3 输入缓冲区

input_system.c 中实现了一个环形缓冲区,用于存储输入事件:

c 复制代码
#define INPUT_BUFFER_SIZE 10
static InputEvent g_tInputBuffer[INPUT_BUFFER_SIZE];
static volatile int g_iInputBufferRead = 0;
static volatile int g_iInputBufferWrite = 0;

3. 调用流程

3.1 系统初始化

  1. 调用 AddInputDevices() 注册所有输入设备
  2. 调用 InitInputDevices() 初始化所有输入设备
  3. 创建 key_scan_task 任务,每10ms进行一次按键扫描

3.2 按键扫描与事件处理

  1. key_scan_task 每10ms调用一次 bsp_KeyScan10ms() 进行按键扫描
  2. bsp_KeyScan10ms() 检测按键状态并将按键事件放入按键FIFO
  3. 调用 GetInputEvent() 从所有输入设备中获取输入事件
  4. 调用 InputBufferPut() 将输入事件放入输入缓冲区

3.3 事件获取与处理

  1. 应用层调用 InputBufferGet() 从输入缓冲区中获取输入事件
  2. 根据事件类型和事件数据进行相应的处理

4. 代码实现

4.1 输入系统层实现

4.1.1 设备注册
c 复制代码
/**********************************************************************
 * 函数名称: InputDeviceRegister 
 * 功能描述: 注册一个输入设备(头插法)
 ***********************************************************************/
void InputDeviceRegister(PInputDevice ptInputDevice)
{
    if (ptInputDevice == NULL)
        return;
        
    ptInputDevice->pNext = g_ptInputDevices;
    g_ptInputDevices = ptInputDevice;
}

/**********************************************************************
 * 函数名称: AddInputDevices 
 * 功能描述: 注册所有需要的输入设备
 ***********************************************************************/
void AddInputDevices(void)
{
    /* 注册按键设备 */
    extern void AddInputDeviceKey(void);
    AddInputDeviceKey();
    
    /* 未来扩展:触摸、网络等设备 */
    // extern void AddInputDeviceTouch(void);
    // AddInputDeviceTouch();
}
4.1.2 设备初始化
c 复制代码
/**********************************************************************
 * 函数名称: InitInputDevices 
 * 功能描述: 初始化所有已注册的输入设备
 ***********************************************************************/
void InitInputDevices(void)
{
    PInputDevice pDev = g_ptInputDevices;
    
    while (pDev)
    {
        if (pDev->DeviceInit != NULL)
        {
            pDev->DeviceInit();
        }
        pDev = pDev->pNext;
    }
}
4.1.3 事件获取
c 复制代码
/**********************************************************************
 * 函数名称: GetInputEvent
 * 功能描述: 从所有输入设备中获取输入事件
 * 输入参数: ptInputEvent - 输入事件指针
 * 输出参数: 无
 * 返 回 值: 0 - 成功,非0 - 失败
 ***********************************************************************/
int GetInputEvent(PInputEvent ptInputEvent)
{
    PInputDevice pDev = g_ptInputDevices;
    
    while (pDev)
    {
        if (pDev->GetInputEvent != NULL)
        {
            if (pDev->GetInputEvent(ptInputEvent) == 0)
            {
                return 0;
            }
        }
        pDev = pDev->pNext;
    }
    
    return -1;
}
4.1.4 输入缓冲区管理
c 复制代码
/**********************************************************************
 * 函数名称: InputBufferPut
 * 功能描述: 向输入缓冲区中放入一个输入事件
 * 输入参数: ptInputEvent - 输入事件指针
 * 输出参数: 无
 * 返 回 值: 0 - 成功,非0 - 失败
 ***********************************************************************/
int InputBufferPut(PInputEvent ptInputEvent)
{
    int iNextWrite = (g_iInputBufferWrite + 1) % INPUT_BUFFER_SIZE;
    
    if (iNextWrite == g_iInputBufferRead)
    {
        /* 缓冲区已满 */
        return -1;
    }
    
    g_tInputBuffer[g_iInputBufferWrite] = *ptInputEvent;
    g_iInputBufferWrite = iNextWrite;
    
    return 0;
}

/**********************************************************************
 * 函数名称: InputBufferGet
 * 功能描述: 从输入缓冲区中获取一个输入事件
 * 输入参数: ptInputEvent - 输入事件指针
 * 输出参数: 无
 * 返 回 值: 0 - 成功,非0 - 失败
 ***********************************************************************/
int InputBufferGet(PInputEvent ptInputEvent)
{
    if (g_iInputBufferRead == g_iInputBufferWrite)
    {
        /* 缓冲区为空 */
        return -1;
    }
    
    *ptInputEvent = g_tInputBuffer[g_iInputBufferRead];
    g_iInputBufferRead = (g_iInputBufferRead + 1) % INPUT_BUFFER_SIZE;
    
    return 0;
}

/**********************************************************************
 * 函数名称: InputBufferClear
 * 功能描述: 清空输入缓冲区
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/
void InputBufferClear(void)
{
    g_iInputBufferRead = 0;
    g_iInputBufferWrite = 0;
}

4.2 设备抽象层实现

4.2.1 按键设备初始化
c 复制代码
static int GPIOKeyInit(void)
{
	KAL_GPIOKkeyInit();
	/* 创建按键事件队列 */
	xKeyQueue = xQueueCreate(10, sizeof(InputEvent));
	if (xKeyQueue == NULL)
	{
		return -1;
	}
	/* 初始化按键驱动 */
	bsp_InitKey();
	return 0;
}
4.2.2 按键事件获取
c 复制代码
static int GPIOKeyGetInputEvent(PInputEvent ptInputEvent)
{
	uint8_t key_code;
	InputEvent event;
	
	/* 获取按键事件 */
	key_code = bsp_GetKey();
	if (key_code != KEY_NONE)
	{
		/* 填充InputEvent结构 */
		event.time = xTaskGetTickCount();
		event.eType = INPUT_EVENT_TYPE_KEY;
		
		/* 根据按键代码解析按键信息 */
		switch (key_code)
		{
			case KEY_1_DOWN:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_PRESSED;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 0;
				break;
				
			case KEY_1_UP:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_RELEASED;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 1;
				break;
				
			case KEY_1_LONG_DOWN:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_LONG_PRESS;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 0;
				break;
				
			case KEY_1_LONG_UP:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_LONG_RELEASED;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 0;
				break;
				
			case KEY_1_AUTO_UP:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_REPEAT;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 0;
				break;

			case KEY_1_DB_UP:
				event.data.key.iKey = 1;
				event.data.key.eState = KEY_STATE_DOUBLE_CLICK;
				event.data.key.iTime = event.time;
				event.data.key.iClickCount = 2;
				break;
				
			default:
				return -1;
		}
		
		/* 复制事件到输出参数 */
		*ptInputEvent = event;
		return 0;
	}
	
	return -1;
}
4.2.3 按键设备注册
c 复制代码
static InputDevice g_tKeyDevice = {
	"gpio_key",
	GPIOKeyGetInputEvent,
	GPIOKeyInit,
	NULL,
};

void AddInputDeviceKey(void)
{
	InputDeviceRegister(&g_tKeyDevice);
}

4.3 应用层实现

4.3.1 按键扫描任务
c 复制代码
/**********************************************************************
 * 函数名称: key_scan_task
 * 功能描述: 按键扫描任务,每10ms调用一次bsp_KeyScan10ms
 * 输入参数: pvParameters - 任务参数
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/
void key_scan_task(void *pvParameters)
{
	InputEvent event;
	
	while (1)
	{
		/* 每10ms调用一次按键扫描函数 */
		bsp_KeyScan10ms();
		
		/* 获取按键事件并放入输入缓冲区 */
		if (GetInputEvent(&event) == 0)
		{
			InputBufferPut(&event);
		}
		
		/* 延时10ms */
		vTaskDelay(10);
	}
}
4.3.2 输入测试任务
c 复制代码
static void input_test_task(void *pvParameters)
{
    InputEvent event;
    
    while (1)
    {
        /* 从队列中获取按键事件 */
        if (xQueueReceive(xKeyQueue, &event, portMAX_DELAY) == pdTRUE)
        {
            /* 打印按键事件信息 */
            DBG_log("[INFO] Key event: type=%d, key=%d, state=%d, iTime=%d, click_count=%d\n", 
                   event.eType, event.data.key.iKey, event.data.key.eState, 
                   event.data.key.iTime, event.data.key.iClickCount);
        }
    }
}
4.3.3 输入系统单元测试
c 复制代码
/**********************************************************************
 * 函数名称: input_test
 * 功能描述: 输入系统单元测试函数
 * 输入参数: 无
 * 输出参数: 无
 * 返 回 值: 无
 ***********************************************************************/
void input_test(void)
{
	InputEvent event;
	
	AddInputDevices();
	InitInputDevices();
	
	DBG_log("Input system unit test start...\n");
	
	while (1)
	{
		if (GetInputEvent(&event) == 0)
		{
			/* 打印按键事件信息 */
			DBG_log("[INFO] Key event: type=%d, key=%d, state=%d, iTime=%d, click_count=%d\n", 
			       event.eType, event.data.key.iKey, event.data.key.eState, 
			       event.data.key.iTime, event.data.key.iClickCount);
			
			/* 将事件放入输入缓冲区 */
			if (InputBufferPut(&event) == 0)
			{
				DBG_log("[INFO] Event put into buffer success\n");
			}
			else
			{
				DBG_log("[ERROR] Event put into buffer failed\n");
			}
		}
		
		/* 尝试从输入缓冲区获取事件 */
		if (InputBufferGet(&event) == 0)
		{
			DBG_log("[INFO] Event get from buffer: type=%d, key=%d, state=%d\n", 
			       event.eType, event.data.key.iKey, event.data.key.eState);
		}
		
		/* 延时10ms */
		vTaskDelay(10);
	}
}

5. 使用方法

5.1 初始化输入系统

c 复制代码
/* 注册并初始化输入设备 */
AddInputDevices();
InitInputDevices();

/* 创建按键扫描任务 */
xTaskCreate(
    key_scan_task,
    "key_scan",
    128,
    NULL,
    1,
    &xKeyScanTask
);

5.2 获取并处理输入事件

c 复制代码
/* 从输入缓冲区中获取输入事件 */
InputEvent event;
if (InputBufferGet(&event) == 0)
{
    /* 根据事件类型进行处理 */
    switch (event.eType)
    {
        case INPUT_EVENT_TYPE_KEY:
            /* 处理按键事件 */
            printf("Key event: key=%d, state=%d\n", 
                   event.data.key.iKey, event.data.key.eState);
            break;
            
        /* 处理其他类型的事件... */
    }
}

6. 扩展建议

  1. 添加更多输入设备:可以添加触摸屏幕、网络输入等其他类型的输入设备
  2. 优化输入缓冲区:可以根据实际需求调整输入缓冲区的大小
  3. 添加事件过滤:可以添加事件过滤机制,过滤掉不需要的事件
  4. 添加事件回调:可以添加事件回调机制,当有输入事件时自动调用回调函数
  5. 添加事件优先级:可以为不同类型的事件添加优先级,优先处理重要的事件

7. 总结

按键输入事件管理系统采用分层设计,通过输入设备抽象和输入缓冲区管理,提供了一个统一、高效的输入事件处理接口。系统支持多种按键状态的检测,包括按下、弹起、长按、双击等,可以满足各种应用场景的需求。

相关推荐
三伏5221 小时前
Cortex-M3权威指南Cn第十章——笔记
笔记·单片机·嵌入式硬件·cortex-m3
你大爷的,这都没注册了2 小时前
AI提示词,zero-shot,few-shot 概念
人工智能
小灰灰搞电子2 小时前
STM32/GD32 字节对齐详解
stm32·单片机·嵌入式硬件
AC赳赳老秦2 小时前
DeepSeek 辅助科研项目申报:可行性报告与经费预算框架的智能化撰写指南
数据库·人工智能·科技·mongodb·ui·rabbitmq·deepseek
瑞华丽PLM2 小时前
国产PLM软件源头厂家的AI技术应用与智能化升级
人工智能·plm·国产plm·瑞华丽plm·瑞华丽
xixixi777772 小时前
基于零信任架构的通信
大数据·人工智能·架构·零信任·通信·个人隐私
玄同7652 小时前
LangChain v1.0+ Prompt 模板完全指南:构建精准可控的大模型交互
人工智能·语言模型·自然语言处理·langchain·nlp·交互·知识图谱
Ryan老房2 小时前
开源vs商业-数据标注工具的选择困境
人工智能·yolo·目标检测·计算机视觉·ai
取个鸣字真的难2 小时前
Obsidian + CC:用AI 打造知识管理系统
人工智能·产品运营