commit
This commit is contained in:
49
john103C6T6/EEPROM_Emul/lib/EEPROM_Emul.h
Normal file
49
john103C6T6/EEPROM_Emul/lib/EEPROM_Emul.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef EEPROM_EMUL_H
|
||||
#define EEPROM_EMUL_H
|
||||
|
||||
#include "stm32f1xx_hal.h"
|
||||
|
||||
// Адреса для эмуляции EEPROM (последние страницы Flash)
|
||||
#define EEPROM_START_ADDRESS 0x0800F000 // Последний килобайт Flash
|
||||
#define EEPROM_PAGE_SIZE 1024 // Размер страницы STM32F103
|
||||
#define EEPROM_SIZE 1024 // Общий размер EEPROM
|
||||
|
||||
// Максимальное количество переменных
|
||||
#define EEPROM_MAX_VARIABLES 64
|
||||
|
||||
// Статусы операций
|
||||
typedef enum {
|
||||
EEPROM_OK = 0,
|
||||
EEPROM_ERROR = 1,
|
||||
EEPROM_INVALID = 2,
|
||||
EEPROM_FULL = 3
|
||||
} EEPROM_Status;
|
||||
|
||||
// Структура элемента данных
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint16_t address; // Адрес переменной (0-EEPROM_MAX_VARIABLES)
|
||||
uint16_t data; // Данные
|
||||
uint32_t timestamp; // Временная метка
|
||||
} EEPROM_Item;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Инициализация EEPROM
|
||||
EEPROM_Status EEPROM_Init(void);
|
||||
|
||||
// Чтение данных
|
||||
EEPROM_Status EEPROM_Read(uint16_t virt_address, uint16_t* data);
|
||||
|
||||
// Запись данных
|
||||
EEPROM_Status EEPROM_Write(uint16_t virt_address, uint16_t data);
|
||||
|
||||
// Массовая запись
|
||||
EEPROM_Status EEPROM_WriteMultiple(uint16_t virt_address, uint8_t* data, uint16_t size);
|
||||
|
||||
// Получение информации о EEPROM
|
||||
void EEPROM_GetInfo(uint32_t* used, uint32_t* total);
|
||||
|
||||
// Полное форматирование
|
||||
EEPROM_Status EEPROM_Format(void);
|
||||
|
||||
#endif
|
||||
256
john103C6T6/EEPROM_Emul/src/EEPROM_Emul.c
Normal file
256
john103C6T6/EEPROM_Emul/src/EEPROM_Emul.c
Normal file
@@ -0,0 +1,256 @@
|
||||
#include "eeprom_emul.h"
|
||||
#include <string.h>
|
||||
|
||||
// Внутренние переменные
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user