2016-11-23 12 views
1

私は4バイト行の列であるメモリを持っています。私は唯一と書くことができます。で、となり、I2Cを使用して4バイト(行単位)で読み込みが行われます。EEPROMへの書き込みアルゴリズム?

私はEEPROMにデータを書き込む方法に興味があります:書き込まれているデータは、2つが可変長であってもよいいくつかの異なる部分から構成されています。たとえば、各文字が4バイトのXYYZまたはXYYYYZZZZZZZを持つことができます。

私の質問は、2つの部分の可変性に対応する16バイトの書き込みを使用してメモリにメッセージを書き込む一般的な方法を持つために、この問題についてどうしたらいいですか?

答えて

0

完全にテストされておらず、 "EEPROMから4バイト読み込み"と "EEPROMに16バイト書き込み"が適切な機能でカプセル化されていることに依存していません。

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バイト単位で作業するのではなく、eepromに小さな(21バイトの)静的キャッシュを使用することを検討できます。 pageは16で割ったアドレスがどこにあるのは、あなたが

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

を持っていると仮定しましょう、と常に16個のバイトのチャンク上で動作します。基本的には、後に - あなたはEEPROMに保存されているいくつかのデータを確保したい場合は、それ自体とその初期化関数は、(あなたは、電源投入時に一度呼びたい)キャッシュがeeprom_flush()機能のみが必要とされている

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; 
    } 
} 

だろう各完全なトランザクション。あなたはいつでもそれを安全に呼び出すことができます。

あなたは

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を省略気軽にアクセサ関数を使用し、EEPROM内の任意のメモリにアクセスするには、単なる最適化です。上記のstatic inlineは、可能であれば、関数をインライン化するようにC99コンパイラに指示します。あなたのコードサイズを少し増やすかもしれませんが、より小さなコードがコードにインライン展開されたときにコンパイラがより良い最適化を行うことができるので、より速いコードを生成するはずです。

eepromページが途中で変更されるための通常のコードが用意されていないため、上記の割り込みハンドラでは使用しないでください。

読み取り操作と書き込み操作を混在させることはできますが、EEPROMに不必要な摩耗が生じる可能性があります。ミックスの読み書きを行う場合は、もちろん、読み取り側と書き込み側を別々のキャッシュに分割することができます。そうすれば、I2Cアクセスの遅延/レイテンシが他の場所に混乱を招くかもしれないが、割り込みコンテキストからEEPROMの読み込みを安全に行うことができます。

関連する問題