Skip to content

点击跳转FreeRTOS介绍

ESP32的FreeRTOS使用

这里我们只介绍常用的API其他API自行查询官方文档 官方文档介绍

创建任务

使用xTaskCreate来创建任务 函数定义如下:

BaseType_t xTaskCreate(
                            TaskFunction_t pvTaskCode,//任务函数
                            const char * const pcName,//任务名称
                            const uint32_t usStackDepth,//堆栈大小 单位字节
                            void * const pvParameters, //传入的参数
                            UBaseType_t uxPriority,//任务优先级
                            TaskHandle_t * const pxCreatedTask)//任务句柄指针

  • pvTaskCode:任务函数 类型为void 函数名 (void* 参数名){}类型
  • pcName:任务名称 不能使用中文
  • usStackDepth:堆栈大小 单位字节,常见取值范围是[128-8192]
  • pvParameters:传入的参数指针 不需要时传入 nullptr
  • uxPriority:任务优先级 数字越大优先级越高 0为最小优先级 普通任务优先级建议为5
  • pxCreatedTask:任务句柄指针 不需要时传入 nullptr

注意xTaskCreate创建的任务是运行在多核上的

xTaskCreatePinnedToCore可以指定运行在那个核心(0-1)

删除任务

void vTaskDelete( TaskHandle_t xTaskToDelete ) FreeRTOS中对任务进行操作都是用任务句柄 - xTaskToDelete 要删除的任务句柄,为NULL时删除当前任务

任务中延时

标准延时

FreeRTOS标准的任务中延时应该用 void vTaskDelay(const TickType_t xTicksToDelay) 但是ESP32的Arduino SDK中的delay函数定义如下: 可以看到和vTaskDelay是等价的,所以esp32使用delay函数是不阻塞CPU的 注意主动delay最小时间为一个时间片,正常情况下是1ms,小于1ms不可用delay实现 而是使用delayMicroseconds(uint32_t us):注意该函数会阻塞CPU

控制频率的延时

首先我们看一个示例

// 控制任务函数
void DMMotorSpeedPos::DMmotortask(void* _motor) {
    DMMotorSpeedPos* motor =reinterpret_cast<DMMotorSpeedPos*>(_motor);
    while (1) {
        motor->sendSpeedPospakage(); // 每次循环发送数据包
        delay(1000 / motor->DataRateHz); // 根据数据频率设置延时
    }
}

这是一个典型的循环任务,目的是以固定频率执行某些任务 我们很容易理解 - 每次循环体执行的时间是1ms 那么整体频率就是1000HZ 真的是这样吗? 大多数的情况下我们执行的任务时间复杂度并不高,在1000个cpu周期以下 那么正常情况语句只需要

1000240MHZ=4.17us

实际频率约为

10.00100417≈996HZ

和预期相差不大可以接受

但是如果语句的执行时间为ms级别,或者会不定阻塞CPU 这时实际频率和预期频率就会差很多,并且一些涉及控制的场景还会影响控制 此时我们就需要使用

xTaskDelayUntil

函数原型 BaseType_t xTaskDelayUntil( TickType_t * const pxPreviousWakeTime,const TickType_t xTimeIncrement )

  • pxPreviousWakeTime :上次更新的时间,传入TickType_t的指针
  • xTimeIncrement :延时的tick数,一般为configTICK_RATE_HZ /{控制频率}。configTICK_RATE_HZ是FreeRTOS的系统频率宏定义 此时改写上面的示例为:
// 控制任务函数
void DMMotorSpeedPos::DMmotortask(void* _motor) {

    DMMotorSpeedPos* motor =reinterpret_cast<DMMotorSpeedPos*>(_motor);

    // 获取当前时间
    auto xLastWakeTime = xTaskGetTickCount ();
    while (1) {
        // 每次循环发送数据包
        motor->sendSpeedPospakage(); 
        // 根据数据频率设置延时
        xTaskDelayUntil(&xLastWakeTime, configTICK_RATE_HZ / motor->DataRateHz);
    }
}

让出CPU

正常情况下任务中有delay或者非阻塞函数的使用,是不需要主动释放CPU的。

vPortYield():主动让出当前CPU,让CPU执行其他任务