ESP32入门开发·FreeRTOS相关·任务的创建和使用

目录

1.  创建工程

2.  代码编辑

2.1  打开工程

2.2  包含头文件

2.3  创建任务

2.4  GPIO口初始化

2.5  创建控制任务

2.6  完整代码

3.  运行

4.  PWM拓展

4.1  增加头文件

4.2  初始化定时器

4.3  初始化ledc通道

4.4  渐变服务配置

4.4.1  安装渐变服务

4.4.2  配置渐变参数

4.4.3  启动渐变

4.4.4  回调函数

4.4.5  回调

4.4.6  任务函数

4.4.7  完整代码


1.  创建工程

        在MobaXterm_Personal输入命令:

idf.py create-project led

        进行创建工程:

        进入到创建的工程,继续输入命令,编译工程文件:

idf.py build

2.  代码编辑

2.1  打开工程

        在VS code打开刚刚创建的工程:

        项目工程如下:

2.2  包含头文件

        首先点击键盘:Shift+Ctrl+P,可以找到:

        要是没找到直接对照输入搜索也可以,找到后点击,这样做的目的是包含头文件,否则之后写入的头文件会显示找不大路径。

        该工程所要使用的头文件:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

2.3  创建任务

        首先定义一下LED灯所在的GPIO口:

#define LED_GPIO GPIO_NUM_27

        创建任务函数,大多都是现成的API接口直接调用就行:

