Top.Mail.Ru
Уведомления
Очистить все

[Решено] Программный переход в System Bootloader STM32

Страница 1 / 2
(@coding_man)
New member

Привет!

В общем такой вот вопрос, как в названии темы - как-то можно из программы перейти в заводской загрузчик STM32, без подачи сигналов на ножки BOOT?


Цитата
Создатель темы Размещено : 30.05.2021 13:25
Aveal
(@aveal)
Top level Admin

Можно использовать вариант с RC-цепочкой. То есть к пину BOOT подключаются внешние дополнительные резистор и конденсатор, с другой стороны цепочка подключается к любому свободному выводу.

Подаем на этот вывод 1, конденсатор заряжается. Затем перезапускаем контроллер. При включении на выходе BOOT будет нужный уровень для входа в System Bootloader (из-за накопленного заряда). Далее выполняем прошивку, конденсатор за некоторое время разряжается через резистор и после следующего перезапуска попадаем снова в основную программу.

Здесь нужно учитывать подключаемые номиналы конденсатора и резистора для задания определенной постоянной времени RC-цепи.

Но мы редко такой вариант используем из-за того, что, во-первых, требуется пусть и минимальная но дополнительная обвязка. А это лишнее место на плате, лишние компоненты, лишний монтаж итд )

Поэтому второй вариант - непосредственно из основной программы перейти на адрес, по которому расположен System Bootloader. Здесь тоже есть свой нюанс, который заключается в правильной перенастройке некоторой периферии перед jump инструкцией. Для разных контроллеров здесь могут быть разные дополнительные условия, которые нужно выполнить.

У вас какой контроллер? Я сделаю пример кода конкретно для него.


ОтветитьЦитата
Размещено : 31.05.2021 13:02
(@coding_man)
New member

@aveal

Спасибо за такой подробный ответ 👍 

Да, мне больше второй вариант подойдет, без дополнительных компонентов.

Контроллер STM32F042, буду очень рад примеру))


ОтветитьЦитата
Создатель темы Размещено : 01.06.2021 17:31
Aveal
(@aveal)
Top level Admin

@coding_man, готово, для STM32F04xxx:

void BootloaderJump()
{
  FLASH_EraseInitTypeDef eraseStruct;
  eraseStruct.NbPages = 1;
  eraseStruct.PageAddress = 0x08000000;
  eraseStruct.TypeErase = FLASH_TYPEERASE_PAGES;
  
  uint32_t error = 0;
  
  HAL_DeInit();
  HAL_RCC_DeInit();
  __disable_irq();
  
  __HAL_RCC_SYSCFG_CLK_ENABLE();
  __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
    
  HAL_FLASH_Unlock();
  HAL_FLASHEx_Erase(&eraseStruct, &error);
  HAL_FLASH_Lock();
  
  const uint32_t ptr = (*((uint32_t *) SYSTEM_MEMORY_ADDR));
  void (*JumpToSystemMemory)(void);
  
  JumpToSystemMemory = (void (*)(void)) (*((uint32_t *) (SYSTEM_MEMORY_ADDR + 4)));
  __set_MSP(ptr);
  JumpToSystemMemory();
  
  while(1);
}

Здесь присутствует нюанс с очисткой flash-памяти перед переходом в System Memory. Это особенность именно этого семейства, из аппноута:

Due to empty check mechanism present on this product, it is not possible to jump from user code to system bootloader. Such jump results in a jump back to user Flash memory space.But if the first 4 bytes of User Flash (at 0x0800 0000) are empty at the moment of jump (i.e. erase first sector before jump or execute code from SRAM while Flash is empty), then system bootloader is executed when jumped to

Так, далее:

#define SYSTEM_MEMORY_ADDR                                      0x1FFFC400

Этот адрес также для разных контроллеров/семейств варьируется.

Вызываем просто из любого места программы:

BootloaderJump();

ОтветитьЦитата
Размещено : 02.06.2021 13:01
(@coding_man)
New member

@aveal Забыл поблагодарить... Вчера все запустил, идеально работает, спасибо!!


ОтветитьЦитата
Создатель темы Размещено : 03.06.2021 16:06
(@xdriver)
Level 1

@aveal День добрый!
Уперся и не могу ничего сделать, не один описанный в сети подход не работает.
чип STM32L073СZT6, загрузчик 4.1

поможете?


ОтветитьЦитата
Размещено : 21.09.2025 16:56
Aveal
(@aveal)
Top level Admin

@xdriver добрый день!

Скиньте свой актуальный код, завтра утром смогу посмотреть скорее всего. 


ОтветитьЦитата
Размещено : 21.09.2025 18:18
(@xdriver)
Level 1

@aveal 

// Адрес системного загрузчика
#define SYSMEM_BOOTLOADER_ADDR 0x1FF00000
// Адрес для обхода механизма Dual Bank (может отличаться, уточните в AN2606)
#define DFU_DUAL_BANK_BYPASS_ADDR 0x1FF00010

