Перейти к загрузчику в STM32 через приложение, т. Е. С помощью контактов Boot 0 и Boot 1 в режиме загрузки с пользовательской вспышки

У меня есть требование для обновления прошивки. Я планирую использовать класс USB DFU. Но команда для обновления прошивки придет из приложения ПК в моем случае. поэтому мне нужно переключиться на загрузчик, который есть в системной памяти. Первоначально я запускаю приложение, поэтому оно загружается с флэш-памяти пользователя, т.е. у меня настроены контакты Boot0 и Boot 1 для флэш-памяти пользователя. Поскольку в системной флеш-памяти есть загрузчик DFU, теперь для этого необходимо изменить настройки контактов Boot0 и Boot1. Есть ли способ, как настройки Boot 0 и Boot 1 остаются такими же, как пользовательская флэш-память и в приложении мы переходим к системной памяти

5 ответов

Решение

Пины Boot0/1 сэмплируются только при запуске процессора, чтобы проверить, должен ли он загружать код пользователя из памяти или загрузчик. Состояние этих выводов не влияет на загрузчик впоследствии.

Я столкнулся с подобным запросом и нашел 2 способа загрузки загрузчика по требованию.

Во-первых, вы можете "прыгать" из кода пользователя в загрузчик. Например, вы можете перейти к загрузчику при нажатии кнопки.

Но... это намного сложнее, чем простая инструкция JUMP: некоторые регистры и устройства должны быть правильно перенастроены для работы с загрузчиком, вы должны убедиться, что IRQ не будет запущен во время JUMP,... На самом деле вы придется перенастроить процессор так, как если бы он был только что запущен после сброса. Вы можете найти некоторую информацию об этой технике: на этом видео от ST.

Мне удалось сделать такие вещи в проекте STM32F1xx. Однако в более сложном проекте, основанном на STM32F4, это стало бы действительно трудным... Мне пришлось бы остановить все устройства (таймеры, интерфейс связи, АЦП, ЦАП,...), убедиться, что IRQ не будет запущен, перенастроить все часы,...

Вместо этого я решил реализовать это второе решение: когда я хочу перейти к загрузчику, я записываю байт в один из резервных регистров и затем выполняю программный сброс. Затем, когда процессор перезапустится, в самом начале программы он прочитает этот регистр. Этот регистр содержит значение, указывающее, что он должен перезагрузиться в режиме загрузчика. Затем перейти к загрузчику намного проще, как представлено в видео на YouTube.

Вы можете смоделировать состояние загрузчика. Подключите конденсатор и параллельный резистор от контакта BOOT к земле. Подключите другой свободный контакт к контакту BOOT. Конденсатор может заряжаться от внешнего контакта и разряжаться резистором. Я не помню точных значений, которые вы можете рассчитать / экспериментировать с ними (важна постоянная времени RC-цепи).

Зарядите этот конденсатор, установив внешний вывод на 1, выполните программный сброс с помощью NVIC_SystemReset, После сброса будет запущен загрузчик. Резистор, подключенный к конденсатору, выполнит разрядку. После обновления прошивки вы можете перезагрузить устройство, и оно будет запущено в вашем приложении.

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

В MicroPython есть функция pyb.bootloader(), которая используется для входа в режим DFU.

Код C, который реализует это, можно найти в их исходном хранилище.

Я широко использовал версию STM32F4 (#else блок), и вариант F7 несколько раз (хотя это было какое-то время).

Я помещу здесь тело функции, так как приведенные выше ссылки могут устареть, если этот файл изменится:

// Activate the bootloader without BOOT* pins.
STATIC NORETURN mp_obj_t machine_bootloader(void) {
    pyb_usb_dev_deinit();
    storage_flush();

    HAL_RCC_DeInit();
    HAL_DeInit();

#if defined(MCU_SERIES_F7)
    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x1FF00000));
    __ASM volatile ("movw r3, #0x0000\nmovt r3, #0x1FF0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x1FF00004))();
#else
    __HAL_REMAPMEMORY_SYSTEMFLASH();

    // arm-none-eabi-gcc 4.9.0 does not correctly inline this
    // MSP function, so we write it out explicitly here.
    //__set_MSP(*((uint32_t*) 0x00000000));
    __ASM volatile ("movs r3, #0\nldr r3, [r3, #0]\nMSR msp, r3\n" : : : "r3", "sp");

    ((void (*)(void)) *((uint32_t*) 0x00000004))();
#endif

    while (1);
}

