Appearance
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 | 核 x | 0 |
---|---|---|---|---|
FreeRTOS 定时器任务 (Tmr Svc ) | 如果应用程序调用了任何 FreeRTOS 定时器 API,FreeRTOS 会创建定时器服务或守护任务 | CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH | 核 0 | CONFIG_FREERTOS_TIMER_TASK_PRIORITY |
主任务 (main ) | 简单调用 app_main 的任务在 app_main 返回时会自我删除 | CONFIG_ESP_MAIN_TASK_STACK_SIZE | CONFIG_ESP_MAIN_TASK_AFFINITY | 1 |
IPC 任务 (ipcx ) | 当 CONFIG_FREERTOS_UNICORE 为假时,为每个 CPU 核创建并分配一个 IPC 任务 (ipcx )。IPC 任务用于实现处理器间调用 (IPC) 功能 | CONFIG_ESP_IPC_TASK_STACK_SIZE | 核 x | 24 |
ESP 定时器任务 (esp_timer ) | ESP-IDF 创建 ESP 定时器任务用于处理 ESP 定时器回调 | CONFIG_ESP_TIMER_TASK_STACK_SIZE | 核 0 | 22 |
双核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);