/** ************************************************************************** * @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