From 2d867be49ad4a0935ff40ad74450cd9184c7af25 Mon Sep 17 00:00:00 2001 From: JiXieShi Date: Sat, 22 Jun 2024 11:08:31 +0800 Subject: [PATCH] =?UTF-8?q?UP=20=E6=8C=89=E9=94=AE=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- demo/iic/test.c | 8 +- demo/key/t_key.h | 6 ++ demo/key/test.c | 56 ++++++++++++++ lib/CMakeLists.txt | 1 + lib/inc/key/key.h | 102 +++++++++++++++++++++++++ lib/inc/list/list.h | 4 +- lib/inc/oled/oled.h | 29 +++++++ lib/inc/task/task.h | 75 ++++++++++++++---- lib/inc/utils/tool.h | 20 ++--- lib/src/key/key.cpp | 170 +++++++++++++++++++++++++++++++++++++++++ lib/src/utils/tool.cpp | 16 ++-- main.c | 5 +- 13 files changed, 454 insertions(+), 40 deletions(-) create mode 100644 demo/key/t_key.h create mode 100644 demo/key/test.c create mode 100644 lib/inc/key/key.h create mode 100644 lib/inc/oled/oled.h create mode 100644 lib/src/key/key.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a26c3f..c93f314 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,4 +17,4 @@ add_executable(HW_Lib main.c ${SOURCES}) #导入库 add_subdirectory(lib) -target_link_libraries(HW_Lib HW_LIB_List HW_LIB_Task HW_LIB_Printf HW_LIB_Utils HW_LIB_Iic HW_LIB_Spi) \ No newline at end of file +target_link_libraries(HW_Lib HW_LIB_List HW_LIB_Task HW_LIB_Printf HW_LIB_Utils HW_LIB_Iic HW_LIB_Spi HW_LIB_Key) \ No newline at end of file diff --git a/demo/iic/test.c b/demo/iic/test.c index 80357a4..5b56717 100644 --- a/demo/iic/test.c +++ b/demo/iic/test.c @@ -2,7 +2,7 @@ #include "iic.h" #include "log.h" #include "tool.h" - +#include "t_iic.h" uint8_t CLK_Pin(uint8_t l) { // LOGT("CLK", "P:%d", l); return l; @@ -47,9 +47,9 @@ void Test_iic() { } PRINT_ARRAY(writeData, "%3d", 16); - foreach(i in writeData) { - writeData[i] = rand() % 200; - } +// foreach(i in writeData) { +// writeData[i] = rand() % 200; +// } PRINT_ARRAY(writeData, "%3d", 16); Buf_Print(" TX", writeData, len, 16); BufPrint(" TX", writeData, TYPE_T(writeData), len, 16); diff --git a/demo/key/t_key.h b/demo/key/t_key.h new file mode 100644 index 0000000..c2e7825 --- /dev/null +++ b/demo/key/t_key.h @@ -0,0 +1,6 @@ +#ifndef HW_LIB_T_KEY_H +#define HW_LIB_T_KEY_H + +void Test_Key(); + +#endif //HW_LIB_T_KEY_H diff --git a/demo/key/test.c b/demo/key/test.c new file mode 100644 index 0000000..f3efc38 --- /dev/null +++ b/demo/key/test.c @@ -0,0 +1,56 @@ +#include +#include "key.h" +#include "log.h" +#include "tool.h" +#include "t_key.h" +#include + +uint8_t Key_Pin(uint8_t l) { + uint8_t k = rand() % 2; +// LOGT("KEY", "P:%d-%d", l,k); + return k; +} + + +void Key_Call(Key_t *key) { + switch (key->event) { + case KEY_PRESS_DOWN: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "按下事件"); + break;// 按下事件 + case KEY_PRESS_UP: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "弹起事件"); + break;// 弹起事件 + case KEY_PRESS_REPEAT: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "重复按下事件"); + break;// 重复按下事件 + case KEY_SINGLE_CLICK: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "单击事件"); + break;// 单击事件 + case KEY_DOUBLE_CLICK: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "双击事件"); + break;// 双击事件 + case KEY_LONG_PRESS_START: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "长按开始事件"); + break;// 长按开始事件 + case KEY_LONG_PRESS_HOLD: + LOGT("KEY", "ID:%d EVENT:%s", key->key_id, "长按保持事件"); + break;// 长按保持事件 + } +} + +void Test_Key() { + + + Key_t k1, k2; + key_init(&k1, Key_Pin, 1, 0); + key_init(&k2, Key_Pin, 1, 1); + key_attach(&k1, KEY_ALL_EVENT, Key_Call); + key_attach(&k2, KEY_ALL_EVENT, Key_Call); + key_start(&k1); + key_start(&k2); + while (1) { + // 每5ms调用一次key_ticks函数 + key_ticks(); +// Sleep(5); // 使用Windows平台的Sleep函数进行5ms延时 + } +} \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bbcf9b6..3f0b58a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ set(LIBRARIES HW_LIB_Utils src/utils inc/utils HW_LIB_Iic src/iic inc/iic HW_LIB_Spi src/spi inc/spi + HW_LIB_Key src/key inc/key ) # 循环浏览库列表以创建它们 diff --git a/lib/inc/key/key.h b/lib/inc/key/key.h new file mode 100644 index 0000000..9f246af --- /dev/null +++ b/lib/inc/key/key.h @@ -0,0 +1,102 @@ +#ifndef HW_LIB_KEY_H +#define HW_LIB_KEY_H +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define KEY_TICKS_INTERVAL 5 // 定时器间隔时间,单位为毫秒 +#define DEBOUNCE_TICKS 3 // 按键去抖动计数阈值,最大为7(0 ~ 7) +#define SHORT_TICKS (300 / KEY_TICKS_INTERVAL) // 短按阈值,单位为定时器间隔的倍数 +#define LONG_TICKS (1000 / KEY_TICKS_INTERVAL) // 长按阈值,单位为定时器间隔的倍数 +#define KEY_STOP_FREE 1 // 按键停止释放状态标识 + + +typedef struct Key_List Key_t; // 定义结构体Key_t为Key_List的别名 +typedef void (*Key_Callback_t)(Key_t *key); // 定义函数指针类型Key_Callback_t,接受一个Key_t结构体指针参数 + +typedef enum { + KEY_PRESS_DOWN = 0, // 按下事件 + KEY_PRESS_UP, // 弹起事件 + KEY_PRESS_REPEAT, // 重复按下事件 + KEY_SINGLE_CLICK, // 单击事件 + KEY_DOUBLE_CLICK, // 双击事件 + KEY_LONG_PRESS_START, // 长按开始事件 + KEY_LONG_PRESS_HOLD, // 长按保持事件 + number_of_event, // 事件总数 + KEY_ALL_EVENT, // 所有事件 + NONE_PRESS // 无按键事件 +} PressEvent; // 定义枚举类型PressEvent,表示按键事件类型 + +struct Key_List { + Key_t *next; // 指向下一个按键结构体的指针 + uint16_t ticks; // 计时器 + uint8_t repeat: 4; // 按键重复次数,占4位 + uint8_t event: 4; // 当前按键事件类型,占4位 + uint8_t state: 3; // 按键状态,占3位 + uint8_t debounce_cnt: 3; // 按键去抖计数,占3位 + uint8_t active_level: 1; // 按键激活电平,占1位 + uint8_t key_level: 1; // 当前按键电平,占1位 + uint8_t key_id; // 按键ID + uint8_t (*hal_key_Level)(uint8_t key_id_); // 函数指针,用于获取按键电平 + Key_Callback_t cb[number_of_event]; // 按键事件回调函数数组 +}; + +/** + * @brief 初始化按键模块 + * @param key: [输入] 指向按键结构体的指针 + * @param pin_level: [输入] 获取按键电平的回调函数 + * @param active_level: [输入] 激活电平 + * @param key_id: [输入] 按键ID + * @return void + * @example key_init(&my_key, get_pin_level, ACTIVE_HIGH, 1); + */ +void key_init(Key_t *key, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t key_id); + +/** + * @brief 绑定按键事件回调函数 + * @param key: [输入] 指向按键结构体的指针 + * @param event: [输入] 按键事件类型 + * @param cb: [输入] 按键事件回调函数 + * @return void + * @example key_attach(&my_key, PRESS_DOWN, key_press_callback); + */ +void key_attach(Key_t *key, PressEvent event, Key_Callback_t cb); + +/** + * @brief 获取按键当前事件 + * @param key: [输入] 指向按键结构体的指针 + * @return 当前按键事件 + * @example PressEvent current_event = get_key_event(&my_key); + */ +PressEvent get_key_event(Key_t *key); + +/** + * @brief 启动按键检测 + * @param key: [输入] 指向按键结构体的指针 + * @return 错误码,0表示成功 + * @example int result = key_start(&my_key); + */ +int key_start(Key_t *key); + +/** + * @brief 停止按键检测 + * @param key: [输入] 指向按键结构体的指针 + * @return void + * @example key_stop(&my_key); + */ +void key_stop(Key_t *key); + +/** + * @brief 按键定时处理函数 + * @return void + * @example key_ticks(); + */ +void key_ticks(void); + +#ifdef __cplusplus +} +#endif +#endif //HW_LIB_KEY_H diff --git a/lib/inc/list/list.h b/lib/inc/list/list.h index a72fe22..f1d0bee 100644 --- a/lib/inc/list/list.h +++ b/lib/inc/list/list.h @@ -13,8 +13,8 @@ typedef struct List_Node_t { //节点结构 } List_Node_t; typedef struct { //链表结构 - struct List_Node_t *head; - struct List_Node_t *tail; + List_Node_t *head; + List_Node_t *tail; long len; } List_t; diff --git a/lib/inc/oled/oled.h b/lib/inc/oled/oled.h new file mode 100644 index 0000000..cd8ec08 --- /dev/null +++ b/lib/inc/oled/oled.h @@ -0,0 +1,29 @@ +#ifndef HW_LIB_OLED_H +#define HW_LIB_OLED_H +#ifdef __cplusplus +extern "C" { +#endif +// OLED初始化指令数组 +const uint8_t OLED_Init_Cmd[] = { + 0xAE, // 关闭显示 + 0xD5, 0x80, // 设置显示时钟分频因子/震荡频率 + 0xA8, 0x3F, // 设置多路复用比 + 0xD3, 0x00, // 设置显示偏移 + 0x40, // 设置起始行 + 0x8D, 0x14, // 启用电荷泵 + 0x20, 0x00, // 设置内存地址模式 + 0xA0, // 设置列地址映射 + 0xC0, // 设置扫描方向 + 0xDA, 0x12, // 设置COM硬件引脚配置 + 0x81, 0xCF, // 设置对比度 + 0xD9, 0xF1, // 设置预充电周期 + 0xDB, 0x40, // 设置VCOMH电压倍率 + 0xA4, // 全局显示开启 + 0xA6, // 设置显示方式 + 0xAF // 打开显示 +}; + +#ifdef __cplusplus +} +#endif +#endif //HW_LIB_OLED_H diff --git a/lib/inc/task/task.h b/lib/inc/task/task.h index e0fadcc..26ba849 100644 --- a/lib/inc/task/task.h +++ b/lib/inc/task/task.h @@ -29,36 +29,85 @@ struct TaskList { void *userdata; }; -//时间基准注册,建议1-10ms的基准 +/** + * @brief 初始化任务模块 + * @param TicksFunc: [输入] 时间基准函数指针 + * @return TaskStatus_t 返回任务状态 + * @example TaskInit(GetPlatformTicks); + */ TaskStatus_t TaskInit(PlatformTicksFunc_t TicksFunc); -//创建任务 -// time:间隔时间 -// runcnt:执行次数 为-1无限制执行 为0只创建不运行 -// callback:回调函数 -// userdata:用户数据传参 + +/** + * @brief 创建任务 + * @param task: [输入/输出] 任务结构体指针 + * @param time: [输入] 间隔时间 + * @param runcnt: [输入] 执行次数,-1为无限制执行,0为只创建不运行 + * @param callback: [输入] 回调函数 + * @param userdata: [输入] 用户数据传参 + * @return TaskStatus_t 返回任务状态 + * @example TaskCreat(&myTask, 100, -1, myCallbackFunc, &myData); + */ TaskStatus_t TaskCreat(Task_t *task, TaskTime_t time, TaskCnt_t runcnt, Task_Callback_t callback, void *userdata); -//添加任务 +/** + * @brief 添加任务 + * @param task: [输入] 任务结构体指针 + * @return TaskStatus_t 返回任务状态 + * @example TaskAdd(&myTask); + */ TaskStatus_t TaskAdd(Task_t *task); -//删除任务 +/** + * @brief 删除任务 + * @param task: [输入] 任务结构体指针 + * @return TaskStatus_t 返回任务状态 + * @example TaskDel(&myTask); + */ TaskStatus_t TaskDel(Task_t *task); -//重新开始以停止的任务 +/** + * @brief 重新开始停止的任务 + * @param task: [输入] 任务结构体指针 + * @return TaskStatus_t 返回任务状态 + * @example TaskStart(&myTask); + */ TaskStatus_t TaskStart(Task_t *task); -//暂停任务执行 +/** + * @brief 暂停任务执行 + * @param task: [输入] 任务结构体指针 + * @return TaskStatus_t 返回任务状态 + * @example TaskStop(&myTask); + */ TaskStatus_t TaskStop(Task_t *task); -//重设任务执行次数 +/** + * @brief 重设任务执行次数 + * @param task: [输入] 任务结构体指针 + * @param runcnt: [输入] 新的执行次数 + * @return TaskStatus_t 返回任务状态 + * @example TaskSetCnt(&myTask, 5); + */ TaskStatus_t TaskSetCnt(Task_t *task, TaskCnt_t runcnt); -//重设任务间隔时间 +/** + * @brief 重设任务间隔时间 + * @param task: [输入] 任务结构体指针 + * @param time: [输入] 新的间隔时间 + * @return TaskStatus_t 返回任务状态 + * @example TaskSetTime(&myTask, 50); + */ + TaskStatus_t TaskSetTime(Task_t *task, TaskTime_t time); -//任务调度 +/** + * @brief 任务调度 + * @return TaskStatus_t 返回任务状态 + * @example TaskRun(); + */ TaskStatus_t TaskRun(void); + #ifdef __cplusplus } #endif diff --git a/lib/inc/utils/tool.h b/lib/inc/utils/tool.h index 2b51cc5..0dc6b27 100644 --- a/lib/inc/utils/tool.h +++ b/lib/inc/utils/tool.h @@ -29,16 +29,16 @@ typedef union { u16 u16; } Data16_t; typedef enum { // 定义枚举类型Type_t,包含不同数据类型 - U8, // 无符号8位整数 - U16, // 无符号16位整数 - U32, // 无符号32位整数 + T_U8, // 无符号8位整数 + T_U16, // 无符号16位整数 + T_U32, // 无符号32位整数 - CHAR, // 字符 - SHORT, // 短整数 - INT, // 整数 + T_CHAR, // 字符 + T_SHORT, // 短整数 + T_INT, // 整数 - FLOAT, // 浮点数 - DOUBLE, // 双精度浮点数 + T_FLOAT, // 浮点数 + T_DOUBLE, // 双精度浮点数 } Type_t; #define TYPE_T(v) _Generic((v), \ @@ -69,7 +69,7 @@ typedef enum { // 定义枚举类型Type_t,包含不同数据类型 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) // 计算数组的元素个数 -#define in , // 定义逗号为in +//#define in , // 定义逗号为in #define _foreach(e, a) for(size_t e = 0; e < ARRAY_SIZE(a); e++) // 实现foreach宏,遍历数组a,e为当前元素下标 #define foreach(exp) _foreach(exp) // 定义foreach宏,用于遍历数组 @@ -150,7 +150,7 @@ float Str2Float(char *str); **/ #define PRINT_ARRAY(arr, fmt, frame) do { \ printf("\n"#arr ":\n"); \ - foreach(i in arr) {\ + for(size_t i = 0; i < ARRAY_SIZE(arr); i++) {\ if(i%frame==0&&i!=0) printf("\n");\ printf(fmt " ", arr[i]); }\ printf("\n"); } while (0) diff --git a/lib/src/key/key.cpp b/lib/src/key/key.cpp new file mode 100644 index 0000000..5bf21b6 --- /dev/null +++ b/lib/src/key/key.cpp @@ -0,0 +1,170 @@ +#include +#include "key.h" + + +#define EVENT_CB(ev) if(key->cb[ev])key->cb[ev](key) +#define PRESS_REPEAT_MAX_NUM 15 + + +static Key_t *head_key = NULL; + +static void key_state_switch(Key_t *key); + + +void key_init(Key_t *key, uint8_t(*pin_level)(uint8_t), uint8_t active_level, uint8_t key_id) { + memset(key, 0, sizeof(Key_t)); + key->event = (uint8_t) NONE_PRESS; + key->hal_key_Level = pin_level; + key->key_level = !active_level; + key->active_level = active_level; + key->key_id = key_id; +} + + +void key_attach(Key_t *key, PressEvent event, Key_Callback_t cb) { + // 如果事件类型为ALL_EVENT,则将回调函数cb分别赋值给所有事件类型 + if (event == KEY_ALL_EVENT) { + for (uint8_t i = KEY_PRESS_UP; i < number_of_event; i++) { + key->cb[i] = cb; + } + } else { + // 否则,将回调函数cb赋值给指定的事件类型 + key->cb[event] = cb; + } +} + +PressEvent get_key_event(Key_t *key) { + return (PressEvent) (key->event); +} + +static void key_state_switch(Key_t *key) { + // 读取按键的GPIO电平 + uint8_t read_gpio_level = key->hal_key_Level(key->key_id); + + // 如果按键状态大于0,则增加ticks计数 + if ((key->state) > 0) key->ticks++; + + // 按键去抖动处理 + if (read_gpio_level != key->key_level) { + if (++(key->debounce_cnt) >= DEBOUNCE_TICKS) { + key->key_level = read_gpio_level; + key->debounce_cnt = 0; + } + } else { + key->debounce_cnt = 0; + } + + // 状态机处理按键事件 + switch (key->state) { + case 0: + if (key->key_level == key->active_level) { + key->event = (uint8_t) KEY_PRESS_DOWN; + EVENT_CB(KEY_PRESS_DOWN); + key->ticks = 0; + key->repeat = 1; + key->state = 1; + } else { + key->event = (uint8_t) NONE_PRESS; + } + break; + + case 1: + if (key->key_level != key->active_level) { + key->event = (uint8_t) KEY_PRESS_UP; + EVENT_CB(KEY_PRESS_UP); + key->ticks = 0; + key->state = 2; + } else if (key->ticks > LONG_TICKS) { + key->event = (uint8_t) KEY_LONG_PRESS_START; + EVENT_CB(KEY_LONG_PRESS_START); + key->state = 5; + } + break; + + case 2: + if (key->key_level == key->active_level) { + key->event = (uint8_t) KEY_PRESS_DOWN; + EVENT_CB(KEY_PRESS_DOWN); + if (key->repeat != PRESS_REPEAT_MAX_NUM) { + key->repeat++; + } + EVENT_CB(KEY_PRESS_REPEAT); + key->ticks = 0; + key->state = 3; + } else if (key->ticks > SHORT_TICKS) { + if (key->repeat == 1) { + key->event = (uint8_t) KEY_SINGLE_CLICK; + EVENT_CB(KEY_SINGLE_CLICK); + } else if (key->repeat == 2) { + key->event = (uint8_t) KEY_DOUBLE_CLICK; + EVENT_CB(KEY_DOUBLE_CLICK); + } + key->state = 0; + } + break; + + case 3: + if (key->key_level != key->active_level) { + key->event = (uint8_t) KEY_PRESS_UP; + EVENT_CB(KEY_PRESS_UP); + if (key->ticks < SHORT_TICKS) { + key->ticks = 0; + key->state = 2; + } else { + key->state = 0; + } + } else if (key->ticks > SHORT_TICKS) { + key->state = 1; + } + break; + + case 5: + if (key->key_level == key->active_level) { + key->event = (uint8_t) KEY_LONG_PRESS_HOLD; + EVENT_CB(KEY_LONG_PRESS_HOLD); + } else { + key->event = (uint8_t) KEY_PRESS_UP; + EVENT_CB(KEY_PRESS_UP); + key->state = 0; + } + break; + + default: + key->state = 0; + break; + } +} + +int key_start(Key_t *key) { + Key_t *target = head_key; + while (target) { + if (target == key) return -1; + target = target->next; + } + key->next = head_key; + head_key = key; + return 0; +} + +void key_stop(Key_t *key) { + Key_t **curr; + for (curr = &head_key; *curr;) { + Key_t *entry = *curr; + if (entry == key) { + *curr = entry->next; +#if KEY_STOP_FREE + free(entry); +#endif + return; + } else { + curr = &entry->next; + } + } +} + +void key_ticks(void) { + Key_t *target; + for (target = head_key; target; target = target->next) { + key_state_switch(target); + } +} \ No newline at end of file diff --git a/lib/src/utils/tool.cpp b/lib/src/utils/tool.cpp index 8d1555f..96cc927 100644 --- a/lib/src/utils/tool.cpp +++ b/lib/src/utils/tool.cpp @@ -14,28 +14,28 @@ void BufPrint(char *name, void *buf, Type_t type, unsigned int len, unsigned cha for (int i = 0; i < len; ++i) { if (i % frame == 0 && i != 0) printf("\n"); switch (type) { - case U8: + case T_U8: printf("%02X ", *((unsigned char *) buf + i)); break; - case U16: + case T_U16: printf("%04X ", *((unsigned short *) buf + i)); break; - case U32: + case T_U32: printf("%08X ", *((unsigned int *) buf + i)); break; - case CHAR: + case T_CHAR: printf("%c ", *((char *) buf + i)); break; - case SHORT: + case T_SHORT: printf("%d ", *((short *) buf + i)); break; - case INT: + case T_INT: printf("%d ", *((int *) buf + i)); break; - case FLOAT: + case T_FLOAT: printf("%0.2f ", *((float *) buf + i)); break; - case DOUBLE: + case T_DOUBLE: printf("%0.2f ", *((double *) buf + i)); break; default: diff --git a/main.c b/main.c index a17b1d7..d7b3c6d 100644 --- a/main.c +++ b/main.c @@ -6,6 +6,7 @@ #include "t_task.h" #include "t_arg.h" #include "t_list.h" +#include "t_key.h" #include "tool.h" #include @@ -23,7 +24,7 @@ int main() { Test_RunTime("ArgPase", Test_argpase); Test_RunTime("List", Test_List); Test_RunTime("Queue", Test_Queue); - Test_RunTime("Task", Test_task); - +// Test_RunTime("Task", Test_task); + Test_RunTime("Key", Test_Key); return 0; }