Как перейти к загрузчику (режим DFU) в ПО на STM32 F072?

В примечании 2606 к приложению STM32 это обсуждается, но простого примера кода нет.


person Brad Grissom    schedule 02.02.2015    source источник
comment
Насколько я могу судить, это невозможно! Вы можете перейти к системному загрузчику, но он по-прежнему проверяет строку BOOT0 и все равно запускает ваше приложение. Было бы интересно, если бы кто-нибудь действительно заставил это работать, пока BOOT0 привязан к GND. И в любом случае не удалось заставить STM32CubeProgrammer работать с моим STM32L072, он подключается, а затем сразу теряет соединение. Слишком много времени потратил на это.   -  person Daraius Hathiram    schedule 28.11.2020


Ответы (2)


Этот ответ был протестирован на плате STM32F072 Nucleo с использованием IAR EWARM. В этом ответе используется «Стандартная периферийная библиотека STM32» и ничего больше.

Обратите внимание, что лучший / самый простой способ убедиться, что вы успешно находитесь в режиме загрузчика (режим DFU), - это подключить преобразователь USB-2-UART (его можно получить здесь от Sparkfun: http://sfe.io/p9873 за 15 долларов) на линиях PA_9 (USART1_TX) и PA_10 (USART1_RX) (не забудьте также подключить землю). Мне не удалось использовать соединение Nucleo USART2 по умолчанию (/ dev / ttyACM0), следовательно, внешнее соединение USB-2-USART. Затем создайте простую программу C для записи 0x7F в соединении USART. Если вы находитесь в режиме DFU, он ответит одним байтом: 0x79. Я использую Ubuntu, поэтому моя тестовая программа компилируется и работает в Linux.

Кроме того, самый простой способ проверить режим загрузчика (также известный как режим DFU) - это подключить линию BOOT0 к + 3,3 В. На Nucleo они расположены рядом друг с другом.

Добавьте в main.c подпрограмму main ():

// Our STM32 F072 has:
// 16k SRAM in address 0x2000 0000 - 0x2000 3FFF
*((unsigned long *)0x20003FF0) = 0xDEADBEEF;

// Reset the processor
NVIC_SystemReset();

Добавьте код в Libraries / sysconfig / system_stm32f0xx.c в начале функции SystemInit ():

// Define our function pointer
void (*SysMemBootJump)(void);

void SystemInit (void)
{
  // Check if we should go into bootloader mode.
  //
  // Set the main stack pointer __set_MSP() to its default value.  The default
  // value of the main stack pointer is found by looking at the default value
  // in the System Memory start address. Do this in IAR View -> Memory.  I
  // tried this and it showed address: 0x200014A8 which I then tried here.
  // The IAR compiler complained that it was out of range.  After some
  // research, I found the following from "The STM32 Cortex-M0 Programming
  // Manual":
  //         Main Stack Pointer (MSP)(reset value). On reset, the processor
  //         loads the MSP with the value from address 0x00000000.
  //
  // So I then looked at the default value at address 0x0 and it was 0x20002250
  //
  // Note that 0x1fffC800 is "System Memory" start address for STM32 F0xx
  //
  if ( *((unsigned long *)0x20003FF0) == 0xDEADBEEF ) {
       *((unsigned long *)0x20003FF0) =  0xCAFEFEED; // Reset our trigger
      __set_MSP(0x20002250);
                                                     // 0x1fffC800 is "System Memory" start address for STM32 F0xx
      SysMemBootJump = (void (*)(void)) (*((uint32_t *) 0x1fffC804)); // Point the PC to the System Memory reset vector (+4)
      SysMemBootJump();
      while (1);
  }

  ... // The rest of the vanilla SystemInit() function

Создайте простую утилиту, чтобы узнать, находитесь ли вы в режиме загрузчика (он же режим DFU). Это компилируется и работает в Linux. Убедитесь, что у вас правильный последовательный порт. Скорее всего, это будет / dev / ttyUSB0, как показано ниже.

//
// A bare-bones utility: Test if the STM32 is in DFU mode
// (aka bootloader mode, aka firmware update mode).
//
// If it is in DFU mode, you can send it 0x7F over a UART port and it
// will send 0x79 back.
//
// For details, see the STM32 DFU USART spec.
//

#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>  // errno

#define DEFAULT_SERDEVICE  "/dev/ttyUSB0"
//#define DEFAULT_SERDEVICE  "/dev/ttyACM0"

int main(int argc, char **argv)
{
    int              fd, cooked_baud = B9600;
    char            *sername = DEFAULT_SERDEVICE;
    struct termios   oldsertio, newsertio;
    unsigned char mydata[2] = {0};

    mydata[0] = 0x7F;
    mydata[1] = 0;

    /* Not a controlling tty: CTRL-C shouldn't kill us. */
    fd = open(sername, O_RDWR | O_NOCTTY);
    if ( fd < 0 )
    {
        perror(sername);
        exit(-1);
    }

    tcgetattr(fd, &oldsertio); /* save current modem settings */

    /*
     * 8 data, EVEN PARITY, 1 stop bit. Ignore modem control lines. Enable
     * receive. Set appropriate baud rate. NO HARDWARE FLOW CONTROL!
     */
    newsertio.c_cflag = cooked_baud | CS8 | CLOCAL | CREAD | PARENB;

    /* Raw input. Ignore errors and breaks. */
    newsertio.c_iflag = IGNBRK | IGNPAR;

    /* Raw output. */
    newsertio.c_oflag = OPOST;

    /* No echo and no signals. */
    newsertio.c_lflag = 0;

    /* blocking read until 1 char arrives */
    newsertio.c_cc[VMIN]=1;
    newsertio.c_cc[VTIME]=0;

    /* now clean the modem line and activate the settings for modem */
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW,&newsertio);

    // Here is where the magic happens
    write(fd,&mydata[0],1);
    int red = read(fd,&mydata[1],1);
    if (red < 0) {
        fprintf(stderr, "Error: read() failed, errno [%d], strerrer [%s]\n",
                errno, strerror(errno));
    }

    tcsetattr(fd,TCSANOW,&oldsertio);
    close(fd);

    printf("Read [%d] bytes: [0x%x]\n", red, mydata[1]);

    return 0;
}
person Brad Grissom    schedule 02.02.2015
comment
Очень четко объяснено, спасибо. Когда вы говорите, что самый простой способ проверить DFU - это замкнуть BOOT0 на 3,3 В, это только для независимого тестирования DFU, верно? Не тестировать с кодом, которым вы поделились? - person Plasty Grove; 11.05.2019

В своем проекте я, по сути, делаю то же, что и Брэд, но без изменения функции SystemInit ().

CubeMX HAL определяет как

void __attribute__((weak)) __initialize_hardware_early(void);

который в моем случае не делает ничего, кроме вызова SystemInit ();

Так что вы можете просто перезаписать эту функцию:

#include <stdint.h>
#include "stm32f0xx_hal.h"

#define SYSMEM_RESET_VECTOR            0x1fffC804
#define RESET_TO_BOOTLOADER_MAGIC_CODE 0xDEADBEEF
#define BOOTLOADER_STACK_POINTER       0x20002250

uint32_t dfu_reset_to_bootloader_magic;

void __initialize_hardware_early(void)
{
    if (dfu_reset_to_bootloader_magic == RESET_TO_BOOTLOADER_MAGIC_CODE) {
        void (*bootloader)(void) = (void (*)(void)) (*((uint32_t *) SYSMEM_RESET_VECTOR));
        dfu_reset_to_bootloader_magic = 0;
        __set_MSP(BOOTLOADER_STACK_POINTER);
        bootloader();
        while (42);
    } else {
        SystemInit();
    }
}

void dfu_run_bootloader()
{
    dfu_reset_to_bootloader_magic = RESET_TO_BOOTLOADER_MAGIC_CODE;
    NVIC_SystemReset();
}
person Hubert Denkmair    schedule 22.04.2016
comment
как вы установили BOOTLOADER_STACK_POINTER? - person Jon Nordby; 12.09.2019