ESP32开发之路(4)— 定时器实现LED灯闪烁及中断检测按键
本次开发是在Ubuntu下的,使用的模块是GOOUUU-ESP32,使用VSCode编辑项目。基于工程:ESP32开发之路(3)— 点亮第一个LED灯及按键输入
一、使用RTOS的定时器实现LED灯闪烁
我们使用freeRTOS提供的软件定时器来进行定时,定时时间设为1s,则LED就2s闪烁一次,首先我们查看freeRTOS的软件定时器功能有没有打开,打开FreeRTOSConfig.h
,查看如下几个定义:
configUSE_TIMERS
为1时表示RTOS的软件定时器功能已经打开,定时器服务任务就会在启动FreeRTOS 调度器的时候自动创建;
configTIMER_TASK_PRIORITY
为软件定时器服务任务的任务优先级,本例中为1;
configTIMER_QUEUE_LENGTH
用来设置定时器命令队列的队列长度,本例中为10;
configTIMER_TASK_STACK_DEPTH
此宏用来设置定时器服务任务的任务堆栈大小,单位为字,不是字节!本例中为2048。
要创建一个定时器,我们要定义一个定时器句柄和定时器回调函数,
/* 定义LED闪烁定时器句柄*/
TimerHandle_t LED_Timer_Handle;
/* 声明定时器回调函数 */
void LED_Timer_Callback(TimerHandle_t xTimer);
然后使用xTiemrCreate()函数创建一个定时器。其中定时器周期单位为时钟节拍,我们通过portTICK_PERIOD_MS
转化为ms;然后定时器模式,当为pdTRUE
时为周期定时,为pdFALSE
时为单次定时;定时器ID号,因为FreeRTOS 支持多个定时器共用同一个回调函数,所以在回调函数中根据定时器的 ID 号来处理不同的定时器。
LED_Timer_Handle = xTimerCreate((const char*)"LED Timer", /* 软件定时器名称 */
(TickType_t )(1000 / portTICK_PERIOD_MS), /* 定时周期,单位为时钟节拍 */
(UBaseType_t)pdTRUE, /* 定时器模式,是否为周期定时模式 */
(void*)1, /* 定时器ID号 */
(TimerCallbackFunction_t)LED_Timer_Callback);/*定时器回调函数 */
软件定时器创建成功的话会返回软件定时器句柄,失败则返回NULL,当创建成功后,我们就使用函数xTimerStart()开启这个定时器,第一个参数为开启的软件定时器的句柄,第二个参数为阻塞时间,调用函数xTimerStart()开启软件定时器其实就是向定时器命令队列发送一条 tmrCOMMAND_START 命令,既然是向队列发送消息,那肯定会涉及到入队阻塞时间的设置,当返回值为pdPASS
时表示定时器启动成功,为pdFAIL
时表示启动失败;
if((LED_Timer_Handle != NULL))
ret = xTimerStart(LED_Timer_Handle,0); /* 创建成功,开启定时器*/
else
printf("LED Timer Create failure !!! \n"); /* 定时器创建失败 */
if(ret == pdPASS)
printf("LED Timer Start OK. \n"); /* 定时器启动成功*/
else
printf("LED Timer Start err. \n"); /* 定时器启动失败*/
然后我们实现定时器回调函数,让LED闪烁:
/* 定时器回调函数 */
void LED_Timer_Callback(TimerHandle_t xTimer)
{
static int led_flag=0;
led_flag = !led_flag; /* led电平翻转*/
gpio_set_level(GPIO_LED_NUM, led_flag); /* 根据led_flag设置led电平,实现LED闪烁*/
}
我们烧录程序到开发板上,可以看到,定时器启动成功
LED也在闪烁:
二、中断方式检测按键
首先,修改BOOT按键的gpio的配置,将其中断模式修改为下降沿,
gpio_config_structure.intr_type = GPIO_PIN_INTR_NEGEDGE; /* 下降沿触发中断 */
然后开启gpio中断,并设置中断回调函数,gpio_install_isr_service()的参数为中断设置的标志,在esp_intr_alloc.h
定义,除了优先级还有其他标志;然后gpio_isr_handler_add()的第三个参数将会作为参数传递给中断处理函数。
/* 开启gpio中断服务 */
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); /* LEVEL1为最低优先级 */
/* 设置GPIO的中断回调函数 */
gpio_isr_handler_add(GPIO_KEY_NUM, gpio_isr_handler, (void*) GPIO_KEY_NUM);
然后我们定义一个按键检测使用的定时器,当检测到下降沿50ms后,按键仍然处于按下状态,就表示按键真正被按下了,防止抖动和误触发;
/* 创建按键检测定时器,50ms,单次定时*/
Key_Timer_Handle = xTimerCreate("Key Timer", (50/portTICK_PERIOD_MS), pdFALSE, 1, KEY_Timer_Callback);
然后我们在中断处理函数复位定时器,让其开始运行
static void gpio_isr_handler(void* arg)
{
xTimerResetFromISR(Key_Timer_Handle,NULL);
}
在定时器回调函数里打印出按键被按下
void KEY_Timer_Callback(TimerHandle_t xTimer)
{
printf("BOOT KEY have pressed. \n");
}
烧录,实现效果
三、将按键检测添加到消息队列
我们创建一个消息队列,当按键按下时,往消息队列里发送按键按下信息,然后在主任务里读取这个消息队列,获取按键消息,首先,创建一个消息队列:
/* 定义一个按键值消息队列句柄 */
QueueHandle_t Key_Queue;
/* 创建按键检测消息队列 */
Key_Queue = xQueueCreate((UBaseType_t )10, /* 队列长度,这里是队列的项目数,即这个队列可以接受多少个消息*/
(UBaseType_t )10); /* 队列中每个消息的长度,单位为字节 */
然后在定时器回调函数里将按键信息发送到消息队列
void KEY_Timer_Callback(TimerHandle_t xTimer)
{
static int key_times = 0;
char msg[50];
key_times++;
printf("BOOT KEY have pressed. \n");
sprintf(msg,"BOOT KEY have pressed %d times.",key_times);
xQueueSendFromISR(Key_Queue, msg, NULL);
}
在主任务里接收消息队列
char msg[50];
while(1)
{
if(xQueueReceive(Key_Queue, msg, portMAX_DELAY))
{
printf("in app_main : %s \n", msg);
}
vTaskDelay(100 / portTICK_PERIOD_MS); /* 延时100ms*/
}
烧录下载,功能实现:
四、代码
贴上app_main()的代码
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"
#include "esp_intr_alloc.h"
#define GPIO_LED_NUM 2
#define GPIO_KEY_NUM 0
/* 定义LED闪烁定时器句柄*/
TimerHandle_t LED_Timer_Handle;
/* 声明定时器回调函数 */
void LED_Timer_Callback(TimerHandle_t xTimer);
/* 定义LED闪烁定时器句柄*/
TimerHandle_t Key_Timer_Handle;
/* 声明定时器回调函数 */
void KEY_Timer_Callback(TimerHandle_t xTimer);
/* 定义一个按键值消息队列句柄 */
QueueHandle_t Key_Queue;
/* gpio中断处理函数*/
static void gpio_isr_handler(void* arg)
{
xTimerResetFromISR(Key_Timer_Handle,NULL);
}
void app_main(void)
{
BaseType_t ret = 0;
/* 打印Hello world! */
printf("Hello world!\n");
/* 定义一个gpio配置结构体 */
gpio_config_t gpio_config_structure;
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_LED_NUM);/* 选择gpio2 */
gpio_config_structure.mode = GPIO_MODE_OUTPUT; /* 输出模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_PIN_INTR_DISABLE; /* 禁止中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
/* 初始化gpio配置结构体*/
gpio_config_structure.pin_bit_mask = (1ULL << GPIO_KEY_NUM);/* 选择gpio0 */
gpio_config_structure.mode = GPIO_MODE_INPUT; /* 输入模式 */
gpio_config_structure.pull_up_en = 0; /* 不上拉 */
gpio_config_structure.pull_down_en = 0; /* 不下拉 */
gpio_config_structure.intr_type = GPIO_PIN_INTR_NEGEDGE; /* 下降沿触发中断 */
/* 根据设定参数初始化并使能 */
gpio_config(&gpio_config_structure);
/* 开启gpio中断服务 */
gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); /* LEVEL1为最低优先级 */
/* 设置GPIO的中断回调函数 */
gpio_isr_handler_add(GPIO_KEY_NUM, gpio_isr_handler, (void*) GPIO_KEY_NUM);
/* 输出高电平,点亮LED*/
gpio_set_level(GPIO_LED_NUM, 1);
LED_Timer_Handle = xTimerCreate((const char*)"LED Timer", /* 软件定时器名称 */
(TickType_t )(1000 / portTICK_PERIOD_MS), /* 定时周期,单位为时钟节拍 */
(UBaseType_t)pdTRUE, /* 定时器模式,是否为周期定时模式 */
(void*)1, /* 定时器ID号 */
(TimerCallbackFunction_t)LED_Timer_Callback);/*定时器回调函数 */
if((LED_Timer_Handle != NULL))
ret = xTimerStart(LED_Timer_Handle,0); /* 创建成功,开启定时器*/
else
printf("LED Timer Create failure !!! \n"); /* 定时器创建失败 */
if(ret == pdPASS)
printf("LED Timer Start OK. \n"); /* 定时器启动成功*/
else
printf("LED Timer Start err. \n"); /* 定时器启动失败*/
/* 创建按键检测定时器,50ms,单次定时*/
Key_Timer_Handle = xTimerCreate("Key Timer", (50/portTICK_PERIOD_MS), pdFALSE, (void*)1, KEY_Timer_Callback);
/* 创建按键检测消息队列 */
Key_Queue = xQueueCreate((UBaseType_t )10, /* 队列长度,这里是队列的项目数,即这个队列可以接受多少个消息*/
(UBaseType_t )50); /* 队列中每个消息的长度,单位为字节 */
char msg[50];
while(1)
{
if(xQueueReceive(Key_Queue, msg, portMAX_DELAY))
{
printf("in app_main : %s \n", msg);
}
vTaskDelay(100 / portTICK_PERIOD_MS); /* 延时100ms*/
}
}
/* 定时器回调函数 */
void LED_Timer_Callback(TimerHandle_t xTimer)
{
static int led_flag=0;
led_flag = !led_flag; /* led电平翻转*/
gpio_set_level(GPIO_LED_NUM, led_flag); /* 根据led_flag设置led电平,实现LED闪烁*/
}
void KEY_Timer_Callback(TimerHandle_t xTimer)
{
static int key_times = 0;
char msg[50];
key_times++;
printf("BOOT KEY have pressed. \n");
sprintf(msg,"BOOT KEY have pressed %d times.",key_times);
xQueueSendFromISR(Key_Queue, msg, NULL);
}