You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言实现外部Flash环形缓冲区:回绕时无限循环问题求解

环形缓冲区回绕逻辑问题修复(BR24G32-3 Flash)

问题背景

我用C语言为4KB的BR24G32-3外部Flash实现环形缓冲区时,回绕逻辑出现异常:写完0x0000~0x1000所有地址后,回绕覆盖旧数据时,检查逻辑触发无限循环。

当前算法流程:

  • 调用ringbuf_head_position()获取头位置
  • 向头位置写入数据
  • 检查是否已写入,若是则将位置增加0x20后写入下一个位置

问题表现:写完结束地址0x1000并重置到0x0000时,缓冲区检测到每个数据块都带有WRITTEN(0x31)标志,导致无限循环。

原代码:

void rb_write_to_flash(ring_buffer_t *rb)
{
    uint8_t base_head_address[2] = {0x01, 0x0C};
    ringbuf_head_position(head_position); // Load initial state of the ring buffer

    if (head_position[0] == 0xFF && head_position[1] == 0xFF)
    {
        NRF_LOG_INFO("HEAD == 0xFF");
        head_position[0] = 0x00;
        head_position[1] = 0x00;
    }

    // Convert head_position to a uint16_t value
    uint16_t head_pos_value = ((uint16_t)head_position[0] << 8) | head_position[1];

    do
    {
        // Check if position has been written
        nrf_drv_twi_tx(&m_twi, EEPROM_ADDRESS, head_position, 2, true);
        nrf_drv_twi_rx(&m_twi, EEPROM_ADDRESS, head_buffer, sizeof(head_buffer));

        if (head_buffer[28] == WRITTEN)
        {
            NRF_LOG_INFO("HEAD ALREADY WRITTEN");

            // Increment position by 0x20
            head_pos_value += 0x20;
            if (head_pos_value >= 0x1000) // Checking for overflow
            {
                NRF_LOG_INFO("WRAPPING AROUND");
                head_pos_value = 0x00; // Reset if it exceeds 0xFFFF
                uint8_t initial_address[2] = {0x00, 0x00};
                write_record(base_head_address, initial_address, 2);

                // Clear the WRITTEN flag for the first position in EEPROM
                uint8_t clear_buffer[30] = {0xFF}; // Assuming 0xFF is the default/empty state
                write_record(initial_address, clear_buffer, sizeof(clear_buffer));
            }

            NRF_LOG_INFO("HEAD POSITION: 0x%04x", head_pos_value);

            // Convert back to array
            head_position[0] = (head_pos_value >> 8) & 0xFF;
            head_position[1] = head_pos_value & 0xFF;
        }

    } while (head_buffer[28] == WRITTEN);

    // Write data to the correct position
    rb->buffer[0].written = WRITTEN; // Set flag as written
    write_record(head_position, (uint8_t *)rb->buffer, sizeof(rb->buffer));

    // Write the new head position to flash memory
    write_record(base_head_address, head_position, 2);
}

问题根源

  1. 回绕操作不彻底:回绕时仅清除第一个位置的WRITTEN标志,其余旧块的标志仍保留,导致循环检查时所有位置都显示已写入,触发无限循环。
  2. 逻辑误解:环形缓冲区满时应直接覆盖最旧数据,而非寻找未写入位置,原逻辑的do-while循环违背了环形缓冲区的核心设计。
  3. 头位置更新不同步:回绕时修改了base_head_address存储的头位置,但未同步更新当前循环中的head_position变量,导致后续检查逻辑混乱。

修复方案

修复后代码

// 定义缓冲区常量:每个块32字节(0x20),总容量4KB(0x1000)
#define BLOCK_SIZE 0x20
#define BUFFER_SIZE 0x1000
#define TOTAL_BLOCKS (BUFFER_SIZE / BLOCK_SIZE)

void rb_write_to_flash(ring_buffer_t *rb)
{
    uint8_t base_head_address[2] = {0x01, 0x0C};
    uint8_t head_position[2];
    ringbuf_head_position(head_position); // 加载当前头位置

    // 初始化头位置(首次使用)
    if (head_position[0] == 0xFF && head_position[1] == 0xFF)
    {
        NRF_LOG_INFO("Initializing ring buffer head to 0x0000");
        head_position[0] = 0x00;
        head_position[1] = 0x00;
        write_record(base_head_address, head_position, 2);
    }

    uint16_t head_pos_value = ((uint16_t)head_position[0] << 8) | head_position[1];
    uint16_t next_head_pos = head_pos_value + BLOCK_SIZE;

    // 计算下一个写位置,满了则回绕到起始地址
    if (next_head_pos >= BUFFER_SIZE)
    {
        NRF_LOG_INFO("Wrapping around to 0x0000");
        next_head_pos = 0x0000;
    }

    // 直接写入当前头位置(环形缓冲区满时覆盖最旧数据)
    rb->buffer[0].written = WRITTEN;
    write_record(head_position, (uint8_t *)rb->buffer, sizeof(rb->buffer));

    // 更新头位置为下一个地址
    head_position[0] = (next_head_pos >> 8) & 0xFF;
    head_position[1] = next_head_pos & 0xFF;
    write_record(base_head_address, head_position, 2);

    NRF_LOG_INFO("Wrote to position 0x%04x, next head is 0x%04x", head_pos_value, next_head_pos);
}

关键修改说明

  • 常量定义:明确块大小、缓冲区总容量和总块数,避免魔法数值,提升代码可读性。
  • 回绕逻辑简化:直接计算下一个写位置,超过缓冲区边界时重置为0,无需逐个检查旧块的WRITTEN标志,符合环形缓冲区"满则覆盖"的设计。
  • 移除无限循环:删除原有的do-while循环,避免因所有块都带WRITTEN标志导致的死循环。
  • 头位置更新同步:每次写操作后,直接将头位置更新为下一个地址并写入Flash,确保状态一致。

内容的提问来源于stack exchange,提问作者Flavio Andrade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 08:15:13