Files
STM32_MemorySPI/Src/params_flash.c
Razvalyaev 643391038e Большой апгрейд:
- исправлены баги библиотеки memspi
- добавлены модули для сохранения настреок в eeprom и flash (с равномерным износом)
- надо тестить, проверять и рефакторить
2026-02-17 18:34:50 +03:00

1233 lines
43 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
**************************************************************************
* @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, &sector_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