Перейти к содержанию
    

Прошивка STM32F7 через другой STM32F7 (UART)

1. Правильно ли я полагаю, что в ОЗУ Вы копируете код (и обработчики исключений) лишь для того, чтобы не потерять возможные прерывания и события при остановке CPU при стирании или программировании Flash?

Нет, не только. Я же пишу, что:

1) чтобы не создавать позиционно-независимый бутлоадер (у нас же две копии его во флешь!);

2) функции программирования флешь в моём МК требуют, чтобы код, стирающий/пишущий во FLASH, выполнялся из ОЗУ.

 

2. Копии загрузчиков хранятся в разных секторах Flash и не пересекаются, правильно понимаю? Иначе при стирании одного сектора обновляемого загрузчика, можно зацепить активный и при сбросе питания оба загрузчика будут неработоспособными.

Конечно.

 

Так? Ну вернее я так вижу, как бы сделал я (на беглый взгляд).

Да, примерно так. В начале FLASH есть код (вообще-то у меня именно он и называется BOOTLOADER; а те две копии - они у меня называются UPDATER), который выбирает один из двух UPDATER (активный), копирует его в ОЗУ и передаёт управление ему (сообщая ему его позицию 0 или 1). Уже UPDATER читает внешнюю SPI-FLASH, проверяет наличие в ней прошивки нужного типа и шьёт если надо.

Регионов у меня всего 4, а не 3: BOOTLOADER, 0-й UPDATER, 1-й UPDATER, WORK.

BOOTLOADER сделан максимально простым и на асме и без обращения к какой-либо периферии, чтобы минимизировать возможность багов в нём. Он размером всего около 128 байт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет, не только. Я же пишу, что:

1) чтобы не создавать позиционно-независимый бутлоадер (у нас же две копии его во флешь!);

Ох. Осталось переварить как это понять :biggrin:

 

Регионов у меня всего 4, а не 3: BOOTLOADER, 0-й UPDATER, 1-й UPDATER, WORK.

Ну да, я без WORK/Application имел в виду.

 

BOOTLOADER сделан максимально простым и на асме и без обращения к какой-либо периферии, чтобы минимизировать возможность багов в нём. Он размером всего около 128 байт.

Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше :rolleyes:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ох. Осталось переварить как это понять :biggrin:

Когда Вы линкуете код для выполнения с каких-то адресов, то он и будет только в тех адресах выполняться. Это называется позиционно-зависимый код. Компоновщик его и делает обычно.

Так что для двух копий UPDATER мне пришлось бы линковать 2 разные прошивки (если запускать во флешь).

Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу).

 

Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше :rolleyes:

В первом секторе у меня BOOTLOADER находится (плюс ещё некоторая уникальная инфа, которая зашивается один раз при изготовлении: MAC-адрес, серийный номер; и которая не должна обновляться).

А размеры секторов в моём МК: 64КБ, 128КБ, 256КБ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу).

Дошло, спасибо. На мой взгляд действительно гибко и удобно :laughing:

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.

У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:

#include "stm32load.h"
#include "stm32f4xx.h"
#include "myassert.h"
#include "serial.h"
#include "systime.h"
#include "pt.h"
#include "uart.h"
#include "../../pilot/pilot.h"
#include "../../dfb/dfb.h"
#include "../../sbs/sbs.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

#define NSLAVES 3
#define ACK     0x79

struct mem_op_data
{
   int addr, len;
   uint8_t* ptr;
};

static uint8_t rxfifo0[256];
static uint8_t rxfifo1[256];
static uint8_t rxfifo2[64];
static uint8_t txfifo[NSLAVES][64];
static struct uart_idx idx[NSLAVES];
static struct pt boot_pt[NSLAVES];
static int slave = 0;
static bool burning;

