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

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

相关推荐
fie888919 小时前
基于51单片机的航模遥控器6通道接收机程序
单片机·嵌入式硬件·51单片机
llilian_1620 小时前
选择北斗导航卫星信号模拟器注意事项总结 北斗导航卫星模拟器 北斗导航信号模拟器
功能测试·单片机·嵌入式硬件·测试工具·51单片机·硬件工程
Yyq1302086968220 小时前
MH2457,‌国产 32 位屏驱 MCU‌芯片,支持‌1080P 高清显示‌与‌以太网通信‌,广泛应用于两轮车仪表盘及工控屏等领域
单片机·嵌入式硬件
Freak嵌入式21 小时前
LVGL基础知识和概念:视觉样式与资源系统
ide·驱动开发·嵌入式·lvgl·micropython·upypi
爱吃程序猿的喵21 小时前
南邮计科电工电子实验B《RLC串联谐振电路》实验报告
单片机·嵌入式硬件
bubiyoushang8881 天前
利用STM32实现Modbus通信(RTU从机方案)
stm32·单片机·嵌入式硬件
cmpxr_1 天前
【单片机】常用设计模式
单片机·嵌入式硬件·设计模式
杰杰桀桀桀1 天前
4*4无时延矩阵键盘(非阻塞)--附代码链接
stm32·单片机·嵌入式硬件·矩阵·计算机外设·无时延矩阵键盘
奶茶拌火锅1 天前
EB配置Tc27x——MCU
单片机·嵌入式硬件
cmpxr_1 天前
【单片机】STM32Fxx启动模式怎么接
stm32·单片机·嵌入式硬件