2015-02-02 5 views

ответ

6

Этот ответ был протестирован на плате Nucleo STM32F072 с использованием 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.3V. Они находятся рядом друг с другом на Нуклео.

Добавить в main.c основной() рутина:

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

// Reset the processor 
NVIC_SystemReset(); 

добавить код библиотеки/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; 
} 
1

В моем проекте, я по сути делает то же самое, как Брэд, но без изменения функции 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(); 
}