2016-11-23 79 views
1

我有一个内存是4字节行的列。我只能在16个字节和读取是在4个字节(逐行,即是)使用I2C完成。写入EEPROM的算法?

我对如何将数据写入EEPROM感兴趣:正在写入的数据由几个不同的部分组成,其中两个部分的长度可变。例如,我可以有XYYZ或XYYYYZZZZZZZ,其中每个字母是4个字节。

我的问题是,我应该如何解决这个问题,以便使用16字节的写入方式将消息写入内存,以适应这两部分的变量性质?

回答

0

未针对您的示例量身打造,完全未经测试,依靠“从EEPROM中读取4个字节”和“将16个字节写入EEPROM”封装在合适的功能中。

void write_to_eeprom(uint32_t start, size_t len, uint8_t *data) { 
    uint32_t eeprom_dst = start & 0xfffffff0; 
    uint8_t buffer[16]; 
    ssize_t data_offset; 

    for (data_offset = (start - eeprom_dst); data_offset < len; data_offset += 16, eeprom_dst+= 16) { 
    if (data_offset < 0) || ((len - data_offset) < 16) { 
     // we need to fill our buffer with EEPROM data 
     read_from_eeprom(eeprom_dst, buffer); // read 4 bytes, place at ptr 
     read_from_eeprom(eeprom_dst+4, buffer+4); 
     read_from_eeprom(eeprom_dst+8, buffer+8); 
     read_from_eeprom(eeprom_dst+12, buffer+12); 
     for (int buf_ix=0, ssize_t tmp_offset = data_offset; buf_ix < 16; buf_ix++, offset++) { 
     if ((offset >= 0) && (buf_ix < 16)) { 
      // We want to copy actual data 
      buffer[buf_ix] = data[offset]; 
     } 
     } 
    } else { 
     // We don't need to cater for edge cases and can simply shift 
     // 16 bytes into our tmp buffer. 
     for (int ix = 0; ix < 16; ix++) { 
     buffer[ix] = data[data_offset + ix]; 
     } 
    } 
    write_to_eeprom(eeprom_dst, buffer); 
    } 
} 
1

与其尝试以4或16字节单位工作,您可以考虑使用一个小的(21字节)静态缓存来存储eeprom。让我们假设你有

void eeprom_read16(uint32_t page, uint8_t *data); 
void eeprom_write16(uint32_t page, const uint8_t *data); 

其中page是除以16的地址,并始终在16分字节块操作。本身和它的初始化函数(你会在上电时调用一次)高速缓存将

static uint32_t eeprom_page;  /* uint16_t suffices for 2 MiB EEPROM */ 
static uint8_t eeprom_cache[16]; 
static uint8_t eeprom_dirty; 

static void eeprom_init(void) 
{ 
    eeprom_page = 0x80000000U; /* "None", at 32 GiB */ 
    eeprom_dirty = 0; 
} 

static void eeprom_flush(void) 
{ 
    if (eeprom_dirty) { 
     eeprom_write16(eeprom_page, eeprom_cache); 
     eeprom_dirty = 0; 
    } 
} 

时才需要eeprom_flush()功能,如果你希望确保一些数据被存储在EEPROM - 基本上,后每个完整的交易。您可以随时调用它。

要访问EEPROM中的任何内存,使用访问函数

static inline uint8_t eeprom_get(const uint32_t address) 
{ 
    const uint32_t page = address >> 4; 
    if (page != eeprom_page) { 
     if (eeprom_dirty) { 
      eeprom_write(eeprom_page, eeprom_cache); 
      eeprom_dirty = 0; 
     } 
     eeprom_read(page, eeprom_cache); 
     eeprom_page = page; 
    } 
    return eeprom_cache[address % 0xFU]; 
} 

static inline void eeprom_set(const uint32_t address, const uint8_t value) 
{ 
    const uint32_t page = address >> 4; 
    if (page != eeprom_page) { 
     if (eeprom_dirty) { 
      eeprom_write(eeprom_page, eeprom_cache); 
      eeprom_dirty = 0; 
     } 
     eeprom_read(page, eeprom_cache); 
     eeprom_page = page; 
    } 
    eeprom_dirty = 1; 
    eeprom_cache[address % 0xFU] = value; 
} 

随意省略inline如果你喜欢;这只是一个优化。上面的static inline告诉C99编译器在可能的情况下内联函数。它可能会增加您的代码大小,但它应该会产生更快的代码(因为编译器可以在将这些小函数内联到代码中时进行更好的优化)。

请注意,您不应该在中断处理程序中使用上述内容,因为没有为eeprom页面准备正常的代码以更改中间操作。

您可以混合使用读取和写入操作,但这可能会导致EEPROM上不必要的磨损。当然,如果您混合读取和写入,则可以将读取和写入两边分开来分隔缓存。这也可以让你安全地从中断环境中进行EEPROM读操作(尽管I2C访问的延迟/延迟可能会造成其他地方的严重破坏)。