void led_run_task(void* param)
{
    int gpio_level = 0;

    while(1)
    {
        gpio_level = gpio_level ? 0 : 1;

        //API设置指定GPIO引脚的电平状态
        gpio_set_level(LED_GPIO,gpio_level);

        // 任务延迟500毫秒
        // pdMS_TO_TICKS将毫秒转换为FreeRTOS的时钟节拍
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

2.4  GPIO口初始化

        这里STM32是直接创建一个函数进行初始化操作,在这里是使用结构体进行初始化,然后再调用API接口,将初始化信息写入到底层代码当中,其中对于结构体成员:


gpio_config_t led_cfg = {
    .intr_type = GPIO_INTR_DISABLE,       // 禁用 GPIO 中断(LED 控制不需要中断)
    .mode = GPIO_MODE_OUTPUT,             // 设置为输出模式(控制 LED 亮灭)
    .pin_bit_mask = (1 << LED_GPIO),      // 设置要配置的 GPIO 引脚(通过位掩码指定)
    .pull_down_en = GPIO_PULLDOWN_DISABLE,// 禁用内部下拉电阻
    .pull_up_en = GPIO_PULLUP_DISABLE,    // 禁用内部上拉电阻
};

        将配置好的结构体配置到硬件:

// 应用 GPIO 配置到硬件
// 参数:led_cfg 是已定义好的 GPIO 配置结构体(gpio_config_t 类型)
gpio_config(&led_cfg);

2.5  创建控制任务

        函数原型:

BaseType_t xTaskCreatePinnedToCore(
    TaskFunction_t pvTaskCode,      // 任务函数指针
    const char * const pcName,      // 任务名称(字符串标识)
    const uint32_t usStackDepth,    // 任务堆栈大小(单位:字)
    void * const pvParameters,      // 任务参数(传递给任务函数)
    UBaseType_t uxPriority,         // 任务优先级(数值越大优先级越高)
    TaskHandle_t * const pvCreatedTask, // 任务句柄(用于管理任务)
    const BaseType_t xCoreID         // 指定运行的核心(0 或 1,或 tskNO_AFFINITY)
);

部分参数注解: 

usStackDepth

类型:uint32_t

作用:指定任务堆栈大小,单位是字(4字节)。

本例 2048 表示堆栈大小为 2048 * 4 = 8192 字节(8KB)。

注意:需根据任务复杂度调整,过小会导致堆栈溢出。

uxPriority

类型:UBaseType_t

作用:任务优先级(0 为最低,configMAX_PRIORITIES-1 为最高)。

本例 3 表示中等优先级(具体取决于系统配置)。

优先级规则:高优先级任务会抢占低优先级任务的 CPU 时间。

xCoreID

类型:BaseType_t

作用:指定任务运行的核心(仅限多核系统如 ESP32)。

0:核心 0(通常运行 WiFi/BT 协议栈)

1:核心 1(通常运行用户任务)

tskNO_AFFINITY(-1):由调度器自动分配

本例 1 表示强制任务运行在核心 1,避免干扰核心 0 的系统任务。


        配置文件如下:

xTaskCreatePinnedToCore(
    led_run_task,   // 任务函数
    "led",          // 任务名称
    2048,           // 堆栈大小(8192 字节),8KB
    NULL,           // 无参数传递
    3,              // 优先级
    NULL,           // 不需要任务句柄
    1               // 运行在核心 1
);

2.6  完整代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#define LED_GPIO GPIO_NUM_27

void led_run_task(void* param)
{
    int gpio_level = 0;

    while(1)
    {
        gpio_level = gpio_level ? 0 : 1;
        gpio_set_level(LED_GPIO,gpio_level);

        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

void app_main(void)
{
    gpio_config_t led_cfg ={
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = (1 << LED_GPIO),
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE,
    };

    gpio_config(&led_cfg);

    xTaskCreatePinnedToCore(led_run_task,"led",2048,NULL,3,NULL,1);

}

3.  运行

        保存代码,返回到MobaXterm_Personal输入编译命令:

idf.py build

        输入烧录命令:

idf.py flash

        可以看到led进行简单的闪烁。

4.  PWM拓展

4.1  增加头文件

        因为我们需要使用PWM的相关API,这些API函数存放在ledc文件中:

#include "driver/ledc.h"

4.2  初始化定时器

        相关参数:

        其结构体:

typedef struct {
    ledc_mode_t speed_mode;      // PWM 速度模式(高速/低速)
    ledc_timer_bit_t duty_resolution; // 占空比分辨率(位数)
    ledc_timer_t timer_num;      // 定时器编号(0-3)
    uint32_t freq_hz;           // PWM 频率(Hz)
    ledc_clk_cfg_t clk_cfg;      // 时钟源配置(可选)
} ledc_timer_config_t;

speed_mode

  • 类型ledc_mode_t

  • 作用:选择 PWM 控制器的工作模式。

  • 可选值

    • LEDC_HIGH_SPEED_MODE:高速模式(由 APB 时钟驱动,80MHz)

    • LEDC_LOW_SPEED_MODE:低速模式(由 RTC 时钟驱动,8MHz)

duty_resolution

  • 类型ledc_timer_bit_t

  • 作用:设置占空比的分辨率(即 PWM 精度)。

  • 可选值LEDC_TIMER_1_BIT 到 LEDC_TIMER_20_BIT(1~20 位)。

  • 计算方式

    • 若设为 LEDC_TIMER_10_BIT,则占空比范围为 0 ~ 1023(2^10 - 1)。

    • 占空比实际值 = (duty / 2^resolution) * 100%

  • 权衡

    • 分辨率越高,PWM 越精细,但频率上限会降低(因计数器位数增加)。

timer_num

  • 类型ledc_timer_t

  • 作用:选择硬件定时器编号(LEDC 模块共有 4 个定时器)。

  • 可选值LEDC_TIMER_0 到 LEDC_TIMER_3

  • 注意

    • 每个定时器可独立配置频率和分辨率,但需分配给不同的通道使用。

freq_hz

  • 类型uint32_t

  • 作用:设置 PWM 信号的频率(单位 Hz)。

  • 取值范围

    • 受限于分辨率和时钟源。例如:

      • 10 位分辨率 + 80MHz 时钟 → 最大频率约 78.125kHz80MHz / 2^10)。

    • 典型 LED 调光频率:1kHz ~ 5kHz(避免人眼可见闪烁)。

clk_cfg(可选)

  • 类型ledc_clk_cfg_t

  • 作用:选择时钟源(通常无需手动配置,默认为 LEDC_AUTO_CLK)。

  • 可选值

    • LEDC_USE_APB_CLK:APB 时钟(高速模式默认)

    • LEDC_USE_RTC8M_CLK:RTC 8MHz 时钟(低速模式默认)

    • LEDC_AUTO_CLK:自动选择(推荐)。

编写代码:

ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_LOW_SPEED_MODE,   // 低速模式(RTC时钟驱动)
    .duty_resolution = LEDC_TIMER_13_BIT, // 13位占空比分辨率(0~8191)
    .timer_num = LEDC_TIMER_0,           // 使用定时器0
    .freq_hz = 5000,                     // PWM频率5kHz
    .clk_cfg = LEDC_AUTO_CLK,            // 自动选择时钟源
};
ledc_timer_config(&ledc_timer);          // 应用配置

4.3  初始化ledc通道

结构体定义:

typedef struct {
    int gpio_num;               // GPIO 引脚号
    ledc_mode_t speed_mode;     // 速度模式(高速/低速)
    ledc_channel_t channel;     // PWM 通道号(0-7)
    ledc_timer_sel_t timer_sel; // 绑定的定时器编号(0-3)
    uint32_t duty;              // 初始占空比(0 ~ 2^duty_resolution - 1)
    int hpoint;                 // 相位偏移(通常设为0)
    struct {
        unsigned int output_invert: 1; // 是否输出电平反转
    } flags;                    // 附加标志位
} ledc_channel_config_t;

代码编写:

ledc_channel_config_t ledc_channel = {
    .speed_mode = LEDC_LOW_SPEED_MODE,  // 低速模式(RTC时钟驱动)
    .channel = LEDC_CHANNEL_0,         // 使用通道0
    .timer_sel = LEDC_TIMER_0,         // 绑定到定时器0
    .gpio_num = LED_GPIO,              // PWM输出引脚(需替换为具体GPIO号)
    .duty = 0,                         // 初始占空比为0(LED默认熄灭)
    .intr_type = LEDC_INTR_DISABLE,    // 禁用中断
};   
ledc_channel_config(&ledc_channel);

4.4  渐变服务配置

4.4.1  安装渐变服务

函数功能:

esp_err_t ledc_fade_func_install(int intr_alloc_flags);
  • 作用:安装 LEDC 渐变(淡入淡出)功能所需的软件服务和中断。

  • 调用时机:在需要使用 LEDC 渐变功能(如呼吸灯效果)之前调用,通常只需调用一次。

  • 底层操作

    • 初始化内部数据结构。

    • 注册中断服务程序(ISR),用于处理占空比自动渐变。

    ledc_fade_func_install(0);//默认中断优先级

4.4.2  配置渐变参数

ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000);
  • LEDC_LOW_SPEED_MODE:低速模式(需与定时器配置一致)。

  • LEDC_CHANNEL_0:操作通道 0。

  • 8191:目标占空比(由于之前定时器为 13 位分辨率,范围 0~8191)。

  • 2000:渐变时间(2000ms,即 2 秒)。

4.4.3  启动渐变

ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
  • LEDC_LOW_SPEED_MODE:低速模式(需与定时器配置一致)。

  • LEDC_CHANNEL_0:操作通道 0。

  • LEDC_FADE_NO_WAIT:非阻塞模式,函数立即返回,渐变在后台运行。

注意:若需等待渐变完成,可使用 LEDC_FADE_WAIT_DONE,但会阻塞当前任务。

4.4.4  回调函数

事件标志位定义:

#define FULL_EV_BIT BIT0  // 满占空比事件标志(LED亮度100%)
#define EMPTY_EV_BIT BIT1 // 零占空比事件标志(LED亮度0%)

事件组句柄:

static EventGroupHandle_t ledc_event_handle;

 回调函数:

bool IRAM_ATTR ledc_finish_cb(const ledc_cb_param_t *param, void *user_arg) {
    BaseType_t taskWoken = pdFALSE; 

    if(param->duty) {
        xEventGroupSetBitsFromISR(ledc_event_handle, FULL_EV_BIT, &taskWoken);
    } else {
        xEventGroupSetBitsFromISR(ledc_event_handle, EMPTY_EV_BIT, &taskWoken);
    }

    return taskWoken;
}
  • IRAM_ATTR:强制将函数放入IRAM(避免Flash访问延迟,确保中断实时性)。

  • param->duty:判断当前占空比是否非零(duty=0表示LED完全熄灭)。

  • xEventGroupSetBitsFromISR:在中断中安全设置事件标志,taskWoken 用于触发上下文切换。

4.4.5  回调

ledc_cbs_t cbs = {
    .fade_cb = ledc_finish_cb, // 已定义回调函数
};
ledc_cb_register(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, &cbs, NULL);

4.4.6  任务函数

void led_run_task(void* param)
{
    EventBits_t ev;

    while(1)
    {
        ev = xEventGroupWaitBits(ledc_event_handle, FULL_EV_BIT|EMPTY_EV_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(5000));
        if(ev & FULL_EV_BIT)
        {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,0,2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,LEDC_FADE_NO_WAIT);
        }
        if(ev & EMPTY_EV_BIT)
        {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,8191,2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,LEDC_FADE_NO_WAIT);
        }

        ledc_cbs_t cbs = 
        {
            .fade_cb = ledc_finish_cb,
        };
    
        ledc_cb_register(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,&cbs,NULL);
    }
}

