Привет!
В общем такой вот вопрос, как в названии темы - как-то можно из программы перейти в заводской загрузчик STM32, без подачи сигналов на ножки BOOT?
Можно использовать вариант с RC-цепочкой. То есть к пину BOOT подключаются внешние дополнительные резистор и конденсатор, с другой стороны цепочка подключается к любому свободному выводу.
Подаем на этот вывод 1, конденсатор заряжается. Затем перезапускаем контроллер. При включении на выходе BOOT будет нужный уровень для входа в System Bootloader (из-за накопленного заряда). Далее выполняем прошивку, конденсатор за некоторое время разряжается через резистор и после следующего перезапуска попадаем снова в основную программу.
Здесь нужно учитывать подключаемые номиналы конденсатора и резистора для задания определенной постоянной времени RC-цепи.
Но мы редко такой вариант используем из-за того, что, во-первых, требуется пусть и минимальная но дополнительная обвязка. А это лишнее место на плате, лишние компоненты, лишний монтаж итд )
Поэтому второй вариант - непосредственно из основной программы перейти на адрес, по которому расположен System Bootloader. Здесь тоже есть свой нюанс, который заключается в правильной перенастройке некоторой периферии перед jump инструкцией. Для разных контроллеров здесь могут быть разные дополнительные условия, которые нужно выполнить.
У вас какой контроллер? Я сделаю пример кода конкретно для него.
Спасибо за такой подробный ответ 👍
Да, мне больше второй вариант подойдет, без дополнительных компонентов.
Контроллер STM32F042, буду очень рад примеру))
@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();
@aveal Забыл поблагодарить... Вчера все запустил, идеально работает, спасибо!!
@aveal День добрый!
Уперся и не могу ничего сделать, не один описанный в сети подход не работает.
чип STM32L073СZT6, загрузчик 4.1
поможете?
// Адрес системного загрузчика #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); }
Благодарю!
// Адрес системного загрузчика #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 не удается?
если делать аппаратно, то
при подтяжке boot0 к питанию и нажатию ресет появляется устройство
я ожидаю точно такого же поведения при выполнении своего
jump_to_bootloader_v4_1
при чем тут uart?
@xdriver Исторически базовый способ для проверки бутлодера - коммуникация по uart. Я не особо слежу, что ST обновили, но если в данном случае при аппаратном переключении в диспетчере устройств появляется usb-устройство, то можно считать это достоверным маркером работы бутлодера.
Навскидку в первую очередь смущает отсутствие деиницилизации периферии, RCC, прерываний итд. Если под отладчиком смотреть - переход адекватно происходит, то есть в целом с каких адресов продолжается выполнение?
Можете скинуть, откуда информация о DFU_DUAL_BANK_BYPASS_ADDR?
@aveal
https://stackoverflow.com/questions/42020893/stm32l073rz-rev-z-iap-jump-to-bootloader-system-memory/42029607
по моему отсюда, может и нет я уже запутался полностью.