const struct uart_config uart3 =
{
   .rxfifo = rxfifo0,
   .rxsz = sizeof rxfifo0,
   .txfifo = txfifo[0],
   .txsz = sizeof txfifo[0],
   .idx = &idx[0],
   .regs = USART3,
   .rxport = GPIOB,
   .rxpin = 11,
   .rxaf = 7,
   .txport = GPIOB,
   .txpin = 10,
   .txaf = 7,
   .irq = USART3_IRQn,
   .prio = 7,
   .apb_clock = SYSTIME_TPS / 4,
   .baudrate = 57600,
};
const struct uart_config uart6 =
{
   .rxfifo = rxfifo1,
   .rxsz = sizeof rxfifo1,
   .txfifo = txfifo[1],
   .txsz = sizeof txfifo[1],
   .idx = &idx[1],
   .regs = USART6,
   .rxport = GPIOC,
   .rxpin = 7,
   .rxaf = 8,
   .txport = GPIOC,
   .txpin = 6,
   .txaf = 8,
   .irq = USART6_IRQn,
   .prio = 7,
   .apb_clock = SYSTIME_TPS / 2,
   .baudrate = 115200,
};
const struct uart_config uart4 =
{
   .rxfifo = rxfifo2,
   .rxsz = sizeof rxfifo2,
   .txfifo = txfifo[2],
   .txsz = sizeof txfifo[2],
   .idx = &idx[2],
   .regs = UART4,
   .rxport = GPIOC,
   .rxpin = 11,
   .rxaf = 8,
   .txport = GPIOC,
   .txpin = 10,
   .txaf = 8,
   .irq = UART4_IRQn,
   .prio = 7,
   .apb_clock = SYSTIME_TPS / 4,
   .baudrate = 115200,
};
static const struct uart_config* const uart[NSLAVES] =
{
   &uart3, &uart6, &uart4
};

void
usart3_handler(void)
{
   uart_handler(&uart3);
}

void
usart6_handler(void)
{
   uart_handler(&uart6);
}

void
uart4_handler(void)
{
   uart_handler(&uart4);
}

void
stm32load_init(void)
{
/*
   // reset line for the pilot MCU, enable pull-up, configure as open-drain
   GPIOD->BSRR    = 1 << 11;
   GPIOD->OTYPER |= GPIO_OTYPER_OT_11;
   GPIOD->PUPDR  |= GPIO_PUPDR_PUPDR11_0;
   GPIOD->MODER  |= GPIO_MODER_MODER11_0;
   // reset line for the DFB MCU, configure as GPO
   GPIOG->BSRR    = 1 << 7;
   GPIOG->OTYPER |= GPIO_OTYPER_OT_7;
   GPIOG->PUPDR  |= GPIO_PUPDR_PUPDR7_0;
   GPIOG->MODER  |= GPIO_MODER_MODER7_0;
   // reset line for the SBS MCU, configure as GPO
   GPIOA->BSRR    = 1 << 11;
   GPIOA->OTYPER |= GPIO_OTYPER_OT_11;
   GPIOA->PUPDR  |= GPIO_PUPDR_PUPDR11_0;
   GPIOA->MODER  |= GPIO_MODER_MODER11_0;
*/
   for (int i = 0; i < NSLAVES; i++)
   {
       uart_init(uart[i], true);
       PT_INIT(&boot_pt[i]);
   }
}

static
PT_THREAD(getbyte_thread(struct pt* pt, int* ret))
{
   static unsigned int start[NSLAVES];
   PT_BEGIN(pt);
   start[slave] = systime_ticks();
   do
   {
       PT_YIELD(pt);
       if (uart_rxcount(uart[slave]) > 0)
       {
           *ret = uart_getbyte(uart[slave]);
           PT_EXIT(pt);
       }
   }
   while (systime_ticks() - start[slave] < SYSTIME_TPS / 10);
   *ret = -1;
   PT_END(pt);
}

static void
send_address(int addr)
{
   int csum = 0;
   for (int j = 24; j >= 0; j -= 8)
   {
       uart_putbyte(uart[slave], addr >> j);
       csum ^= addr >> j;
   }
   uart_putbyte(uart[slave], csum);
}

