一、環境
硬件: NodeMcu DEVKIT
SDK: ESP8266 RTOS SDK 3.0
二、硬件原理
三、軟件功能
通過按鍵S2長按的時間長度不同來實現調用軟件上的功能。
1、當按鍵S2按下時間大於40ms,執行程序1
2、當按鍵S2按下時間大於2000ms,執行程序2
3、當按鍵S2按下時間大於6000ms,執行程序3
四、代碼實現
/*********************************************************************
* INCLUDES
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_system.h"
#include "zlog.h"
#include "key.h"
/*********************************************************************
* MACROS
*/
/*********************************************************************
* CONSTANTS
*/
//定義事件類型
#define KEY_GPIO_LL_PRESS_EVT 0x01 //超長按事件
#define KEY_GPIO_L_PRESS_EVT 0x02 //長按事件
#define KEY_GPIO_S_PRESS_EVT 0x03 //短按事件
#define GPIO_INPUT_IO_0 GPIO_NUM_0
#define GPIO_INPUT_PIN_SEL (1ULL<<GPIO_INPUT_IO_0)
/*********************************************************************
* TYPEDEFS
*/
typedef void (*post_delay_call_t)(void *arg);
typedef void (*key_click_cb_t)(uint8_t event);
/*********************************************************************
* GLOBAL VARIABLES
*/
/*********************************************************************
* LOCAL VARIABLES
*/
static uint64_t elink_time = 0;
static key_click_cb_t pfnKeyClickCallback = NULL;
static xQueueHandle key_evt_queue = NULL;
/*********************************************************************
* LOCAL FUNCTIONS
*/
//軟件定時器實現定時調用指定函數
static void post_delayed_action(int ms, post_delay_call_t action, void *parg)
{
static os_timer_t os_timer;
os_timer_disarm(&os_timer);
os_timer_setfn(&os_timer, action, parg);
os_timer_arm(&os_timer, ms,0);
}
static void key_poll_func(void *arg)
{
uint64_t diff;
uint8_t events = 0;
//獲取引腳狀態
uint32_t level = gpio_get_level(GPIO_INPUT_IO_0);
//按鍵未鬆開
if (level == 0) {
post_delayed_action(10, key_poll_func, NULL);
}
//按鍵已經鬆開
else {
//獲取按下到鬆開的間隔時間
diff = get_now_ms() - elink_time;
if (diff > 6000) { /*long long press */
elink_time = 0;
events = KEY_GPIO_LL_PRESS_EVT;
} else if (diff > 2000) { /* long press */
elink_time = 0;
events = KEY_GPIO_L_PRESS_EVT;
} else if (diff > 40) { /* short press */
elink_time = 0;
events = KEY_GPIO_S_PRESS_EVT;
} else {
post_delayed_action(10, key_poll_func, NULL);
}
}
//發送消息給task
if(events)
{
xQueueSendFromISR(key_evt_queue, &events, NULL);
events = 0;
}
}
static void handle_elink_key()
{
uint32_t level = gpio_get_level(GPIO_INPUT_IO_0);
if ((level == 0) && (elink_time == 0)) {
elink_time = get_now_ms();
key_poll_func(NULL);
}
}
//中斷處理
static void gpio_isr_handler(void *arg)
{
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
if (gpio_status & GPIO_INPUT_PIN_SEL)
{
handle_elink_key();
}
}
static void key_task_handler(void *arg)
{
uint8_t event;
zlog_debug("gpio_task_example");
for (;;) {
if (xQueueReceive(key_evt_queue, &event, portMAX_DELAY)) {
ESP_LOGI("KEY", "%d\n", event);
//接收到消息,調用回調函數,在回調函數中對不同的按鍵類型進行處理
if(pfnKeyClickCallback != NULL)
pfnKeyClickCallback(event);
}
}
}
/*********************************************************************
* GLOBAL FUNCTIONS
*/
/*********************************************************************
* @fn funtion_name
*
* @brief Add device to descovery list.
*
* @param pSimpleDescRsp - SimpleDescRsp containing epInfo of new EP.
*
* @return index of device or 0xFF if no room in list
*/
void key_gpio_init(key_click_cb_t cb)
{
gpio_config_t io_conf;
pfnKeyClickCallback = cb;
memset(&io_conf,0,sizeof(gpio_config_t));
//interrupt of falling edge
io_conf.intr_type = GPIO_INTR_NEGEDGE;
//bit mask of the pins
io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
//set as input mode
io_conf.mode = GPIO_MODE_INPUT;
//enable pull-up mode
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
key_evt_queue = xQueueCreate(10, sizeof(uint32_t));
xTaskCreate(key_task_handle, "key_task_handle", 1024, NULL, 10, NULL);
//install gpio isr service
gpio_install_isr_service(0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *) GPIO_INPUT_IO_0);
}
NOTE: 在調試的過程中,在這些函數中添加打印信息都會造成程序崩潰,只有在key_task_handler任務中打印調試信息程序不會崩潰。問題待查,懷疑是棧的問題。