
В программировании микроконтроллеров часто приходится работать с энергонезависимой памятью: читать и писать массивы. Однако порой реализация энергонезависимой памяти накладывает некоторые свои ограничения. Примером тому является 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 для микроконтроллеров | |
2 | NVRAM Поверх off-chip SPI-NOR Flash | |
3 | Открытый проект файловой системы для внутренней памяти STM32H | |
4 | ESP32 и файловая система SPIFFS | |
5 | LittleFS – компактная и экономичная файловая система для ARM микроконтроллеров в составе mbed os | |
6 | Устройство NVRAM в UEFI-совместимых прошивках, часть первая | |
7 | Как избежать износа EEPROM |
