简体   繁体   English

STM32 F072上的软件如何跳转到bootloader(DFU模式)?

[英]How do you jump to the bootloader (DFU mode) in software on the STM32 F072?

STM32 App Note 2606 对此进行了讨论,但没有简单的代码示例。

This answer has been tested on the STM32F072 Nucleo board using IAR EWARM.此答案已在 STM32F072 Nucleo 板上使用 IAR EWARM 进行了测试。 This answer uses the "STM32 Standard Peripheral Library" and nothing else.此答案使用“STM32 标准外设库”,仅此而已。

Note that the best/easiest way to verify you are successfully in bootloader mode (DFU mode) is to hookup a USB-2-UART converter (get one here from Sparkfun: http://sfe.io/p9873 for $15) on lines PA_9 (USART1_TX) and PA_10 (USART1_RX) (don't forget to connect ground as well).请注意,验证您是否成功进入引导加载程序模式(DFU 模式)的最佳/最简单的方法是在线连接 USB-2-UART 转换器(从 Sparkfun 获得一个: http ://sfe.io/p9873,售价 15 美元) PA_9 (USART1_TX) 和 PA_10 (USART1_RX)(也不要忘记接地)。 I was not able to use the Nucleo USART2 default connection (/dev/ttyACM0), hence the external USB-2-USART connection.我无法使用 Nucleo USART2 默认连接 (/dev/ttyACM0),因此无法使用外部 USB-2-USART 连接。 Then create a simple C program to write 0x7F on the USART connection.然后创建一个简单的 C 程序在 USART 连接上写入 0x7F。 If you are in DFU mode, it will reply with one byte: 0x79.如果您处于 DFU 模式,它将回复一个字节:0x79。 I use Ubuntu, so my test program compiles and runs on Linux.我使用 Ubuntu,所以我的测试程序可以在 Linux 上编译和运行。

Also, the easiest way to test bootloader mode (aka DFU mode) is to jumper the BOOT0 line to +3.3V.此外,测试引导加载程序模式(又名 DFU 模式)的最简单方法是将 BOOT0 线跳接到 +3.3V。 These are right next to each other on the Nucleo.它们在 Nucleo 上彼此相邻。

Add to main.c main() routine:添加到 main.c main() 例程:

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

// Reset the processor
NVIC_SystemReset();

Add some code to Libraries/sysconfig/system_stm32f0xx.c at the beginning of the SystemInit() function:在 SystemInit() 函数的开头向 Libraries/sysconfig/system_stm32f0xx.c 添加一些代码:

// 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

Create a simple utility to see if you are in bootloader mode (aka DFU mode).创建一个简单的实用程序来查看您是否处于引导加载程序模式(又名 DFU 模式)。 This compiles and runs on Linux.这将在 Linux 上编译并运行。 Make sure you get your serial port right.确保你的串口是正确的。 It will likely be /dev/ttyUSB0 as shown below.它可能是 /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;
}

In my project, I'm essentially doing the same as Brad, but without modifying the SystemInit() function.在我的项目中,我基本上和 Brad 一样,但没有修改 SystemInit() 函数。

The CubeMX HAL defines as CubeMX HAL 定义为

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

which does - in my case - nothing but calling SystemInit();这确实 - 在我的情况下 - 除了调用 SystemInit();

So you can just overwrite this function:所以你可以覆盖这个函数:

#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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM