Форум РадиоКот https://radiokot.ru/forum/ |
|
Проблема с записью в внутреннюю Flash на МК STM32F407VGT6 https://radiokot.ru/forum/viewtopic.php?f=59&t=197161 |
Страница 1 из 1 |
Автор: | Rudthaky [ Пт мар 14, 2025 11:56:18 ] |
Заголовок сообщения: | Проблема с записью в внутреннюю Flash на МК STM32F407VGT6 |
Здравствуй сообщество! Столкнулся с проблемой об которую бьюсь целую неделю, а решения так и не вижу. ![]() ![]() Так вот, в данном проекте, уже собралась целая куча вариантов записи в эту самую внутреннюю Flash-память. И все они действительно пишут значения в Flash. ![]() Но проблема в том, что после записи программы в контроллер (через китайский свисток st-link или стандартный программатор расположенный на плате STM32F4DISCOVERY Discovery kit) и повторной попытке отладчиком подключится к МК получаю следующую ошибку: ![]() Если с помощью STM32 ST-LINK Utility очистить МК или хотя бы 0 сектор, то при записи программы в отладочном режиме получаем следующее: ![]() Такая картина наблюдается при попытке записи/стирание в Flash. Чтение такого эффекта не вызывает. Вопрос, где я ошибся? Если лень качать проект, прилагаю функцию (одну из) в самом низу и саму функцию main(){..} и инициализации сделанные с помощью STM32Cube. Так же немного слов о скрипте компоновщика (для не внимательных). Скрипт я немного модифицировал, так как основная идея была создания bootloader-а с памятью под сам загрузчик, под данные о загрузчике/прошивке и выделение памяти под основную программу. Код: MEMORY { CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 32K BOOT_DATA (rx) : ORIGIN = 0x8008000, LENGTH = 16K APP (rx) : ORIGIN = 0x800C000, LENGTH = 976K } .boot_data 0x08008000 : { _smem = ORIGIN(BOOT_DATA); KEEP(*(.boot_data)) } > BOOT_DATA _siccmram = LOADADDR(.ccmram); Возможно, что-то здесь напутал ![]() В общем, люди добрые, не проходите мимо! Покажите неопытному "котенку", где он ошибся! Можно прям тапкам или носом потыкать ![]() Весь мозг уже себе вынес ![]() ![]() ![]() Архив проект на STM32CubeIDE сюда: https://wdfiles.ru/1tW67~d?c91db132b4e9 ... 2b7994ea1d Код: /** * @brief Стирает сектора в Flash-памяти. * @param sector Номер начального сектора. * @param nbSectors Количество секторов для стирания. * @param voltageRange Диапазон напряжения для операции стирания. * @return Код ошибки (0 - успех, иначе код ошибки). */ FlashError_t bl_flash_erase_main_application(uint32_t sector, uint32_t nbSectors, uint32_t voltageRange){ HAL_StatusTypeDef status; // Статус выполнения операций HAL FLASH_EraseInitTypeDef erase_init; // Структура для настройки стирания uint32_t sector_error = 0; // Переменная для хранения ошибки стирания // Проверка корректности параметров if (sector >= FLASH_SECTOR_TOTAL || nbSectors == 0 || (sector + nbSectors) > FLASH_SECTOR_TOTAL) { return FLASH_ERR_INVALID_PARAM; // Некорректные параметры } // Отключение прерываний для обеспечения атомарности операции __disable_irq(); // Разблокировка Flash-памяти status = HAL_FLASH_Unlock(); if(status != HAL_OK){ __enable_irq(); // Включение прерываний в случае ошибки return FLASH_ERR_UNLOCK_FAILED; // Ошибка разблокировки } // Настройка структуры для стирания сектора erase_init.TypeErase = FLASH_TYPEERASE_SECTORS; // Тип стирания: сектор erase_init.Sector = sector; // Номер сектора erase_init.NbSectors = nbSectors; // Количество секторов для стирания erase_init.VoltageRange = voltageRange; // Диапазон напряжения // Стирание сектора status = HAL_FLASHEx_Erase(&erase_init, §or_error); while((FLASH->SR & FLASH_SR_BSY)!= 0); if(status != HAL_OK || sector_error != 0xFFFFFFFF){ HAL_FLASH_Lock(); __enable_irq(); // Включение прерываний в случае ошибки return FLASH_ERR_ERASE_FAILED; // Ошибка стирания Flash-памяти } // Блокировка Flash-памяти HAL_FLASH_Lock(); // Включение прерываний после завершения операций __enable_irq(); return FLASH_OK; // Успешное завершение операции } Код: /** * @brief Записывает данные в Flash-память. * * Эта функция позволяет записывать данные в Flash-память с поддержкой записи байтами, 16-битными словами и 32-битными словами. * Перед записью необходимо убедиться, что сектор Flash-памяти был стерт. * * @param addres Адрес в Flash-памяти, по которому начнется запись. Должен быть выровнен в соответствии с типом данных. * @param data Указатель на массив данных для записи. * @param length Количество элементов данных для записи. * @param type Тип данных для записи: * - FLASH_TYPEPROGRAM_BYTE: Запись байтов (8 бит). * - FLASH_TYPEPROGRAM_HALFWORD: Запись 16-битных слов. * - FLASH_TYPEPROGRAM_WORD: Запись 32-битных слов. * @return FlashError_t: * - FLASH_OK: Успешная запись. * - FLASH_ERR_INVALID_PARAM: Некорректный адрес, длина или тип данных. * - FLASH_ERR_WRITE_FAILED: Ошибка записи в Flash-память. */ FlashError_t bl_flash_write(const uint32_t addres, const void* data, const uint32_t length, uint32_t type) { HAL_StatusTypeDef status; // Проверка на нулевой указатель данных if (data == NULL) { return FLASH_ERR_INVALID_DATA; } // Проверка корректности типа данных if (type != FLASH_TYPEPROGRAM_BYTE && type != FLASH_TYPEPROGRAM_HALFWORD && type != FLASH_TYPEPROGRAM_WORD) { return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Определение размера элемента данных uint32_t data_size; switch (type) { case FLASH_TYPEPROGRAM_BYTE: data_size = 1; break; case FLASH_TYPEPROGRAM_HALFWORD: data_size = 2; break; case FLASH_TYPEPROGRAM_WORD: data_size = 4; break; default: return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Проверка корректности адреса и длины const uint32_t FLASH_START = 0x08008000; // Начальный адрес разрешённой области // Проверка корректности адреса и длины if (addres < FLASH_START || addres + length * data_size - 1 > FLASH_END) { return FLASH_ERR_INVALID_PARAM; // Некорректный адрес или длина } // Проверка выравнивания адреса if (addres % data_size != 0) { return FLASH_ERR_INVALID_PARAM; // Адрес не выровнен } // Разблокировка Flash-памяти __disable_irq(); status = HAL_FLASH_Unlock(); if (status != HAL_OK) { __enable_irq(); return FLASH_ERR_UNLOCK_FAILED; } // Цикл записи for (uint32_t i = 0; i < length; i++) { uint32_t current_address = addres + i * data_size; uint32_t current_data; // Получение данных в зависимости от типа switch (type) { case FLASH_TYPEPROGRAM_BYTE: current_data = ((uint8_t*)data)[i]; break; case FLASH_TYPEPROGRAM_HALFWORD: current_data = ((uint16_t*)data)[i]; break; case FLASH_TYPEPROGRAM_WORD: current_data = ((uint32_t*)data)[i]; break; default: HAL_FLASH_Lock(); // Блокировка Flash-памяти в случае ошибки __enable_irq(); return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Запись данных while (FLASH->SR & FLASH_SR_BSY); status = HAL_FLASH_Program(type, current_address, current_data); if (status != HAL_OK) { HAL_FLASH_Lock(); // Блокировка Flash-памяти в случае ошибки __enable_irq(); return FLASH_ERR_WRITE_FAILED; // Ошибка записи } } // Блокировка Flash-памяти HAL_FLASH_Lock(); __enable_irq(); return FLASH_OK; // Успешная запись } в хедере: Код: #define BOOT_DATA_SECTOR_START 0x08008000 #define BOOT_DATA_SECTOR_END 0x0800BFFF #define BOOT_DATA_SECTOR FLASH_SECTOR_2 #define MAIN_APP_SECTOR_START FLASH_SECTOR_3 #define MAIN_APP_SECTOR_END FLASH_SECTOR_11 #define BOOT_FLAG_B 0xFFEECCBB typedef struct{ uint32_t magic; uint32_t version; uint32_t crc; uint32_t size; }BootData; // Определение кодов ошибок typedef enum { FLASH_OK = 0, FLASH_ERR_UNLOCK_FAILED = -1, FLASH_ERR_ERASE_FAILED = -2, FLASH_ERR_INVALID_PARAM = -3, FLASH_ERR_WRITE_FAILED = - 4, FLASH_ERR_INVALID_DATA = -5 } FlashError_t; функция main() {...} и инициализация: Код: // Переменная, размещенная в секции .boot_data сектор 2
__attribute__((section(".boot_data"))) BootData_1 bootaddress; /* Private function prototypes ----------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); BootData bootData; bootData.magic = 0x22AA55AA; // Пример значения для magic bootData.version = 1; // Версия bootData.size = sizeof(BootData); // Размер структуры bootData.crc = 0; FlashError_t write_status; Стираем необходимые сектора перед записью write_status = bl_flash_erase_main_application(2, 1, VOLTAGE_RANGE_3); // Например, стираем сектор 2 if (write_status != FLASH_OK) { __asm__("BKPT #0"); } write_status = bl_flash_write((uint32_t)&bootaddress, &bootData, sizeof(bootData), FLASH_TYPEPROGRAM_BYTE); if (write_status != FLASH_OK) { __asm__("BKPT #0"); } BootData readDataBoot; memcpy(&readDataBoot, (void*)&bootaddress, sizeof(bootData)); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 80; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } |
Автор: | GARMIN [ Сб мар 15, 2025 12:25:01 ] |
Заголовок сообщения: | Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT |
Текст на скринах не виден совершенно. Задача не понятна. Сверните код под спойлеры. |
Автор: | jcxz [ Сб мар 15, 2025 13:34:08 ] |
Заголовок сообщения: | Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT |
Для записи во flash этих STM32 нужно прочитать описание всего двух регистров: FLASH_SR и FLASH_CR. В которых всего-то с десяток полезных бит. И написать процедуру записи во флешь (вместе с чтением мануала на эти регистры) можно всего за час. Непонятно - на кой тут калокуб? ![]() Функцию: FlashError_t bl_flash_write(const uint32_t addres, const void* data, const uint32_t length, uint32_t type) { Выполняете откуда? Из ОЗУ? А все функции, которые она вызывает - где расположены? В ОЗУ или флешь? И можно ли выполнять код из флешь на вашем МК при записи стирании/флешь?Аналогично - функция стирающая флешь. Добавлено after 6 minutes 54 seconds: Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT6 // Запись данных Перед началом записи ждёте готовности флешь, а после HAL_FLASH_Program() - не ждёте. Если внутри HAL_FLASH_Program() сама ждёт завершения записи (опросом бита занятости флешь), то начальный цикл ожидания - не нужен. А если не ждёт, то выйти из своей функции можете когда флешь ещё занята. Что будет при этом с чтением из флеша исполняющегося кода программы? Видимо - будет сбой.
while (FLASH->SR & FLASH_SR_BSY); status = HAL_FLASH_Program(type, current_address, current_data); if (status != HAL_OK) { HAL_FLASH_Lock(); // Блокировка Flash-памяти в случае ошибки __enable_irq(); return FLASH_ERR_WRITE_FAILED; // Ошибка записи } } // Блокировка Flash-памяти HAL_FLASH_Lock(); __enable_irq(); return FLASH_OK; // Успешная запись }[/code] |
Автор: | Rudthaky [ Сб мар 15, 2025 21:46:49 ] |
Заголовок сообщения: | Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT |
Да вы абсолютно правы на счет оперативной памяти. Действительно адрес передаваемый в функцию записи по факту из оперативной памяти, заменил его: Код: #define BOOT_DATA_SECTOR_START 0x08008000 Теперь запись выглядит так: Код: write_status = bl_flash_write((uint32_t)BOOT_DATA_SECTOR_START, &bootData, sizeof(bootData), FLASH_TYPEPROGRAM_BYTE); if (write_status != FLASH_OK) { __asm__("BKPT #0"); } СпойлерКод: FlashError_t bl_flash_write(const uint32_t addres, const void* data, const uint32_t length, uint32_t type) { HAL_StatusTypeDef status; // Проверка на нулевой указатель данных if (data == NULL) { return FLASH_ERR_INVALID_DATA; } // Проверка корректности типа данных if (type != FLASH_TYPEPROGRAM_BYTE && type != FLASH_TYPEPROGRAM_HALFWORD && type != FLASH_TYPEPROGRAM_WORD) { return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Определение размера элемента данных uint32_t data_size; switch (type) { case FLASH_TYPEPROGRAM_BYTE: data_size = 1; break; case FLASH_TYPEPROGRAM_HALFWORD: data_size = 2; break; case FLASH_TYPEPROGRAM_WORD: data_size = 4; break; default: return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Проверка корректности адреса и длины const uint32_t FLASH_START = 0x08008000; // Начальный адрес разрешённой области // Проверка корректности адреса и длины if (addres < FLASH_START || addres + length * data_size - 1 > FLASH_END) { return FLASH_ERR_INVALID_PARAM; // Некорректный адрес или длина } // Проверка выравнивания адреса if (addres % data_size != 0) { return FLASH_ERR_INVALID_PARAM; // Адрес не выровнен } // Разблокировка Flash-памяти __disable_irq(); status = HAL_FLASH_Unlock(); if (status != HAL_OK) { __enable_irq(); return FLASH_ERR_UNLOCK_FAILED; } // Цикл записи for (uint32_t i = 0; i < length; i++) { uint32_t current_address = addres + i * data_size; uint32_t current_data; // Получение данных в зависимости от типа switch (type) { case FLASH_TYPEPROGRAM_BYTE: current_data = ((uint8_t*)data)[i]; break; case FLASH_TYPEPROGRAM_HALFWORD: current_data = ((uint16_t*)data)[i]; break; case FLASH_TYPEPROGRAM_WORD: current_data = ((uint32_t*)data)[i]; break; default: HAL_FLASH_Lock(); // Блокировка Flash-памяти в случае ошибки __enable_irq(); return FLASH_ERR_INVALID_PARAM; // Некорректный тип данных } // Запись данных status = HAL_FLASH_Program(type, current_address, current_data); if (status != HAL_OK) { HAL_FLASH_Lock(); // Блокировка Flash-памяти в случае ошибки __enable_irq(); return FLASH_ERR_WRITE_FAILED; // Ошибка записи } } // Блокировка Flash-памяти HAL_FLASH_Lock(); __enable_irq(); return FLASH_OK; // Успешная запись } и да, Вы правы, ждать не надо. Библиотека HAL все это уже делает за нас. Для пример возьмем стандартную функцию стирания памяти: СпойлерКод: HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError) { HAL_StatusTypeDef status = HAL_ERROR; uint32_t index = 0U; /* Process Locked */ __HAL_LOCK(&pFlash); /* Check the parameters */ assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase)); /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE); if (status == HAL_OK) { /*Initialization of SectorError variable*/ *SectorError = 0xFFFFFFFFU; if (pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE) { /*Mass erase to be done*/ FLASH_MassErase((uint8_t) pEraseInit->VoltageRange, pEraseInit->Banks); /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE); /* if the erase operation is completed, disable the MER Bit */ FLASH->CR &= (~FLASH_MER_BIT); } else { /* Check the parameters */ assert_param(IS_FLASH_NBSECTORS(pEraseInit->NbSectors + pEraseInit->Sector)); /* Erase by sector by sector to be done*/ for (index = pEraseInit->Sector; index < (pEraseInit->NbSectors + pEraseInit->Sector); index++) { FLASH_Erase_Sector(index, (uint8_t) pEraseInit->VoltageRange); /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE); /* If the erase operation is completed, disable the SER and SNB Bits */ CLEAR_BIT(FLASH->CR, (FLASH_CR_SER | FLASH_CR_SNB)); if (status != HAL_OK) { /* In case of error, stop erase procedure and return the faulty sector*/ *SectorError = index; break; } } } /* Flush the caches to be sure of the data consistency */ FLASH_FlushCaches(); } /* Process Unlocked */ __HAL_UNLOCK(&pFlash); return status; } В ней есть задержка СпойлерКод: HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout) { uint32_t tickstart = 0U; /* Clear Error Code */ pFlash.ErrorCode = HAL_FLASH_ERROR_NONE; /* Wait for the FLASH operation to complete by polling on BUSY flag to be reset. Even if the FLASH operation fails, the BUSY flag will be reset and an error flag will be set */ /* Get tick */ tickstart = HAL_GetTick(); while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET) { if(Timeout != HAL_MAX_DELAY) { if((Timeout == 0U)||((HAL_GetTick() - tickstart ) > Timeout)) { return HAL_TIMEOUT; } } } /* Check FLASH End of Operation flag */ if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP) != RESET) { /* Clear FLASH End of Operation pending bit */ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP); } #if defined(FLASH_SR_RDERR) if(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \ FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR | FLASH_FLAG_RDERR)) != RESET) #else if(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \ FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR)) != RESET) #endif /* FLASH_SR_RDERR */ { /*Save the error code*/ FLASH_SetErrorCode(); return HAL_ERROR; } /* If there is no error flag set */ return HAL_OK; } Но все это проблему не снимает. Почему даже без записи, а при простом стирании получаю такую ошибку при повторной попытки войти в отладку: При этом если использовать в качестве отладчика ST-LINK(OpenOCD) в место ST-LINK GDB Server, то повторно войти в отладку можно. Ни кто не знает, это причуды GDB или я все же накосячел? Я склонен ко второму, т.к. при любой другой ситуации, данный отладчик работал штатно и без нариканий. Добавлено after 2 hours 53 minutes 53 seconds: Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT6 А ларчик просто открывался! ![]() ![]() ![]() По этому перед стирание и записью в нее родимую просто поставил Код: HAL_Delay(1000); Вот и все. Можно отлаживаться дальше. Пользуйтесь! ![]() |
Автор: | smacorp [ Вс мар 16, 2025 02:29:10 ] |
Заголовок сообщения: | Re: Проблема с записью в внутреннюю Flash на МК STM32F407VGT |
Rudthaky, надеюсь до бана Вы успели сообщить сообществу ST, что их STM прекрасно работают в новой модификации "Орешника"? Должно же сообщество ST тоже порадоваться её (модификации) скорому выходу. |
Страница 1 из 1 | Часовой пояс: UTC + 3 часа |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |