├── MCUKeys.c ├── MCUKeys.h └── README.MD /MCUKeys.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xupenghu/MCUKeys/HEAD/MCUKeys.c -------------------------------------------------------------------------------- /MCUKeys.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xupenghu/MCUKeys/HEAD/MCUKeys.h -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | # MCUKeys 3 | 4 | ## 1 简介 5 | MCUKeys完全使用C语言编写,是一个简洁小巧可配置的、灵活的、通用的的按键检测程序,全部源码都使用中文注释,方便阅读。有基于按键事件回调和按键缓冲区两种方式解决按键事件。截至到目前为止它可以检测如下输入事件: 6 | 7 | - 按键按下 8 | - 按键抬起 9 | - 长按(时间可配置) 10 | - 连击(时间可配置) 11 | - 双击(时间可配置) 12 | - 组合按键(可以实现以上五种状态) 13 | 14 | 同时,所有的事件都支持注册回调函数,在事件发生时执行。如果当前系统希望只检测按键按下事件,则可以屏蔽其他所有事件,对于其他按键事件亦然。 15 | 16 | 17 | ## 2 使用方法 18 | 19 | ### 2.1 先定义一个按键,如果是多个按键可以用数组来实现 20 | 21 | ```c 22 | enum 23 | { 24 | KEY_ID_UP = 0, 25 | KEY_ID_DOWN, 26 | KEY_ID_LEFT, 27 | KEY_ID_RIGHT, 28 | KEY_ID_ENTER, 29 | KEY_ID_MAX, 30 | }; 31 | static key my_key[KEY_ID_MAX]; 32 | ``` 33 | **KEY_ID**是按键的唯一标识符,不可重复,可以使用enum来实现。 34 | ### 2.2 初始化按键 35 | 36 | ```c 37 | for (int i = 0; i < (KEY_ID_MAX); i++) 38 | { 39 | key_init(&my_key[i]); 40 | my_key[i].e_key.id = i; 41 | my_key[i].key_event_cb = key_event_cb; // 屏蔽这行表示不使用事件回调函数 42 | my_key[i].get_key_status = get_key_state_cb; 43 | key_add(&my_key[i]); 44 | } 45 | ``` 46 | 注意:这里的**get_key_state_cb**和硬件息息相关,是唯一需要移植的地方,我这里给出一个使用stm32HAL库开发的实例。 47 | 48 | ```c 49 | 50 | uint8_t get_key_state_cb(const event_key e_key) 51 | { 52 | uint8_t ret = KEY_LIFT; 53 | switch (e_key.id) 54 | { 55 | case KEY_ID_UP: 56 | { 57 | if (HAL_GPIO_ReadPin(GPIOD, KEY0_Pin) == GPIO_PIN_RESET) 58 | { 59 | ret = KEY_PRESS; 60 | } 61 | else 62 | { 63 | ret = KEY_LIFT; 64 | } 65 | } 66 | break; 67 | case KEY_ID_DOWN: 68 | { 69 | if (HAL_GPIO_ReadPin(GPIOD, KEY1_Pin) == GPIO_PIN_RESET) 70 | { 71 | ret = KEY_PRESS; 72 | } 73 | else 74 | { 75 | ret = KEY_LIFT; 76 | } 77 | } 78 | break; 79 | case KEY_ID_LEFT: 80 | { 81 | if (HAL_GPIO_ReadPin(GPIOD, KEY2_Pin) == GPIO_PIN_RESET) 82 | { 83 | ret = KEY_PRESS; 84 | } 85 | else 86 | { 87 | ret = KEY_LIFT; 88 | } 89 | } 90 | break; 91 | case KEY_ID_RIGHT: 92 | { 93 | if (HAL_GPIO_ReadPin(GPIOD, KEY3_Pin) == GPIO_PIN_RESET) 94 | { 95 | ret = KEY_PRESS; 96 | } 97 | else 98 | { 99 | ret = KEY_LIFT; 100 | } 101 | } 102 | break; 103 | case KEY_ID_ENTER: 104 | { 105 | if (HAL_GPIO_ReadPin(GPIOB, KEY4_Pin) == GPIO_PIN_RESET) 106 | { 107 | ret = KEY_PRESS; 108 | } 109 | else 110 | { 111 | ret = KEY_LIFT; 112 | } 113 | } 114 | break; 115 | default: 116 | break; 117 | } 118 | return ret; 119 | } 120 | ``` 121 | 其他类型的MCU需要根据自己的硬件来做相应的修改,我这里的硬件接法是按键按下低电平,按键抬起高电平。 122 | ### 2.3 在一个固定周期的函数中调用key_scan 123 | 这里的固定周期可以是定时器的中断服务函数,也可以是RTOS中的一个线程,周期应该是KEY_TICKS, 124 | 125 | ```c 126 | /* 127 | * 按键循环扫描周期(ms) key_scan()函数在哪个固定扫描周期中 该值就等于多少 一般设置为10ms 128 | */ 129 | #define KEY_TICKS 10 130 | ``` 131 | 比如我在这里将它放在了RTOS的一个单独线程中: 132 | 133 | ```c 134 | void keys_thread_entry(void *param) 135 | { 136 | while (1) 137 | { 138 | key_scan(); 139 | rt_thread_mdelay(KEY_TICKS); 140 | } 141 | } 142 | ``` 143 | 144 | ### 2.4 实现按键事件回调函数 145 | 如果初始化的时候,挂接了按键事件回调函数,则这里必须要实现该函数,当有对应的按键事件时,会回调该函数。 146 | 147 | ```c 148 | void key_event_cb(const event_key e_key) 149 | { 150 | LOG_D("e_key id = %d state = %d", e_key.id, e_key.state); 151 | } 152 | ``` 153 | 这里传入的参数中, e_key.id就是我们初始化时定义的按键ID,e_key.state就是按键的事件,用户可以在这里实现按键回调的具体逻辑。 154 | 实测输出如下: 155 | 156 | ```c 157 | [D/key] e_key id = 0 state = 2 158 | [D/key] e_key id = 4 state = 1 159 | [D/key] e_key id = 4 state = 2 160 | [D/key] e_key id = 0 state = 1 161 | [D/key] e_key id = 0 state = 2 162 | [D/key] e_key id = 1 state = 1 163 | [D/key] e_key id = 1 state = 2 164 | [D/key] e_key id = 3 state = 1 165 | [D/key] e_key id = 3 state = 2 166 | [D/key] e_key id = 2 state = 1 167 | [D/key] e_key id = 2 state = 2 168 | [D/key] e_key id = 4 state = 1 169 | [D/key] e_key id = 4 state = 2 170 | [D/key] e_key id = 0 state = 1 171 | [D/key] e_key id = 0 state = 1 172 | [D/key] e_key id = 0 state = 1 173 | [D/key] e_key id = 0 state = 1 174 | [D/key] e_key id = 0 state = 1 175 | [D/key] e_key id = 0 state = 1 176 | [D/key] e_key id = 0 state = 2 177 | [D/key] e_key id = 0 state = 1 178 | [D/key] e_key id = 0 state = 2 179 | [D/key] e_key id = 0 state = 1 180 | [D/key] e_key id = 0 state = 2 181 | [D/key] e_key id = 0 state = 1 182 | [D/key] e_key id = 0 state = 2 183 | [D/key] e_key id = 0 state = 1 184 | [D/key] e_key id = 0 state = 2 185 | [D/key] e_key id = 0 state = 1 186 | [D/key] e_key id = 0 state = 2 187 | [D/key] e_key id = 0 state = 1 188 | [D/key] e_key id = 0 state = 2 189 | [D/key] e_key id = 1 state = 1 190 | [D/key] e_key id = 1 state = 2 191 | [D/key] e_key id = 1 state = 1 192 | [D/key] e_key id = 1 state = 2 193 | [D/key] e_key id = 1 state = 4 194 | [D/key] e_key id = 2 state = 1 195 | [D/key] e_key id = 2 state = 1 196 | [D/key] e_key id = 2 state = 1 197 | [D/key] e_key id = 2 state = 1 198 | [D/key] e_key id = 2 state = 1 199 | [D/key] e_key id = 2 state = 1 200 | [D/key] e_key id = 2 state = 2 201 | 202 | ``` 203 | 204 | ### 2.5 在main函数中轮询按键状态 205 | 当我们不使用按键的事件回调机制时,还可以使用FIFO缓冲机制,这样就不会漏掉任何一个按键事件。 206 | 207 | ```c 208 | int main(void) 209 | { 210 | /* 必要的初始化代码...*/ 211 | // .... 212 | event_key e_key; 213 | while (1) 214 | { 215 | e_key = key_out_fifo(); 216 | if(e_key.state != KEY_NONE) 217 | { 218 | LOG_D( "e_key id = %d state = %d",e_key.id, e_key.state); 219 | } 220 | rt_thread_delay(800); 221 | } 222 | } 223 | 224 | ``` 225 | 实测输出: 226 | 227 | ```c 228 | [D/main] e_key id = 0 state = 2 229 | [D/main] e_key id = 1 state = 1 230 | [D/main] e_key id = 1 state = 2 231 | [D/main] e_key id = 2 state = 1 232 | [D/main] e_key id = 2 state = 2 233 | [D/main] e_key id = 3 state = 1 234 | [D/main] e_key id = 3 state = 2 235 | [D/main] e_key id = 0 state = 1 236 | [D/main] e_key id = 0 state = 2 237 | [D/main] e_key id = 0 state = 1 238 | [D/main] e_key id = 0 state = 2 239 | [D/main] e_key id = 0 state = 1 240 | [D/main] e_key id = 0 state = 1 241 | [D/main] e_key id = 0 state = 1 242 | [D/main] e_key id = 0 state = 1 243 | [D/main] e_key id = 0 state = 1 244 | [D/main] e_key id = 0 state = 1 245 | [D/main] e_key id = 0 state = 1 246 | [D/main] e_key id = 0 state = 2 247 | [D/main] e_key id = 1 state = 1 248 | [D/main] e_key id = 1 state = 1 249 | [D/main] e_key id = 1 state = 1 250 | [D/main] e_key id = 1 state = 2 251 | 252 | ``` 253 | 254 | 在上述代码中,我在main函数的while(1)中特意加入了800ms延时,实测没有漏掉一个按键状态,如果在使用中有漏按键的情况,请将FIFO缓冲区开大一点。默认是50,即缓冲50个按键状态。 255 | 256 | ```c 257 | #define KEY_FIFO_SIZE 50 //按键FIFO缓冲大小 258 | ``` 259 | 获取源码请点击[这里](https://github.com/xupenghu/MCUKeys),如果有帮助到你,请给star支持! 260 | 261 | 联系作者:[xph](xupenghu@outlook.com) 262 | 263 | 264 | --------------------------------------------------------------------------------