void jump_to_bootloader_v4_1(void) {
    // 1. Первый прыжок: стандартная инициализация загрузчика
    void (*sysmem_bootloader)(void);

    // Установка указателя стека из вектора сброса загрузчика
    uint32_t stack_pointer = *((volatile uint32_t *)SYSMEM_BOOTLOADER_ADDR);
    __set_MSP(stack_pointer);

    // Получение адреса точки входа
    uint32_t reset_handler = *((volatile uint32_t *)(SYSMEM_BOOTLOADER_ADDR + 4));
    sysmem_bootloader = (void (*)(void))reset_handler;

    // Вызов загрузчика
    sysmem_bootloader();

    // 2. После возврата из первого загрузчика (из-за Dual Bank management)
    //    выполняем второй прыжок на обходной адрес

    // Повторная установка указателя стека (на случай, если он был изменен)
    __set_MSP(stack_pointer);

    // Прыжок на специальный адрес, обходящий проверки Dual Bank
    void (*dfu_bypass)(void) = (void (*)(void))DFU_DUAL_BANK_BYPASS_ADDR;
    dfu_bypass();

    // Код не должен достичь этой точки
    while(1);
}

ОтветитьЦитата
Размещено : 21.09.2025 18:24
(@xdriver)
Level 1

Благодарю!


ОтветитьЦитата
Размещено : 21.09.2025 18:25
Aveal
(@aveal)
Top level Admin

Запись от: @xdriver

@aveal 

// Адрес системного загрузчика
#define SYSMEM_BOOTLOADER_ADDR 0x1FF00000
// Адрес для обхода механизма Dual Bank (может отличаться, уточните в AN2606)
#define DFU_DUAL_BANK_BYPASS_ADDR 0x1FF00010

void jump_to_bootloader_v4_1(void) {
    // 1. Первый прыжок: стандартная инициализация загрузчика
    void (*sysmem_bootloader)(void);

    // Установка указателя стека из вектора сброса загрузчика
    uint32_t stack_pointer = *((volatile uint32_t *)SYSMEM_BOOTLOADER_ADDR);
    __set_MSP(stack_pointer);

    // Получение адреса точки входа
    uint32_t reset_handler = *((volatile uint32_t *)(SYSMEM_BOOTLOADER_ADDR + 4));
    sysmem_bootloader = (void (*)(void))reset_handler;

    // Вызов загрузчика
    sysmem_bootloader();

    // 2. После возврата из первого загрузчика (из-за Dual Bank management)
    //    выполняем второй прыжок на обходной адрес

    // Повторная установка указателя стека (на случай, если он был изменен)
    __set_MSP(stack_pointer);

    // Прыжок на специальный адрес, обходящий проверки Dual Bank
    void (*dfu_bypass)(void) = (void (*)(void))DFU_DUAL_BANK_BYPASS_ADDR;
    dfu_bypass();

    // Код не должен достичь этой точки
    while(1);
}

И в итоге переход на нужный адрес происходит, но до бутлодера достучаться по uart не удается?

 


ОтветитьЦитата
Размещено : 22.09.2025 10:56
(@xdriver)
Level 1

@aveal 

если делать аппаратно, то

при подтяжке boot0 к питанию и нажатию ресет появляется устройство

image

я ожидаю точно такого же поведения при выполнении своего

jump_to_bootloader_v4_1

при чем тут uart?


ОтветитьЦитата
Размещено : 22.09.2025 12:34
Aveal
(@aveal)
Top level Admin

@xdriver Исторически базовый способ для проверки бутлодера - коммуникация по uart. Я не особо слежу, что ST обновили, но если в данном случае при аппаратном переключении в диспетчере устройств появляется usb-устройство, то можно считать это достоверным маркером работы бутлодера.

Навскидку в первую очередь смущает отсутствие деиницилизации периферии, RCC, прерываний итд. Если под отладчиком смотреть - переход адекватно происходит, то есть в целом с каких адресов продолжается выполнение?


ОтветитьЦитата
Размещено : 22.09.2025 17:35
Aveal
(@aveal)
Top level Admin

Можете скинуть, откуда информация о DFU_DUAL_BANK_BYPASS_ADDR?


ОтветитьЦитата
Размещено : 22.09.2025 18:47
(@xdriver)
Level 1

@aveal
https://stackoverflow.com/questions/42020893/stm32l073rz-rev-z-iap-jump-to-bootloader-system-memory/42029607

по моему отсюда, может и нет я уже запутался полностью.


ОтветитьЦитата
Размещено : 22.09.2025 18:51
Aveal
(@aveal)
Top level Admin

Идея в целом понятна, надо оказаться в зеленой точке, адрес сомнительный.

изображение

ОтветитьЦитата
Размещено : 22.09.2025 18:56
Страница 1 / 2
Поделиться: