打印
[经验分享]

FreeRTOS任务管理与调度

[复制链接]
3847|50
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-11-1 16:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
任务管理基础
在嵌入式系统中,任务管理是操作系统的核心功能之一。FreeRTOS作为一个轻量级的实时操作系统,提供了丰富的任务管理功能,使得多任务处理变得简单高效。任务管理包括任务的创建、删除、挂起、恢复、优先级设置等操作。在FreeRTOS中,每个任务都有自己的任务控制块(TCB),用于存储任务的状态、优先级、堆栈等信息。

任务控制块(TCB)
任务控制块(Task Control Block, TCB)是FreeRTOS中用于管理任务的数据结构。每个任务在创建时都会分配一个TCB,TCB中包含了任务的所有管理信息,如任务状态、优先级、堆栈指针等。以下是一个TCB的简化结构示例:

// 简化的TCB结构示例
typedef struct TCB {
    char *pcTaskName;          // 任务名称
    UBaseType_t uxPriority;    // 任务优先级
    StackType_t *pxStack;      // 任务堆栈指针
    configSTACK_DEPTH_TYPE usStackDepth; // 任务堆栈深度
    ListItem_t xStateItem;     // 任务状态列表项
    ListItem_t xEventListItem; // 任务事件列表项
    TickType_t xBlockTime;     // 任务阻塞时间
    // 其他管理信息
} TCB_t;


任务创建
在FreeRTOS中,任务的创建通过xTaskCreate函数实现。该函数需要提供任务的入口函数、任务名称、堆栈深度、任务参数、任务优先级以及一个指向任务句柄的指针。以下是一个创建任务的示例:

// 任务入口函数
void vTaskFunction(void *pvParameters) {
    // 任务名称
    char *pcTaskName = (char *)pvParameters;

    while (1) {
        // 任务主体代码
        printf("Task %s running\n", pcTaskName);
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务
    TaskHandle_t xHandle;
    xTaskCreate(vTaskFunction, "Task 1", configMINIMAL_STACK_SIZE, (void *)"Task 1", 1, &xHandle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



任务删除
任务的删除通过vTaskDelete函数实现。该函数会释放任务的堆栈和其他资源,并将任务从任务列表中移除。以下是一个删除任务的示例:

// 任务入口函数
void vTaskFunction(void *pvParameters) {
    // 任务名称
    char *pcTaskName = (char *)pvParameters;

    while (1) {
        // 任务主体代码
        printf("Task %s running\n", pcTaskName);
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 删除任务
        vTaskDelete(NULL);
    }
}

// 主函数
int main(void) {
    // 创建任务
    TaskHandle_t xHandle;
    xTaskCreate(vTaskFunction, "Task 1", configMINIMAL_STACK_SIZE, (void *)"Task 1", 1, &xHandle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



任务挂起与恢复
任务可以被挂起(暂停)或恢复。挂起任务通过vTaskSuspend函数实现,恢复任务通过vTaskResume函数实现。以下是一个挂起和恢复任务的示例:

// 任务入口函数
void vTaskFunction(void *pvParameters) {
    // 任务名称
    char *pcTaskName = (char *)pvParameters;

    while (1) {
        // 任务主体代码
        printf("Task %s running\n", pcTaskName);
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务
    TaskHandle_t xHandle;
    xTaskCreate(vTaskFunction, "Task 1", configMINIMAL_STACK_SIZE, (void *)"Task 1", 1, &xHandle);

    // 启动调度器
    vTaskStartScheduler();

    // 挂起任务
    vTaskSuspend(xHandle);

    // 延迟10秒后恢复任务
    vTaskDelay(pdMS_TO_TICKS(10000));
    vTaskResume(xHandle);

    // 应该不会到达这里
    for (;;);
}



任务调度机制
FreeRTOS的任务调度机制是基于优先级的抢占式调度。每个任务都有一个优先级,调度器会根据优先级选择最高优先级的就绪任务来执行。如果多个任务具有相同的优先级,调度器会使用时间片轮转机制来调度这些任务。

优先级调度
优先级调度是FreeRTOS中最基本的调度机制。每个任务在创建时都会指定一个优先级,优先级越高的任务越先被调度执行。FreeRTOS支持的任务优先级范围通常从0到(configMAX_PRIORITIES - 1),其中0是最低优先级。

以下是一个优先级调度的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务1,优先级为1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2,优先级为2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务2的优先级高于任务1,因此任务2会优先执行。

时间片轮转
当多个任务具有相同的优先级时,FreeRTOS会使用时间片轮转机制来调度这些任务。每个任务在运行一定时间后会被挂起,调度器会选择下一个就绪的任务来执行。时间片的长度可以通过configTICK_RATE_HZ配置。

以下是一个时间片轮转的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务1,优先级为1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2,优先级为1
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 1, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1和任务2具有相同的优先级,因此调度器会使用时间片轮转机制来交替执行这两个任务。

任务优先级的动态调整
FreeRTOS允许在运行时动态调整任务的优先级。可以通过vTaskPrioritySet函数来设置任务的优先级。以下是一个动态调整任务优先级的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 动态提高任务1的优先级
        vTaskPrioritySet(xTask1Handle, 2);
    }
}

// 主函数
int main(void) {
    // 创建任务1,优先级为1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2,优先级为2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务2在运行时会动态提高任务1的优先级,使得任务1的优先级高于任务2,从而被优先调度执行。

任务间的同步与通信
在多任务系统中,任务间的同步与通信是非常重要的。FreeRTOS提供了多种机制来实现任务间的同步与通信,包括信号量、互斥量、事件组、消息队列等。

信号量
信号量用于实现任务间的同步。FreeRTOS提供了二值信号量和计数信号量两种类型。二值信号量通常用于任务间的互斥访问,计数信号量用于计数资源的使用情况。

二值信号量
以下是一个使用二值信号量的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 等待信号量
        xSemaphoreTake(xBinarySemaphore, portMAX_DELAY);

        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒

        // 释放信号量
        xSemaphoreGive(xBinarySemaphore);
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 释放信号量
        xSemaphoreGive(xBinarySemaphore);
    }
}

// 主函数
int main(void) {
    // 创建二值信号量
    xBinarySemaphore = xSemaphoreCreateBinary();

    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1和任务2通过二值信号量来同步。任务1在获取信号量后执行,任务2在释放信号量后任务1继续执行。

计数信号量
以下是一个使用计数信号量的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 等待信号量
        xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);

        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 释放信号量
        xSemaphoreGive(xCountingSemaphore);
    }
}

// 主函数
int main(void) {
    // 创建计数信号量,初始值为0,最大值为1
    xCountingSemaphore = xSemaphoreCreateCounting(1, 0);

    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1在获取计数信号量后执行,任务2在释放计数信号量后任务1继续执行。

互斥量
互斥量用于保护共享资源,防止多个任务同时访问。以下是一个使用互斥量的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);

        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒

        // 释放互斥量
        xSemaphoreGive(xMutex);
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);

        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 释放互斥量
        xSemaphoreGive(xMutex);
    }
}

// 主函数
int main(void) {
    // 创建互斥量
    xMutex = xSemaphoreCreateMutex();

    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1和任务2通过互斥量来保护共享资源,确保同一时间只有一个任务在访问资源。

事件组
事件组用于实现任务之间的复杂同步机制。每个事件组可以包含多个事件位,任务可以通过设置或清除这些事件位来进行同步。以下是一个使用事件组的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 等待事件组中的某个事件
        EventBits_t uxBits = xEventGroupWaitBits(
            xEventGroup,       // 事件组句柄
            0x01,              // 等待的事件位
            pdTRUE,            // 清除事件位
            pdFALSE,           // 仅等待指定的事件位
            portMAX_DELAY      // 等待时间
        );

        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 设置事件组中的某个事件
        xEventGroupSetBits(xEventGroup, 0x01);
    }
}

// 主函数
int main(void) {
    // 创建事件组
    xEventGroup = xEventGroupCreate();

    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1在等待事件组中的某个事件位被设置后执行,任务2在运行时设置事件组中的某个事件位。

消息队列
消息队列用于任务间的通信。任务可以通过消息队列发送和接收消息。以下是一个使用消息队列的示例:

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 发送消息到队列
        int message = 1;
        xQueueSend(xQueue, &message, portMAX_DELAY);

        // 任务主体代码
        printf("Task 1 running, sent message: %d\n", message);
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 从队列接收消息
        int message;
        if (xQueueReceive(xQueue, &message, portMAX_DELAY)) {
            // 任务主体代码
            printf("Task 2 running, received message: %d\n", message);
        }

        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建消息队列,队列长度为10,每个消息的大小为4字节
    xQueue = xQueueCreate(10, sizeof(int));

    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1通过消息队列发送消息,任务2从消息队列接收消息。任务1和任务2通过消息队列进行通信,确保任务2在接收到消息后执行相应的操作。

任务调度与调度器配置
FreeRTOS的调度器负责管理任务的执行顺序和时间。调度器的配置可以通过修改FreeRTOSConfig.h文件中的宏定义来实现。以下是一些重要的配置选项:

配置选项
configMAX_PRIORITIES: 定义系统支持的最大优先级数。默认值为5,可以根据需要调整。
configTICK_RATE_HZ: 定义系统时钟的频率,单位为Hz。例如,设置为1000表示每秒1000个时钟滴答。
configMINIMAL_STACK_SIZE: 定义系统中最小的堆栈大小。不同平台和任务的需求可能不同,可以根据实际情况调整。
configUSE_PREEMPTION: 启用或禁用抢占式调度。启用抢占式调度可以提高系统的实时性。
调度器启动与运行
调度器通过vTaskStartScheduler函数启动。启动后,调度器会根据任务的优先级和状态来调度任务。调度器的运行依赖于系统时钟中断,通过时钟中断来触发任务的切换。

// 主函数
int main(void) {
    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 如果调度器启动失败,会到达这里
    for (;;);
}



调度器的停止
在某些情况下,可能需要停止调度器。可以通过vTaskEndScheduler函数来停止调度器。需要注意的是,停止调度器后,系统将不再调度任何任务,所有任务都会停止执行。

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒

        // 停止调度器
        vTaskEndScheduler();
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1在运行一段时间后会调用vTaskEndScheduler函数来停止调度器,所有任务将不再执行。

任务调度的性能优化
为了提高系统的实时性和性能,FreeRTOS提供了一些优化机制和技巧。以下是一些常见的性能优化方法:

优先级继承
优先级继承是一种防止优先级反转的机制。当一个低优先级任务持有互斥量,而高优先级任务需要访问该互斥量时,低优先级任务的优先级会被临时提高到高优先级任务的优先级,以加快互斥量的释放。

// 任务1入口函数
void vTask1Function(void *pvParameters) {
    while (1) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);

        // 任务主体代码
        printf("Task 1 running\n");
        vTaskDelay(pdMS_TO_TICKS(500)); // 延迟500毫秒

        // 释放互斥量
        xSemaphoreGive(xMutex);
    }
}

// 任务2入口函数
void vTask2Function(void *pvParameters) {
    while (1) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);

        // 任务主体代码
        printf("Task 2 running\n");
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒

        // 释放互斥量
        xSemaphoreGive(xMutex);
    }
}

// 任务3入口函数
void vTask3Function(void *pvParameters) {
    while (1) {
        // 获取互斥量
        xSemaphoreTake(xMutex, portMAX_DELAY);

        // 任务主体代码
        printf("Task 3 running\n");
        vTaskDelay(pdMS_TO_TICKS(1500)); // 延迟1500毫秒

        // 释放互斥量
        xSemaphoreGive(xMutex);
    }
}

