【Telink B91】2. 矩阵按键以及freeRTOS应用
【Telink B91】3. 硬件I2C驱动OLED
【Telink B91】4. 硬件I2C采集HS3003温湿度信息
前言
试用了一下B91的FreeRTOS以及矩阵按钮,本文将讲解如何使用矩阵按键结合freeRTOS实现按钮对LED的控制。
1 FreeRTOS
官方提供的SDK中有FreeRTOS的示例工程,本文就是在此基础上实现的矩阵按钮的控制代码。
关于SDK获取途径,可以参考我上一篇帖子:【Telink B91】1. B91入坑总结以及串口Demo测试
如上图,选择Freertos_Demo可以验证对freeRTOS的应用。但是有以下事项需要注意:
官方提供的FreeRTOS的时钟配置有问题,并不精确,需要重新设置,打开3rd-party->freertos-V5->include->FreeRTOSConfig.h,并修改如下参数,因为时钟频率是24MHz。
2 矩阵按钮控制
2.1 原理图
矩阵按钮的原理图如下,按钮两边分别连接到两个引脚上,构成了一组2X2的矩阵按钮。
2.2 工作原理
矩阵按钮的两个方向:行和列分别连接到一个按钮的两边,所以就需要检测两次,先检测行引脚状态(列引脚设置为输出低,行引脚设置为输入),再检测列引脚状态(行引脚设置为输出低,列引脚设置为输入)就可以确定具体是哪个按钮按下了。
工作流程如下:
1. 设置TL_Key3和TL_Key4引脚为输出引脚,输出低电平;
2. 设置TL_Key1和TL_Key2引脚为输入,并且设置内部电阻拉高,用于检测引脚是否按下;此时,如果key1和key2被按下,TL_Key1都会检测到低电平,如果key3和key4任何一个按下,TL_Key2都会检测到低电平;
3. 如果上面检测到了TL_Key1或者TL_Key2为低电平,则说明有按钮被按下了,此时需要开始检测行引脚状态用于确实具体按下的引脚;
4. 设置TL_Key1和TL_Key1引脚为输出引脚,输出低电平;
5. 设置TL_Key3和TL_Key4引脚为输入,并且设置内部电阻拉高,用于检测引脚是否按下;此时,如果key1和key3被按下,TL_Key3都会检测到低电平,如果key2和key4任何一个按下,TL_Key4都会检测到低电平;
示例:
如果上述第2步检测到了TL_Key2为低电平,则说明Key3或者Key4被按下了(也可能同时按下);如果第5步检测到了TL_Key3为低电平,则说明按下的按钮为Key3,如果低5步TL_Key3和TL_Key4都检测到了低电平,说明按钮Key3和Key4都按下了。
3 代码实现
从上面可以知道矩阵按钮的工作原理,现在就可以开始实现代码逻辑了,如下代码从兼容性的角度出发,考虑了后期扩展功能,所以稍微复杂了一点点。
3.1 LED引脚初始化
下面为LED的引脚初始化:
- /*
- @hehung
- 2023-7-8
- email: 1398660197@qq.com
- wechat: hehung95
- reproduced and please indicate the source @hehung
- */
- #include "app_led.h"
- // Initialize for Led driver
- void Led_Init(void)
- {
- /* Initialize the GPIO of LEDs */
- gpio_function_en(LED1);
- gpio_output_en(LED1);
- gpio_input_dis(LED1);
- gpio_function_en(LED2);
- gpio_output_en(LED2);
- gpio_input_dis(LED2);
- gpio_function_en(LED3);
- gpio_output_en(LED3);
- gpio_input_dis(LED3);
- gpio_function_en(LED4);
- gpio_output_en(LED4);
- gpio_input_dis(LED4);
- }
3.2 按钮程序
下面为按键程序:
app_key.c
其中:
- Key_Scan为处理逻辑函数,可以在任务中调用这个函数即可
- 如果需要正确使用按钮按下功能或者释放功能,还需要注册相关的函数,需要自己先实现对应的按键按下或者释放对应的动作功能,并通过函数Key_InstallPressFunc以及Key_InstallReleaseFunc注册功能,下面会举例说明
- /*
- @hehung
- 2023-7-8
- email: 1398660197@qq.com
- wechat: hehung95
- reproduced and please indicate the source @hehung
- */
- #include "app_key.h"
- static key_status_t key_status[KEY_TOTAL_NUM] =
- {
- KEY_RELEASED,
- KEY_RELEASED,
- KEY_RELEASED,
- KEY_RELEASED
- };
- static key_status_t last_key_status[KEY_TOTAL_NUM] =
- {
- KEY_RELEASED,
- KEY_RELEASED,
- KEY_RELEASED,
- KEY_RELEASED
- };
- static Key_Func key_press_func[KEY_TOTAL_NUM] =
- {
- NULL,
- NULL,
- NULL,
- NULL
- };
- static Key_Func key_release_func[KEY_TOTAL_NUM] =
- {
- NULL,
- NULL,
- NULL,
- NULL
- };
- static void Key_InitOutput(gpio_pin_e key);
- static void Key_InitInput(gpio_pin_e key);
- static void Key_StatusJudge(key_num_t key);
- static void Key_InitOutput(gpio_pin_e key)
- {
- gpio_function_en(key);
- gpio_output_en(key);
- gpio_set_low_level(key);
- }
- static void Key_InitInput(gpio_pin_e key)
- {
- gpio_function_en(key);
- gpio_output_dis(key);
- gpio_input_en(key);
- gpio_set_up_down_res(key, GPIO_PIN_PULLUP_10K);
- }
- static void Key_StatusJudge(key_num_t key)
- {
- if ((KEY_PRESSED == key_status[key]) && (KEY_RELEASED == last_key_status[key]))
- {
- // pressed
- if (key_press_func[key] != NULL)
- {
- key_press_func[key]();
- }
- }
- else if ((KEY_PRESSED == key_status[key]) && (KEY_PRESSED == last_key_status[key]))
- {
- // keep pressing: TODO
- }
- else if ((KEY_RELEASED == key_status[key]) && (KEY_PRESSED == last_key_status[key]))
- {
- // released
- if (key_release_func[key] != NULL)
- {
- key_release_func[key]();
- }
- }
- else
- {
- // keep releasing: TODO
- }
- last_key_status[key] = key_status[key];
- }
- // matrix key scan logic
- void Key_Scan(void)
- {
- uint8_t key_pending = 0xFF;
- //clear all key status
- key_status[KEY_NUM1] = KEY_RELEASED;
- key_status[KEY_NUM2] = KEY_RELEASED;
- key_status[KEY_NUM3] = KEY_RELEASED;
- key_status[KEY_NUM4] = KEY_RELEASED;
- // scan column
- // Key3 and Key4 output low used for as base volt for check KEY1 and KEY2
- Key_InitOutput(KEY3);
- Key_InitOutput(KEY4);
- // KEY1 and KEY2 as output used for check button pressed signals
- Key_InitInput(KEY1);
- Key_InitInput(KEY2);
- if ((0 == gpio_get_level(KEY1)) || (0 == gpio_get_level(KEY2)))
- {
- //filer: TODO
- if (0 == gpio_get_level(KEY1))
- {
- key_pending = 0x00;
- }
- else
- {
- key_pending = 0x01;
- }
- // Scan row
- // Key1 and Key2 output low used for as base volt for check KEY3 and KEY4
- Key_InitOutput(KEY1);
- Key_InitOutput(KEY2);
- // KEY3 and KEY4 as output used for check button pressed signals
- Key_InitInput(KEY3);
- Key_InitInput(KEY4);
- if ((0 == gpio_get_level(KEY3)) || (0 == gpio_get_level(KEY4)))
- {
- // filter: TODO
- if (0 == gpio_get_level(KEY3))
- {
- key_pending |= 0x00;
- }
- else
- {
- key_pending |= 0x10;
- }
- }
- }
- switch (key_pending)
- {
- case 0x00: key_status[KEY_NUM1] = KEY_PRESSED; break;
- case 0x01: key_status[KEY_NUM3] = KEY_PRESSED; break;
- case 0x10: key_status[KEY_NUM2] = KEY_PRESSED; break;
- case 0x11: key_status[KEY_NUM4] = KEY_PRESSED; break;
- }
- Key_StatusJudge(KEY_NUM1);
- Key_StatusJudge(KEY_NUM2);
- Key_StatusJudge(KEY_NUM3);
- Key_StatusJudge(KEY_NUM4);
- }
- // Used for install the key press function
- void Key_InstallPressFunc(key_num_t key, Key_Func func)
- {
- key_press_func[key] = func;
- }
- // Used for install the key released function
- void Key_InstallReleaseFunc(key_num_t key, Key_Func func)
- {
- key_release_func[key] = func;
- }
- key_status_t Key_GetStatus(key_num_t key)
- {
- return key_status[key];
- }
app_key.h
- /*
- @hehung
- 2023-7-8
- email: 1398660197@qq.com
- wechat: hehung95
- reproduced and please indicate the source @hehung
- */
- #ifndef APP_KEY_H_
- #define APP_KEY_H_
- #include "app_config.h"
- #define KEY_TOTAL_NUM (4U)
- // function pointer
- typedef void (*Key_Func)(void);
- typedef enum
- {
- KEY_PRESSED = 0U,
- KEY_RELEASED = 1U
- } key_status_t;
- typedef enum
- {
- KEY_NUM1 = 0U,
- KEY_NUM2,
- KEY_NUM3,
- KEY_NUM4
- } key_num_t;
- extern void Key_Scan(void);
- extern key_status_t Key_GetStatus(key_num_t key);
- extern void Key_InstallPressFunc(key_num_t key, Key_Func func);
- #endif /* APP_KEY_H_ */
3.3 FreeRTOS
注册了两个任务:
- 一个是LED任务,用来控制LED4每500ms翻转一次
- 一个是按键任务,用于控制对应按键动作
当按键KEY1按下时,LED1翻转
当按键KEY2按下时,LED2翻转
当按键KEY3按下时,LED3翻转
当按键KEY4按下时,LED1,LED2,LED3都会翻转
任务Os_TaskKey中会调用Key_InstallPressFunc先注册Key1~Key4的按下函数,代码如下,但是调用Key_Scan开始按键的周期采样按断以及处理:
- /*
- @hehung
- 2023-7-8
- email: 1398660197@qq.com
- wechat: hehung95
- reproduced and please indicate the source @hehung
- */
- #include "app_freeRTOS.h"
- #include <FreeRTOS.h>
- #include <task.h>
- #include "app_led.h"
- #include "app_key.h"
- #include "printf.h"
- static void Os_TaskLed(void *pvParameters);
- static void Os_TaskKey(void *pvParameters);
- /* Initialize for OS driver */
- void Os_Start(void)
- {
- // Installing the tasks
- xTaskCreate(Os_TaskLed, "led_t", 256, (void*)0, 8, 0);
- xTaskCreate(Os_TaskKey, "key_t", 512, (void*)0, 2, 0);
- vTaskStartScheduler();
- }
- // Led control task
- static void Os_TaskLed(void *pvParameters)
- {
- (void)(pvParameters);
- // Initialize LEDs
- Led_Init();
- while(1)
- {
- gpio_toggle(LED4);
- vTaskDelay(500);
- }
- }
- void Os_TaskKey1_pressed(void)
- {
- gpio_toggle(LED1);
- }
- void Os_TaskKey2_pressed(void)
- {
- gpio_toggle(LED2);
- }
- void Os_TaskKey3_pressed(void)
- {
- gpio_toggle(LED3);
- }
- void Os_TaskKey4_pressed(void)
- {
- gpio_toggle(LED1);
- gpio_toggle(LED2);
- gpio_toggle(LED3);
- }
- static void Os_TaskKey(void *pvParameters)
- {
- (void)(pvParameters);
- // Installing the key press handler function
- Key_InstallPressFunc(KEY_NUM1, Os_TaskKey1_pressed);
- Key_InstallPressFunc(KEY_NUM2, Os_TaskKey2_pressed);
- Key_InstallPressFunc(KEY_NUM3, Os_TaskKey3_pressed);
- Key_InstallPressFunc(KEY_NUM4, Os_TaskKey4_pressed);
- while(1)
- {
- Key_Scan();
- vTaskDelay(10);
- }
- }
3.4 主函数
主函数如下:
- int main (void)
- {
- sys_init(LDO_1P4_LDO_1P8, VBAT_MAX_VALUE_GREATER_THAN_3V6);
- //Note: This function can improve the performance of some modules, which is described in the function comments.
- //Called immediately after sys_init, set in other positions, some calibration values may not take effect.
- user_read_flash_value_calib();
- CCLK_24M_HCLK_24M_PCLK_24M;
- printf("\r\n"); // user for avoid print garbled code on the first character
- printf("\r\n ==== Test project for B91 ==== \r\n");
- // Install the tasks and start FreeRTOS scheduler
- Os_Start();
- while (1) {}
- return 0;
- }
4 实现效果
如下演示视频,演示了4个按钮的控制功能。