Как стать автором
Обновить

NVRAM из EEPROM

Уровень сложностиПростой
Время на прочтение3 мин
Количество просмотров2.6K

В программировании микроконтроллеров часто приходится работать с энергонезависимой памятью: читать и писать массивы. Однако порой реализация энергонезависимой памяти накладывает некоторые свои ограничения. Примером тому является EEPROM память.

Постановка задачи

Дана функция чтения и записи EEPROM dword.

bool eeprom_dword_erase(const uint32_t phy_addr);
bool eeprom_dword_write(const uint32_t phy_addr, const uint32_t dword);
bool eeprom_dword_read(const uint32_t phy_addr, uint32_t * const dword);

Причем запись и стирание возможно только пословно по выровненному адресу: 0 4 8 N*4. Где N-натуральное число.

Надо поверх EEPROM API сделать функцию чтения, записи и стирания для произвольных массивов байт, по произвольному адресу. Вот так.

bool eeprom_byte_erase(const uint32_t phy_addr);
bool eeprom_byte_write(const uint32_t phy_addr, const uint8_t byte);
bool eeprom_byte_read(const uint32_t phy_addr, uint8_t * const byte);

Простыми словами надо состыковать Dword API с Byte API. Только и всего.

Стертые данные положим как 0xFF. На адресацию слова условно выделяется 11 бит. Можно адресовать 2048 слов (8192 байт). Процессор порядком байт little-endian.

Исходя из специфики задачи возникают вот такие три неудобных случая.

Терминология

dword (double word) — это 4х байтная переменная (32 бит).

word — это двухбайтная переменная (16 бит)

byte — это 8ми битная переменная

Почему это нужно?

Byte API более универсальный и нужен для запуска NVRAM поверх EEPROM памяти. Дело в том, что API для NVRAM как раз пишет и читает по произвольному смещению и произвольный размер данных.

Решение

Задача по записи чтению массивов сводится к задаче чтения и записи отдельного байта. В связи с этим надо сделать высокоуровневую функцию для чтения, записи и удаления отдельного байта по произвольному адресу EEPROM. Нужен вот такой API.

bool eeprom_byte_erase(const uint32_t phy_addr);
bool eeprom_byte_write(const uint32_t phy_addr, const uint8_t byte);
bool eeprom_byte_read(const uint32_t phy_addr, uint8_t * const byte);

Основная идея в том, что отдельный байт всегда попадет только в один dword. От этого и надо танцевать.

Чтение байта из ЕЕPROM

Учитывая то, что EEPROM, как правило отображается в физическую память микроконтроллера, то можно читать EEPROM память по Cи-шному указателю. Вот так:

uint8_t read_addr_8bit(const uint32_t address) {
    uint8_t value = 0u;

    volatile uint8_t* p_addr = NULL;
    /*MISRA 2012 Rule 11.4: integer should not be converted to pointer */
    p_addr = (volatile uint8_t*)address;
    if(p_addr) {
        value = *p_addr;
    }

    return value;
}

Записать байт в EEPROM

1--перед записью байта можно проверить нужна ли вообще запись? Дело в том, что с вероятностью 1/256 =0.39 % по заданному адресу уже лежит то же самое значение, которое мы желаем записать. А раз так, то можно и вовсе ничего не делать и отрапортовать наверх об успехе. Это обычно называют lazy write. Если же нам не повезло (вероятность 99.60 %), то надо прибегнуть к следующему алгоритму.

2--Определить dword в котором оказался физический адрес байта

3--Прочитать в RAM dword из EEPROM

4--Записать новый байт в копию dword что сейчас хранится в RAM

5--Стереть старый dword

6--Прописать модернизированный dword.

typedef union {
    uint32_t u32;
    uint8_t u8[4];
} Type32Union_t;

bool eeprom_byte_write(const uint32_t phy_addr, const uint8_t byte) {
    bool res = false;
    LOG_DEBUG(EEPROM,"Write [0x%x]=0x%x",phy_addr,byte);
    res = eeprom_is_address_range(  phy_addr,   1);
    if(res) {
        /*determine the address of the word in which the byte is 
         located (dword_phy_addr) */
      
        //clear the two least significant bits
        uint32_t dword_phy_addr = phy_addr & (~MASK_2BIT); 

        // select the two least significant bits
        uint32_t byte_index =  phy_addr & MASK_2BIT ; 
      
        Type32Union_t dWord = {0};
        dWord.u32 = 0;
        res = eeprom_dword_read(dword_phy_addr, &dWord.u32);
        if(res) {
            dWord.u8[byte_index] = byte;
            res = eeprom_dword_erase(dword_phy_addr);
            if(res) {
                res = eeprom_dword_write(dword_phy_addr, dWord.u32);
            }
        }
    }
    return res;
}

Удалить байт из EEPROM

Стирание байта это по сути запись 0xFF. Поэтому можно просто выполнить функцию

bool eeprom_byte_erase(const uint32_t phy_addr) {
    bool res = false;
    res = eeprom_byte_write(phy_addr, 0xFF);
    return res;
}

Разумеется тут есть простор для оптимизаций. Запросы на запись длинных массивов можно существенно оптимизировать, однако это потребует дополнительных ресурсов по RAM памяти.

Итоги

Удалось придумать binding-и для состыковки Byte API и Dword API. Таким образом EEPROM можно превратить в NVRAM.

Ссылки

Название

URL

1

NVRAM для микроконтроллеров

https://habr.com/ru/articles/706972/

2

NVRAM Поверх off-chip SPI-NOR Flash

https://habr.com/ru/articles/732442/

3

Открытый проект файловой системы для внутренней памяти STM32H

https://habr.com/ru/articles/584156/

4

ESP32 и файловая система SPIFFS

https://habr.com/ru/articles/483280/

5

LittleFS – компактная и экономичная файловая система для ARM микроконтроллеров в составе mbed os

https://habr.com/ru/articles/347348/

6

Устройство NVRAM в UEFI-совместимых прошивках, часть первая

https://habr.com/ru/articles/281242/

7

Как избежать износа EEPROM

https://habr.com/ru/articles/385213/

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы программировали EEPROM?
86.67% да26
13.33% нет4
Проголосовали 30 пользователей. Воздержались 2 пользователя.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Вы работали с NVRAM?
51.72% да15
48.28% нет14
Проголосовали 29 пользователей. Воздержались 2 пользователя.
Теги:
Хабы:
+3
Комментарии28

Публикации

Работа

Программист С
40 вакансий

Ближайшие события