Функция pyb_usb_dev_deinit() отключает USB, а storage_flush записывает любые данные из кэшированной файловой системы. Функции HAL взяты из файлов STM32Cube HAL.

Если вы используете более новую версию dfu-util (IIRC 0.8 или новее), вы можете указать -s :leave опция командной строки, чтобы ваша недавно прошитая программа выполнялась в конце прошивки. В сочетании с вышесказанным я прохожу циклы прошивки / тестирования, не касаясь платы, и использую BOOT0/RESET только в случае сбоя прошивки.

Существует также флешер Python DFU под названием pydfu.py: https://github.com/micropython/micropython/blob/master/tools/pydfu.py который немного быстрее, чем dfu-util.

Перейти к новому образу не так сложно. Я сделал это успешно как часть самопроверки.

  1. Вы должны передать адрес, где ваше второе изображение (в данном случае загрузчик) находится во флэш-памяти компоновщику, когда вы связываете второе изображение. Вы могли бы вместо этого использовать независимый от позиции код, но это имеет другие проблемы.
  2. Очевидно, что вам необходимо запрограммировать второе изображение, начиная с того же адреса, который вы указали для компоновщика.
  3. Установите функцию прыжка: void (* const jumpFunction)(void) = (void (*)(void))(APPLICATION_ADDRESS + 4ul + 1ul); Смещение на четыре должно пройти указатель стека, смещение на единицу - для Thumbmode.
  4. Укажите новый начальный указатель стека: __set_MSP((uint32_t)*APPLICATION_ADDRESS) первые четыре байта второго изображения будут содержать указатель нового стека.
  5. Перейти, вызвав функцию: jumpFunction();
  6. Во второй программе инициализация по умолчанию попытается установить смещение таблицы векторов (VTOR). SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;, Вы должны изменить это на SCB->VTOR = APPLICATION_ADDRESS | VECT_TAB_OFFSET;

У меня есть программа POST в FLASH_BASE он использует SRAM, связанный с ядром, для своего стека, а затем запускает проверки памяти на основной SRAM, проверяет подлинность основной программы и затем переходит к основной программе.

Я все еще могу отлаживать основную программу, как будто ничего не изменилось.

NB! Я только недавно сделал это сам. Есть несколько вещей, которые мне нужно проверить. Одна проблема заключается в том, что произойдет с программным сбросом. Если вызывается из второй программы, она, я думаю, перейдет к процедуре сброса второй программы, а не первой.

Я некоторое время боролся с этой проблемой, пытаясь перейти из приложения FreeRTOS на к USB OTG DFU. После долгих проб и ошибок я смог заставить его работать, поэтому решил опубликовать его здесь, поскольку нигде больше не мог найти четких инструкций по этому поводу.

Примечание . Этот код предназначен для STM32L4, тот же шаблон должен работать и для других.

Также, когда вы прошиваете образ с помощью STM32CubeProgrammer, обязательно установите флажок «Запустить приложение», иначе он будет оставаться в режиме загрузчика.

      void JumpToBootloader(void)
{
     HAL_SuspendTick();

     /* Clear Interrupt Enable Register & Interrupt Pending Register */
     for (int i=0;i<5;i++)
     {
         NVIC->ICER[i]=0xFFFFFFFF;
         NVIC->ICPR[i]=0xFFFFFFFF;
     }

     HAL_FLASH_Unlock();

     HAL_FLASH_OB_Unlock();

     // RM0351 Rev 7 Page 93/1903
     // AN2606 Rev 44 Page 23/372
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT0);
     SET_BIT(FLASH->OPTR, FLASH_OPTR_nBOOT1);
     CLEAR_BIT(FLASH->OPTR, FLASH_OPTR_nSWBOOT0);

     SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);

     while(READ_BIT(FLASH->SR, FLASH_SR_BSY));

     HAL_FLASH_OB_Launch();
}
Другие вопросы по тегам