// 主函数
int main(void) {
    // 创建互斥量
    xMutex = xSemaphoreCreateMutex();

    // 创建任务1,优先级为1
    TaskHandle_t xTask1Handle;
    xTaskCreate(vTask1Function, "Task 1", configMINIMAL_STACK_SIZE, NULL, 1, &xTask1Handle);

    // 创建任务2,优先级为2
    TaskHandle_t xTask2Handle;
    xTaskCreate(vTask2Function, "Task 2", configMINIMAL_STACK_SIZE, NULL, 2, &xTask2Handle);

    // 创建任务3,优先级为3
    TaskHandle_t xTask3Handle;
    xTaskCreate(vTask3Function, "Task 3", configMINIMAL_STACK_SIZE, NULL, 3, &xTask3Handle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}





在这个示例中,任务3的优先级最高,任务1的优先级最低。当任务1持有互斥量,任务3需要访问该互斥量时,任务1的优先级会被临时提高到任务3的优先级,以加快互斥量的释放。

空闲任务和内存管理
FreeRTOS提供了一个空闲任务(Idle Task),它在没有其他任务可运行时执行。空闲任务通常用于处理系统空闲时的一些后台任务,如内存管理。FreeRTOS还支持动态内存分配和静态内存分配,可以根据系统的实际需求选择合适的内存管理方式。

动态内存分配
动态内存分配使用pvPortMalloc和vPortFree函数来分配和释放内存。动态内存分配提供了灵活性,但可能会导致内存碎片。

// 任务入口函数
void vTaskFunction(void *pvParameters) {
    while (1) {
        // 动态分配内存
        char *pcMessage = (char *)pvPortMalloc(100 * sizeof(char));
        if (pcMessage != NULL) {
            sprintf(pcMessage, "Task %s running", (char *)pvParameters);
            printf("%s\n", pcMessage);

            // 释放内存
            vPortFree(pcMessage);
        }

        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 创建任务
    TaskHandle_t xHandle;
    xTaskCreate(vTaskFunction, "Task 1", configMINIMAL_STACK_SIZE, (void *)"Task 1", 1, &xHandle);

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



静态内存分配
静态内存分配使用预先分配的内存区域来创建任务。静态内存分配可以避免内存碎片,但需要在创建任务时提供内存区域。

// 任务入口函数
void vTaskFunction(void *pvParameters) {
    while (1) {
        // 任务主体代码
        printf("Task %s running\n", (char *)pvParameters);
        vTaskDelay(pdMS_TO_TICKS(1000)); // 延迟1000毫秒
    }
}

// 主函数
int main(void) {
    // 预先分配的任务堆栈和TCB
    StaticTask_t xTaskBuffer;
    StackType_t xStack[configMINIMAL_STACK_SIZE];

    // 创建任务
    TaskHandle_t xHandle = xTaskCreateStatic(
        vTaskFunction,          // 任务入口函数
        "Task 1",               // 任务名称
        configMINIMAL_STACK_SIZE, // 堆栈深度
        (void *)"Task 1",        // 任务参数
        1,                      // 任务优先级
        xStack,                  // 任务堆栈
        &xTaskBuffer             // 任务控制块
    );

    // 启动调度器
    vTaskStartScheduler();

    // 应该不会到达这里
    for (;;);
}



在这个示例中,任务1的堆栈和TCB是在创建任务时预先分配的,使用xTaskCreateStatic函数创建任务,可以避免动态内存分配带来的内存碎片问题。

总结
FreeRTOS的多任务管理与调度功能非常强大,提供了丰富的API来创建、删除、挂起、恢复任务,以及设置任务的优先级。通过使用信号量、互斥量、事件组和消息队列,可以实现任务间的同步与通信。合理配置调度器和优化内存管理方式,可以提高系统的实时性和性能。理解这些基本概念和机制,有助于开发高效、可靠的嵌入式实时系统。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/weixin_42749425/article/details/142647388

使用特权

评论回复
沙发
10299823| | 2024-11-7 20:25 | 只看该作者
FreeRTOS 的任务管理与调度为嵌入式系统提供了强大的多任务处理能力。正确地使用任务管理API,可以确保系统的实时性和可靠性。在实际应用中,开发者需要根据系统的需求来设计任务的优先级和调度策略。

使用特权

评论回复
板凳
earlmax| | 2024-11-7 20:45 | 只看该作者
在某一时刻,调度器选择了一个任务并将 CPU 资源分配给它,这个任务就处于运行状态。同一时刻只有一个任务可以处于运行状态(在单核处理器上)。

使用特权

评论回复
地板
lzbf| | 2024-11-7 21:04 | 只看该作者
FreeRTOS的任务调度机制是基于优先级的抢占式调度。这意味着调度器会根据任务的优先级选择最高优先级的就绪任务来执行。

使用特权

评论回复
5
abotomson| | 2024-11-7 21:25 | 只看该作者
FreeRTOS 允许为每个任务设置优先级。优先级数值越高,任务的优先级越高。在任务创建时,可以通过参数指定任务的优先级。合理地设置任务优先级是保证系统正常运行的关键。例如,对于一个实时控制系统,用于控制电机的任务优先级可能会设置得比较高,因为电机的控制需要及时响应,而一些用于显示数据更新的任务优先级可以设置得相对较低。

使用特权

评论回复
6
lzmm| | 2024-11-7 21:53 | 只看该作者
FreeRTOS 支持时间片轮转调度(Round Robin Scheduling),允许相同优先级的任务共享CPU时间。

使用特权

评论回复
7
belindagraham| | 2024-11-7 22:12 | 只看该作者
在FreeRTOS中,任务是基本的执行单元。每个任务都是一个独立的无限循环,可以执行特定的功能。任务的创建、删除、挂起、恢复等操作都是通过API函数实现的。

使用特权

评论回复
8
mnynt121| | 2024-11-8 08:10 | 只看该作者
每个任务都有一个优先级,优先级范围从 0 到 (configMAX_PRIORITIES - 1),其中 0 是最低优先级。

使用特权

评论回复
9
youtome| | 2024-11-8 08:54 | 只看该作者
提供了任务管理、时间管理、队列管理、内存管理等功能,其中任务管理是其核心功能之一。

使用特权

评论回复
10
chenci2013| | 2024-11-8 09:44 | 只看该作者
任务可以被挂起,挂起后的任务不会被调度器调度,除非被其他任务或代码明确地恢复。这通常用于暂停某个任务的执行,例如在系统进入低功耗模式时,某些非关键任务可以被挂起。

使用特权

评论回复
11
vivilyly| | 2024-11-8 10:40 | 只看该作者
任务可以被挂起(暂停)或恢复。使用vTaskSuspend函数可以挂起任务,而使用vTaskResume函数可以恢复任务。
挂起任务时,任务的上下文(包括CPU寄存器、堆栈指针等)会被保存,以便在恢复任务时能够继续执行。

使用特权

评论回复
12
mmbs| | 2024-11-8 11:33 | 只看该作者
FreeRTOS 支持时间片轮转调度(Round Robin Scheduling),允许相同优先级的任务共享CPU时间。

使用特权

评论回复
13
wilhelmina2| | 2024-11-8 12:18 | 只看该作者
在使用优先级抢占式调度策略时,可能会遇到优先级反转问题。为了解决这一问题,FreeRTOS提供了多种方法,其中最常用的是使用信号量来进行资源的互斥访问。通过使用信号量,可以确保高优先级任务能够优先获得资源,从而避免优先级反转问题。

使用特权

评论回复
14
cemaj| | 2024-11-8 13:13 | 只看该作者
当多个任务具有相同优先级时,FreeRTOS支持时间片轮转调度,每个任务都会获得一定的时间片来执行。
时间片的大小可以根据应用程序的需要进行调整。

使用特权

评论回复
15
juliestephen| | 2024-11-8 14:06 | 只看该作者
在 FreeRTOS 中,使用xTaskCreate函数(以 C 语言为例)来创建一个任务。这个函数需要传递任务函数(即任务的主体代码)、任务名称、任务栈大小、任务参数等信息。

使用特权

评论回复
16
zerorobert| | 2024-11-8 15:00 | 只看该作者
FreeRTOS使用基于优先级的抢占式调度算法。每个任务都有一个优先级,优先级高的任务会抢占优先级低的任务的执行。调度器会根据任务的优先级和状态来决定哪个任务应该运行。

使用特权

评论回复
17
pixhw| | 2024-11-8 15:55 | 只看该作者
FreeRTOS提供了任务通知功能,用于任务间的通信。任务可以通过发送和接收通知来进行同步和数据传递。

使用特权

评论回复
18
mnynt121| | 2024-11-8 16:43 | 只看该作者
FreeRTOS 中的任务状态可以分为几种:

运行态(Running):当前正在执行的任务。
就绪态(Ready):准备好运行但未被调度的任务。
阻塞态(Blocked):等待某个事件(如定时器、信号量等)的任务。
挂起态(Suspended):被挂起的任务,不会被调度。

使用特权

评论回复
19
hearstnorman323| | 2024-11-8 17:33 | 只看该作者
FreeRTOS 支持多种调度算法,最常用的是基于优先级的抢占式调度。

使用特权

评论回复
20
caigang13| | 2024-11-8 18:37 | 只看该作者
嵌入式OS的内核运行原理差别不大

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2103

主题

16208

帖子

16

粉丝