基于 ESP32 - S3 实现互斥锁的 C 语言例程

下面是一个基于 ESP32 - S3 实现互斥锁的 C 语言例程,该例程利用 FreeRTOS(ESP32 - S3 所使用的实时操作系统)提供的互斥锁机制,创建两个任务,这两个任务会尝试访问一个共享资源,通过互斥锁来保证同一时间只有一个任务可以访问该资源,避免数据竞争。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

// 定义一个互斥锁句柄
SemaphoreHandle_t mutex;

// 共享资源
int shared_resource = 0;

// 任务 1 函数
void task1(void *pvParameters) {
    while (1) {
        // 尝试获取互斥锁
        if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) {
            // 成功获取互斥锁,访问共享资源
            shared_resource++;
            printf("Task 1: Shared resource value = %d\n", shared_resource);
            // 释放互斥锁
            xSemaphoreGive(mutex);
        }
        // 任务延时
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

// 任务 2 函数
void task2(void *pvParameters) {
    while (1) {
        // 尝试获取互斥锁
        if (xSemaphoreTake(mutex, portMAX_DELAY) == pdTRUE) {
            // 成功获取互斥锁,访问共享资源
            shared_resource--;
            printf("Task 2: Shared resource value = %d\n", shared_resource);
            // 释放互斥锁
            xSemaphoreGive(mutex);
        }
        // 任务延时
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

void app_main() {
    // 创建互斥锁
    mutex = xSemaphoreCreateMutex();
    if (mutex == NULL) {
        printf("Failed to create mutex!\n");
        return;
    }

    // 创建任务 1
    xTaskCreate(task1, "Task 1", 2048, NULL, 1, NULL);
    // 创建任务 2
    xTaskCreate(task2, "Task 2", 2048, NULL, 1, NULL);
}

代码说明:

  1. 互斥锁的创建:在 app_main 函数中,使用 xSemaphoreCreateMutex 函数创建一个互斥锁,并将其句柄存储在 mutex 变量中。如果创建失败,会输出错误信息。
  2. 任务的创建:使用 xTaskCreate 函数创建两个任务 task1 和 task2,这两个任务会不断循环执行。
  3. 任务对共享资源的访问:在 task1 和 task2 中,使用 xSemaphoreTake 函数尝试获取互斥锁。portMAX_DELAY 表示如果互斥锁当前不可用,任务会一直阻塞直到获取到互斥锁。获取到互斥锁后,任务可以安全地访问共享资源 shared_resource,完成操作后使用 xSemaphoreGive 函数释放互斥锁。
  4. 任务延时:使用 vTaskDelay 函数让任务每隔 1 秒执行一次。

编译和运行:

将上述代码保存为 .c 文件,使用 ESP-IDF 开发环境进行编译和烧录,你可以在串口监视器中看到两个任务交替访问共享资源的输出信息。

<think>好的,我现在需要帮助用户找到ESP32-S3环形缓冲区读写操作的示例代码或教程。用户之前已经得到过一个关于环形缓冲区实现的详细回答,现在他们再次询问示例代码,可能希望看到更具体的读写操作部分。 首先,回顾之前的回答,用户已经提到了使用静态内存分配、互斥锁、DMA优化以及SPP示例代码。用户现在可能希望看到更基础的读写操作示例,而不仅仅是整体结构。需要确保代码示例足够详细,并且符合ESP-IDF的环境。 接下来,用户的引用中提到了内存分配和RMT配置结构体,这可能意味着他们对内存管理和硬件模块的配置感兴趣。但用户当前的问题集中在环形缓冲区的读写操作上,所以需要专注于提供读写相关的代码,同时可能涉及到内存分配的最佳实践。 用户可能需要了解如何初始化环形缓冲区,如何安全地进行多线程或多核的读写操作,以及如何处理中断服务例程中的读写。同时,用户可能关心性能优化,比如DMA的使用或缓存对齐,这在ESP32-S3中尤为重要。 需要检查之前的回答是否已经涵盖了这些方面,或者是否需要补充更基础的示例。例如,在之前的回答中,有一个完整的SPP示例,但用户可能需要一个更简化的版本,专注于核心的读写函数。 另外,用户可能对错误处理、缓冲区状态监控以及调试方法感兴趣。在之前的回答中提到了CLI命令和内存调试工具,这些可以作为补充内容加入,但主要应提供读写代码示例。 还需要确保代码符合ESP-IDF的最新版本,特别是针对ESP32-S3的特性,比如PSRAM的使用和双核安全操作。可能需要强调静态分配与动态分配的区别,以及在多核环境中使用互斥锁的重要性。 最后,考虑用户可能的后续问题,比如如何处理缓冲区溢出或如何调整缓冲区大小,因此在回答中可以预见到这些问题,并在示例代码中留出扩展的空间,比如动态调整缓冲区大小的注释或建议。</think>### ESP32-S3环形缓冲区读写操作实现详解 根据ESP-IDF V5.1内存管理特性和S3硬件架构优化设计,以下是完整读写操作实现方案: --- #### **一、基础环形缓冲区实现** ```c #include <freertos/FreeRTOS.h> #include <freertos/semphr.h> #define RINGBUF_SIZE 2048 typedef struct { uint8_t buffer[RINGBUF_SIZE]; volatile size_t head; // 原子操作指针 volatile size_t tail; SemaphoreHandle_t mutex; } ringbuf_t; // 初始化函数(带互斥锁创建) void ringbuf_init(ringbuf_t *rb) { rb->head = rb->tail = 0; rb->mutex = xSemaphoreCreateMutex(); assert(rb->mutex != NULL); } // 安全写操作(支持多核操作) size_t ringbuf_write(ringbuf_t *rb, const uint8_t *data, size_t len) { xSemaphoreTake(rb->mutex, portMAX_DELAY); size_t free_space = (RINGBUF_SIZE - 1) - ((rb->head - rb->tail) % RINGBUF_SIZE); len = (len > free_space) ? free_space : len; size_t first_part = RINGBUF_SIZE - (rb->head % RINGBUF_SIZE); first_part = (len < first_part) ? len : first_part; memcpy(&rb->buffer[rb->head % RINGBUF_SIZE], data, first_part); if (len > first_part) { memcpy(rb->buffer, data + first_part, len - first_part); } rb->head += len; // 原子操作保证可见性 xSemaphoreGive(rb->mutex); return len; } // 安全读操作(支持任务/中断上下文) size_t ringbuf_read(ringbuf_t *rb, uint8_t *dest, size_t len) { xSemaphoreTake(rb->mutex, portMAX_DELAY); size_t avail_data = (rb->head - rb->tail) % RINGBUF_SIZE; len = (len > avail_data) ? avail_data : len; size_t first_part = RINGBUF_SIZE - (rb->tail % RINGBUF_SIZE); first_part = (len < first_part) ? len : first_part; memcpy(dest, &rb->buffer[rb->tail % RINGBUF_SIZE], first_part); if (len > first_part) { memcpy(dest + first_part, rb->buffer, len - first_part); } rb->tail += len; // 内存屏障保障数据一致性 xSemaphoreGive(rb->mutex); return len; } ``` --- #### **二、DMA优化版本(基于ESP32-S3 GDMA)** ```c #include "esp_cache.h" #include "soc/soc.h" // DMA专用缓冲区(32字节对齐) __attribute__((aligned(32))) static uint8_t dma_buf[RINGBUF_SIZE]; size_t dma_ringbuf_write(ringbuf_t *rb, const uint8_t *data, size_t len) { esp_cache_msync((void*)data, len, ESP_CACHE_MSYNC_FLAG_DIR_C2M); // 数据缓存同步 // ...(同基础写操作,省略重复代码) } size_t dma_ringbuf_read(ringbuf_t *rb, uint8_t *dest, size_t len) { // ...(同基础读操作) esp_cache_msync(dest, len, ESP_CACHE_MSYNC_FLAG_DIR_M2C); // 读取后刷新缓存 } ``` --- #### **三、中断服务例程(ISR)安全操作** ```c // 中断上下文写操作(不带锁) size_t IRAM_ATTR ringbuf_write_from_isr(ringbuf_t *rb, const uint8_t *data, size_t len) { portENTER_CRITICAL_SAFE(&spinlock); // ...(同基础写操作,但跳过互斥锁) portEXIT_CRITICAL_SAFE(&spinlock); return len; } // 中断上下文读操作(不带锁) size_t IRAM_ATTR ringbuf_read_from_isr(ringbuf_t *rb, uint8_t *dest, size_t len) { portENTER_CRITICAL_SAFE(&spinlock); // ...(同基础读操作,但跳过互斥锁) portEXIT_CRITICAL_SAFE(&spinlock); return len; } ``` --- #### **四、多核操作最佳实践** ```c // 双核共享缓冲区声明(放置于NoCache内存区域) DRAM_ATTR static ringbuf_t dualcore_buf; // 核间通信任务示例 void core1_task(void *arg) { ringbuf_init(&dualcore_buf); while(1) { uint8_t sensor_data[128]; // 从传感器读取数据 ringbuf_write(&dualcore_buf, sensor_data, sizeof(sensor_data)); } } // 主核处理任务 void app_main() { xTaskCreatePinnedToCore(core1_task, "core1", 4096, NULL, 5, NULL, 1); while(1) { uint8_t process_buf[128]; size_t len = ringbuf_read(&dualcore_buf, process_buf, sizeof(process_buf)); if(len > 0) { // 数据处理逻辑 } vTaskDelay(pdMS_TO_TICKS(10)); } } ``` --- #### **五、性能调优技巧** 1. **内存分配策略** 根据引用[1]的内存池设计原则,建议采用混合分配方案: ```c // 静态分配核心缓冲区 static uint8_t static_buf[1024] __attribute__((aligned(32))); // 动态扩展缓冲区(使用PSRAM) uint8_t *psram_buf = heap_caps_malloc(4096, MALLOC_CAP_SPIRAM); ``` 2. **RMT通道集成** 结合引用[2]的RMT配置方法,实现硬件级数据收发: ```c void config_rmt_with_ringbuf() { rmt_config_t rmt_cfg = RMT_DEFAULT_CONFIG_TX(GPIO_NUM_4, RMT_CHANNEL_0); rmt_cfg.mem_block_num = 2; // 提升内存块数量提升吞吐量 rmt_cfg.clk_div = 40; // 20MHz时钟 rmt_driver_install(rmt_cfg.channel, 4096, 0); } ``` --- ### 调试与验证方法 1. **实时状态监控** 添加调试接口查看读写指针状态: ```c void show_ringbuf_status(ringbuf_t *rb) { printf("[Head:%zu Tail:%zu Free:%zu Used:%zu]\n", rb->head, rb->tail, RINGBUF_SIZE - ((rb->head - rb->tail) % RINGBUF_SIZE), (rb->head - rb->tail) % RINGBUF_SIZE); } ``` 2. **压力测试工具** 使用FreeRTOS任务进行极限测试: ```c void writer_task(void *arg) { uint8_t test_data[256]; while(1) { ringbuf_write((ringbuf_t *)arg, test_data, sizeof(test_data)); vTaskDelay(1); } } void reader_task(void *arg) { uint8_t recv_data[256]; while(1) { size_t len = ringbuf_read((ringbuf_t *)arg, recv_data, sizeof(recv_data)); assert(len == sizeof(recv_data)); } } ``` --- ### 典型应用场景 1. **蓝牙数据缓冲** SPP/BLE数据包接收时,在`esp_spp_cb()`回调中直接调用`ringbuf_write_from_isr()` 2. **ADC采样队列** 结合ESP32-S3的SAR_ADC连续采样模式,实现零拷贝数据缓冲 3. **图像传感器接口** 处理OV2640等摄像头模块的JPEG数据流时,使用双环形缓冲区实现乒乓操作 --- ### 推荐学习资源 1. [ESP-IDF环形缓冲官方文档](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/system/freertos_additions.html#ring-buffers) 2. [双核安全操作指南](https://blog.youkuaiyun.com/ESP32/article/details/131123579)(含代码分析)[^1] 3. [GDMA优化实践视频教程](https://www.bilibili.com/video/BV1sV4y1G7vJ)(中文) --- ### 相关问题 1. 如何检测环形缓冲区溢出并实现自动扩容? 2. ESP32-S3双核操作环形缓冲区时如何避免缓存一致性问题? 3. 环形缓冲区在LoRaWAN通信中有哪些具体应用?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值