jcxz 241 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба 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 байт. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 183 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Нет, не только. Я же пишу, что: 1) чтобы не создавать позиционно-независимый бутлоадер (у нас же две копии его во флешь!); Ох. Осталось переварить как это понять :biggrin: Регионов у меня всего 4, а не 3: BOOTLOADER, 0-й UPDATER, 1-й UPDATER, WORK. Ну да, я без WORK/Application имел в виду. BOOTLOADER сделан максимально простым и на асме и без обращения к какой-либо периферии, чтобы минимизировать возможность багов в нём. Он размером всего около 128 байт. Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше :rolleyes: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Ох. Осталось переварить как это понять :biggrin: Когда Вы линкуете код для выполнения с каких-то адресов, то он и будет только в тех адресах выполняться. Это называется позиционно-зависимый код. Компоновщик его и делает обычно. Так что для двух копий UPDATER мне пришлось бы линковать 2 разные прошивки (если запускать во флешь). Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу). Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше :rolleyes: В первом секторе у меня BOOTLOADER находится (плюс ещё некоторая уникальная инфа, которая зашивается один раз при изготовлении: MAC-адрес, серийный номер; и которая не должна обновляться). А размеры секторов в моём МК: 64КБ, 128КБ, 256КБ. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 183 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу). Дошло, спасибо. На мой взгляд действительно гибко и удобно :laughing: Буду и у себя так делать в следующий раз. Хотя прошиваторы у меня редко меняются, от слова почти никогда. Но иметь такую возможность, особенно если памяти всегда хватает, не помешает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
scifi 1 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба Я нашел кучу примеров 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; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 241 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее: Ай-яй-яй! goto используете! не комильфо :rolleyes: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 183 30 мая, 2018 Опубликовано 30 мая, 2018 · Жалоба PT_THREAD... А не тот ли это протопоток который Адаму Данкелсу в страшном сне приснился? :biggrin: Кстати надо бы разобраться, как по-человечески протопотоки использовать :smile3046: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться