1、解释一下什么是句柄以及句柄的作用

句柄通常是一个指针(Pointer)或者一个整数(Integer/ID)

  • 在 FreeRTOS/ESP-IDF 中,绝大多数句柄都是结构体指针。
  • A. 任务句柄 (Task Handle)

    当你创建一个任务时,系统会返回一个句柄,让你以后能控制这个任务(比如删除它、挂起它)。

    TaskHandle_t xHandleTouch = NULL; // 定义一个触摸任务的句柄
    
    // 创建任务,将句柄地址传入
    xTaskCreatePinnedToCore(
        touch_task_function,   // 任务函数
        "TouchTask",           // 任务名
        4096,                  // 栈大小
        NULL,                  // 参数
        20,                    // 优先级
        &xHandleTouch,         // 【关键】这里填入句柄的地址,创建成功后它会指向任务控制块(TCB)
        1                      // 绑定核心
    );
    
    // 后续使用:想删除这个任务?不用知道它在哪,直接用句柄
    vTaskDelete(xHandleTouch); 
    B. 外设驱动句柄 (Driver Handle - ESP-IDF 特色)

    ESP-IDF 的驱动程序(如 I2C, SPI, LCD)广泛使用句柄来管理设备实例。

    #include "driver/i2c.h"
    #include "esp_lcd_panel_ops.h"
    
    // I2C 句柄 (其实是个配置结构体的标识)
    i2c_master_bus_handle_t i2c_bus; 
    
    // LCD 面板句柄 (指向 AXS15231B 驱动实例的指针)
    esp_lcd_panel_handle_t panel_handle = NULL; 
    
    // 初始化屏幕,系统分配资源并返回句柄,将地址传给panel_handle,此时他指向一个结构体
    esp_lcd_new_panel_st7789(..., &panel_handle); 
    
    // 后续操作:刷新屏幕、休眠、设置亮度,全部通过这个句柄
    esp_lcd_panel_draw_bitmap(panel_handle, ...); 
    esp_lcd_panel_disp_on_off(panel_handle, true);
    C. LVGL 对象句柄

    LVGL 中几乎所有控件(按钮、标签、图表)都是对象,通过句柄(lv_obj_t *)操作。

2、信号量

  • 二值信号量 (Binary Semaphore)

    • 取值:只有两个状态:0 或 1(也可理解为“空”或“满”,“锁定”或“解锁”)。
    • 本质:类似于一个互斥锁(Mutex),但通常没有“所有权”概念(即哪个任务释放都可以,不一定非要是获取它的那个任务)。
    • 初始状态:通常初始化为 1(表示资源可用/事件未发生)或 0(表示资源不可用/事件已发生,视具体逻辑而定)。
  • 普通信号量 / 计数信号量 (Counting Semaphore)

    • 取值:可以是 0 到任意正整数 N 之间的任何值。
    • 本质:用于管理具有多个实例的资源池,或者统计事件发生的次数。
    • 初始状态:初始化为资源的最大数量 N。
    • 特性 二值信号量 计数信号量 (普通信号量)
      主要用途 同步 (Synchronization) 或 互斥 (Mutual Exclusion)。 资源计数 (Resource Counting) 或 多事件同步。
      典型场景 1. 任务间同步:任务A等待任务B完成某个动作(如中断服务程序通知任务数据已准备好)。
      2. 互斥访问:保护临界区(虽然 Mutex 更常用,但二值信号量也可实现,只是缺乏优先级继承等高级特性)。
      1. 资源池管理:例如系统有 5 个缓冲区,信号量初值为 5。每取走一个减 1,归还一个加 1。
      2. 生产者-消费者模型:统计缓冲区中有多少个数据项可供消费。
      事件累积 不累积(通常情况下)。如果事件发生多次而没人接收,信号量保持为 1,后续的事件可能会丢失(取决于具体实现,有些RTOS允许累积但无意义,因为上限是1)。 累积。如果事件发生了 5 次而没人接收,信号量值会变为 5。后续可以有 5 个任务依次获取成功。

示例代码二值信号量

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "gpio.h" // 假设的GPIO驱动头文件

// 1. 定义信号量句柄
SemaphoreHandle_t xBtnSemaphore;

// 模拟硬件中断服务程序 (ISR)
// 在实际芯片中,这是绑定到 GPIO 中断的函数
void BUTTON_ISR_Handler(void)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    // 清除硬件中断标志 (具体代码取决于芯片)
    HAL_GPIO_ClearInterruptFlag();

    // 【关键操作】在中断中“释放”(Give) 信号量
    // 注意:中断中必须使用带 "FromISR" 后缀的函数
    xSemaphoreGiveFromISR(xBtnSemaphore, &xHigherPriorityTaskWoken);

    // 如果释放信号量唤醒了一个高优先级任务,请求进行上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

