我有一个4字节行的内存。我只能写到 16个字节,并且使用I2C以4个字节(逐行)完成读取。
我对如何将数据写入EEPROM感兴趣:正在写入的数据由几个不同的部分组成,其中两个部分可以是可变长度的。例如,我可以有XYYZ或XYYYYZZZZZZZ,每个字母是4个字节。
我的问题是,我应该如何解决这个问题,以便使用16字节写入将消息写入内存的一般方法,以适应两部分的可变性质?
答案 0 :(得分:1)
您可以考虑为eeprom使用一个小的(21字节)静态缓存,而不是尝试以4或16字节为单位工作。我们假设你有
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中时才需要eeprom_flush()
功能 - 基本上,在每次完成交易后。您可以随时安全地拨打电话。
要访问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访问的延迟/延迟可能会在其他地方造成严重破坏)。
答案 1 :(得分: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);
}
}