52Hz——FreeRTOS学习笔记——进/出临界区
主要API
taskENTER_CRITICAL 和 taskEXIT_CRITICAL
总结
临界区核心概念
临界区是一段必须原子性执行的代码,在这段代码执行期间,不会被其他任务或中断打断。
原子性操作:原子性操作是指一个或多个操作不可被中断的执行单元。这些操作要么全部完成,要么全部不执行,不存在"执行了一半"的中间状态。
作用与目的
保护共享资源(如全局变量、外设寄存器等)在访问过程中不被中断或任务切换打断,确保操作的原子性。
例子
1. 防止多个任务或中断同时访问共享数据造成竞争条件。
// 危险的非原子操作 void unsafe_increment(void) {int temp = shared_counter; // 步骤1:读取temp += 1; // 步骤2:计算shared_counter = temp; // 步骤3:写入// 可能在步骤1和3之间被中断,导致数据错误 }// 安全的临界区保护 void safe_increment(void) {taskENTER_CRITICAL();int temp = shared_counter;temp += 1;shared_counter = temp;taskEXIT_CRITICAL(); // 整个操作原子性完成 }
2. 确保对硬件寄存器的连续操作不被中断。
void configure_serial_port(void) {taskENTER_CRITICAL();// 配置串口寄存器序列(必须连续完成)UART1->CR1 = 0x00; // 先禁用UART1->BRR = 0x0341; // 设置波特率UART1->CR2 = 0x0000; // 设置数据位UART1->CR1 = 0x2000; // 最后使能taskEXIT_CRITICAL(); // 确保配置完整生效 }
3.保护复杂数据结构的修改过程。
void add_to_linked_list(ListNode_t* new_node) {taskENTER_CRITICAL();// 链表操作必须原子性完成new_node->next = head;head = new_node;list_count++;taskEXIT_CRITICAL(); // 防止链表处于不一致状态 }
特点:
临界区可以嵌套使用,但是必须确保嵌套的进入和退出次数匹配。
临界区代码应尽可能短,因为关闭中断会影响系统的实时性。
注意:
在临界区内,不能调用任何会使任务阻塞的FreeRTOS API函数(如vTaskDelay、队列操作等),因为这会可能导致任务切换,而临界区内是不允许任务切换的。
案例
#include "MyTask.h"typedef enum
{TASK_PRIORITY_0,TASK_PRIORITY_1,TASK_PRIORITY_2,TASK_PRIORITY_3,TASK_PRIORITY_4
} Task_Priority_t;void Increament_Task(void *pvParameters);
TaskHandle_t Increament_task_handle;
#define Increament_TASK_NAME "Increament_Task"
#define Increament_TASK_STACK_DEPTH 128void Decreament_Task(void *pvParameters);
TaskHandle_t Decreament_task_handle;
#define Decreament_TASK_NAME "Decreament_Task"
#define Decreament_TASK_STACK_DEPTH 128void Print_Task(void *pvParameters);
TaskHandle_t Print_task_handle;
#define Print_TASK_NAME "Print_Task"
#define Print_TASK_STACK_DEPTH 128int16_t count = 0;void MyTask_Start_Work(void)
{// 1. 创建任务xTaskCreate(Increament_Task, Increament_TASK_NAME, Increament_TASK_STACK_DEPTH, NULL, TASK_PRIORITY_4, &Increament_task_handle);xTaskCreate(Decreament_Task, Decreament_TASK_NAME, Decreament_TASK_STACK_DEPTH, NULL, TASK_PRIORITY_4, &Decreament_task_handle);xTaskCreate(Print_Task, Print_TASK_NAME, Print_TASK_STACK_DEPTH, NULL, TASK_PRIORITY_4, &Print_task_handle);// 2. 开启调度vTaskStartScheduler();
}void Increament_Task(void *pvParameters)
{taskENTER_CRITICAL();for (uint16_t i = 0; i < 10000; i++){count++; // count = count + 1}taskEXIT_CRITICAL();vTaskDelete(NULL);
}void Decreament_Task(void *pvParameters)
{for (uint16_t i = 0; i < 10000; i++){taskENTER_CRITICAL();count--; // count = count - 1taskEXIT_CRITICAL();}vTaskDelete(NULL);
}void Print_Task(void *pvParameters)
{while (1){printf("count = %d \n", count);vTaskDelay(100);}
}