// 2. 处理任务
void vButtonTask(void *pvParameters)
{
    // 初始化:先获取一次信号量,将其置为 0 (空)
    // 这样任务第一次执行 xSemaphoreTake 时就会立即阻塞,等待中断
    xSemaphoreTake(xBtnSemaphore, portMAX_DELAY);

    for(;;)
    {
        // 【关键操作】“获取”(Take) 信号量
        // portMAX_DELAY 表示无限期等待,直到信号量有效
        // 此时任务进入 Blocked 状态,CPU 调度给其他任务
        if(xSemaphoreTake(xBtnSemaphore, portMAX_DELAY) == pdTRUE)
        {
            // 只有当中断调用了 Give,这里才会返回 pdTRUE
            
            // --- 临界区/处理逻辑开始 ---
            // 1. 读取按键状态 (消抖等)
            // 2. 执行具体的业务逻辑
            printf("Button Pressed! Handling event...\n");
            
            // 模拟处理耗时
            vTaskDelay(pdMS_TO_TICKS(50)); 
            // --- 临界区/处理逻辑结束 ---
            
            // 注意:这里不需要 Release 信号量,因为这是“事件通知”,
            // 下一次等待需要由新的中断来触发。
        }
    }
}

// 3. 初始化函数
void vStartSystem(void)
{
    // 创建二值信号量
    // 宏定义通常实现为创建一个计数最大值为1的信号量
    xBtnSemaphore = xSemaphoreCreateBinary();

    if(xBtnSemaphore != NULL)
    {
        // 创建任务
        xTaskCreate(vButtonTask, "BtnTask", 256, NULL, 2, NULL);
        
        // 配置并启用 GPIO 中断...
        HAL_GPIO_InitInterrupt(BUTTON_PIN, BUTTON_ISR_Handler);
    }
    
    // 启动调度器
    vTaskStartScheduler();
}

示例代码计数信号量

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

// 1. 定义信号量句柄
SemaphoreHandle_t xDmaSemaphore;

#define TOTAL_DMA_CHANNELS 3

// 2. 通用工作任务 (模拟多个任务竞争资源)
void vWorkerTask(void *pvParameters)
{
    const char *pcTaskName = (const char *)pvParameters;
    
    for(;;)
    {
        // 模拟做其他无关工作
        vTaskDelay(pdMS_TO_TICKS(100 + rand() % 100));

        // 【关键操作】请求资源
        // 尝试获取信号量,超时时间为 1 秒
        // 如果当前信号量计数 > 0,计数减 1,任务继续
        // 如果当前信号量计数 == 0,任务阻塞,直到有资源释放或超时
        if(xSemaphoreTake(xDmaSemaphore, pdMS_TO_TICKS(1000)) == pdTRUE)
        {
            // --- 成功获取资源 ---
            printf("[%s] Got a DMA channel. Remaining: %d\n", 
                   pcTaskName, uxSemaphoreGetCount(xDmaSemaphore));
            
            // 使用资源 (例如配置 DMA 并传输数据)
            // 假设耗时 200ms
            vTaskDelay(pdMS_TO_TICKS(200)); 
            
            // 使用完毕,归还资源
            // 【关键操作】释放信号量,计数加 1
            // 如果有其他任务在等待,最高优先级的等待任务会被唤醒
            xSemaphoreGive(xDmaSemaphore);
            
            printf("[%s] Released DMA channel.\n", pcTaskName);
        }
        else
        {
            // --- 获取失败 (超时) ---
            printf("[%s] Timeout! No DMA channel available.\n", pcTaskName);
        }
    }
}

// 3. 初始化函数
void vStartResourceSystem(void)
{
    // 创建计数信号量
    // 参数1: 初始计数 (Initial Count) = 3 (一开始有3个资源可用)
    // 参数2: 最大计数 (Maximum Count) = 3 (资源总数上限)
    xDmaSemaphore = xSemaphoreCreateCounting(TOTAL_DMA_CHANNELS, TOTAL_DMA_CHANNELS);

    if(xDmaSemaphore != NULL)
    {
        // 创建 5 个任务来竞争这 3 个资源
        xTaskCreate(vWorkerTask, "Worker1", 256, (void*)"T1", 2, NULL);
        xTaskCreate(vWorkerTask, "Worker2", 256, (void*)"T2", 2, NULL);
        xTaskCreate(vWorkerTask, "Worker3", 256, (void*)"T3", 2, NULL);
        xTaskCreate(vWorkerTask, "Worker4", 256, (void*)"T4", 2, NULL);
        xTaskCreate(vWorkerTask, "Worker5", 256, (void*)"T5", 2, NULL);
    }
    
    vTaskStartScheduler();
}

3、互斥锁

在普通的互斥锁中,如果一个任务已经持有了锁,再次尝试获取同一把锁时,通常会因为“锁已被占用”而进入阻塞状态,从而导致该任务永远等待自己释放锁,形成死锁。递归互斥锁通过引入持有计数(Hold Count)所有者记录(Owner ID)机制解决了这个问题。作用:允许函数多层嵌套调用

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