//#include "eeprom_emul.h" #include // Внутренние переменные static uint32_t eeprom_current_write_address = EEPROM_START_ADDRESS; static uint8_t eeprom_initialized = 0; // Прототипы внутренних функций static EEPROM_Status EEPROM_FindLatestData(uint16_t virt_address, uint16_t* data); static EEPROM_Status EEPROM_WriteItem(EEPROM_Item* item); static EEPROM_Status EEPROM_ErasePage(uint32_t address); static uint32_t EEPROM_FindNextWriteAddress(void); static uint8_t EEPROM_IsPageErased(uint32_t address); static uint32_t EEPROM_CalculateCRC(EEPROM_Item* item); // Инициализация EEPROM EEPROM_Status EEPROM_Init(void) { if (eeprom_initialized) { return EEPROM_OK; } // Находим следующий адрес для записи eeprom_current_write_address = EEPROM_FindNextWriteAddress(); // Если вся память заполнена, выполняем сборку мусора (форматирование) if (eeprom_current_write_address >= EEPROM_START_ADDRESS + EEPROM_SIZE) { EEPROM_Format(); } else { eeprom_initialized = 1; } return EEPROM_OK; } // Чтение данных по виртуальному адресу EEPROM_Status EEPROM_Read(uint16_t virt_address, uint16_t* data) { if (!eeprom_initialized) { return EEPROM_ERROR; } if (virt_address >= EEPROM_MAX_VARIABLES || data == NULL) { return EEPROM_INVALID; } return EEPROM_FindLatestData(virt_address, data); } // Запись данных по виртуальному адресу EEPROM_Status EEPROM_Write(uint16_t virt_address, uint16_t data) { EEPROM_Item item; if (!eeprom_initialized) { return EEPROM_ERROR; } if (virt_address >= EEPROM_MAX_VARIABLES) { return EEPROM_INVALID; } // Подготавливаем элемент данных item.address = virt_address; item.data = data; item.timestamp = HAL_GetTick(); // Используем системный таймер // Записываем элемент return EEPROM_WriteItem(&item); } // Поиск последних данных для виртуального адреса static EEPROM_Status EEPROM_FindLatestData(uint16_t virt_address, uint16_t* data) { uint32_t address = EEPROM_START_ADDRESS; EEPROM_Item current_item; uint32_t latest_timestamp = 0; uint16_t latest_data = 0; uint8_t data_found = 0; // Сканируем всю область EEPROM while (address < EEPROM_START_ADDRESS + EEPROM_SIZE) { // Читаем элемент memcpy(¤t_item, (void*)address, sizeof(EEPROM_Item)); // Проверяем, является ли это валидными данными if (current_item.address == virt_address) { if (current_item.timestamp >= latest_timestamp) { latest_timestamp = current_item.timestamp; latest_data = current_item.data; data_found = 1; } } address += sizeof(EEPROM_Item); // Проверяем конец страницы if ((address - EEPROM_START_ADDRESS) % EEPROM_PAGE_SIZE == 0) { address += (EEPROM_PAGE_SIZE - (sizeof(EEPROM_Item) * 2)); } } if (data_found) { *data = latest_data; return EEPROM_OK; } return EEPROM_INVALID; } // Запись элемента в EEPROM static EEPROM_Status EEPROM_WriteItem(EEPROM_Item* item) { HAL_StatusTypeDef hal_status; // Проверяем, нужно ли стирать страницу if ((eeprom_current_write_address - EEPROM_START_ADDRESS) % EEPROM_PAGE_SIZE == 0) { if (!EEPROM_IsPageErased(eeprom_current_write_address)) { if (EEPROM_ErasePage(eeprom_current_write_address) != EEPROM_OK) { return EEPROM_ERROR; } } } // Разблокируем Flash HAL_FLASH_Unlock(); // Записываем данные по словам (32 бита) uint32_t* data_ptr = (uint32_t*)item; for (uint8_t i = 0; i < sizeof(EEPROM_Item) / 4; i++) { hal_status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, eeprom_current_write_address + (i * 4), data_ptr[i]); if (hal_status != HAL_OK) { HAL_FLASH_Lock(); return EEPROM_ERROR; } } // Блокируем Flash HAL_FLASH_Lock(); // Обновляем адрес для следующей записи eeprom_current_write_address += sizeof(EEPROM_Item); // Проверяем переполнение if (eeprom_current_write_address >= EEPROM_START_ADDRESS + EEPROM_SIZE) { // Выполняем сборку мусора (в данном случае - форматирование) EEPROM_Format(); } return EEPROM_OK; } // Стирание страницы Flash static EEPROM_Status EEPROM_ErasePage(uint32_t address) { FLASH_EraseInitTypeDef erase; uint32_t page_error; // Определяем номер страницы uint32_t page = (address - FLASH_BASE) / EEPROM_PAGE_SIZE; HAL_FLASH_Unlock(); erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = address; erase.NbPages = 1; if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK) { HAL_FLASH_Lock(); return EEPROM_ERROR; } HAL_FLASH_Lock(); return EEPROM_OK; } // Поиск следующего адреса для записи static uint32_t EEPROM_FindNextWriteAddress(void) { uint32_t address = EEPROM_START_ADDRESS; EEPROM_Item item; // Ищем первую свободную позицию while (address < EEPROM_START_ADDRESS + EEPROM_SIZE) { memcpy(&item, (void*)address, sizeof(EEPROM_Item)); // Если нашли пустой элемент (все FFFF), это свободная позиция if (item.address == 0xFFFF && item.data == 0xFFFF && item.timestamp == 0xFFFFFFFF) { break; } address += sizeof(EEPROM_Item); // Проверяем границу страницы if ((address - EEPROM_START_ADDRESS) % EEPROM_PAGE_SIZE == 0) { address += (EEPROM_PAGE_SIZE - (sizeof(EEPROM_Item) * 2)); } } return address; } // Проверка, стерта ли страница static uint8_t EEPROM_IsPageErased(uint32_t address) { uint32_t* check_addr = (uint32_t*)address; // Проверяем первые несколько слов for (uint8_t i = 0; i < 8; i++) { if (check_addr[i] != 0xFFFFFFFF) { return 0; } } return 1; } // Полное форматирование EEPROM EEPROM_Status EEPROM_Format(void) { uint32_t address = EEPROM_START_ADDRESS; // Стираем все страницы, используемые для EEPROM while (address < EEPROM_START_ADDRESS + EEPROM_SIZE) { if (EEPROM_ErasePage(address) != EEPROM_OK) { return EEPROM_ERROR; } address += EEPROM_PAGE_SIZE; } eeprom_current_write_address = EEPROM_START_ADDRESS; eeprom_initialized = 1; return EEPROM_OK; } // Получение информации об использовании EEPROM void EEPROM_GetInfo(uint32_t* used, uint32_t* total) { uint32_t address = EEPROM_START_ADDRESS; uint32_t used_bytes = 0; if (used) { // Подсчитываем использованные байты while (address < EEPROM_START_ADDRESS + EEPROM_SIZE) { EEPROM_Item item; memcpy(&item, (void*)address, sizeof(EEPROM_Item)); if (item.address != 0xFFFF || item.data != 0xFFFF || item.timestamp != 0xFFFFFFFF) { used_bytes += sizeof(EEPROM_Item); } address += sizeof(EEPROM_Item); } *used = used_bytes; } if (total) { *total = EEPROM_SIZE; } }