4.4.7  完整代码

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/ledc.h"


#define LED_GPIO GPIO_NUM_27

#define FULL_EV_BIT BIT0
#define EMPTY_EV_BIT BIT1

static EventGroupHandle_t ledc_event_handle;

bool IRAM_ATTR ledc_finish_cb(const ledc_cb_param_t *param, void *user_arg)
{
    BaseType_t taskWoken = pdFALSE;

    if(param -> duty)
    {
        xEventGroupSetBitsFromISR(ledc_event_handle,FULL_EV_BIT,&taskWoken);
    }
    else
    {
        xEventGroupSetBitsFromISR(ledc_event_handle,EMPTY_EV_BIT,&taskWoken);
    }

    return taskWoken;
}

void led_run_task(void* param)
{
    EventBits_t ev;

    while(1)
    {
        ev = xEventGroupWaitBits(ledc_event_handle, FULL_EV_BIT|EMPTY_EV_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(5000));
        if(ev & FULL_EV_BIT)
        {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,0,2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,LEDC_FADE_NO_WAIT);
        }
        if(ev & EMPTY_EV_BIT)
        {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,8191,2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,LEDC_FADE_NO_WAIT);
        }

        ledc_cbs_t cbs = 
        {
            .fade_cb = ledc_finish_cb,
        };
    
        ledc_cb_register(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,&cbs,NULL);
    }
}

