我有一個內存是4字節行的列。我只能寫它在16個字節和讀取是在4個字節(逐行,即是)使用I2C完成。寫入EEPROM的算法?
我對如何將數據寫入EEPROM感興趣:正在寫入的數據由幾個不同的部分組成,其中兩個部分的長度可變。例如,我可以有XYYZ或XYYYYZZZZZZZ,其中每個字母是4個字節。
我的問題是,我應該如何解決這個問題,以便使用16字節的寫入方式將消息寫入內存,以適應這兩部分的變量性質?
我有一個內存是4字節行的列。我只能寫它在16個字節和讀取是在4個字節(逐行,即是)使用I2C完成。寫入EEPROM的算法?
我對如何將數據寫入EEPROM感興趣:正在寫入的數據由幾個不同的部分組成,其中兩個部分的長度可變。例如,我可以有XYYZ或XYYYYZZZZZZZ,其中每個字母是4個字節。
我的問題是,我應該如何解決這個問題,以便使用16字節的寫入方式將消息寫入內存,以適應這兩部分的變量性質?
未針對您的示例量身打造,完全未經測試,依靠「從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);
}
}
與其嘗試以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訪問的延遲/延遲可能會造成其他地方的嚴重破壞)。