Skip to content

FreeRTOS

这里的FreeRTOS使用SMP(对称多处理), 可使一个 FreeRTOS FreeRTOS 内核实例在多个相同的处理器内核上调度任务。 这些内核架构必须相同,并共享相同的内存。

在ESP-IDF里面大部分的配置文件是不可以修改的

CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY 向后兼容

程序

ESP-IDF已经启动了cTaskStartScheduler这一个函数, 用户需要自定义一个app_main的函数, 作为用户程序的入口

这一个函数可以返回, 可以用于启动其他的任务

空闲任务 (IDLEx)为每个 CPU 核创建并分配一个空闲任务 (IDLEx),其中 x 是 CPU 核的编号。 当启用单核配置时,x 将被删除。CONFIG_FREERTOS_IDLE_TASK_STACKSIZE核 x0
FreeRTOS 定时器任务 (Tmr Svc)如果应用程序调用了任何 FreeRTOS 定时器 API,FreeRTOS 会创建定时器服务或守护任务CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH核 0CONFIG_FREERTOS_TIMER_TASK_PRIORITY
主任务 (main)简单调用 app_main 的任务在 app_main 返回时会自我删除CONFIG_ESP_MAIN_TASK_STACK_SIZECONFIG_ESP_MAIN_TASK_AFFINITY1
IPC 任务 (ipcx)CONFIG_FREERTOS_UNICORE 为假时,为每个 CPU 核创建并分配一个 IPC 任务 (ipcx)。IPC 任务用于实现处理器间调用 (IPC) 功能CONFIG_ESP_IPC_TASK_STACK_SIZE核 x24
ESP 定时器任务 (esp_timer)ESP-IDF 创建 ESP 定时器任务用于处理 ESP 定时器回调CONFIG_ESP_TIMER_TASK_STACK_SIZE核 022

双核SMP

对称多处理

对称多处理是一种计算架构,其中,两个及以上相同的 CPU 核连接到单个共享的主内存,并由单个操作系统控制。SMP 系统通常具有以下特点:

  • 多个核独立运行。每个核都有自己的寄存器文件、中断和中断处理。
  • 对每个核呈现相同的内存视图。因此,无论在哪个核上运行,访问特定内存地址的代码都会产生相同的效果。

与单核或非对称多处理系统相比,SMP 系统的主要优势在于:

  • 存在多个核,支持多个硬件线程,从而提高整体处理吞吐量。
  • 对称内存支持线程在执行期间切换核,从而提高 CPU 利用率。

尽管 SMP 系统支持线程切换核,但在某些情况下,线程必须或应该仅在特定核上运行。因此,在 SMP 系统中,线程也具备核亲和性,指定线程在哪个特定核上运行。

  • 分配给特定核的线程只能在该核上运行。
  • 未分配给特定核的线程支持在执行期间切换核。

esp32的SMP

  • 具有两个完全相同的核,分别称为核 0 和核 1。代码段无论在哪个核上运行,都有相同的执行效果。
  • 具有对称内存(除了少数例外情况)。
    • 如果多个核同时访问相同的内存地址,它们的访问会被内存总线串行化。
    • 通过 ISA 提供的原子比较和交换指令,可以实现对同一内存地址的真正原子访问。
  • 跨核中断支持由一个核触发另一个核上的中断,这使得核间可以互相发送信号,如请求在另一个核上进行上下文切换。

任务

c
BaseType_t xTaskCreatePinnedToCore( TaskFunction_t pxTaskCode,
                                   const char * const pcName,
                                   const configSTACK_DEPTH_TYPE usStackDepth,
                                   void * const pvParameters,
                                   UBaseType_t uxPriority,
                                   TaskHandle_t * const pvCreatedTask,
                                   const BaseType_t xCoreID );

在一个特定的内核创建一个任务

pxTaskCode 任务 pcName 名字 usStackDepth 栈的大小 pvParameters 参数 uxPriority 优先级

pvCreatedTask 返回任务句柄 xCoreID 使用的核

一般情况使用普通的创建函数即可, 内部已经经过调整了

在释放任务的时候可以释放另一个内核上面的任务, 但是这一个任务需要把互斥锁之类的资源手动释放

临界区

在 SMP 系统中,仅禁用中断并不能构成临界区,因为存在其他核意味着共享资源仍可以同时访问。因此,IDF FreeRTOS 中的临界区是使用自旋锁实现的。

  • 自旋锁为 portMUX_TYPE (请勿与 FreeRTOS 互斥混淆)
  • taskENTER_CRITICAL(&spinlock) 从任务上下文进入临界区
  • taskEXIT_CRITICAL(&spinlock) 从任务上下文退出临界区
  • taskENTER_CRITICAL_ISR(&spinlock) 从中断上下文进入临界区
  • taskEXIT_CRITICAL_ISR(&spinlock) 从中断上下文退出临界区

这一个可以重复调用, 但是注意不要死锁

实际使用静态

c
// 静态分配并初始化自旋锁
static portMUX_TYPE my_spinlock = portMUX_INITIALIZER_UNLOCKED;

void some_function(void)
{
    taskENTER_CRITICAL(&my_spinlock);
    // 此时已处于临界区
    taskEXIT_CRITICAL(&my_spinlock);
}
  • 动态
c
// 动态分配自旋锁
portMUX_TYPE *my_spinlock = malloc(sizeof(portMUX_TYPE));
// 动态初始化自旋锁
portMUX_INITIALIZE(my_spinlock);

...

taskENTER_CRITICAL(my_spinlock);
// 访问资源
taskEXIT_CRITICAL(my_spinlock);