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继续调度。

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

相关推荐
ShiMetaPi2 小时前
GM-3568JHF丨ARM+FPGA异构开发板应用开发教程:01 UART读写案例
arm开发·单片机·嵌入式硬件·fpga开发·fpga·rk3568
松涛和鸣2 小时前
55、ARM与IMX6ULL入门
c语言·arm开发·数据库·单片机·sqlite·html
国科安芯3 小时前
AS32X601的I2C模块操作EEPROM详解
stm32·单片机·嵌入式硬件·架构·安全威胁分析·安全性测试
xu_wenming3 小时前
沁恒MCU 和乐鑫ESP32MCU选型对比
单片机·嵌入式硬件
QK_004 小时前
STM32--IIC
stm32·单片机·嵌入式硬件
超级码农ProMax4 小时前
stm32——使用位图自定义内存池
stm32·单片机·嵌入式硬件
vsropy4 小时前
基于HAL库的STM32工程模板
stm32·单片机·嵌入式硬件
LCMICRO-133108477465 小时前
长芯微LD8574完全P2P替代PCF8574,主要用于扩展通用输入输出端口(GPIO)
单片机·嵌入式硬件·fpga开发·硬件工程·dsp开发
时空自由民.5 小时前
I3C协议介绍
汇编·stm32·单片机