ESP32-S3开发教程三:蜂鸣器与FreeRTOS多任务协同

现在我们来讲解蜂鸣器和FreeRTOS的多任务协同

一、新建工程

按照上节讲述的方法,建立一个名为"Buzzer"的工程:

选择一个空白模板:

打开工程:

二、添加头文件

分别完善CMakeList.txt和main.c文件:

cpp 复制代码
idf_component_register(SRCS "main.c"
                    PRIV_REQUIRES driver freertos
                    INCLUDE_DIRS ".")
cpp 复制代码
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main(void)
{

}

点击编译,验证没有问题:

三、编写逻辑代码

cpp 复制代码
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main(void)
{
    gpio_config_t io_config = {
        .pin_bit_mask = (1ULL << 18),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_config);
    
    while (1) 
    {
        gpio_set_level(18, 1);
        vTaskDelay(pdMS_TO_TICKS(1000));
        gpio_set_level(18, 0);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

我手头的蜂鸣器是低电平触发,也就是I/O口处为低电平时蜂鸣器会发出声音。我们在主函数内写一个死循环,每隔一秒改变一次蜂鸣器的状态,让它响一秒再沉默一秒,如此循环。

四、编译烧录

选择好串口号和烧录方式后,进行烧录,可以听到蜂鸣器响一秒沉默一秒:

五、多任务协同

现在,我想让流水灯和蜂鸣器同时作用。我们先打开上一节的工程,将代码简单粗暴的复制进去,看一下效果:

cpp 复制代码
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main(void)
{
    gpio_config_t io_config = {
        .pin_bit_mask = (1ULL << 18),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_config);
    
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL<<15)|(1ULL<<16)|(1ULL<<17),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_conf);
 
    int a = 0;
    while (1)
    {
        if (a > 2)
        {
            a = 0;
        }
        if (a == 0)
        {
            gpio_set_level(15, 1);
            gpio_set_level(16, 0);
            gpio_set_level(17, 0);
        }
        else if (a == 1)
        {
            gpio_set_level(15, 0);
            gpio_set_level(16, 1);
            gpio_set_level(17, 0);
        }
        else if (a == 2)
        {
            gpio_set_level(15, 0);
            gpio_set_level(16, 0);
            gpio_set_level(17, 1);
        }
        a++;
        vTaskDelay(pdMS_TO_TICKS(1000));
    
        gpio_set_level(18, 1);
        vTaskDelay(pdMS_TO_TICKS(1000));
        gpio_set_level(18, 0);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

点击编译烧录后,可以看到,因为我们把流水灯和蜂鸣器都放在了一个任务里,它们的延时函数会相互影响,所以最后呈现的效果,流水灯和蜂鸣器的时间间隔都会改变。

如何修改?

这里,就需要用到FreeRTOS里的多任务协同功能了,我们先把代码给出:

cpp 复制代码
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"


void gpio_init_all(void)
{
    gpio_config_t io_config = {
        .pin_bit_mask = (1ULL << 18),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_config);

    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL<<15)|(1ULL<<16)|(1ULL<<17),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io_conf);
}

void led_task(void *pvParameters)
{
    int a = 0;
    while (1)
    {
        if (a > 2)
        {
            a = 0;
        }
        if (a == 0)
        {
            gpio_set_level(15, 1);
            gpio_set_level(16, 0);
            gpio_set_level(17, 0);
        }
        else if (a == 1)
        {
            gpio_set_level(15, 0);
            gpio_set_level(16, 1);
            gpio_set_level(17, 0);
        }
        else if (a == 2)
        {
            gpio_set_level(15, 0);
            gpio_set_level(16, 0);
            gpio_set_level(17, 1);
        }
        a++;
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void buzzer_task(void *pvParameters)
{
    while (1)
    {
        gpio_set_level(18, 1);
        vTaskDelay(pdMS_TO_TICKS(1000));
        gpio_set_level(18, 0);
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main(void)
{
    gpio_init_all();
    xTaskCreate(
        buzzer_task, 
        "buzzer_task",
         2048, 
         NULL, 
         10, 
         NULL);
    xTaskCreate(
        led_task, 
        "led_task",
         2048, 
         NULL, 
         10, 
         NULL);
}

在FreeRTOS中,app_main()函数知识任务创建入口,执行完创建任务的代码后就会退出,真正的业务逻辑在两个任务中。

因为我们的gpio初始化代码只需要运行一次,所以我们单独封装一个函数gpio_init_all(),只在上电的时候初始化一次。接下来是将流水灯和蜂鸣器分开拆成两个任务,一个命名为"led_task",另一个命名为"buzzer_task"。

在app_main()函数中,我们使用xTaskDelay来创建任务,其所需要的参数如下:

  1. 任务函数,我们在创建任务时,需要先定义一个函数把逻辑代码装进去,而且需要注意的是,任务的返回值必须是void *pvParameters,否则会报错。
  2. 任务名称,一般任务的名称和任务函数名称一样就可以。
  3. 任务栈大小,单位是字节,2048即可。
  4. 任务参数,这里我们填NULL。
  5. 任务优先级,因为这里流水灯和蜂鸣器并没有优先级一说,所以两个都设置为一样的就可以。
  6. 任务句柄,这里不需要,所以填NULL。

关于FreeRTOS内部任务是如何协调的,这里有一个流程图可以参考:

简单来说,在FreeRTOS中,每个人物都有"就绪态"和"阻塞态",当任务就绪时,CPU就会按照任务优先级进行调度。还记得上一节使用的vTaskDelay吗?当执行到这里时,当前的任务就会进入阻塞态,此时CPU会去执行非阻塞态的任务。而当vTaskDelay结束后,任务又回到就绪态,可以被CPU继续调度。

编译烧录后,可以看到流水灯和蜂鸣器都正常的每隔一秒切换一次状态。

相关推荐
代码游侠10 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200512 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT13 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen14 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
程序员良许15 小时前
三极管推挽输出电路分析
后端·嵌入式
嵌入小生00716 小时前
标准IO---核心函数接口延续(嵌入式Linux)
c语言·vscode·vim·嵌入式·小白·标准io·函数接口
全栈游侠16 小时前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
深圳市九鼎创展科技19 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
辰哥单片机设计19 小时前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件
jllllyuz19 小时前
针对酒店KTV声控DMX512灯光系统的节目选择与实现
嵌入式