static
PT_THREAD(readmem_thread(struct pt* pt, struct mem_op_data* md, bool* success))
{
   static struct pt slave_pt[NSLAVES];
   int byte;
   *success = false;
   PT_BEGIN(pt);
   // send command code
   uart_putbytes(uart[slave], "\x11\xEE", 2);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   send_address(md->addr);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   // send number of bytes
   uart_putbyte(uart[slave], md->len - 1);
   uart_putbyte(uart[slave], ~(md->len - 1));
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   // receive data
   static int i[NSLAVES];
   for (i[slave] = 0; i[slave] < md->len; i[slave]++)
   {
       PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
       if (byte < 0)
       {
           PT_EXIT(pt);
       }
       md->ptr[i[slave]] = byte;
   }
   *success = true;
   PT_END(pt);
}

static
PT_THREAD(writemem_thread(struct pt* pt, struct mem_op_data* md, bool* success))
{
   static struct pt slave_pt[NSLAVES];
   int byte;
   *success = false;
   PT_BEGIN(pt);
   // send command code
   uart_putbytes(uart[slave], "\x31\xCE", 2);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   send_address(md->addr);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   // send number of bytes
   uart_putbyte(uart[slave], md->len - 1);
   static int i[NSLAVES], csum[NSLAVES];
   csum[slave] = md->len - 1;
   for (i[slave] = 0; i[slave] < md->len; i[slave]++)
   {
       PT_WAIT_WHILE(pt, uart_txcount(uart[slave]) == 0);
       uart_putbyte(uart[slave], md->ptr[i[slave]]);
       csum[slave] ^= md->ptr[i[slave]];
   }
   uart_putbyte(uart[slave], csum[slave]);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   *success = true;
   PT_END(pt);
}

static
PT_THREAD(erase_thread(struct pt* pt, int sector, bool* success))
{
   static struct pt slave_pt[NSLAVES];
   static unsigned int start[NSLAVES];
   int byte;
   *success = false;
   PT_BEGIN(pt);
   // send command code
   uart_putbytes(uart[slave], "\x44\xBB", 2);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   char buf[5] = "\0\0\0";
   buf[3] = buf[4] = sector;
   uart_putbytes(uart[slave], buf, 5);
   start[slave] = systime_ticks();
   do
   {
       PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   }
   while (byte < 0 && systime_ticks() - start[slave] < 5 * SYSTIME_TPS);
   if (byte != ACK)
   {
       PT_EXIT(pt);
   }
   *success = true;
   PT_END(pt);
}

static
PT_THREAD(boot_thread(struct pt* pt))
{
   static unsigned int i[NSLAVES], attempt[NSLAVES];
   static struct pt slave_pt[NSLAVES];
   int byte;
   bool success;
   PT_BEGIN(pt);
   static const int reset_mask[NSLAVES] = {
       SHREG2_RSTPLT, SHREG2_RSTDFB, SHREG2_RSTSBS
   };
reset:
   // assert reset
   shiftreg2_modify(reset_mask[slave], 0);
   // deassert reset
   shiftreg2_modify(0, reset_mask[slave]);
   // initial delay, wait for bootloader to become ready
   i[slave] = systime_ticks();
   PT_WAIT_WHILE(pt, systime_ticks() - i[slave] < SYSTIME_TPS / 5);
   uart_flush(uart[slave]);
   // send 0x7F for automatic baud rate detection
   uart_putbyte(uart[slave], 0x7F);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       goto error;
   }
   // synchronization successful
   static uint8_t buf[0x24];
   static bool buflock;
   static struct mem_op_data md[NSLAVES];
   // read first 0x24 bytes
   md[slave].addr = 0x08000000;
   md[slave].len = 0x24;
   md[slave].ptr = buf;
   PT_WAIT_WHILE(pt, buflock);
   buflock = true;
   PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
   buflock = false;
   if (!success)
   {
       goto error;
   }
   // compare with same in image
   static const uint8_t* const image[NSLAVES] = { image_pilot, image_dfb, image_sbs };
   static const int image_size[NSLAVES] = { sizeof image_pilot, sizeof image_dfb, sizeof image_sbs };
   if (memcmp(buf, image[slave], 0x24) != 0)
   {
       goto burn;
   }
   // read last 16 bytes
   md[slave].addr = 0x08000000 + image_size[slave] - 16;
   md[slave].len = 16;
   md[slave].ptr = buf;
   PT_WAIT_WHILE(pt, buflock);
   buflock = true;
   PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
   buflock = false;
   if (!success)
   {
       goto error;
   }
   // compare with same in image
   if (memcmp(buf, image[slave] + image_size[slave] - 16, 16) != 0)
   {
       goto burn;
   }
   else
   {
       goto done;
   }
