From 643391038e47429acfce2579c1d5fa68b641533e Mon Sep 17 00:00:00 2001 From: Razvalyaev Date: Tue, 17 Feb 2026 18:34:50 +0300 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=B0=D0=BF=D0=B3=D1=80=D0=B5=D0=B9=D0=B4:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - исправлены баги библиотеки memspi - добавлены модули для сохранения настреок в eeprom и flash (с равномерным износом) - надо тестить, проверять и рефакторить --- Inc/memspi.h | 18 +- Inc/memspi_core.h | 72 ++- Inc/params_flash.h | 275 ++++++++++ Inc/set_to_mem.h | 136 +++++ Src/memspi.c | 312 +++++++---- Src/memspi_core.c | 302 +++++++---- Src/params_flash.c | 1232 ++++++++++++++++++++++++++++++++++++++++++++ Src/set_to_mem.c | 380 ++++++++++++++ 8 files changed, 2534 insertions(+), 193 deletions(-) create mode 100644 Inc/params_flash.h create mode 100644 Inc/set_to_mem.h create mode 100644 Src/params_flash.c create mode 100644 Src/set_to_mem.c diff --git a/Inc/memspi.h b/Inc/memspi.h index f34b869..21530ff 100644 --- a/Inc/memspi.h +++ b/Inc/memspi.h @@ -39,31 +39,35 @@ #define __SPI_MEMORY_H_ #include "memspi_core.h" - +extern MEMSPI_HandleTypeDef memory_spi; ///////////////////////////////////////////////////////////////////// ///////////////////////---FUNCTIONS FOR USER---////////////////////// /* Initialize SPI and GPIO for MEMSPI FLASH */ void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi, SPI_HandleTypeDef *hspi); /* Read external FLASH */ -HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout); +HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint32_t Size, uint32_t Timeout); /* Write external EEPROM */ -HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd); +HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd); /* Write external FLASH */ HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout, uint8_t WaitForEnd); /* Program external FLASH */ -HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd); +HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd); /* Erase external FLASH */ -HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd); +HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd); +/* @brief Set protection for FLASH sectors */ +HAL_StatusTypeDef MEMSPI_FLASH_Protection(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Size, uint32_t ProtectState, uint32_t Timeout); ///////////////////////---FUNCTIONS FOR USER---////////////////////// ///////////////////////////////////////////////////////////////////// ////////////////////////---SERVICE FUNCTIONS---////////////////////// /* Write page in external EEPROM */ -HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); +HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); /* Erase external FLASH Sector */ HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); /* Program page in external FLASH */ -HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); +HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); +/* Program data in external FLASH */ +HAL_StatusTypeDef MEMSPI_FLASH_Program_Bytes(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd); /* Setting WEL bit until it setted or until timeout */ HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart); /* Wait for flag until timeout */ diff --git a/Inc/memspi_core.h b/Inc/memspi_core.h index 42bb3b3..dea2dd8 100644 --- a/Inc/memspi_core.h +++ b/Inc/memspi_core.h @@ -26,30 +26,61 @@ #include "memspi_config.h" -#ifndef local_time -#define local_time() HAL_GetTick() ///< Локальное время -#endif /** @defgroup MEMSPI_Commands Memory SPI Commands * @brief Определения команд SPI-памяти * @{ */ -#define MEMSPI_READ_JEDEC_ID 0x9F ///< Команда чтения JEDEC ID -#define MEMSPI_READ_UNIQUE_ID 0x4B ///< Команда чтения уникального идентификатора + +#ifdef RUFLASH + +#define MEMSPI_READ_DATA (0x03) + +#define MEMSPI_ERASE_SECTOR (0xD8) +#define MEMSPI_ERASE_CHIP (0x60) + +#define MEMSPI_WRITE_ENABLE (0x06) +#define MEMSPI_WRITE_DISABLE (0x04) + +#define MEMSPI_PAGE_PROGRAM (0x02) +#define MEMSPI_BYTE_PROGRAM (0x02) +#define MEMSPI_WRITE_EEPROM (MEMSPI_PAGE_PROGRAM) + +#define MEMSPI_PROTECT_SECTOR (0x36) +#define MEMSPI_UNPROTECT_SECTOR (0x39) +#define MEMSPI_READ_PROTECT_REG (0x3C) + + +#define MEMSPI_READ_STATUS_REG (0x05) +#if defined(SEPARATED_STATUS_REGISTER) + #define MEMSPI_READ_STATUS_REG_2 (0x35) +#endif +#define MEMSPI_WRITE_STATUS_REG (0x01) + + +#define MEMSPI_READ_JEDEC_ID (0x9F) +#define MEMSPI_READ_UNIQUE_ID (0x4B) + +#else +#define MEMSPI_READ_DATA 0x03 ///< Чтение данных из памяти + +#define MEMSPI_ERASE_SECTOR 0x20 ///< Стирание одного сектора + #define MEMSPI_WRITE_ENABLE 0x06 ///< Разрешение записи #define MEMSPI_WRITE_DISABLE 0x04 ///< Запрещение записи -#define MEMSPI_WRITE_STATUS_REG 0x01 ///< Запись в регистр состояния -#define MEMSPI_ERASE_SECTOR 0x20 ///< Стирание одного сектора + #define MEMSPI_PAGE_PROGRAM 0x02 ///< Программирование одной страницы #define MEMSPI_WRITE_EEPROM MEMSPI_PAGE_PROGRAM ///< Псевдоним для программирования EEPROM -#define MEMSPI_READ_STATUS_REG 0x05 ///< Чтение регистра состояния +#define MEMSPI_READ_STATUS_REG 0x05 ///< Чтение регистра состояния #if defined(MEMSPI_SEPARATED_STATUS_REGISTER) #define MEMSPI_READ_STATUS_REG_2 0x35 ///< Чтение второго регистра состояния (если поддерживается) #endif +#define MEMSPI_WRITE_STATUS_REG 0x01 ///< Запись в регистр состояния -#define MEMSPI_READ_DATA 0x03 ///< Чтение данных из памяти - +#define MEMSPI_READ_JEDEC_ID 0x9F ///< Команда чтения JEDEC ID +#define MEMSPI_READ_UNIQUE_ID 0x4B ///< Команда чтения уникального идентификатора +#endif /** //MEMSPI_Commands * @} */ @@ -82,6 +113,8 @@ #define MEMSPI_SR_SRWD MEMSPI_SR_SRP0 ///< Status Register Write Disable (аналог SRP0) #define MEMSPI_SR_WIP MEMSPI_SR_WEL ///< Write-In-Progress (используется в некоторых EEPROM) +// Exclusive (by name) RUFLASH SR bits +#define MEMSPI_SR_EPE (1<<5) ///< Ошибка/стирания записи /** * @} */ @@ -144,8 +177,16 @@ HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t F HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); /* Send command to page program in FLASH */ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout); +/* Send command to byte program in FLASH */ +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Byte_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t Byte, uint32_t Timeout); /* Send command to erase sector of FLASH */ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout); +/* Send command to Unprotect Sector */ +HAL_StatusTypeDef MEMSPI_CMD_Unprotect_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout); +/* Send command to Protect sector */ +HAL_StatusTypeDef MEMSPI_CMD_Protect_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout); +/* Send command to read Sector Protection Register */ +HAL_StatusTypeDef MEMSPI_CMD_Read_Sector_Protection(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pStatus, uint32_t Timeout); /* Send command to read JEDEC ID */ uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout); /* Send command to read JEDEC ID */ @@ -158,6 +199,17 @@ HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t F * @} */ +#ifndef local_time +#define local_time() HAL_GetTick() ///< Локальное время +#endif + +#ifndef printf_memspi +#define printf_memspi(...) +#endif +#ifndef printf_memspi_err +#define printf_memspi_err(...) +#endif + #endif // __SPI_MEMORY_CORE_H_ /** //MEMSPI_CORE diff --git a/Inc/params_flash.h b/Inc/params_flash.h new file mode 100644 index 0000000..ebb9e14 --- /dev/null +++ b/Inc/params_flash.h @@ -0,0 +1,275 @@ +/** +************************************************************************** +* @file params_flash.h +* @brief Заголовочный файл модуля записи параметров в Flash +************************************************************************** +* @details +Модуль позволяет сохранять данные в Flash и читать их обратно по номерам блоков. + +@par Параметры +- @ref PARAMS_FLASH_MAX_BUFFER_SIZE - Максимальный размер блока данных +- @ref PARAMS_FLASH_ASYNCH_BUFFER_SIZE - Размер блока для записи в асинхронном режиме. + Заданное количество слов будет записыватся при каждом вызове @ref ParamsFlash_AsynchHandle + пока весь блок не будет записан +- @ref PARAMS_FLASH_ADDR_START - Начало области Flash для хранения параметров +- @ref PARAMS_FLASH_ADDR_END - Конец области Flash для хранения параметров + +@par Пример использования + @code + #include "params_flash.h" + ParamsFlash_HandleInit(&flash_handle, 9, 10, 16); // Инициализация 9 и 10 секторов для записи, размер блока 16 слов + read_block_numb = ParamsFlash_ReadBlock(&flash_handle, PARAMS_LAST_BLOCK, read_data, size, read_size); // читаем последний блок + // ... + if(flag_to_write) + { + if(ParamsFlash_IsFlashFree(&flash_handle)) // првеоряем свободность флеш + { + ParamsFlash_WriteBlock(&flash_handle, PARAMS_LAST_BLOCK, data, size); // записываем новый блок + if(flash_handle.status == FLASH_BLOCK_FLASH_FULL) // если по какой-то причине не получилось + { + if(ParamsFlash_Pack(&flash_handle, 0)) // пробуем упаковку + { + // ошибка при ините упаковке, мб не удалось найти валидный блок + ParamsFlash_Pack(&flash_handle, 1); // erase без копирования блока + } + } + else // получилось + { + flag_to_write = 0; // успешно записали, сбрасываем флаг + } + } + } + // уходим и возврашаемся к этому коду позже... + @endcode + +@par Возможности + +- Каждый блок имеет уникальный номер (numb) и хранится в фиксированной + структуре @ref ParamsFlash_t + @code + [numb][size][data_start.......data_end][crc] + @endcode + +- Блоки располагаются последовательно в выделенной области Flash. + В процессе работы хранится адрес куда будет записан следующий блок. + @code + [ParamsFlash_t #1][ParamsFlash_t #2][ParamsFlash_t #3]... + ^ + | + next_block_adr + @endcode + + От next_block_adr вправо идет поиск свободного места. А влево идет поиск уже записанных блоков + @ref __ParamsFlash_FindBlock и @ref __ParamsFlash_FindFreeSpace для подробностей + +- Можно создать несколько хендлов @ref ParamsFlashHandle_t, каждый из которых + будет работать с своими параметрами и своей областью Flash памяти. + Для работы с несколькими handle необходимо использовать разные сектора Flash памяти + @code + ParamsFlash_HandleInit(&flash_handle1, 9, 10, 16); // 9,10 сектора + ParamsFlash_HandleInit(&flash_handle2, 11, 12, 32); // 11,12 сектора + @endcode + +- Запись и стирание происходит асихнронно @ref __ParamsFlash_WriteHandle и @ref __ParamsFlash_EraseHandle. + Поэтому надо удостоверится что функции @ref ParamsFlash_WriteBlock и @ref ParamsFlash_Pack вызываются корректно + и не накладываются друг на друга. Иначе может произойти игнорирование команд для записи/стирания. **См. Пример использования** + +- При записи c @ref PARAMS_LAST_BLOCK номер нового блока вычисляется + автоматически (последний_номер + 1) + + При записи конкретного блока он запишется только в том случае, если его номер не равен последнему номер + @code + writen_block_numb = ParamsFlash_WriteBlock(&flash_handle, PARAMS_LAST_BLOCK, data, size); // пишем с инкрементом + // ждем пока асихнронно запишется... + writen_block_numb = ParamsFlash_WriteBlock(&flash_handle, writen_block_numb, data, size); // ошибка + // ждем пока асихнронно запишется... + writen_block_numb = ParamsFlash_WriteBlock(&flash_handle, 1, data, size); // норм + @endcode + +- При чтении c @ref PARAMS_LAST_BLOCK читается последний инициализированный блок + @code + read_block_numb = ParamsFlash_ReadBlock(&flash_handle, PARAMS_LAST_BLOCK, read_data, size, NULL); + @endcode + + При чтении конкретного блока он считается только в том случае, если он валидный (CRC correct) + +- При переполнении сектора Flash будет выполнено стирание текщуего сектора и переход на другой + Erase будет пытаться выполнится до тех пор пока не будет успеха. + Если вдруг будет ошибка FLASH_BLOCK_FLASH_FULL (нет места в flash, оба сектора заполнены и не стерты по какой-то причине). + Можно попытаться сделать упаковку. **См. Пример использования**. + +- Поддерживается проверка целостности и корректности блоков + @code + if(ParamsFlash_VerifyBlock(&flash_handle, PARAMS_LAST_BLOCK, &last_valid_block_numb)) + { + // crc неправильный или еще что-то + } + + if(ParamsFlash_Compare(&flash_handle, block_numb, data, size)) + { + // данные НЕ совпадают + } + @endcode + +- При вызове каждой функции в структуре заполняется status по которому можно + отследить разные ошибки @ref FlashBlockStatus_t +*************************************************************************/ +#ifndef PARAMS_FLASH_H +#define PARAMS_FLASH_H + +#include "mylibs_include.h" +#include "memspi.h" +#include + + +//#define TRACE_WRITE_READ //led1 трейс ParamsFlash_WriteBlock и ParamsFlash_ReadBlock +//#define TRACE_FIND_BLOCK //led1 трейс внутренниъх функцйи по поиску блоков/свобождных мест +//#define TRACE_FLASH_BUSY //led2 трейс заняносит флеш FLASH_WORKING_ENTER и FLASH_WORKING_EXIT +//#define TRACE_ASYNCH //led1 трейс работы asynch + +//#define FLASH_WORKING_NONBLOCKING ///< Режим неблокирующей работы с Flash + + +#if !defined(PARAMS_FLASH_MAX_BUFFER_SIZE) +#define PARAMS_FLASH_MAX_BUFFER_SIZE 1096 ///< Максимальный размер блока данных +#endif +#if !defined(PARAMS_FLASH_ASYNCH_BUFFER_SIZE) +#define PARAMS_FLASH_ASYNCH_BUFFER_SIZE 100 ///< Размер блока для записи в асинхронном режиме. Заданное количество слов будет записыватся при каждом вызове @ref ParamsFlash_AsynchHandle +#endif +#if !defined(PARAMS_FLASH_ADDR_START) +#define PARAMS_FLASH_ADDR_START 0x0 ///< Начало допустимой адресации Flash +#endif +#if !defined(PARAMS_FLASH_ADDR_END) +#define PARAMS_FLASH_ADDR_END 0x1FFFF+1 ///< Конец допустимой адресации Flash +#endif + + + + + + +#define PARAMS_LAST_BLOCK 0xFFFF ///< Команда считать последний блок + +#define FLASH_WORKING_CHECK(_h_) (flash_working == 1) ///< Проверка занятности Flash +#define FLASH_WORKING_ENTER(_h_) do{flash_working = 1; LED2_TRACE_ENTER(); /*DINT;flash_reset();*/ }while(0) ///< Начало работы с Flash +#define FLASH_WORKING_EXIT(_h_) do{flash_working = 0; LED2_TRACE_EXIT(); /*EnableInterrupts();*/ }while(0) ///< Конец работы с Flash + + +#ifndef SETTINGS_USE_MEMORY_EEPROM +#define EMPTY_MEMORY 0xFFFF +#else +#define EMPTY_MEMORY 0x0000 +#endif + + + +#define __SET_CURR_BLOCK(pcur, adr) do{ pcur = &__buff; MEMSPI_Read_Memory(&memory_spi, adr, (uint8_t *)pcur, sizeof(ParamsFlash_t), 1000); }while(0); +#define __WRITE_CURR_BLOCK(pcur, adr, size) MEMSPI_FLASH_Program(&memory_spi, adr, (uint8_t *)pcur, size, 1000, 1) +#define __READ_BYTE(adr, data, size) MEMSPI_Read_Memory(&memory_spi, adr, (uint8_t *)data, size, 1000) +//#define __SET_CURR_BLOCK(pcur) do{ cur = (volatile ParamsFlash_t*)addr }while(0); + +/** + * @brief Варианты ошибок блока + */ +typedef enum { + FLASH_BLOCK_OK = 0, ///< Блок корректен + FLASH_BLOCK_EMPTY , ///< Блок не инициализирован + FLASH_BLOCK_CRC_ERROR , ///< CRC блока не совпадает + FLASH_BLOCK_ALREADY_EXIST , ///< Блок уже существует + FLASH_BLOCK_NOT_FOUND , ///< Блок не найден + FLASH_BLOCK_FLASH_FULL , ///< Нет места в Flash для блока + FLASH_BLOCK_INIT_ERR , ///< Ошибка инициализации блока (неадекватные параметры) + FLASH_BLOCK_WRITE_ERR , ///< Ошибка записи + FLASH_BLOCK_BUSY , ///< Работа с флеш уже идет + FLASH_BLOCK_UNKNOWN_ERR ///< Неизвестная ошибка +} FlashBlockStatus_t; + +/** + * @brief Структура данных, которая будет положена в Flash + */ +typedef struct { + uint16_t numb; ///< Номер блока + uint16_t size; ///< Размер данных + uint16_t data[PARAMS_FLASH_MAX_BUFFER_SIZE]; ///< Буфер данных + uint16_t crc16; ///< CRC блока +} ParamsFlash_t; + + +/** + * @brief Handle конкретного сектора + */ +typedef struct { + // общая инфа о секторе + uint16_t sector_num; ///< Номер сектора + uint32_t addr_start; ///< Абсолютный адрес начала сектора + uint32_t addr_end; ///< Абсолютный адрес конца сектора + uint16_t size; ///< Размер сектора словах + uint16_t capacity; ///< Вместимость - сколько блоков можно положить + + // всякие служебные штуки + uint32_t next_block_adr; ///< Адрес следующего блока в секторе + uint16_t erase_pending; ///< Флаг, что нужен erase сектора +} FlashSector_t; + +/** + * @brief Handle для работы с конкретной областью Flash + */ +typedef struct { + int initialized; ///< Флаг что модуль успешно инициализирован + FlashSector_t sector[2]; ///< Два сектора для записи + int sect_curr_ind; ///< Номер текущего сектора (0 или 1) + uint16_t buffer_size; ///< Размер буфера в словах (1 слово = 16 бит) + + ParamsFlash_t blk_tmp; ///< Временная структура для операций + + uint32_t addr_start; ///< Начало текущей области Flash + uint32_t addr_end; ///< Конец текущей области Flash + uint32_t next_block_adr; ///< Адрес следующего блока + uint16_t write_pending; ///< Флаг для записи @ref blk_tmp в Flash + uint16_t write_word_cnt; ///< Количество записанных байт + + FlashBlockStatus_t status; ///< Статус хендла +} ParamsFlashHandle_t; + +/* ======= Инициализация модуля ======================================= */ +/* Инициализация handle */ +int ParamsFlash_HandleInit(ParamsFlashHandle_t *h, int sector_1, int sector_2, int buf_size); +/* Обработчик неблокирующей работы модуля */ +int ParamsFlash_AsynchHandle(ParamsFlashHandle_t *h); + +/* ======= Функции управления ======================================= */ +/* Проверка свободности флеш */ +int ParamsFlash_IsFlashFree(ParamsFlashHandle_t *h); +/* Запись блока во Flash */ +int ParamsFlash_WriteBlock(ParamsFlashHandle_t *h, int block_num, const uint16_t *Buffer, int size); +/* Чтение блока из Flash */ +int ParamsFlash_ReadBlock(ParamsFlashHandle_t *h, int block_num, uint16_t *Buffer, int size, int *read_size); +/* Упаковка Flash: стирание всей области и запись последнего блока в начало */ +int ParamsFlash_Pack(ParamsFlashHandle_t *h, int force); +/* Полная инициализация Flash (стирание всей области) */ +int ParamsFlash_Init(ParamsFlashHandle_t *h); +/* Проверка блока на корректность */ +FlashBlockStatus_t ParamsFlash_VerifyBlock(ParamsFlashHandle_t *h, int block_num, int *last_valid_num); +/* Сравнение данных с блоком из Flash */ +int ParamsFlash_Compare(ParamsFlashHandle_t *h, int block_num, const uint16_t *data, int size); +/* Получение статистики по блокам */ +int ParamsFlash_GetInfo(ParamsFlashHandle_t *h, int *totalBlocks, int *validBlocks, int *invalidBlocks); + + + + + +#if defined(TRACE_FLASH_BUSY) +#define LED2_TRACE_ENTER() i_led2_on_off(1); +#define LED2_TRACE_EXIT() i_led2_on_off(0); +#else +#define LED2_TRACE_ENTER() +#define LED2_TRACE_EXIT() +#endif + +#define LED1_TRACE_ENTER() i_led1_on_off(1); +#define LED1_TRACE_EXIT() i_led1_on_off(0); + + + +#endif //PARAMS_FLASH_H diff --git a/Inc/set_to_mem.h b/Inc/set_to_mem.h new file mode 100644 index 0000000..c5a84a3 --- /dev/null +++ b/Inc/set_to_mem.h @@ -0,0 +1,136 @@ +/** +************************************************************************** +* @file set_to_mem.h +* @brief Заголовочный файл для записи в память настроек. +************************************************************************** +* @defgroup SETMEM_TOOLS Settings to Memory Tools +* @brief Модуль для записи/считывания настроек в память +************************************************************************** +@details Есть следующие настройки: + +- @ref SETTINGS_USE_SETTINGS_FROM_BUFFER : выкидывать в память данные по указателю на разные настройки + - Для работы надо объявить в структуре данные которые будут записываться (@ref SettingsInMemTypeDef) + и указатели на эти данные (@ref SettingsSourceTypeDef) + - Добавить @ref CompareSettings на каждый массив настроек, для отслеживания изменений. + При отличии - выставляется флаг для записи настроек в память + - Добавить @ref WriteSettingToBuffer и @ref ReadSettingFromBuffer для синхронизации + каждого элемента структуры буфера и реальных настроек. + +(_CompareSettings`, WriteSettingToBuffer` и ReadSettingFromBuffer` это дефайны которые принимают название элемента структуры и уже вызывают нужные функции_) + +  +- @ref SETTINGS_USE_SETTINGS_FROM_POINTER : заполнять необходимыми настройками буфер и полностью выкидывать его в память + - Для работы надо объявить в структуре данные которые будут записываться (@ref SettingsInMemTypeDef) + - Инициализировать каждый элемент структуры функцией @ref InitSettingsToMem + - Добавить @ref WriteSettingsArrayToMem и @ref ReadSettingsArrayFromMem для записи/считывания + каждого элемента структуры в память + - Выставлять флаг @ref update_settings_flag для записи настроек в память, + когда необходимо это сделать (само по себе оно не может определить когда надо записать, + т.к. нет буфера для отслеживания изменений) + +- @ref SETTINGS_MEMORY_PROTECT_ENABLE : включение защиты на память. + Перед записью защита снимается, и после записи ставится обратно. +* @{ +*************************************************************************/ +#ifndef _SET_TO_MEM_H_ +#define _SET_TO_MEM_H_ + +#include "mylibs_include.h" +#include "params_flash.h" +#include "memspi.h" + + +/** + * @brief Структура для записи одного массива настроек по указателю. + * @details Используется при @ref SETTINGS_USE_SETTINGS_FROM_POINTER + */ +typedef struct +{ + uint32_t adr; ///< Адрес в памяти + uint32_t length; ///< Размер блока + + uint8_t *real_ptr; ///< Указатель на реальные рабочие данные + uint8_t *mem_ptr; ///< Указатель на данные, которые пишутся в память +}SettingArrayTypeDef; + + +#define MEMORY_ERROR_EMPTY ((uint32_t)(1<<0)) ///< Бит ошибки - память пустая +#define MEMORY_ERROR_WRITE ((uint32_t)(1<<1)) ///< Бит ошибки - запись +#define MEMORY_ERROR_READ ((uint32_t)(1<<2)) ///< Бит ошибки - чтение + +/** + * @brief Структура для хранения настроек устройства. + * @details Содержит указатели на настройки и другие параметры для их обработки. + */ +typedef struct +{ + MEMSPI_HandleTypeDef *hmemspi; ///< Указатель на хендл для работы с памятью + +#ifdef SETTINGS_USE_WEAR_LEVELING_FLASH + ParamsFlashHandle_t flash_handle; ///< Хендл для равномерного использования флеш памяти +#endif + +#ifdef SETTINGS_USE_SETTINGS_FROM_BUFFER + uint8_t *buffer; +#endif + + SettingArrayTypeDef setarr[32]; ///< Структура настроек для хранения в памяти + uint8_t setarr_count; + + uint32_t start_adr; ///< Начальный адрес в памяти для записи настроек + uint32_t settings_size; ///< Размер всего массива настроек + + uint8_t update_settings_flag; ///< Флаг для обновления настроек в памяти + uint32_t settings_error; ///< Флаг ошибки настроек +}SettingsTypeDef; +extern SettingsTypeDef Settings; + + +/** + * @defgroup SETMEM_GENERAL_FUNC General functions for writing/reading settings + * @ingroup SETMEM_TOOLS + * @brief Общие функции для работы с настройками в памяти. + * @{ + */ + +/* Инициализаия обзего хендла для работы с настройками */ +HAL_StatusTypeDef Settings_Init(SettingsTypeDef *settings, SPI_HandleTypeDef *hspi, uint32_t adr); +/* Добавление массива настроек для хранения в памяти */ +HAL_StatusTypeDef Settings_AddArray(SettingsTypeDef *settings, uint8_t *pRealArray, uint32_t *startadr, uint32_t sizeofarray); +/* Запись настроек в память */ +void Settings_WriteSettings(SettingsTypeDef *settings); +/* Чтение настроек из памяти */ +void Settings_ReadSettings(SettingsTypeDef *settings); +/* Проверить настройки на валидность. */ +void Settings_CheckSettings(SettingsTypeDef *settings); + +/** SETMEM_GENERAL_FUNC + * @} + */ + +/** + * @defgroup SETMEM_SUPPORT_FUNC Support functions for writing/reading + * @ingroup SETMEM_TOOLS + * @brief Служебные функции для работы с настройками в памяти + * @{ + */ + +/* Запись настроек в память в зависимости от конфигурации */ +void WriteSettingsToMem(SettingsTypeDef *settings); +/* Чтение настроек из памяти в зависимости от конфигурации */ +void ReadSettingsFromMem(SettingsTypeDef *settings); + +/* Записывает массив настроек через указатель в память */ +void WriteSettingsArrayToMem(SettingsTypeDef *settings, SettingArrayTypeDef *settingarr); +/* итает массив настроек через указатель в память */ +void ReadSettingsArrayFromMem(SettingsTypeDef *settings, SettingArrayTypeDef *settingarr); + +/** SETMEM_SUPPORT_FUNC + * @} + */ + +/** SETMEM_TOOLS + * @} + */ + +#endif //_SET_TO_MEM_H_ diff --git a/Src/memspi.c b/Src/memspi.c index 3c83332..2fc60f5 100644 --- a/Src/memspi.c +++ b/Src/memspi.c @@ -21,6 +21,7 @@ #include "memspi.h" uint8_t sector_buff[MEMSPI_SECTOR_SIZE]; +MEMSPI_HandleTypeDef memory_spi; //------------------------------------------------------------- //--------------------------FOR USER--------------------------- /** @@ -63,7 +64,7 @@ void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi, SPI_HandleTypeDef *hspi) * @return HAL status. * @note Включает в себя проверку на доступность памяти (флаг BUSY) */ -HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) +HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint32_t Size, uint32_t Timeout) { HAL_StatusTypeDef MEMSPI_Status; uint32_t tickstart = local_time(); @@ -90,7 +91,7 @@ HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLA * @return HAL status. * @note Позволяет записать участок памяти. Можно записывать несколько страниц. */ -HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd) +HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd) { uint32_t tickstart = local_time(); HAL_StatusTypeDef MEMSPI_Status; @@ -180,7 +181,7 @@ HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_Write uint32_t addr_shift = WriteInit->Data_Address - WriteInit->Sector_Address; for(int i = 0; i < WriteInit->Data_Size; i++) { - sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i]; + sector_buff[addr_shift+i] = WriteInit->pDataPtr[i]; } // CALC AREA TO REPROGRAM @@ -243,54 +244,57 @@ HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_Write } /** - * @brief Program external FLASH. - * @param hmemspi Указатель на хендл внешней памяти. - * @param FLASH_Address Адресс куда начинать записывать. - * @param pData Откуда брать данные для записи в FLASH. - * @param Size Вколько байтов записать. - * @param Timeout Время, за которое должно быть осуществлено чтение. - * @param WaitForEnd Ожидание, пока память не выполненит операцию. - * @return HAL status. - * @note Программирование участка памяти, без ограничений на кол-во байт - */ -HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd) + * @brief Program external FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param WaitForEnd - ожидание, пока память не выполненит операцию. + * @return HAL status. + * @note Программирование участка памяти, без ограничений на кол-во байт + */ +HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd) { - uint32_t tickstart = local_time(); - HAL_StatusTypeDef MEMSPI_Status; - - // CALC AREA TO PROGRAM - uint16_t lastpage_size = Size; - uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE); - uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE); - if(firstpage != lastpage) // if area is on several pages - { - lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE; // set size of data on last page - } - - // PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST - hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable - hmemspi->hNextPage = firstpage+1; // address would automatically increase in this variable - uint16_t bytecnt = 0; - uint16_t bytes_to_next_page = 0; - for(int i = 0; i < lastpage - firstpage; i++) - { - // calc bytes to next sector - bytes_to_next_page = hmemspi->hNextPage*MEMSPI_PAGE_SIZE - hmemspi->hNextAddr; - - MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], bytes_to_next_page, &Timeout, &tickstart, 0); // programm page - if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy - return MEMSPI_Status; - - // then we shift byte count to data, that shoud be on the next page - bytecnt += bytes_to_next_page; - } - - // PROGRAM LAST PAGE - MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd); // programm page - if(MEMSPI_Status != HAL_OK) - return MEMSPI_Status; - - return HAL_OK; // if all ok return HAL_OK + uint32_t tickstart = HAL_GetTick(); + HAL_StatusTypeDef MEMSPI_Status; + +#ifdef FLASHTYPE_BYTE_PROGRAMM + // PROGRAM DATA: FROM FIRST BYTE TO THE LAST + MEMSPI_Status = MEMSPI_FLASH_Program_Bytes(hmemspi, FLASH_Address, pData, Size, &Timeout, &tickstart, WaitForEnd); // programm data + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; +#else + // CALC AREA TO PROGRAM + uint16_t lastpage_size = Size; + uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE); + uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE); + if(firstpage != lastpage) // if area is on several pages + { + lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE; // set size of data on last page + } + + // PROGRAM PAGES: FROM FIRST PAGE TO THE PREVIOUS TO THE LAST + hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable + hmemspi->hNextPage = firstpage+1; // address would automatically increase in this variable + uint16_t bytecnt = 0; + for(int i = 0; i < lastpage - firstpage; i++) + { + // calc bytes to next sector + hmemspi->TxSize = hmemspi->hNextPage*MEMSPI_PAGE_SIZE - hmemspi->hNextAddr; + + MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], hmemspi->TxSize, &Timeout, &tickstart, 0); // programm page + if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy + return MEMSPI_Status; + // then we shift byte count to data, that shoud be on the next page + bytecnt += hmemspi->TxSize; + } + // PROGRAM LAST PAGE + MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd); // programm page + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; +#endif + return HAL_OK; // if all ok return HAL_OK } @@ -305,7 +309,7 @@ HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t F * @note Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить * И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1 */ -HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd) +HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Size, uint32_t Timeout, uint8_t WaitForEnd) { uint32_t tickstart = local_time(); HAL_StatusTypeDef MEMSPI_Status; @@ -325,7 +329,70 @@ HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLA return HAL_OK; } - +/** + * @brief Set protection for FLASH sectors. + * @param hmemspi Указатель на хендл внешней памяти. + * @param FLASH_Address Адресс сектора для выставления защиты. + * @param Size Сколько байтов защитить. + * @param ProtectState Состояние защиты: ENABLE / DISABLE. + * @param Timeout Время, за которое должно быть осуществлено выставление защиты. + * @return HAL status. + */ +HAL_StatusTypeDef MEMSPI_FLASH_Protection(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Size, uint32_t ProtectState, uint32_t Timeout) +{ +#ifdef MEMSPI_PROTECT_SECTOR + uint32_t tickstart = local_time(); + HAL_StatusTypeDef MEMSPI_Status; + + // CALC AREA TO PROTECT + uint16_t bytecnt = 0; + uint16_t firstsector = (FLASH_Address/MEMSPI_SECTOR_SIZE); + uint16_t lastsector = ((FLASH_Address+Size-1)/MEMSPI_SECTOR_SIZE); + + for(int i = 0; i <= (lastsector - firstsector); i++) + { + + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, &Timeout, &tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + if(ProtectState == ENABLE) + { + MEMSPI_Status = MEMSPI_CMD_Protect_Sector(hmemspi, FLASH_Address, Timeout); + } + else + { + MEMSPI_Status = MEMSPI_CMD_Unprotect_Sector(hmemspi, FLASH_Address, Timeout); + } + + if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if operation isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; // return timeout because erasing instruction accepted, but arent done + + uint8_t ProtectStateCheck; + if(MEMSPI_CMD_Read_Sector_Protection(hmemspi, FLASH_Address, &ProtectStateCheck, Timeout) == HAL_OK) + { + uint8_t expectedState = (ProtectState == ENABLE) ? 0xFF : 0x00; + if(ProtectStateCheck == expectedState) + { +// return HAL_OK; + } + else + { + return HAL_ERROR; + } + } + else + { + return HAL_ERROR; + } + FLASH_Address += MEMSPI_SECTOR_SIZE; + } + + + return MEMSPI_Status; +#else + return HAL_ERROR; +#endif +} //------------------------------------------------------------- //----------------------SERVICE FUNCTIONS---------------------- /** @@ -341,7 +408,7 @@ HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLA * @note Позволяет записывать только байты в пределах одной страницы. Для более гибкой записи есть функция MEMSPI_EEPROM_Write, которая программирует участки любой длины (в теории). */ -HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd) +HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd) { HAL_StatusTypeDef MEMSPI_Status; // enable writting and waiting for unbusy @@ -397,45 +464,114 @@ HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint3 /** * @brief Program page in external FLASH. - * @param hmemspi Указатель на хендл внешней памяти. - * @param FLASH_Address Адресс куда начинать записывать. - * @param pData Откуда брать данные для записи в FLASH. - * @param Size Вколько байтов записать. - * @param Timeout Время, за которое должно быть осуществлено чтение. - * @param tickstart Время, относительно которого надо отсчитывать таймаут. - * @param WaitForEnd Ожидание, пока память не выполненит операцию. - * @return HAL status. - * @note Позволяет перепрограммировать только байты в прелелах одной страницы. - Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории). - */ -HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd) + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @param WaitForEnd - ожидание, пока память не выполненит операцию. + * @return HAL status. + * @note Позволяет перепрограммировать только байты в прелелах одной страницы. + Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории). + */ +HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd) { - HAL_StatusTypeDef MEMSPI_Status; - // enable writting and waiting for unbusy - if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable - return HAL_TIMEOUT; // return timeout + HAL_StatusTypeDef MEMSPI_Status; - // check if flash range is placed at one page - if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte - return HAL_ERROR; // return error +#ifndef FLASHTYPE_BYTE_PROGRAMM + // PROGRAM WHOLE PAGE + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, &Timeout, &tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + // check if flash range is placed at one page + if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte + return HAL_ERROR; // return error + + // programm page (instruction) + MEMSPI_Status = MEMSPI_CMD_FLASH_Page_Program(hmemspi, FLASH_Address, pData, Size, Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of writting if need + if(WaitForEnd) + if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; +#else + // PROGRAM PAGE BY BYTES - // programm page (instruction) - MEMSPI_Status = MEMSPI_CMD_FLASH_Page_Program(hmemspi, FLASH_Address, pData, Size, *Timeout); - if(MEMSPI_Status != HAL_OK) - return MEMSPI_Status; - - // waiting for ending of writting if need - if(WaitForEnd) - if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) - return HAL_TIMEOUT; - - // update handle variables - hmemspi->hNextAddr = (FLASH_Address+Size); - hmemspi->hNextPage = (hmemspi->hNextAddr+Size)/MEMSPI_PAGE_SIZE; - hmemspi->hNextSector = (hmemspi->hNextAddr+Size)/MEMSPI_SECTOR_SIZE; - - return HAL_OK; + for(int j = 0; j < MEMSPI_PAGE_SIZE; j++) + { + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + // check if flash range is placed at one page + if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte + return HAL_ERROR; // return error + + // programm page (instruction) + MEMSPI_Status = MEMSPI_CMD_FLASH_Byte_Program(hmemspi, FLASH_Address, pData[j], *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of writting if need + if(WaitForEnd) + if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; + } +#endif + + // update handle variables + hmemspi->hNextAddr = (FLASH_Address+Size); + hmemspi->hNextPage = (hmemspi->hNextAddr+Size)/MEMSPI_PAGE_SIZE; + hmemspi->hNextSector = (hmemspi->hNextAddr+Size)/MEMSPI_SECTOR_SIZE; + + return HAL_OK; } + +/** + * @brief Program data in external FLASH in blocking mode. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param pData - откуда брать данные для записи в FLASH. + * @param Size - сколько байтов записать. + * @param Timeout - время, за которое должно быть осуществлено чтение. + * @param tickstart - время, относительно которого надо отсчитывать таймаут. + * @param WaitForEnd - ожидание, пока память не выполненит операцию. + * @return HAL status. + * @note Позволяет перепрограммировать любое количество байт (без ограничений на количество страниц) + */ +HAL_StatusTypeDef MEMSPI_FLASH_Program_Bytes(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint32_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd) +{ + HAL_StatusTypeDef MEMSPI_Status; + + for(int j = 0; j < Size; j++) + { + // enable writting and waiting for unbusy + if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable + return HAL_TIMEOUT; // return timeout + + // programm page (instruction) + MEMSPI_Status = MEMSPI_CMD_FLASH_Byte_Program(hmemspi, FLASH_Address+j, pData[j], *Timeout); + if(MEMSPI_Status != HAL_OK) + return MEMSPI_Status; + + // waiting for ending of writting if need + if(WaitForEnd) + if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state) + return HAL_TIMEOUT; + } + + // update handle variables + hmemspi->hNextAddr = (FLASH_Address+Size); + hmemspi->hNextPage = (hmemspi->hNextAddr+Size)/MEMSPI_PAGE_SIZE; + hmemspi->hNextSector = (hmemspi->hNextAddr+Size)/MEMSPI_SECTOR_SIZE; + + return HAL_OK; +} + /** * @brief Setting WEL bit until it setted or until timeout. * @param hmemspi Указатель на хендл внешней памяти. diff --git a/Src/memspi_core.c b/Src/memspi_core.c index 5bc0b36..62b4493 100644 --- a/Src/memspi_core.c +++ b/Src/memspi_core.c @@ -32,44 +32,44 @@ */ HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t RequestedBits, uint8_t EndCMD, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - uint8_t command[2]; - uint8_t *pSRPtr = 0; - uint8_t size = 1; - - if(RequestedBits >> 8) // if its high byte of status register - { + HAL_StatusTypeDef SPI_RES; + uint8_t command[2]; + uint8_t *pSRPtr = 0; + uint8_t size = 1; + + if(RequestedBits >> 8) // if its high byte of status register + { #ifdef MEMSPI_READ_STATUS_REG_2 - command[0] = MEMSPI_READ_STATUS_REG_2; - pSRPtr = (uint8_t *)(&hmemspi->SR) + 1; // set pointer to HI byte of SR register - size = 1; - if(RequestedBits & 0xFF) // if low byte also requester - { - size = 2; // set size to 2 bytes - command[1] = MEMSPI_READ_STATUS_REG; - } -#endif // MEMSPI_READ_STATUS_REG_2 - } - else // of its low byte of status register + command[0] = MEMSPI_READ_STATUS_REG_2; + pSRPtr = (uint8_t *)(&hmemspi->SR) + 1; // set pointer to HI byte of SR register + size = 1; + if(RequestedBits & 0xFF) // if low byte also requester { - command[0] = MEMSPI_READ_STATUS_REG; - pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to LO byte of SR register - size = 1; - } - MEMSPI_Select(hmemspi); + size = 2; // set size to 2 bytes + command[1] = MEMSPI_READ_STATUS_REG; + } +#endif // MEMSPI_READ_STATUS_REG_2 + } + else // of its low byte of status register + { + command[0] = MEMSPI_READ_STATUS_REG; + pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to LO byte of SR register + size = 1; + } + MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); // send insctruction to read SR SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr, 1, Timeout); // receive response - if(size > 1) // if 2 bytes are requested - { - MEMSPI_Deselect(hmemspi); - MEMSPI_Select(hmemspi); - SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command+1, 1, Timeout); // send insctruction to read SR - SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr-1, 1, Timeout); // receive response - MEMSPI_Deselect(hmemspi); - } - if(EndCMD) - MEMSPI_Deselect(hmemspi); - return SPI_RES; + if(size > 1) // if 2 bytes are requested + { + MEMSPI_Deselect(hmemspi); + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command+1, 1, Timeout); // send insctruction to read SR + SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr-1, 1, Timeout); // receive response + MEMSPI_Deselect(hmemspi); + } + if(EndCMD) + MEMSPI_Deselect(hmemspi); + return SPI_RES; } /** @@ -82,21 +82,21 @@ HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, */ HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t WrittenBits, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - uint8_t command[3]; - uint8_t size; + HAL_StatusTypeDef SPI_RES; + uint8_t command[3]; + uint8_t size; command[0] = MEMSPI_WRITE_STATUS_REG; - command[1] = WrittenBits; - command[2] = WrittenBits >> 8; - - size = 3; + command[1] = WrittenBits; + command[2] = WrittenBits >> 8; + + size = 3; MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, size, Timeout); MEMSPI_Deselect(hmemspi); - return SPI_RES; + return SPI_RES; } /** @@ -107,7 +107,7 @@ HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi */ HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; + HAL_StatusTypeDef SPI_RES; uint8_t command[1]; command[0] = MEMSPI_WRITE_ENABLE; @@ -115,7 +115,7 @@ HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_ SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); MEMSPI_Deselect(hmemspi); - return SPI_RES; + return SPI_RES; } @@ -130,9 +130,9 @@ HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_ */ HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - uint8_t command[4]; - uint8_t response[2] = {0}; + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + uint8_t response[2] = {0}; command[0] = MEMSPI_READ_DATA; command[1] = FLASH_Address >> 16 & 0xFF; @@ -144,7 +144,7 @@ HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t F SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout); MEMSPI_Deselect(hmemspi); - return SPI_RES; + return SPI_RES; } /** @@ -158,22 +158,22 @@ HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t F */ HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - // 1 command byte + 3 address bytes + 256 data bytes - uint8_t command[1+3+MEMSPI_PAGE_SIZE]; - FLASH_Address = FLASH_Address & 0xFFFFFF; + HAL_StatusTypeDef SPI_RES; + // 1 command byte + 3 address bytes + 256 data bytes + uint8_t command[1+3+MEMSPI_PAGE_SIZE]; + FLASH_Address = FLASH_Address & 0xFFFFFF; command[0] = MEMSPI_WRITE_EEPROM; - command[1] = FLASH_Address >> 16 & 0xFF; - command[2] = FLASH_Address >> 8 & 0xFF; - command[3] = FLASH_Address & 0xFF; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); // send insctruction to write SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout); // send data to write MEMSPI_Deselect(hmemspi); - return SPI_RES; + return SPI_RES; } /** * @brief Send command to page program in FLASH. @@ -187,26 +187,57 @@ HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_ */ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - // 1 command byte + 3 address bytes + 256 data bytes - uint8_t command[1+3+MEMSPI_PAGE_SIZE]; - FLASH_Address = FLASH_Address & 0xFFFFFF; + HAL_StatusTypeDef SPI_RES; + // 1 command byte + 3 address bytes + 256 data bytes + uint8_t command[1+3+MEMSPI_PAGE_SIZE]; + FLASH_Address = FLASH_Address & 0xFFFFFF; command[0] = MEMSPI_PAGE_PROGRAM; - command[1] = FLASH_Address >> 16 & 0xFF; - command[2] = FLASH_Address >> 8 & 0xFF; - command[3] = FLASH_Address & 0xFF; - - // check if flash range is placed at one page - if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte - return HAL_ERROR; // return error + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + + // check if flash range is placed at one page + if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte + return HAL_ERROR; // return error MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); // send insctruction to write SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout); // send data to write MEMSPI_Deselect(hmemspi); - return SPI_RES; + return SPI_RES; +} + +/** + * @brief Send command to byte program in FLASH. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс куда начинать записывать. + * @param Byte - байт для записи в FLASH. + * @param Timeout - время, за которое должна быть осуществлена запись. + */ +HAL_StatusTypeDef MEMSPI_CMD_FLASH_Byte_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t Byte, uint32_t Timeout) +{ + HAL_StatusTypeDef SPI_RES; + // 1 command byte + 3 address bytes + 256 data bytes + uint8_t command[1+3+MEMSPI_PAGE_SIZE]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_BYTE_PROGRAM; + command[1] = FLASH_Address >> 16 & 0xFF; + command[2] = FLASH_Address >> 8 & 0xFF; + command[3] = FLASH_Address & 0xFF; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); // send insctruction to write + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, &Byte, 1, Timeout); // send byte to write + MEMSPI_Deselect(hmemspi); + + if(SPI_RES != HAL_OK) + { + printf_memspi_err("Error Program Byte: 0x%08lX", (unsigned long)FLASH_Address); + } + return SPI_RES; } /** @@ -218,23 +249,118 @@ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, u */ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - uint8_t command[4]; + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; uint8_t response[8]; - FLASH_Address = FLASH_Address & 0xFFFFFF; + FLASH_Address = FLASH_Address & 0xFFFFFF; command[0] = MEMSPI_ERASE_SECTOR; - command[1] = FLASH_Address >> 16; - command[2] = FLASH_Address >> 8; - command[3] = FLASH_Address; + command[1] = FLASH_Address >> 16; + command[2] = FLASH_Address >> 8; + command[3] = FLASH_Address; MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); MEMSPI_Deselect(hmemspi); - - return SPI_RES; + + return SPI_RES; } +/** + * @brief Send command to unprotect sector. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс сектора, с которого надо снять защиту. + * @param Timeout - время, за которое должна быть осуществлена операция. + * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. + */ +HAL_StatusTypeDef MEMSPI_CMD_Unprotect_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout) +{ +#ifdef MEMSPI_PROTECT_SECTOR + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_UNPROTECT_SECTOR; + command[1] = FLASH_Address >> 16; + command[2] = FLASH_Address >> 8; + command[3] = FLASH_Address; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +#else + return HAL_ERROR; +#endif +} +/** + * @brief Send command to protect sector. + * @param hmemspi - указатель на хендл внешней памяти. + * @param FLASH_Address - адресс сектора, на который надо поставить защиту. + * @param Timeout - время, за которое должна быть осуществлена операция. + * @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом. + */ +HAL_StatusTypeDef MEMSPI_CMD_Protect_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout) +{ +#ifdef MEMSPI_PROTECT_SECTOR + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_PROTECT_SECTOR; + command[1] = FLASH_Address >> 16; + command[2] = FLASH_Address >> 8; + command[3] = FLASH_Address; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); + MEMSPI_Deselect(hmemspi); + + return SPI_RES; +#else + return HAL_ERROR; +#endif +} + +/** + * @brief Send command to read Sector Protection Register. + * @param hmemspi Указатель на хендл внешней памяти. + * @param FLASH_Address Адрес в пределах интересующего сектора (A16-A0). + * @param pStatus Указатель для сохранения статуса защиты (0x00 - не защищен, 0xFF - защищен). + * @param Timeout Время, за которое должно быть осуществлено чтение. + * @return HAL_StatusTypeDef. + * @note Команда 0x3C. После адреса микросхема начинает выдавать байты FFh или 00h. + * Рекомендуется читать минимум 2 байта для корректного определения статуса на высокой частоте. + */ +HAL_StatusTypeDef MEMSPI_CMD_Read_Sector_Protection(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pStatus, uint32_t Timeout) +{ +#ifdef MEMSPI_PROTECT_SECTOR + HAL_StatusTypeDef SPI_RES; + uint8_t command[4]; + uint8_t receive[2] = {0}; // читаем 2 байта для надежности + FLASH_Address = FLASH_Address & 0xFFFFFF; + + command[0] = MEMSPI_READ_PROTECT_REG; + command[1] = FLASH_Address >> 16; + command[2] = FLASH_Address >> 8; + command[3] = FLASH_Address; + + MEMSPI_Select(hmemspi); + SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); + SPI_RES = MEMSPI_SPI_Receive(hmemspi, receive, 2, Timeout); // Читаем данные защиты (рекомендуется минимум 2 байта для корректного определения на высокой частоте SCK) + MEMSPI_Deselect(hmemspi); + + if(SPI_RES == HAL_OK) { + *pStatus = receive[1]; // Второй байт более надежен на высокой частоте + // Согласно документации: 0x00 - сектор не защищен, 0xFF - сектор защищен + } + + return SPI_RES; +#else + return HAL_ERROR; +#endif +} /** * @brief Send command to read JEDEC ID. @@ -244,10 +370,10 @@ HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, u */ uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; + HAL_StatusTypeDef SPI_RES; uint8_t command[1] = {0}; uint8_t receive[4] = {0}; - uint32_t return_val; + uint32_t return_val; command[0] = MEMSPI_READ_JEDEC_ID; MEMSPI_Select(hmemspi); @@ -267,19 +393,19 @@ uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeou */ uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; + HAL_StatusTypeDef SPI_RES; uint8_t command[1] = {0}; uint8_t receive[8] = {0}; - uint64_t return_val_LO; - uint64_t return_val_HI; + uint64_t return_val_LO; + uint64_t return_val_HI; command[0] = MEMSPI_READ_UNIQUE_ID; MEMSPI_Select(hmemspi); SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); SPI_RES = MEMSPI_SPI_Receive(hmemspi, receive, 8, Timeout); MEMSPI_Deselect(hmemspi); - return_val_LO = (*(uint64_t *)receive) >> 32; - return_val_HI = (*(uint64_t *)receive) & 0xFFFFFFFF; + return_val_LO = (*(uint64_t *)receive) >> 32; + return_val_HI = (*(uint64_t *)receive) & 0xFFFFFFFF; return ((uint64_t)__REV(return_val_HI) << 32) | __REV(return_val_LO); } /** @@ -293,9 +419,9 @@ uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeo */ HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout) { - HAL_StatusTypeDef SPI_RES; - uint8_t command[5] = {0}; - uint8_t response[2] = {0}; + HAL_StatusTypeDef SPI_RES; + uint8_t command[5] = {0}; + uint8_t response[2] = {0}; command[0] = MEMSPI_READ_DATA; command[1] = FLASH_Address >> 16 & 0xFF; @@ -307,8 +433,8 @@ HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t F SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 5, Timeout); SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout); MEMSPI_Deselect(hmemspi); - - return SPI_RES; + + return SPI_RES; } //------------------------------------------------------------- diff --git a/Src/params_flash.c b/Src/params_flash.c new file mode 100644 index 0000000..f981ebc --- /dev/null +++ b/Src/params_flash.c @@ -0,0 +1,1232 @@ +/** +************************************************************************** +* @file params_flash.c +* @brief Модуль записи параметров в Flash +*************************************************************************/ +#include "params_flash.h" +#include "__crc_algs.h" + + +extern uint32_t ParamsFlash_GetSectorInfo(uint32_t SectorNum, uint32_t *pSectorSize); // return SectorAddr + +/* Внутренние штуки */ +static volatile int flash_working = 0; ///< общий флаг занятости флеш +static volatile int flash_block_size = sizeof(ParamsFlash_t); ///< размер структуры которая будет положена в флеш +static int __ParamsFlash_CheckHandle(ParamsFlashHandle_t *h, int check_busy); +static int __ParamsFlash_SwitchSector(ParamsFlashHandle_t *h, int force_ind); +static FlashBlockStatus_t __ParamsFlash_FindBlock(ParamsFlashHandle_t *h, uint16_t block_num, uint32_t *found_addr, volatile ParamsFlash_t **found_blk); +static FlashBlockStatus_t __ParamsFlash_FindFreeSpace(ParamsFlashHandle_t *h, uint16_t block_num, uint32_t *free_addr, uint16_t *free_numb); +static FlashBlockStatus_t __ParamsFlash_VerifyBlock(volatile ParamsFlash_t *blk); +#if defined(FLASH_WORKING_NONBLOCKING) +static int __ParamsFlash_WriteHandle(ParamsFlashHandle_t *h); +static int __ParamsFlash_EraseHandle(ParamsFlashHandle_t *h); +#endif + +ParamsFlash_t __buff; + +/* === Публичные функции ============================================ */ + +/** + * @brief Проверка свободности флеш + * @param h Указатель на handle параметров в Flash + * @return 1 — свободна, 0 — занята + */ +int ParamsFlash_IsFlashFree(ParamsFlashHandle_t *h) +{ + return flash_working || h->write_pending || h->sector[!h->sect_curr_ind].erase_pending; +} + +/** + * @brief Инициализация handle + * @param h Указатель на handle параметров в Flash + * @param sector_1 Номер первого сектора + * @param sector_2 Номер второго сектора + * @param buf_size Размер буфера данных, который будет сохранятся в Flash + * (размер структуры будет все равно @ref PARAMS_FLASH_MAX_BUFFER_SIZE, + * этот buf_size определяет сколько данных будет считано и упаковывано) + * @return 0 — успех, <0 — ошибка + */ +int ParamsFlash_HandleInit(ParamsFlashHandle_t *h, int sector_1, int sector_2, int buf_size) +{ + // переменные для инициалиазции секторов + unsigned int sector_size; + FlashBlockStatus_t sector_status = FLASH_BLOCK_UNKNOWN_ERR; + int sector_1_empty = 0; + int sector_2_empty = 0; + int long sector_1_max_block = 0; + int long sector_2_max_block = 0; + int sector_erase_flag = 0; + // переменные для поиска блока + volatile ParamsFlash_t *found_blk = NULL; + int i = 0; unsigned long j = 0; + + if(h == NULL) + { + return -1; + } + memset(h, 0, sizeof(ParamsFlashHandle_t)); + h->status = FLASH_BLOCK_UNKNOWN_ERR; + + + if(buf_size > PARAMS_FLASH_MAX_BUFFER_SIZE) + { + h->status = FLASH_BLOCK_INIT_ERR; + h->buffer_size = 0xFFFF; + return -1; + } + h->buffer_size = buf_size; + + + h->sector[0].sector_num = sector_1; + h->sector[1].sector_num = sector_2; + + for(i = 0; i < 2; i++) + { + h->sector[i].addr_start = ParamsFlash_GetSectorInfo(h->sector[i].sector_num, §or_size); + h->sector[i].addr_end = h->sector[i].addr_start + sector_size; + + if( (h->sector[i].addr_start < PARAMS_FLASH_ADDR_START) || + (h->sector[i].addr_start > PARAMS_FLASH_ADDR_END)) + { + h->status = FLASH_BLOCK_INIT_ERR; + h->sector[i].addr_start = 0xFFFFFFFF; + return -1; + } + + if( (h->sector[i].addr_end < PARAMS_FLASH_ADDR_START) || + (h->sector[i].addr_end > PARAMS_FLASH_ADDR_END) || + (h->sector[i].addr_end <= h->sector[i].addr_start)) + { + h->status = FLASH_BLOCK_INIT_ERR; + h->sector[i].addr_end = 0xFFFFFFFF; + return -1; + } + + h->sector[i].capacity = (h->sector[i].addr_end-h->sector[i].addr_start)/flash_block_size; + + __ParamsFlash_SwitchSector(h, i+1); // переключится на 1 или 2 сектор + if(__ParamsFlash_FindFreeSpace(h, PARAMS_LAST_BLOCK, &h->next_block_adr, NULL) == FLASH_BLOCK_FLASH_FULL) // инициализация h->next_block_adr + { + h->next_block_adr = h->sector[i].addr_end - 1; + } + } + + + // проверка ерейзнутости секторов + uint8_t tmp_data = (uint16_t)(~EMPTY_MEMORY & 0xFF); + __ParamsFlash_SwitchSector(h, 1); // переключится на 1 сектор + for(j = h->addr_start; j < h->addr_end; j++) + { + __READ_BYTE(j, &tmp_data, 1); + if(tmp_data != (EMPTY_MEMORY & 0xFF)) + { + // если сектор не пустой - начинаем проверяеть другой сектор + sector_1_empty = 0; + break; + } + sector_1_empty = 1; + } + __ParamsFlash_SwitchSector(h, 2); // переключится на 2 сектор + for(j = h->addr_start; j < h->addr_end; j++) + { + __READ_BYTE(j, &tmp_data, 1); + if(tmp_data != (EMPTY_MEMORY & 0xFF)) + { + // если сектор не пустой - сразу идем дальше + sector_2_empty = 0; + break; + } + sector_2_empty = 1; + } + + + // Если сектор 1 не пуст, а 2 пуст + if(!sector_1_empty && sector_2_empty) + { + __ParamsFlash_SwitchSector(h, 1); // переключится на 1 сектор + } + // Если сектор 2 не пуст, а 1 пуст + else if(sector_1_empty && !sector_2_empty) + { + __ParamsFlash_SwitchSector(h, 2); // переключится на 2 сектор + } + // Если оба сектора не пустые - смотрим в каком более свежие данные + else if(!sector_1_empty && !sector_2_empty) + { + sector_erase_flag = 1; // один из секторов будет стерт + // нет учета переполнения... типа старые данные 65535, а 1 - новые данные + // хз что пока делать + __ParamsFlash_SwitchSector(h, 1); // переключится на 1 сектор + sector_status = __ParamsFlash_FindBlock(h, PARAMS_LAST_BLOCK, NULL, &found_blk); + if(sector_status == FLASH_BLOCK_OK && found_blk != NULL) { + sector_1_max_block = found_blk->numb; + } + + __ParamsFlash_SwitchSector(h, 2); // переключится на 2 сектор + sector_status = __ParamsFlash_FindBlock(h, PARAMS_LAST_BLOCK, NULL, &found_blk); + if(sector_status == FLASH_BLOCK_OK && found_blk != NULL) { + sector_2_max_block = found_blk->numb; + } + // если максимальный номер блока больше в втором сектора - он актуальнее + if(sector_2_max_block > sector_1_max_block) + { + __ParamsFlash_SwitchSector(h, 2); // переключится на 2 сектор + } + // если оба сектора нулевые - сбрасываем оба + else if ((sector_2_max_block == sector_1_max_block) && (sector_1_max_block == 0)) + { + sector_erase_flag = 2; // оба сектора будут стерты + } + // если нет - первый сектор актуальнее + else + { + __ParamsFlash_SwitchSector(h, 1); // переключится на 1 сектор + } + } + // Если оба сектора пустые - берем первый + else if(sector_1_empty && sector_2_empty) + { + __ParamsFlash_SwitchSector(h, 1); // переключится на 1 сектор + } + + + h->status = FLASH_BLOCK_OK; + h->initialized = 1; + + if(sector_erase_flag) + { + sector_erase_flag--; + ParamsFlash_Init(h); // стирание другого сектора + } + + + if(sector_erase_flag) + { + __ParamsFlash_SwitchSector(h, 0); // переключится на другой сектор + ParamsFlash_Init(h); // стирание другого сектора + } + + return 0; +} + +/** + * @brief Запись блока во Flash + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для записи, @ref PARAMS_LAST_BLOCK — записать в следующий после последнего + * @param Buffer Буфер данных для записи + * @param size Размер данных + * @return >=0 — номер записанного блока, <0 — ошибка + */ +int ParamsFlash_WriteBlock(ParamsFlashHandle_t *h, int block_num, const uint16_t *Buffer, int size) +{ + FlashBlockStatus_t local_status = FLASH_BLOCK_UNKNOWN_ERR; + // переменные для поиска блока + uint32_t free_addr = 0; + uint16_t free_numb = 0; + uint16_t switch_to_new_sector = 0; + + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } + if(h->write_pending) + { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + + if(!Buffer || size==0 || size>PARAMS_FLASH_MAX_BUFFER_SIZE) + { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } +#if defined(TRACE_WRITE_READ) + LED1_TRACE_ENTER(); +#endif + local_status = __ParamsFlash_FindFreeSpace(h, block_num, &free_addr, &free_numb); + + // если сектор заполнен - переходим на другой + if(local_status == FLASH_BLOCK_FLASH_FULL) + { + switch_to_new_sector = 1; + __ParamsFlash_SwitchSector(h, 0); + local_status = __ParamsFlash_FindFreeSpace(h, free_numb, &free_addr, &free_numb); + } + + if(local_status != FLASH_BLOCK_OK) + { + h->status = local_status; + return -1; + } + //LED1_TRACE_ENTER(); + // Подготавливаем блок для записи + memset(&h->blk_tmp, 0, sizeof(h->blk_tmp)); + h->blk_tmp.numb = free_numb; + h->blk_tmp.size = size; + memcpy(h->blk_tmp.data, Buffer, size); + h->blk_tmp.crc16 = 0; // Обнуляем навсякий + h->blk_tmp.crc16 = crc16((uint8_t*)&h->blk_tmp, flash_block_size-2); + + __ParamsFlash_VerifyBlock(&h->blk_tmp); + h->write_pending = 1; + +#if !defined(FLASH_WORKING_NONBLOCKING) + if(FLASH_WORKING_CHECK(h)) + { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + FLASH_WORKING_ENTER(h); + // Записываем блок во Flash + + if(__WRITE_CURR_BLOCK(&h->blk_tmp, free_addr, flash_block_size) != HAL_OK) + { + h->status = FLASH_BLOCK_WRITE_ERR; + FLASH_WORKING_EXIT(h); + return -1; + } + FLASH_WORKING_EXIT(h); + + h->next_block_adr = free_addr + flash_block_size; + h->write_pending = 0; +#endif + + // Если перешли на новый сектор - старый стираем + if(switch_to_new_sector) + { + if(ParamsFlash_Init(h)) + { + return -1; + } + } + +#if defined(TRACE_WRITE_READ) + LED1_TRACE_EXIT(); +#endif + + + h->status = FLASH_BLOCK_OK; + return h->blk_tmp.numb; +} + + +/** + * @brief Чтение блока из Flash + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для считывания, @ref PARAMS_LAST_BLOCK - считать последний. + * @param Buffer Буфер куда сохранить считанные данные. + * @param size Максимальный размер буфера в словах. + * @param read_size Указатель на переменную для сохранения кол-ва считанных слов (опционально). + * @return >=0 — номер считанного блока, <0 — ошибка + * + * @details + * - Если block_num == PARAMS_LAST_BLOCK, читается последний валидный блок в памяти. + * - Если block_num != PARAMS_LAST_BLOCK, читается именно этот блок. + * - CRC проверяется перед копированием, при ошибке возвращается <0. + */ +int ParamsFlash_ReadBlock(ParamsFlashHandle_t *h, int block_num, uint16_t *Buffer, int size, int *read_size) +{ + FlashBlockStatus_t local_status = FLASH_BLOCK_UNKNOWN_ERR; + // переменные для поиска блока + uint32_t found_addr = 0; + volatile ParamsFlash_t *found_blk = NULL; + // сколько данных копировать + uint16_t to_copy = 0; + + + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } + + if ((Buffer == NULL) || (size == 0) || (size>PARAMS_FLASH_MAX_BUFFER_SIZE)) + { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } +#if defined(TRACE_WRITE_READ) + LED1_TRACE_ENTER(); +#endif + + local_status = __ParamsFlash_FindBlock(h, block_num, &found_addr, &found_blk); + + if (local_status != FLASH_BLOCK_OK) + { + h->status = local_status; +#if defined(TRACE_WRITE_READ) + LED1_TRACE_EXIT(); +#endif + return -1; + } + + //LED1_TRACE_ENTER(); + to_copy = (found_blk->size < (uint16_t)size) ? found_blk->size : size; + memcpy((void*)Buffer, (uint16_t*)found_blk->data, to_copy); + + if (read_size) + *read_size = to_copy; + +#if defined(TRACE_WRITE_READ) + LED1_TRACE_EXIT(); +#endif + h->status = FLASH_BLOCK_OK; + return found_blk->numb; +} + +/** + * @brief Упаковка Flash: запись последнего блока в второй сектор и стирание первого сектора + * @param h Указатель на handle параметров в Flash + * @param force Принудительно очистить Flash, даже если не удалось считать блок + * @return 0 — успех, <0 — ошибка + */ +int ParamsFlash_Pack(ParamsFlashHandle_t *h, int force) +{ + // номер записанного блока / если <0 ошибка + int pack_numb = 0; // Номер блока для упаковки + int pack_size = 0; // Размер блока для упаковки + int recheck_sector = 0; // Номер блока для упаковки + + int numb_rewrite = 0; + + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } + + pack_numb = ParamsFlash_ReadBlock(h, PARAMS_LAST_BLOCK, h->blk_tmp.data, h->buffer_size, &pack_size); + + if(force == 0) + { + if(pack_numb < 0) + { + return -1; + } + } + + __ParamsFlash_SwitchSector(h, 0); // переходим на новый сектор + + recheck_sector = h->sect_curr_ind; + if(pack_numb >= 0) + { + numb_rewrite = ParamsFlash_WriteBlock(h, pack_numb, h->blk_tmp.data, pack_size); + if(numb_rewrite < 0) + { + return -1; + } + } + + + if(recheck_sector == h->sect_curr_ind) // стираем сектор, если он НЕ поменялся в ParamsFlash_WriteBlock + { + if(ParamsFlash_Init(h)) // стирание старого сектора + { + return -1; + } + } + + h->status = FLASH_BLOCK_OK; + return 0; +} + +/** + * @brief Инициализация сектора Flash (стирание) + * @param h Указатель на handle параметров в Flash + * @return 0 — успех, <0 — ошибка + * @details Инициирует стирание сектора Flash, который сейчас не используется + */ +int ParamsFlash_Init(ParamsFlashHandle_t *h) +{ + // переменные для определения что ерейзить +#if !defined(FLASH_WORKING_NONBLOCKING) + uint32_t current_addr = 0; + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } +#else + if(__ParamsFlash_CheckHandle(h, 0)) + { + return -1; + } +#endif + + h->sector[!h->sect_curr_ind].erase_pending = 1; +#ifndef SETTINGS_USE_MEMORY_EEPROM + if(h->sector[!h->sect_curr_ind].erase_pending) + { + if(MEMSPI_FLASH_Erase(&memory_spi, h->sector[!h->sect_curr_ind].addr_start, 1, 5000, 1) != HAL_OK) + { + h->status = FLASH_BLOCK_WRITE_ERR; + FLASH_WORKING_EXIT(h); + return -1; + } + } + h->sector[!h->sect_curr_ind].erase_pending = 0; +#endif + h->status = FLASH_BLOCK_OK; + return 0; +} + + + +/** + * @brief Проверка блока на корректность + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для проверки, @ref PARAMS_LAST_BLOCK — последний инициализированный блок (с записанным numb) + * @param last_valid_num Указатель на переменную куда сохранится номер последнего корректного блока + * @return Статус блока Flash @ref FlashBlockStatus_t + */ +FlashBlockStatus_t ParamsFlash_VerifyBlock(ParamsFlashHandle_t *h, int block_num, int *last_valid_num) +{ + FlashBlockStatus_t local_status = FLASH_BLOCK_UNKNOWN_ERR; + // переменные для поиска блока + uint32_t addr = 0; + uint16_t last_correct_num = 0; + uint32_t found_addr = 0; + volatile ParamsFlash_t *found_blk = NULL; + // переменная для цикла while + volatile ParamsFlash_t *cur = NULL; + + + if(__ParamsFlash_CheckHandle(h, 1)) + { + if(h) + return h->status; + else + return FLASH_BLOCK_INIT_ERR; + } + + local_status = __ParamsFlash_FindBlock(h, block_num, &found_addr, &found_blk); + h->status = local_status; + + // Если не требуется возвращать last_valid_num - выходим сразу + if (last_valid_num == NULL) { + return local_status; + } + + if(local_status == FLASH_BLOCK_OK) + { + // Запрошенный блок корректен - возвращаем его номер + if(found_blk != NULL) + *last_valid_num = found_blk->numb; + } + else // блок некорректен + { + if(block_num == PARAMS_LAST_BLOCK) // Запросили последний, но он некорректен - ищем предыдущий корректный + { + addr = h->addr_start; + last_correct_num = 0; + + while(addr + flash_block_size <= h->addr_end) + { + __SET_CURR_BLOCK(cur, addr); + local_status = __ParamsFlash_VerifyBlock(cur); + + if(local_status == FLASH_BLOCK_OK && cur->numb > last_correct_num) + { + last_correct_num = cur->numb; + } + + addr += flash_block_size; + } + + *last_valid_num = last_correct_num; + } + else // Запросили конкретный блок и он некорректен + { + // последний корректный не ищем - просто устанавливаем в 0xFFFF + *last_valid_num = 0xFFFF; + } + + } + return local_status; +} + + + +/** + * @brief Сравнение данных с блоком из Flash + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для сравнения, @ref PARAMS_LAST_BLOCK - сравнить последний. + * @param data Указатель на данные + * @param size Размер данных + * @return 0 — совпадает, 1 — отличается, <0 - ошибка + */ +int ParamsFlash_Compare(ParamsFlashHandle_t *h, int block_num, const uint16_t *data, int size) +{ + // параметры блока + int real_size = 0; + int numb = 0; + // результат сравнения + int result = 0; + + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } + + if (data == NULL || size == 0 || size > PARAMS_FLASH_MAX_BUFFER_SIZE || (uint16_t)size > h->buffer_size) { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } + + numb = ParamsFlash_ReadBlock(h, block_num, h->blk_tmp.data, h->buffer_size, &real_size); + + if((numb < 0) || (real_size <= 0) || (real_size < size)) + { + return -1; + } + + result = memcmp(h->blk_tmp.data, data, size); + h->status = FLASH_BLOCK_OK; + return (result != 0); +} + + + +/** + * @brief Получение статистики по блокам + * @param h Указатель на handle параметров в Flash + * @param totalBlocks Общее число блоков + * @param validBlocks Количество блоков с корректным CRC + * @param invalidBlocks Количество блоков с некорректным CRC + * @return 0 — успех, <0 — ошибка + */ +int ParamsFlash_GetInfo(ParamsFlashHandle_t *h, int *total, int *valid, int *invalid) +{ + FlashBlockStatus_t local_status = FLASH_BLOCK_UNKNOWN_ERR; + // переменные для просмотра блоков + uint32_t addr = 0; + volatile ParamsFlash_t *blk = NULL; + // счетчики для статистики + uint16_t t=0, v=0, i=0; + + if(__ParamsFlash_CheckHandle(h, 1)) + { + return -1; + } + + addr = h->addr_start; + + while(addr + flash_block_size <= h->addr_end) + { + __SET_CURR_BLOCK(blk, addr); + if(blk->numb == EMPTY_MEMORY && blk->size == EMPTY_MEMORY) break; + + t++; + local_status = __ParamsFlash_VerifyBlock(blk); + if(local_status == FLASH_BLOCK_OK) + v++; + else + i++; + addr += flash_block_size; + } + + if(total) + *total = t; + if(valid) + *valid = v; + if(invalid) + *invalid = i; + + h->status = FLASH_BLOCK_OK; + return 0; +} + + +/* === Асинхронные функции ============================================ */ + + + +/** + * @brief Обработчик неблокирующей работы модуля + * @param h Указатель на handle параметров в Flash + * @details Должен вызываться периодически, в main while(1) или по таймеру + */ +int ParamsFlash_AsynchHandle(ParamsFlashHandle_t *h) +{ + int write_status = 0; + int erase_status = 0; + +#if defined(TRACE_ASYNCH) + LED1_TRACE_ENTER(); +#endif + +#if defined(FLASH_WORKING_NONBLOCKING) + write_status = __ParamsFlash_WriteHandle(h); + erase_status = __ParamsFlash_EraseHandle(h); + +#endif //FLASH_WORKING_NONBLOCKING + + +#if defined(TRACE_ASYNCH) + LED1_TRACE_EXIT(); +#endif + + if(!write_status && !erase_status) // все по нулям + return 0; + else if((write_status < 0) || (erase_status < 0)) // если у кого-то ошибка + return -1; + else + return 1; +} + + + + +/* === Внутренние функции ============================================ */ + +/** + * @brief Проверка структуры @ref ParamsFlashHandle_t + * @param h Указатель на handle параметров в Flash + * @param check_busy Проверять занятость флеш + * @return 0 — успех, <0 — ошибка + */ +static int __ParamsFlash_CheckHandle(ParamsFlashHandle_t *h, int check_busy) +{ + if(h == NULL) + { + return -1; + } + h->status = FLASH_BLOCK_UNKNOWN_ERR; + if(!h->initialized) // неинициализировная структура + { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } + if(check_busy) + { + if(FLASH_WORKING_CHECK(h)) // флеш занята + { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + } + + if( (h->addr_start != h->sector[h->sect_curr_ind].addr_start) || // поехали адреса сектора + (h->addr_end != h->sector[h->sect_curr_ind].addr_end) ) + { + if(__ParamsFlash_SwitchSector(h, h->sect_curr_ind+1)) // обновляем их + { + h->status = FLASH_BLOCK_INIT_ERR; // не получилось обновить + return -1; + } + } + + return 0; +} + +/** + * @brief Переключение на следующий сектор + * @param h Указатель на handle параметров в Flash + * @param force_ind Принудительно переключить на 1 или 2 сектор. 0 для автоматического выбора + * @return 0 — успех, <0 — ошибка + */ +static int __ParamsFlash_SwitchSector(ParamsFlashHandle_t *h, int force_ind) +{ + int next_sector_idx = 0; + if(h == NULL) + return -1; + if((force_ind > 2) || (force_ind < 0)) + return -1; + + + // Автоматическое переключение + if(force_ind == 0) + { + // Определяем индекс следующего сектора инверсией текущего сектора + next_sector_idx = !h->sect_curr_ind; + } + // Принудительное переключение по номеру (1 или 2) + else + { + // Определяем индекс следующего сектора по переданному индексу + next_sector_idx = force_ind - 1; + } + + // Обновляем сектор из основной структуры + h->sector[h->sect_curr_ind].next_block_adr = h->next_block_adr; + + // Обновляем основную структуру из сектора + h->addr_start = h->sector[next_sector_idx].addr_start; + h->addr_end = h->sector[next_sector_idx].addr_end; + h->next_block_adr = h->sector[next_sector_idx].next_block_adr; + + h->sect_curr_ind = next_sector_idx; + return 0; +} + + +/** + * @brief Внутренняя функция поиска блока по номеру + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для поиска, @ref PARAMS_LAST_BLOCK — последний инициализированный блок (numb != EMPTY_MEMORY) + * @param found_addr Возвращаемый адрес найденного блока + * @param found_blk Возвращаемый указатель на найденный блок + * @return Статус поиска блока + * @code Алгоритм + * 1. Если next_block_adr корректен - начинаем поиск с предыдущего блока и идем назад + * 2. Иначе - начинаем с начала сектора и идем вперед + * 4. Для PARAMS_LAST_BLOCK ищется последний валидный блок (который дальше всего от adr_start) + * 5. Для конкретного номера - ищется блок с этим номером + * @endcode + */ +static FlashBlockStatus_t __ParamsFlash_FindBlock(ParamsFlashHandle_t *h, uint16_t block_num, + uint32_t *found_addr, volatile ParamsFlash_t **found_blk) +{ + // переменные для последнего найденного блока + volatile uint32_t last_init_addr = 0; + volatile ParamsFlash_t *last_init_blk = NULL; + volatile FlashBlockStatus_t last_init_status = FLASH_BLOCK_NOT_FOUND; + // переменные для поиска блока + int block_finded_flag = 0; + uint32_t addr = 0; + int search_from_start = 0; + volatile ParamsFlash_t *cur = NULL; + FlashBlockStatus_t st = FLASH_BLOCK_NOT_FOUND; + + + if(h == NULL) + { + return FLASH_BLOCK_INIT_ERR; + } +#if defined(TRACE_FIND_BLOCK) + LED1_TRACE_ENTER(); +#endif + + // Если next_block_adr корректен + if((h->next_block_adr > h->addr_start) && // в диапаоне сектора, но не равен addr_start + (h->next_block_adr <= h->addr_end) && + (((h->next_block_adr - h->addr_start) % flash_block_size) == 0)) // выравивание на начале блока + { + addr = h->next_block_adr - flash_block_size; + search_from_start = 0; + } + else + { + addr = h->addr_start; + search_from_start = 1; + } + + if(found_addr) + *found_addr = 0; + if(found_blk) + *found_blk = NULL; + + while( (addr >= h->addr_start) && + (addr + flash_block_size <= h->addr_end) ) + { + __SET_CURR_BLOCK(cur, addr); + st = __ParamsFlash_VerifyBlock(cur); + + if(st != FLASH_BLOCK_EMPTY) + { + // Если ищем последний + if(block_num == PARAMS_LAST_BLOCK) + { + // запоминаем валидный блок + if(st == FLASH_BLOCK_OK) + { + last_init_addr = addr; + last_init_blk = cur; + last_init_status = st; + if(!search_from_start) // если ищем с конца + break; // сразу выходим, т.к. мы начали с конца - и первый найденый будет последним от adr_start + } + } + // Если ищем кокнретный - запоминаем конкретный инициализировнный (он может быть невалидным) + else if(cur->numb == block_num) + { + // запоминаем любой блок + last_init_addr = addr; + last_init_blk = cur; + last_init_status = st; + // если блок валидный - возвращаем сразу + if(last_init_status == FLASH_BLOCK_OK) + { + break; + } + } + } + + if(search_from_start) + addr += flash_block_size; + else + addr -= flash_block_size; + } + +#if defined(TRACE_FIND_BLOCK) + LED1_TRACE_EXIT(); +#endif + + // если ищем последний блок и нашли валидный - возвращаем его + if((block_num == PARAMS_LAST_BLOCK) && (last_init_status == FLASH_BLOCK_OK)) + { + block_finded_flag = 1; + } + // если ищем конкретный блок и нашли ЛЮБОЙ - возвращаем его + else if((block_num != PARAMS_LAST_BLOCK) && (last_init_status != FLASH_BLOCK_NOT_FOUND)) + { + block_finded_flag = 1; + } + + + if(block_finded_flag) + { // заполняем внешние переменные для блока + __SET_CURR_BLOCK(cur, last_init_addr); + if(found_addr) + *found_addr = last_init_addr; + if(found_blk) + *found_blk = last_init_blk; + return last_init_status; + } + else + { + return FLASH_BLOCK_NOT_FOUND; + } +} + +/** + * @brief Внутренняя функция поиска свободного места для записи + * @param h Указатель на handle параметров в Flash + * @param block_num Номер блока для записи, @ref PARAMS_LAST_BLOCK — следующий после последнего + * @param free_addr Возвращаемый адрес свободного места + * @param free_numb Возвращаемый номер для нового блока + * @return Статус поиска + * @code Алгоритм + * 1. Если next_block_adr корректен - начинаем поиск с него и идем вперед + * 2. Иначе - начинаем с начала сектора и идем вперед + * 4. Поиск идет до тех пор, пока не будет найден пустой блок + * @endcode + */ +static FlashBlockStatus_t __ParamsFlash_FindFreeSpace(ParamsFlashHandle_t *h, uint16_t block_num, + uint32_t *free_addr, uint16_t *free_numb) +{ + // переменные для последнего найденного блока + int64_t first_free_addr = -1; + uint16_t found_free_numb = block_num; + // переменные для поиска блока + uint32_t addr = 0; + uint16_t last_numb = 0; + volatile ParamsFlash_t *cur = NULL; + FlashBlockStatus_t st = FLASH_BLOCK_NOT_FOUND; + + if(h == NULL) + { + return FLASH_BLOCK_INIT_ERR; + } + if(block_num == 0) + return FLASH_BLOCK_INIT_ERR; + +#if defined(TRACE_FIND_BLOCK) + LED1_TRACE_ENTER(); +#endif + + // Если next_block_adr корректен + if((h->next_block_adr > h->addr_start) && // в диапаоне сектора, но не равен addr_start + (h->next_block_adr <= h->addr_end) && + (((h->next_block_adr - h->addr_start) % flash_block_size) == 0)) // выравивание на начале блока + { + addr = h->next_block_adr - flash_block_size; // захватываем предыдущий блок чтобы узнать его номер и инкрементировать + } + else + { + addr = h->addr_start; + } + + + if(free_addr) + *free_addr = 0; + if(free_numb) + *free_numb = PARAMS_LAST_BLOCK; + + while(addr + flash_block_size < h->addr_end) + { + __SET_CURR_BLOCK(cur, addr); + st = __ParamsFlash_VerifyBlock(cur); + + if(st == FLASH_BLOCK_EMPTY) + { + if(first_free_addr == -1) + { + first_free_addr = addr; + if(block_num == PARAMS_LAST_BLOCK) + { + found_free_numb = last_numb + 1; + if(found_free_numb >= INT16_MAX) // если номер переполнен + { + found_free_numb = 1; // зацикливаем + } + break; + } + } + } + else + { + // Проверяем, не существует ли уже блок с таким номером + // Проверка устарела, т.к. оно не просматривает весь блок. Поэтмоу по сути + // при валидном next_block_adr оно всегда будет верно. Т.к. все блоки после next_block_adr - пустые + if(block_num != PARAMS_LAST_BLOCK && cur->numb == block_num) + { + return FLASH_BLOCK_ALREADY_EXIST; + } + last_numb = cur->numb; + } + + addr += flash_block_size; + } + +#if defined(TRACE_FIND_BLOCK) + LED1_TRACE_EXIT(); +#endif + + if(first_free_addr == -1) + { + if(free_numb) + { + *free_numb = last_numb = cur->numb; + } + return FLASH_BLOCK_FLASH_FULL; + } + + if(free_addr) + *free_addr = (uint32_t)first_free_addr; + if(free_numb) + *free_numb = found_free_numb; + return FLASH_BLOCK_OK; +} + +/** + * @brief Внутренняя проверка блока на корректность + * @param blk Указатель на блок ParamsFlash_t + * @return + * - @ref FLASH_BLOCK_OK — блок корректен + * - @ref FLASH_BLOCK_EMPTY — блок не инициализирован + * - @ref FLASH_BLOCK_CRC_ERROR — CRC не совпадает + */ +static FlashBlockStatus_t __ParamsFlash_VerifyBlock(volatile ParamsFlash_t *blk) +{ + uint16_t crc = 0; + uint16_t empty_cnt = 0; + uint16_t *blk_uintptr = NULL; + int i = 0; + + if(blk == NULL) + return FLASH_BLOCK_INIT_ERR; + + blk_uintptr = (uint16_t*)blk; + for(i = 0; i < flash_block_size/2; i++) + { + if(blk_uintptr[i] == EMPTY_MEMORY) + empty_cnt++; + } + if(empty_cnt == flash_block_size/2) + return FLASH_BLOCK_EMPTY; + +// if(blk->numb == EMPTY_MEMORY && blk->size == EMPTY_MEMORY) +// return FLASH_BLOCK_EMPTY; + + crc = crc16((uint8_t*)blk, flash_block_size-2); + if(crc != blk->crc16) + return FLASH_BLOCK_CRC_ERROR; + + return FLASH_BLOCK_OK; +} + + +/* === Внутренние асинхронные функции ==================================== */ + +#if defined(FLASH_WORKING_NONBLOCKING) + +/** + * @brief Обработчик неблокирующей записи в Flash + * @param h Указатель на handle параметров в Flash + * @return 0 — запись завершена, 1 — запись в процессе, <0 — ошибка + * @code Алгоритм + * 1. Проверяется что флеш свободна + * 2. Если выставлен флаг для записи write_pending - инициализируется её начало + * 3. При каждом вызове записывается блок размером PARAMS_FLASH_ASYNCH_BUFFER_SIZE + * 4. Когда будет записан весь блок - сбрасывается флаги + * @endcode + */ +static int __ParamsFlash_WriteHandle(ParamsFlashHandle_t *h) +{ + static ParamsFlashHandle_t* current_handle = NULL; // текущий обратаываемый handle + // размер подблока для записи. Каждый вызов __ParamsFlash_WriteHandle будет записывать это количество слов + static uint16_t sub_block_size = PARAMS_FLASH_ASYNCH_BUFFER_SIZE; + // переменные для контроля записи + uint16_t *blk_ptr = NULL; // вспомогательный указатель на блок для записи + static uint32_t current_addr = 0; + static uint32_t current_block_size = 0; + static int write_in_progress = 0; + + if(h == NULL) + { + return -1; + } + if(!h->initialized) // неинициализировная структура + { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } + /* Если нет активного write - выходим */ + if(!h->write_pending) + { + return 0; + } + /* Если есть write_in_progress у другого handle - выходим*/ + if(write_in_progress && current_handle != h) { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + + /* Проверяем статус аппаратной занятости флеш */ + if(flash_toggle_bit_check(current_addr)) + { + h->status = FLASH_BLOCK_BUSY; + return 1; // операция еще не завершена + } + + /* Если операция только начинается - инициализируем */ + if(!write_in_progress) + { + if(FLASH_WORKING_CHECK(h)) // программная занятость флеш + { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + FLASH_WORKING_ENTER(h); + + /* Запуск WRITE */ + current_handle = h; + current_addr = h->next_block_adr; + write_in_progress = 1; + h->write_word_cnt = 0; + } + + // Записываем блок во Flash + // определяем размер блока на иттерации + if((flash_block_size - h->write_word_cnt) > sub_block_size) + { + current_block_size = sub_block_size; // записываем подблок целиком + } + else + { + current_block_size = flash_block_size - h->write_word_cnt; // записываем оставшиется слова (меньше подблока) + } + + // записываем подблок + blk_ptr = (uint16_t*)&h->blk_tmp; + if(RunFlashData((unsigned long)&blk_ptr[h->write_word_cnt], current_addr, current_block_size, 0)) + { + h->status = FLASH_BLOCK_WRITE_ERR; + FLASH_WORKING_EXIT(h); + write_in_progress = 0; + return -1; + } + + h->write_word_cnt += current_block_size; + current_addr += current_block_size; + + if(h->write_word_cnt < flash_block_size) + { + return 1; // записываем до тех пор пока не запишем весь блок + } + + FLASH_WORKING_EXIT(h); + current_handle = NULL; + h->next_block_adr = current_addr; + h->write_pending = 0; + write_in_progress = 0; + return 0; +} + + +/** + * @brief Обработчик неблокирующего стирания Flash + * @param h Указатель на handle параметров в Flash + * @return 0 — стирание завершено, 1 — стирание в процессе, <0 — ошибка + * @details Стирается неактивный сектор + * @code Алгоритм + * 1. Проверяется что флеш свободна + * 2. Если выставлен флаг для ерейза неактивного сектора erase_pending - инициализируется начало ерейза + * 3. При каждом вызове проверяется состояние флеш (toggle bit) + * 4. Когда ерейз завершен - сбрасываются флаги + * @endcode + */ +static int __ParamsFlash_EraseHandle(ParamsFlashHandle_t *h) +{ + static ParamsFlashHandle_t* current_handle = NULL; // текущий обратаываемый handle + static uint32_t current_addr = 0; + static int erase_in_progress = 0; + + if(h == NULL) + { + return -1; + } + if(!h->initialized) // неинициализировная структура + { + h->status = FLASH_BLOCK_INIT_ERR; + return -1; + } + /* Если нет активного erase - выходим */ + if(!h->sector[!h->sect_curr_ind].erase_pending) + { + return 0; + } + /* Если есть erase_in_progress у другого handle - выходим*/ + if(erase_in_progress && current_handle != h) { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + + /* Если операция только начинается - инициализируем */ + if(!erase_in_progress) + { + /* Проверяем статус занятости флеш */ + if(flash_toggle_bit_check(h->sector[!h->sect_curr_ind].addr_start)) + { + h->status = FLASH_BLOCK_BUSY; + return 1; // операция еще не завершена + } + + if(FLASH_WORKING_CHECK(h)) // программная занятость флеш + { + h->status = FLASH_BLOCK_BUSY; + return -1; + } + FLASH_WORKING_ENTER(h); + + /* Запуск ERASE */ + current_handle = h; + current_addr = h->sector[!h->sect_curr_ind].addr_start; + erase_in_progress = 1; + if(flash_erase_start_abs_address(current_addr)) + { + h->status = FLASH_BLOCK_WRITE_ERR; + FLASH_WORKING_EXIT(h); + erase_in_progress = 0; + current_addr = 0; + return -1; + } + } + + /* Проверяем статус текущей операции стирания */ + if(flash_toggle_bit_check(current_addr)) + { + return 1; // операция еще не завершена + } + + + /* Если дошли до конца - завершаем операцию */ + FLASH_WORKING_EXIT(h); + current_handle = NULL; + erase_in_progress = 0; + h->sector[!h->sect_curr_ind].erase_pending = 0; + h->sector[!h->sect_curr_ind].next_block_adr = h->sector[!h->sect_curr_ind].addr_start; + h->status = FLASH_BLOCK_OK; + return 0; // операция завершена +} + + +#endif diff --git a/Src/set_to_mem.c b/Src/set_to_mem.c new file mode 100644 index 0000000..1ee5bfa --- /dev/null +++ b/Src/set_to_mem.c @@ -0,0 +1,380 @@ +/** +************************************************************************** +* @file set_to_mem.c +* @brief Модуль для записи в память настроек. +*************************************************************************/ +#include "set_to_mem.h" + + + +/** + * @brief Инициализация одного массива настроек для хранения в памяти. + * + * @param array Указатель на структуру для записи настроек. + * @param pRealArray Указатель на реальные массива настроек. + * @param startadr Указатель на начальный адрес в памяти. + * @param sizeofarray Размер массива. + * @details Функция используется при @ref SETTINGS_USE_SETTINGS_FROM_POINTER + */ +HAL_StatusTypeDef Settings_AddArray(SettingsTypeDef *settings, uint8_t *pRealArray, uint32_t *startadr, uint32_t sizeofarray) +{ + if(!settings || !pRealArray || !startadr || !sizeofarray) + return HAL_ERROR; + + if(settings->setarr_count >= SETTINGS_MAX_ARRAYS) + return HAL_ERROR; + + SettingArrayTypeDef *array = &settings->setarr[settings->setarr_count]; + + array->adr = *startadr; // Устанавливаем адрес начала массива настроек + array->length = sizeofarray; // Устанавливаем размер массива настроек + array->real_ptr = pRealArray; + + +#ifdef SETTINGS_USE_SETTINGS_FROM_POINTER + array->mem_ptr = pRealArray; +#else + if(settings->buffer == NULL) + { + settings->buffer = malloc(sizeofarray); + if(!settings->buffer) + return HAL_ERROR; + array->mem_ptr = settings->buffer; + settings->settings_size = sizeofarray; + } + else + { + // расширяем буфер под новый массив + uint8_t *newbuf = realloc(settings->buffer, settings->settings_size + sizeofarray); + if(!newbuf) + return HAL_ERROR; + settings->buffer = newbuf; + array->mem_ptr = settings->buffer + settings->settings_size; + settings->settings_size += sizeofarray; + } +#endif + + *startadr += array->length; // Смещаем начальный адрес настроек + settings->setarr_count++; + return HAL_OK; +} + +HAL_StatusTypeDef Settings_Init(SettingsTypeDef *settings, SPI_HandleTypeDef *hspi, uint32_t adr) +{ + if(!settings) + return HAL_ERROR; + + settings->start_adr = adr; +// settings->setarr_count = 0; + settings->settings_error = 0; + settings->hmemspi = &memory_spi; + + MEMSPI_Base_Init(settings->hmemspi, hspi); + +#ifdef SETTINGS_USE_WEAR_LEVELING_FLASH + if(MEMSPI_FLASH_Protection(&memory_spi, 0, 0x11000, 1000, 1) != HAL_OK) + { + return HAL_ERROR; + } + + ParamsFlash_HandleInit(&settings->flash_handle, 0, 1, settings->settings_size); + + if(MEMSPI_FLASH_Protection(&memory_spi, 0, 0x11000, 1000, 1) != HAL_OK) + { + return HAL_ERROR; + } +#endif + return HAL_OK; +} + +/** + * @brief Запись настроек в память. + * + * @param settings Указатель на хендл для настроек. + * @details Записывает настройки в память, если установлен флаг обновления. + * Можно включить защиту @ref SETTINGS_MEMORY_PROTECT_ENABLE : добавляет защиту памяти перед записью и снимает её после. + */ +void Settings_WriteSettings(SettingsTypeDef *settings) +{ + if(!settings) + return; + + static int update_start = 0; + static uint32_t update_request_tick = 0; + HAL_StatusTypeDef res = HAL_OK; + + +#ifdef SETTINGS_USE_SETTINGS_FROM_BUFFER + // Сравниваем текущие настройки с буфером + for(int i = 0; i < settings->setarr_count; i++) + { + if(memcmp(settings->setarr[i].mem_ptr, + settings->setarr[i].real_ptr, + settings->setarr[i].length) != 0) + { + settings->update_settings_flag = 1; + break; + } + } +#endif + + if(settings->update_settings_flag) + { +// if(GPIO_Read_Switch(&MZKT_DISCIN.err_24V)) +// { +// printf_memspi_err("Power Err, cancel writing"); +// settings->update_settings_flag = 0; +// update_start = 0; +// return; +// } + if(msDelayDone(1000, &update_request_tick)) + { + update_start = 1; + } + } + else + { + msDelayStart(&update_request_tick); + update_start = 0; + } + + + if(update_start) + { + // Сбрасываем флаг обновления + settings->update_settings_flag = 0; + update_start = 0; + +#ifdef SETTINGS_USE_SETTINGS_FROM_BUFFER + // Обновляем буфер новыми настройками + for(uint8_t i = 0; i < settings->setarr_count; i++) + { + memcpy( settings->setarr[i].mem_ptr, + settings->setarr[i].real_ptr, + settings->setarr[i].length); + } +#endif + + // Записываем настройки в память + WriteSettingsToMem(settings); + + Settings_CheckSettings(settings); + __enable_irq(); + } +} + +/** + * @brief Чтение настроек из памяти. + * + * @param settings Указатель на хендл для настроек. + * @details Включает: + * - Чтение настроек из памяти (`ReadSettingsFromMem`). + * - Обновление буфера, если используется @ref SETTINGS_USE_SETTINGS_FROM_BUFFER. + */ +void Settings_ReadSettings(SettingsTypeDef *settings) +{ + if(!settings) + return; + + __disable_irq(); + ReadSettingsFromMem(settings); + +#ifdef SETTINGS_USE_SETTINGS_FROM_BUFFER + for(uint8_t i = 0; i < settings->setarr_count; i++) + { + memcpy(settings->setarr[i].real_ptr, + settings->setarr[i].mem_ptr, + settings->setarr[i].length); + } +#endif + __enable_irq(); + Settings_CheckSettings(settings); +} + +/** + * @brief Проверить настройки на валидность. + * + * @param settings Указатель на хендл для настроек. + * @details Проверяет не пустые ли настройки. Если пустые - выставялет флаг ошибки. + */ +void Settings_CheckSettings(SettingsTypeDef *settings) +{ + if(!settings) + return; + + uint32_t total_size = 0; + uint32_t ff_cnt = 0; + + for(uint8_t i = 0; i < settings->setarr_count; i++) + { + uint8_t *ptr = settings->setarr[i].mem_ptr; + uint32_t size = settings->setarr[i].length; + + total_size += size; + + for(uint32_t j = 0; j < size; j++) + { + if(ptr[j] == 0xFF) + ff_cnt++; + } + } + + if(total_size && ff_cnt > total_size * 0.8) + settings->settings_error |= MEMORY_ERROR_EMPTY; + else + settings->settings_error &= ~MEMORY_ERROR_EMPTY; + + printf_memspi("Zero Bytes: %u/%u", ff_cnt, total_size); +} + +/** + * @brief Записи настроек в память в зависимости от конфигурации. + * + * @param settings Указатель на хендл для настроек. + * @details Реализация зависит от конфигурации: + * - @ref SETTINGS_USE_SETTINGS_FROM_POINTER : запись по отдельным указателям на различные массивы настроек. + * - @ref SETTINGS_USE_SETTINGS_FROM_BUFFER : запись всего буфера настроек целиком. + */ +void WriteSettingsToMem(SettingsTypeDef *settings) +{ + HAL_StatusTypeDef res = HAL_OK; + + + + +#ifdef SETTINGS_USE_WEAR_LEVELING_FLASH + if(MEMSPI_FLASH_Protection(settings->hmemspi, 0, 0x11000, DISABLE, 1000) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + return; + } + + if(ParamsFlash_WriteBlock(&settings->flash_handle, PARAMS_LAST_BLOCK, + (uint16_t *)settings->buffer, + settings->settings_size) < 0) + { + res = HAL_ERROR; + } + + if(MEMSPI_FLASH_Protection(settings->hmemspi, 0, 0x11000, ENABLE, 1000) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + } +#else //SETTINGS_USE_WEAR_LEVELING_FLASH + + if(MEMSPI_FLASH_Protection(settings->hmemspi, settings->start_adr, settings->settings_size, DISABLE, 1000) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + return; + } + + for(uint8_t i = 0; i < settings->setarr_count; i++) + { + + WriteSettingsArrayToMem(settings, &settings->setarr[i]); + + } + + if(MEMSPI_FLASH_Protection(settings->hmemspi, settings->start_adr, settings->settings_size, DISABLE, 1000) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + return; + } +#endif //SETTINGS_USE_WEAR_LEVELING_FLASH + + + if(res != HAL_OK) + { + printf_memspi_err("Write Error: %u", res); + settings->settings_error |= MEMORY_ERROR_WRITE; + } + else + { + printf_memspi("Write Ok"); + settings->settings_error &= ~MEMORY_ERROR_WRITE; + } + +} +/** + * @brief Читает настройки из памяти в зависимости от конфигурации. + * + * @param settings Указатель на хендл для настроек. + * @details Реализация зависит от конфигурации: + * - @ref SETTINGS_USE_SETTINGS_FROM_POINTER : чтение по отдельным указателям на различные массивы настроек. + * - @ref SETTINGS_USE_SETTINGS_FROM_BUFFER : чтение всего буфера настроек целиком. + */ +void ReadSettingsFromMem(SettingsTypeDef *settings) +{ + HAL_StatusTypeDef res = HAL_OK; +#ifdef SETTINGS_USE_WEAR_LEVELING_FLASH + if(ParamsFlash_ReadBlock(&settings->flash_handle, PARAMS_LAST_BLOCK, + (uint16_t *)settings->buffer, + settings->settings_size, NULL) < 0) + { + res = HAL_ERROR; + } +#else //SETTINGS_USE_WEAR_LEVELING_FLASH + + for(uint8_t i = 0; i < settings->setarr_count; i++) + { + ReadSettingsArrayFromMem(settings, &settings->setarr[i]); + } +#endif //SETTINGS_USE_WEAR_LEVELING_FLASH + + if(res != HAL_OK) + { + printf_memspi_err("Read Error: %u", res); + } + else + { + printf_memspi("Read Ok"); + } + +} + +/** + * @brief Записывает массив настроек через указатель в память. + * + * @param settings Указатель на хендл для настроек. + * @param settingarr Указатель на структуру для записи массива настроек. + * @details Функция используется при @ref SETTINGS_USE_SETTINGS_FROM_POINTER + */ +void WriteSettingsArrayToMem(SettingsTypeDef *settings, SettingArrayTypeDef *settingarr) +{ +#ifndef SETTINGS_USE_MEMORY_EEPROM + MEMSPI_WriteInitTypeDef writeinit; + + writeinit.Data_Address = settingarr->adr; + writeinit.Data_Size = settingarr->length; + writeinit.Sector_Address = settings->start_adr; + writeinit.Sector_Size = settings->settings_size; + writeinit.fSavePrevoisData = 1; + writeinit.pDataPtr = settingarr->mem_ptr; + + if(MEMSPI_FLASH_Write(settings->hmemspi, &writeinit, 1000, 1) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + return; + } + +#else + if(MEMSPI_EEPROM_Write(settings->hmemspi, settingarr->adr, settingarr->mem_ptr, settingarr->length, 1000, 1) != HAL_OK) + { + settings->settings_error |= MEMORY_ERROR_WRITE; + return; + } +#endif +} + + +/** + * @brief Читает массив настроек через указатель в память. + * + * @param settings Указатель на хендл для настроек. + * @param settingarr Указатель на структуру для записи массива настроек. + * @details Функция используется при @ref SETTINGS_USE_SETTINGS_FROM_POINTER + */ +void ReadSettingsArrayFromMem(SettingsTypeDef *settings, SettingArrayTypeDef *settingarr) +{ + MEMSPI_Read_Memory(settings->hmemspi, settingarr->adr, settingarr->mem_ptr, settingarr->length, 1000); +}