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); }
问题根源
- 回绕操作不彻底:回绕时仅清除第一个位置的WRITTEN标志,其余旧块的标志仍保留,导致循环检查时所有位置都显示已写入,触发无限循环。
- 逻辑误解:环形缓冲区满时应直接覆盖最旧数据,而非寻找未写入位置,原逻辑的
do-while循环违背了环形缓冲区的核心设计。 - 头位置更新不同步:回绕时修改了
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
相关产品推荐
相关产品推荐