void app_main(void)
{
    gpio_config_t led_cfg ={
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = (1 << LED_GPIO),
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE,
    };
    gpio_config(&led_cfg);

    //安装渐变服务
    ledc_fade_func_install(0);

    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .duty_resolution = LEDC_TIMER_13_BIT,
        .timer_num = LEDC_TIMER_0,
        .freq_hz = 5000,
        .clk_cfg = LEDC_AUTO_CLK,       
    };
    ledc_timer_config(&ledc_timer);

    ledc_channel_config_t ledc_channel = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0, 
        .timer_sel = LEDC_TIMER_0, 
        .gpio_num = LED_GPIO, 
        .duty = 0, 
        .intr_type = LEDC_INTR_DISABLE,
    };
    ledc_channel_config(&ledc_channel);


    ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,8191,2000);

    ledc_fade_start(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,LEDC_FADE_NO_WAIT);

    ledc_cbs_t cbs = 
    {
        .fade_cb = ledc_finish_cb,
    };

    ledc_cb_register(LEDC_LOW_SPEED_MODE,LEDC_CHANNEL_0,&cbs,NULL);

    xTaskCreatePinnedToCore(led_run_task,"led",2048,NULL,3,NULL,1);

}

ps:上边代码有一些错误,以下是订正版本:

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/gpio.h"
#include "driver/ledc.h"

#define LED_GPIO GPIO_NUM_27
#define FULL_EV_BIT BIT0
#define EMPTY_EV_BIT BIT1

static EventGroupHandle_t ledc_event_handle;

bool IRAM_ATTR ledc_finish_cb(const ledc_cb_param_t *param, void *user_arg)
{
    BaseType_t taskWoken = pdFALSE;

    if(param->duty > 0) {
        xEventGroupSetBitsFromISR(ledc_event_handle, FULL_EV_BIT, &taskWoken);
    } else {
        xEventGroupSetBitsFromISR(ledc_event_handle, EMPTY_EV_BIT, &taskWoken);
    }

    return taskWoken;
}

void led_run_task(void* param)
{
    EventBits_t ev;

    while(1) {
        ev = xEventGroupWaitBits(ledc_event_handle, 
                               FULL_EV_BIT | EMPTY_EV_BIT, 
                               pdTRUE,      // 自动清除标志位
                               pdFALSE,     // 不需要所有位
                               portMAX_DELAY); // 无限等待

        if(ev & FULL_EV_BIT) {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0, 2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
        }
        else if(ev & EMPTY_EV_BIT) {
            ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000);
            ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT);
        }
    }
}

void app_main(void)
{
    // 初始化事件组
    ledc_event_handle = xEventGroupCreate();
    assert(ledc_event_handle != NULL);

    // GPIO配置
    gpio_config_t led_cfg = {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = (1 << LED_GPIO),
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE,
    };
    gpio_config(&led_cfg);

    // 安装LEDC渐变服务
    ESP_ERROR_CHECK(ledc_fade_func_install(0));

    // 定时器配置
    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .duty_resolution = LEDC_TIMER_13_BIT,
        .timer_num = LEDC_TIMER_0,
        .freq_hz = 5000,
        .clk_cfg = LEDC_AUTO_CLK,       
    };
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    // 通道配置(修正:移除不存在的intr_type字段)
    ledc_channel_config_t ledc_channel = {
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0,
        .timer_sel = LEDC_TIMER_0,
        .gpio_num = LED_GPIO,
        .duty = 0,
        .hpoint = 0,  // 必须添加
        .flags = {
            .output_invert = 0  // 明确设置
        }
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));

    // 注册回调(只需一次)
    ledc_cbs_t cbs = {
        .fade_cb = ledc_finish_cb,
    };
    ESP_ERROR_CHECK(ledc_cb_register(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, &cbs, NULL));

    // 启动初始渐变
    ESP_ERROR_CHECK(ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8191, 2000));
    ESP_ERROR_CHECK(ledc_fade_start(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, LEDC_FADE_NO_WAIT));

    // 创建任务
    xTaskCreatePinnedToCore(led_run_task, "led", 2048, NULL, 3, NULL, 1);
}

ESP32学习笔记_时光の尘的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时光の尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值