Здравствуй сообщество! Столкнулся с проблемой об которую бьюсь
целую неделю, а решения так и не вижу.

Суть проблемы достаточно проста. Я пытаюсь с помощью библиотеки HAL записать во внутреннюю память 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();
}
}