ESP32开发之路(4)--- 定时器实现LED灯闪烁及中断检测按键

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);
}

发布了62 篇原创文章 · 获赞 13 · 访问量 5572

猜你喜欢

转载自blog.csdn.net/qq_38113006/article/details/105450618