一、输入配置
为了灵活使用,我们将输入的有效电平设置成可配置。同样是列表表示所有IO口。
- // 配置有效电平
- typedef enum
- {
- KEY_INIT_IS_ACTIVE = 0,
- KEY_LOW_IS_ACTIVE = 1,
- KEY_HIGH_IS_ACTIVE = 2,
- } key_active_t;
- #define KEY_CONFIG(gpio, pin) GPIOConfig(gpio, pin, GPIO_MODE_INPUT, GPIO_PULLUP)
- #define KEY_READ(gpio, pin) HAL_GPIO_ReadPin(gpio, pin)
- #define KEY1_PORT GPIOH
- #define KEY1_PIN GPIO_PIN_3
- #define KEY2_PORT GPIOH
- #define KEY2_PIN GPIO_PIN_2
- #define KEY3_PORT GPIOC
- #define KEY3_PIN GPIO_PIN_13
复制代码- // demo代码 只传递思想
- static void key_gpio_config(GPIO_TypeDef *gpio, uint16_t pin)
- {
- KEY_CONFIG(gpio, pin);
- }
- typedef struct
- {
- GPIO_TypeDef *gpio;
- uint16_t pin;
- } key_port_t;
- static key_port_t key_entries[] =
- {
- {KEY1_PORT, KEY1_PIN},
- {KEY2_PORT, KEY2_PIN},
- {KEY3_PORT, KEY3_PIN},
- };
- void KeyInit(void)
- {
- uint32_t i, mask = 1;
- for(i = 0; i < ARRAY_SIZE(key_entries); ++i)
- {
- if(0xFFFFFFFF & mask)
- {
- key_gpio_config(key_entries<span style="font-style: italic;">.gpio, key_entries.pin);
- <span style="font-style: normal;">#if(CONFIG_KEY_TEST == 1) // 测试时使用
- //config.key.total_switch = KEY_MODE_OPEN;
- //config.key.sub_switch = KEY_MODE_OPEN;
- config.key.active_tag = KEY_LOW_IS_ACTIVE;
- #endif
- }
- mask <<= 1;
- }
- }
- // 输入是否可用
- static uint8_t key_is_enable(uint8_t index)
- {
- // 这里可以写成可配置 配置IO口可用或者不可用(类似一个总开关)
- return 1;
- }
- // 有输入到来
- static uint8_t key_is_pressed(uint8_t index)
- {
- if(key_is_enable(index))
- {
- if(KEY_LOW_IS_ACTIVE == config.key.active_tag[index])
- {
- if(KEY_READ(key_entries[index].gpio, key_entries[index].pin) == 0)
- {
- return 1;
- }
- }
- else if(KEY_HIGH_IS_ACTIVE == config.key.active_tag[index])
- {
- if(KEY_READ(key_entries[index].gpio, key_entries[index].pin) == 1)
- {
- return 1;
- }
- }
- }
- return 0;
- }
- // 按键被按下
- uint8_t Key1IsDown(void)
- {
- return key_is_pressed(0);
- }
- uint8_t Key2IsDown(void)
- {
- return key_is_pressed(1);
- }
- uint8_t Key3IsDown(void)
- {
- return key_is_pressed(2);
- }</span></span>
复制代码
二、输入扫描
- // 按键的状态机结构定义
- // 按键状态
- typedef enum
- {
- KEY_STATE_INIT, // 缺省按键状态
- KEY_STATE_UP, // 按键弹起状态
- KEY_STATE_DOWN, // 按键按下状态
- KEY_STATE_LONG, // 按键长按状态
- KEY_STATE_AUTO, // 按键自动连发状态
- } key_state_t;
- // 按键滤波时间20ms, 单位10ms。
- // 只有连续检测到20ms状态不变才认为有效,包括弹起和按下两种事件
- // 即使按键电路不做硬件滤波,该滤波机制也可以保证可靠地检测到按键事件
- #define KEY_FILTER_TIME 2 // 滤波消抖
- #define KEY_LONG_TIME 100 // 单位10ms 持续1秒,认为长按事件
- #define KEY_REPEAT_TIME 100 // 单位10ms 持续1秒,自动连发
- typedef uint8_t (*key_cb)(void);
- typedef struct
- {
- uint8_t state; // 按键当前状态(按下还是弹起)
- uint8_t last; // 上一次按键的状态
- uint8_t count; // 滤波消抖计数器
- uint16_t long_time; // 按键按下持续时间, 0表示不检测长按
- uint16_t long_count; // 长按计数器
- uint8_t repeat_speed; // 连续按键周期
- uint8_t repeat_count; // 连续按键计数器
- key_cb is_down_func; // 按键按下的判断函数,1表示按下
- } key_t;
- static key_t key_items[] =
- {
- {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key1IsDown},
- {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key2IsDown},
- {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key3IsDown},
- };
复制代码- // 按键状态机key_scan
- // 配置限制条件在这个函数里面加
- static void key_scan_ext(key_t *entry, uint8_t i)
- {
- switch(entry->state)
- {
- case KEY_STATE_INIT:
- case KEY_STATE_UP:
- {
- entry->state = KEY_STATE_DOWN; // 按键被按下
- break;
- }
- case KEY_STATE_DOWN:
- {
- if(entry->long_time > 0)
- {
- if(entry->long_count < entry->long_time)
- {
- if(++entry->long_count >= entry->long_time)
- {
- entry->state = KEY_STATE_LONG;
- }
- }
- }
- break;
- }
- case KEY_STATE_LONG:
- {
- if(entry->repeat_speed > 0) // 自动连发时间到 自动连发事件
- {
- if(++entry->repeat_count >= entry->repeat_speed)
- {
- entry->repeat_count = 0;
- // 长按触发
- }
- }
- break;
- }
- }
- entry->last = entry->state; // 最新的按键状态
- }
- static void key_scan(uint8_t i)
- {
- key_t *entry = &key_items<span style="font-style: italic;"><span style="font-style: normal;">;
- uint8_t key;
- if(entry->is_down_func())
- {
- if(entry->count < KEY_FILTER_TIME) // 消抖
- {
- ++entry->count;
- }
- else
- {
- key_scan_ext(entry, i); // 按键扫描状态机
- }
- }
- else
- {
- if(entry->count > KEY_FILTER_TIME)
- {
- entry->count = KEY_FILTER_TIME;
- }
- else if(entry->count > 0)
- {
- --entry->count;
- }
- else
- {
- if(KEY_STATE_DOWN == entry->last) // 一次完整的按键到这里就弹起了
- {
- // 按键按下之后可以加入到队列中,这里的队列可以自己写;如果带系统可以使用系统的消息队列等方式。
- // key = i + 1;
- // xQueueSend(os_key_queue, &key, 10);
- }
- entry->last = KEY_STATE_UP;
- entry->state = KEY_STATE_UP; // 按键弹起状态
- }
- entry->long_count = 0;
- entry->repeat_count = 0; // 清空计数器
- }
- }</span></span>
复制代码
三、输入处理
- // 按键按下之后,会将值加入到队列中,我们读取队列数据,然后扫描列表匹配功能
- static void key1_cb(void);
- static void key2_cb(void);
- static void key3_cb(void);
- #define KEY1_CMD 1
- #define KEY2_CMD 2
- #define KEY3_CMD 3
- typedef struct
- {
- uint8_t cmd;
- void (* key_handle_cb)(void);
- } key_handle_t;
- static const key_handle_t key_entries[] =
- {
- {KEY1_CMD, key1_cb},
- {KEY2_CMD, key2_cb},
- {KEY3_CMD, key3_cb},
- {0xFF, NULL },
- };
- // 按键的通用功能
- static void key_func(uint8_t func)
- {
- }
- static void key1_cb(void)
- {
- }
- static void key2_cb(void)
- {
- }
- static void key3_cb(void)
- {
- }
- static void key_process(uint8_t event)
- {
- const key_handle_t *entry;
- for(entry = key_entries; entry->key_handle_cb; ++entry)
- {
- if(event == entry->cmd)
- {
- entry->key_handle_cb();
- break;
- }
- }
- }
复制代码
裸机:按键扫描20ms执行一次,按键处理直接丢在主函数中。(自己写队列)
系统:在任务中执行按键扫描和按键处理。(系统自带队列或者邮箱)
|