burn:
   burning = true;
   if (slave == 0)
   {
       // set voltage range to speed up flash erase/programming
       md[slave].addr = 0xFFFF0000;
       md[slave].len = 1;
       md[slave].ptr = (void*)"\x03";
       PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
       if (!success)
       {
           goto error;
       }
   }
   static const int sector_size[NSLAVES][16] = {
       { 0x4000, 0x4000, 0x4000, 0x4000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000 },
       { 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 },
       { 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 }
   };
   static int sector[NSLAVES], upper_boundary[NSLAVES];
   // start burning from address 0x24 up, save first 0x24 bytes for last
   i[slave] = 0x24;
   sector[slave] = 0;
   upper_boundary[slave] = 0;
   while (i[slave] < image_size[slave])
   {
       upper_boundary[slave] += sector_size[slave][sector[slave]];
       upper_boundary[slave] = (upper_boundary[slave] > image_size[slave]) ?
                               image_size[slave] : upper_boundary[slave];
       // erase sector
       PT_SPAWN(pt, &slave_pt[slave], erase_thread(&slave_pt[slave], sector[slave], &success));
       if (!success)
       {
           goto error;
       }
       // program firmware
       while (i[slave] < upper_boundary[slave])
       {
           int len;
           len = upper_boundary[slave] - i[slave];
           len = (len > 256) ? 256 : len;
           md[slave].addr = 0x08000000 | i[slave];
           md[slave].len = len;
           md[slave].ptr = (void*)&image[slave][i[slave]];
           i[slave] += len;
           PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
           if (!success)
           {
               goto error;
           }
       }
       sector[slave]++;
   }
   // program last chunk
   md[slave].addr = 0x08000000;
   md[slave].len = 0x24;
   md[slave].ptr = (void*)image[slave];
   PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
   if (!success)
   {
       goto error;
   }
   // verify
   i[slave] = 0;
   while (i[slave] < image_size[slave])
   {
       static uint8_t buf[256];
       int len = (i[slave] + 256 > image_size[slave]) ? image_size[slave] - i[slave] : 256;
       md[slave].addr = 0x08000000 + i[slave];
       md[slave].len = len;
       md[slave].ptr = buf;
       PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
       if (memcmp(buf, image[slave] + i[slave], md[slave].len) != 0)
       {
           goto error;
       }
       i[slave] += md[slave].len;
   }
done:
   // issue Go command
   uart_putbytes(uart[slave], "\x21\xDE", 2);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       goto error;
   }
   uart_putbytes(uart[slave], "\x08\x00\x00\x00\x08", 5);
   PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
   if (byte != ACK)
   {
       goto error;
   }
   uart_parityoff(uart[slave]);
   uart_setrate(uart[slave], 115200);
   PT_EXIT(pt);
error:
   if (attempt[slave]++ < 3)
   {
       goto reset;
   }
   myassert(0);
   PT_END(pt);
}

bool
stm32load_poll(void)
{
   static bool done[NSLAVES];
   static int done_count;
   if (!done[slave])
   {
       if (PT_SCHEDULE(boot_thread(&boot_pt[slave])) == 0)
       {
           done[slave] = true;
           done_count++;
       }
   }
   slave = (slave + 1) % NSLAVES;
   return done_count == NSLAVES;
}

bool
stm32load_burn(void)
{
   return burning;
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:

Ай-яй-яй! goto используете! не комильфо :rolleyes:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

PT_THREAD...

А не тот ли это протопоток который Адаму Данкелсу в страшном сне приснился? :biggrin:

Кстати надо бы разобраться, как по-человечески протопотоки использовать :smile3046:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...