Системный загрузчик
Как ни странно путешеcтвуя по даташиту STM32F205 наткнулся на такое :

Как раз надо было разобраться с обновлением прошивки на готовых (проданных) устройствах STM32.
Поскольку мы программируем STM32 через SWD (JTag) и (как мы думали) снесли все загрузчики к чертовой матери встала вопрос как в контроллер установить загрузчик.
Фраза из даташита: The boot loader is located in system memory. It is used to reprogram the Flash memory by using USART1 (PA9/PA10), USART3 (PC10/PC11 or PB10/PB11), CAN2 (PB5/PB13), USB OTG FS in Device mode (PA11/PA12) through DFU (device firmware upgrade)
— ставит сразу много вопросов : где этот системный загрузчик, можно ли его переустановить и по каким интерфейсам и т.д.
Да (как оказывается) системный загрузчик существует (и по-видимому всегда) и обновляется программой ST Link Upgrade (и тут есть важный нюанс, что запускается системный бутлодер только при подтянутой к 1 ножке boot0 контроллера).

Есть такой документ STM32 microcontroller system memory boot mode (December 2019 AN2606 Rev 421/3581AN2606 Application note ), в котором сказано, что вариантов интерфейсов по которым можно обновлять ПО несколько. Этот файл можно скачать внизу страницы.
The bootloader is stored in the internal boot ROM memory (system memory) of STM32 devices. It is programmed by ST during production. Its main task is to download the application program to the internal Flash memory through one of the available serial peripherals (USART, CAN, USB, I2C, SPI, etc.). A communication protocol is defined for each serial interface, with a compatible command set and sequences. This document applies to the products listed in Table1. They are referred as STM32 throughout the document.
Там описываются встроенные системные загрузчики от производителя, которые удалить нельзя, но можно обновить .
В этом документе явно указано по какому адресу начинается системный закрузчик для данного типа контроллера STM32 :

А можно ли перейти по этому адресу из основного цикла программы для активации загрузчика ? Есть ролик в ютюбе , где показано , что это можно.
По поводу определения версии бутлодера есть оказываются конечно же открытые команды GetId и GetVersion.
Можно послать по UART пару байт и определить версию бутлодера:

Возможно программа ST-LINK UTILITY показывает , но не факт :
ARM, RISC-V контроллеры
AN2606 — STM32 microcontroller system memory boot mode. Для каждой серии написано какие возможности загрузки через встроенный бутлоадер вплоть до имен ножек. UP: Слушает сразу все доступные для загрузки интерфейсы (см. AN2606). Отвечает по тому интерфейсу с которого пришел запрос.
-
- вот дурдом, STM32F031 есть а STM32F030 нет. И получается (к примеру для STM32F031), что не все ножки USART опрашиваются. — Лагунов (27.01.2015 05:58 )
- у F030 UART1 на PA9/PA10 — zeleny (27.01.2015 06:16 — 06:35 )
- да уже понял. Но мне потом UART и в штатном режиме нужен. А при этом PA9 у меня — выход таймера (TIM1_CH2), которого нигде больше нет. И как я их разрулю? Для загрузки перемычки менять? Вроде можно для UART использовать РА14,15. Но там ведь SWCLK.Лагунов (99 знак., 27.01.2015 06:49 )
- РА9 можно коммутировать транзистором. Или шить через SWD — zeleny (27.01.2015 13:38 )
- Качнул с сайта последний AN2606 — там F030 присутствует.Ig_Ra (228 знак., 27.01.2015 12:16 )
Лето 7532 от сотворения мира. При использовании материалов сайта ссылка на caxapу обязательна. Вебмастер
MMI © MMXXIVStm32 с чего начать изучение.
datasheet писал(а): 2.3.8 Boot modes
At startup, boot pins are used to select one of three boot options:
Boot from user Flash
Boot from System memory
Boot from embedded SRAM
The boot loader is located in System memory. It is used to reprogram the Flash memory by
using USART1. For further details refer to AN2606, available on www.st.com.- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 15:18:51
Martian , я хочу попробовать прошить ему загрузчик с https://www.st.com/en/embedded-software . t-software но мне пишит недоступно для скачивания хотя стоит анонимайзер
- Аlex
- Сообщений: 4509
- Зарегистрирован: Чт мар 18, 2010 23:09:57
- Откуда: Планета Земля
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 15:33:34
Di123 , через VPN качается без проблем.
https://disk.yandex.ru/d/RYcedrjlr1R5fA- Martian
- Сообщений: 9821
- Зарегистрирован: Сб дек 18, 2021 19:25:32
- Сайт
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 15:33:41
не знаю, что такое анонимайзер и зачем он. Наверное, тырит данные кредиток? У меня стоит Hola VPN, прекрасно пускает на все зарубежные ресурсы (за исключением соцсетей, но они совершенно не нужны)
- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 19:03:26
Никогда не понимал любовь к встроенным бутам.
АРМ заслуживает покупки китайского Jlink-а.- Martian
- Сообщений: 9821
- Зарегистрирован: Сб дек 18, 2021 19:25:32
- Сайт
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 19:07:42
Возможность дешевого и простого обновления для конечного пользователя?
- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 19:36:02
Я имел ввиду не конечного пользователя, а разработчика. Профессионального или любителя — не важно. А для чего нужен бут — я конечно знаю. У меня практически во всех серийных изделиях он есть. Правда кастомный, но это (кастомность) издержки бизнеса в котором я работаю.
- Martian
- Сообщений: 9821
- Зарегистрирован: Сб дек 18, 2021 19:25:32
- Сайт
Re: Stm32 с чего начать изучение.
Вс июл 16, 2023 19:41:19
Ну, я потому и поставил вопросительный знак
Да, в этом случае несколько странно. Наверное, объяснимо только в первые разы, когда никаких познаний об отладке и лишь загрузка чужих готовых программок.- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 11:09:52
КРАМ , я заложил оба варианта загрузок

ток жизнь и это предусмотрела и они оба не работают
а так да я бы оставил только уарт ибо имея любую плату у которой есть загрузчик нано уно мега есп . любой может прошить контролер при наличии загрузчика в нём наверное
пока что удавалось такну и stm32cube programmer тоже не видит через стлинк сейчас проверил
- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 11:30:09
programmer тоже не видит через стлинк сейчас проверил
Варианты отсутствия коннекта могут быть разные. Например, отсутствует питание контроллера или перепутаны выводы дата и клоков в SWD. Еще может быть активирован режим коннекта после ресета. То есть нужно иметь кнопку на nRST или возможность коротнуть nRST на общий перед запуском коннекта (примерно через полсекунды после запуска нужно отпустить nRST). Ну и на этом самом nRST желательно иметь внешнюю подтяжку примерно 10 кОм к питанию МК.
Но по любому, тактирование МК при коннекте с программатором не требуется.
Кстати, у меня все разъемы программирования АРМов имеют ПЯТЬ контактов: nRST/ +3,3/ общий/ SWD/ SWC.я бы оставил только уарт ибо имея любую плату у которой есть загрузчик
Писать что либо под не совсем примитивный МК без отладчика — это лютый мазохизм.
Отладчик типа JTAG/SWD позволяет практически в реальном времени смотреть и изменять переменные, смотреть флеш, ставить бряки, исполнять код по шагам и кучу других удобных ништяков.- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 11:44:43
не у меня все ножки подписаны на мк что бы не было путаницы

питание везде мерил
и на ресет у меня стоит кнопка притянутая к плюсу
единственное я не вывел ВООТ1 а посадил его на минус а заливка загрузчика как я понил его надо вроде кинуть на плюс
и отрезать минус и напаять на ножку плюс посути для меня уже невозможно
этот ресет я уже как нежал не зажимал без разницыда я видел видео где StFlasher 3 использовался как прошивка в реальном времени при изменении данных в коде
Добавлено after 1 minute 46 seconds:
я всё же склоняюсь что это голый чип- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 11:57:59
на ресет у меня стоит кнопка притянутая к плюсу
И что?
Нужно или в рукопашную на нее жать в момент коннекта, либо вывести ее на STlink.Добавлено after 2 minutes 16 seconds:
я всё же склоняюсь что это голый чип
В смысле? Какое еще «голый»? Для коннекта и прошивки через программатор никаких внутренних софтовых загрузчиков не требуется. В МК пишет аппаратный автомат под управлением SWD.
ЗЫ. Есть смутные подозрения. А обнародуйте фото вашей платы с МК так, чтобы можно было разглядеть сам МК и ближайшее к нему окружение.
- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:22:08
познания у меня маленькие но то что я подчерпнул от макетки на блупил там ничего ресетить не надо
ВООТ1 и ВООТ0 на минусе их вообще не касаемся
просто подключаем стлинк и жмём конект а далее прошиваем а после прошивки жмём ресет что бы код применился
по этому принципу я и развёл платуресетить надо перед заливкой по уарту как мне помнится
- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:29:54
Помнится не правильно.
И фото я просил с припаянным чипом так, чтобы текст на МК хорошо читался.
Мутный кадр печатной платы не интересен- tonyk
- Сообщений: 1022
- Зарегистрирован: Вт ноя 19, 2019 06:10:18
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:38:23
КРАМ писал(а): Для коннекта и прошивки через программатор никаких внутренних софтовых загрузчиков не требуется. В МК пишет аппаратный автомат под управлением SWD.
КРАМ, поточней с формулировками.
Я не сомневаюсь, что КРАМ знает, как и что пишется в АРМ на самом деле.- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:45:04

- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:48:21
КРАМ, поточней с формулировками.
Что не так с формулировками?
Понятно, что автомат пишущий под SWD — это тоже какой то код этого самого автомата. Но к штатному бутлоадеру он не имеет отношения.- Di123
- Сообщений: 931
- Зарегистрирован: Пт авг 05, 2016 04:47:49
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:48:29

- КРАМ
- Сообщений: 21207
- Зарегистрирован: Чт янв 10, 2008 22:01:02
- Откуда: Московская область, Фрязино
Re: Stm32 с чего начать изучение.
Пн июл 17, 2023 12:57:24
Не, с ориентацией микросхемы все норм. Правда плата разведена отвратительно, словно это УНЧ, но к проблеме отсутствия коннекта это отношения не имеет.
Схему тоже приведите пожалуйста. Нужно посмотреть как вы развели питание. У вас AVDD питается от отдельного источника. Вы должны понимать, что на AVDD висит не только АЦП, но и домен осциллятора.Powered by phpBB © phpBB Group.
phpBB Mobile / SEO by Artodia.
STM32 — прошивка «по воздуху» через ESP

В статье описано как через интернет прошить микроконтроллер STM32 используя WIFI-модуль ESP8266 , то есть организовать удалённое обновление программного обеспечения. Прошивка осуществляется через USART_1 в режиме системного загрузчика, подробнее про это смотрите здесь.
Подопытная плата Wemos (ESP8266), программироваться будет в IDE Arduino .
Задача такова, нужно соединить STM с ESP, передать на ESP (через интернет) прошивку, далее ESP должна подтянуть BOOT_0 к «плюсу», нажать ресет, и загрузить прошивку в STM через USART_1. Однако прежде надо научится общаться с системным загрузчиком — там не всё так просто как мне думалось по началу, но и сложного ничего нет. Вот мануал на русском языке (обязательно прочтите), описывающий протокол обмена данными между бутлоадером и внешним устройством.
В мануале говориться что максимальная скорость USART’а не должна превышать 115200 (при увеличении растет погрешность). В примере я указал 57600 так как такая скорость используется по умолчанию утилитой stm32flash. Тем не менее плата успешно прошивалась даже на скорости 921600.
Чтобы «договориться» с загрузчиком, нужно послать ему один байт (0x7F), загрузчик принимает этот байт и по нему распознаёт скорость USART’а. Если байт успешно принят (скорость определена), то загрузчик в ответ посылает байт подтверждения 0x79 — ACK-байт . После этого можно посылать загрузчику различные команды. Большая часть команд состоит из двух байт. На все команды, если они успешно выполнены, загрузчик отвечает байтом подтверждения.
Прежде чем браться за ESP, я написал программу для Arduino Mega (и для stm тоже), чтоб потренироваться «разговаривать» с загрузчиком…
#include #include #define Usart_stm32 Serial1 #define WRITE_ADDR 0x08000000 // адрес для заливки прошивки #define SIZE_WRITE 256 #define FIRMWARE "usart3.bin" #define BOOT_PIN 5 #define RESET_PIN 6 void setup() < Serial.begin(57600); Usart_stm32.begin(57600, SERIAL_8E1); pinMode(BOOT_PIN, OUTPUT); // boot_0 digitalWrite(BOOT_PIN, LOW); pinMode(RESET_PIN, OUTPUT); // reset digitalWrite(RESET_PIN, LOW); if(!SD.begin(53)) Serial.println(F("Card failed - ERROR")); else Serial.println(F("Card initialized - OK")); >void loop() < if(Serial.available()) < uint8_t res = Serial.read(); /////////////// первый запрос для установки скорости (в ответ ждёт 0x79 - ACK-байт)/////////////// if(res == 'B') < entr_bootloader(); >////////////////////////////////// boot_off_and_reset ///////////////////////////////////// else if(res == 'R') < boot_off_and_reset(); >////////////////////////////////// Erase Memory ///////////////////////////////////// else if(res == 'E') < erase_memory(); >///////////////////////////////////// Write_Memory //////////////////////////////////////// else if(res == 'W') < write_memory(); >///////////////////////////////////// on_boot() //////////////////////////////////////// else if(res == 'h') < on_boot(); >///////////////////////////////////// off_boot() //////////////////////////////////////// else if(res == 'l') < off_boot(); >///////////////////////////////////// on_reset() //////////////////////////////////////// else if(res == 'r') < on_reset(); >> > //////////////////////////// entr_bootloader ///////////////////////////////////// void entr_bootloader() < on_off_boot(HIGH); // подтягиваем BOOT_0 к плюсу delay(500); on_reset(); // нажимаем ресет delay(200); while(Usart_stm32.available()) < Usart_stm32.read(); >// в приёмном буфере может быть мусор Usart_stm32.write(0x7F); // первый запрос (для определения скорости) if(ack_byte() == 0) Serial.println(F("Bootloader - OK")); // Log else Serial.println(F("Bootloader - ERROR")); // Log > //////////////////////////// write_memory ///////////////////////////////////// void write_memory() < if(erase_memory() == 0) < File df = SD.open(FIRMWARE); if(df) < uint32_t size_file = df.size(); Serial.print(F("Size file ")); Serial.println(size_file); uint8_t cmd_array[2] = ; // код Write Memory uint32_t count_addr = 0; uint16_t len = 0; uint32_t seek_len = 0; while(true) < if(send_cmd(cmd_array) == 0) < uint8_t ret_adr = send_adress(WRITE_ADDR + count_addr); count_addr = count_addr + SIZE_WRITE; if(ret_adr == 0) < uint8_t write_buff[SIZE_WRITE] = ; len = df.read(write_buff, SIZE_WRITE); seek_len++; df.seek(SIZE_WRITE * seek_len); //write_memory(write_buff, len);file.position() //Serial.print(F("seek_len ")); //Serial.println(seek_len); //Serial.println(df.position()); uint8_t cs, buf[SIZE_WRITE + 2]; uint16_t i, aligned_len; aligned_len = (len + 3) & ~3; cs = aligned_len - 1; buf[0] = aligned_len - 1; for(i = 0; i < len; i++) < cs ^= write_buff[i]; buf[i + 1] = write_buff[i]; >for(i = len; i < aligned_len; i++) < cs ^= 0xFF; buf[i + 1] = 0xFF; >buf[aligned_len + 1] = cs; Usart_stm32.write(buf, aligned_len + 2); uint8_t ab = ack_byte(); if(ab != 0) < Serial.println(F("Block not Write Memory - ERROR")); break; >if(size_file == df.position()) < Serial.println(F("End Write Memory2 - OK")); boot_off_and_reset(); break; >> else < Serial.println(F("Address Write Memory - ERROR")); // Log break; >> else < Serial.println(F("Cmd cod Write Memory - ERROR")); // Log break; >> // end while df.close(); > else Serial.println(F("Not file - ERROR")); > else Serial.println(F("Not erase Write Memory - ERROR")); > ////////////////////////////// erase_memory //////////////////////////////////// uint8_t erase_memory() < uint8_t cmd_array[2] = ; // команда на стирание if(send_cmd(cmd_array) == 0) < uint8_t cmd_array[2] = ; // код стирания (полное) if(send_cmd(cmd_array) == 0) < Serial.println(F("Erase Memory - OK")); // Log return 0; >else Serial.println(F("Cmd cod Erase Memory - ERROR")); // Log > else Serial.println(F("Cmd start Erase Memory - ERROR")); // Log return 1; > ////////////////////////////// send_cmd //////////////////////////////////// uint8_t send_cmd(uint8_t *cmd_array) < Usart_stm32.write(cmd_array, 2); if(ack_byte() == 0) return 0; else return 1; >/////////////////////////////// ack_byte //////////////////////////////////// uint8_t ack_byte() < for(uint16_t i = 0; i < 500; i++) < if(Usart_stm32.available()) < uint8_t res = Usart_stm32.read(); if(res == 0x79) return 0; >delay(1); > return 1; > ///////////////////////////// send_adress //////////////////////////////////// uint8_t send_adress(uint32_t addr) < uint8_t buf[5] = ; buf[0] = addr >> 24; buf[1] = (addr >> 16) & 0xFF; buf[2] = (addr >> 8) & 0xFF; buf[3] = addr & 0xFF; buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; Usart_stm32.write(buf, 5); if(ack_byte() == 0) return 0; else return 1; > ////////////////////////////// on_reset ////////////////////////////////////// void on_reset() < digitalWrite(RESET_PIN, HIGH); // reset delay(50); digitalWrite(RESET_PIN, LOW); >////////////////////////////// on_off_boot /////////////////////////////////// void on_off_boot(uint8_t i) < digitalWrite(BOOT_PIN, i); >//////////////////////////// boot_off_and_reset ////////////////////////////// void boot_off_and_reset() < on_off_boot(LOW); delay(500); on_reset(); Serial.println(F("Boot off and reset")); // Log >////////////////////////////// on_boot /////////////////////////////////// void on_boot() < digitalWrite(BOOT_PIN, HIGH); >////////////////////////////// off_boot /////////////////////////////////// void off_boot()Загружаемая прошивка хранится на SD карте.
Serial1 — соединён с USART’ом stm32. Настраивается на работу с битом чётности (SERIAL_8E1).
Serial — смотрим отладочную инфу, и «рулим» ардуиной.
WRITE_ADDR — начальный адрес для загрузки новой прошивки.
SIZE_WRITE — прошивка загружается в stm блоками, максимальный размер блока 256 байт.
Передача каждого блока происходит так: передаётся команда (0x31, 0xCE), в ответ приходит байт подтверждения, передаётся адрес по которому будет записан блок (адрес каждого последующего блока сдвигается на длину размера блока), в ответ приходит байт подтверждения, передаётся блок, в ответ приходит байт подтверждения, и т.д. Соответственно, чем больше размер блока, тем меньше будет промежуточных операций.
Файл размером 64Кб, на скорости 57600, прошивается за ~15сек.
Я где-то встречал неподтверждённую инфу, что у некоторых МК максимальный размер блока 128 байт.
FIRMWARE — название файла (прошивки). Программа работает только с bin-файлами .
D5 — реле для управления BOOT_0.

2 — на пин BOOT_0. У платы BluePill это центральный штырёк, на который надет джампер.
3 — на «минус».
1 — на «плюс».В отключённом состоянии будет тянуть BOOT_0 на «минус», а при включении на «плюс».
D6 — управляет ресетом. Коротит ресет на «минус» через NPN транзистор.

RX и TX нужно подтянуть к «плюсу» резисторами ~20кОм.

Подключайте все железки, прошивайте ардуину, открывайте и отправляйте ей команды…
B — активирует загрузчик (подтягивает BOOT_0 к «плюсу»), нажимает ресет, отправляет байт для определения скорости (0x7F) и ожидает ACK-байт. Если ACK-байт пришёл, значит МК готов к приёму команд.
R — деактивирует загрузчик и нажимает ресет.
E — посылает команду очистки всей памяти.
W — загружает прошивку в stm32. Сначала вызывается функция очистки памяти — erase_memory() , и если ACK-байт вернулся, то происходит открытие файла, которое тоже проверяется. Далее в бесконечном цикле отправляется команда на запись, потом начальный адрес для записи первого блока (вместе с контрольной суммой адреса), а потом из файла читается блок, создаётся контрольная сумма блока, и это отправляется в stm. Если файл не закончился, то цикл повторяется. При каждом следующем цикле адрес для записи смещается на длину блока. Так повторяется до тех пор пока файл не закончится. Все действия постоянно проверяются на возврат ACK-байта. Если всё прошло успешно, то загрузчик отключается и нажимается ресет, то есть МК переводится в рабочее состояние.
Чтоб загрузить прошивку, нужно положить её на SD-карту, указать имя в дефайне (имя не должно быть длиннее 8 символов), отправить B, дождаться ответа Bootloader — OK, и отправить W…

Обязательно укажите — «Нет конца строки».
Если SD-карты нет, то просто поклацайте релюшкой и ресетом с помощью последних трёх команд, или добавьте в код команды для запуска существующей в МК прогрограммы и вывода ID…
команды
/////////////////////////////// Go Запуск программы ////////////////////////////////// else if(res == 'g') // Go Запуск программы < Serial.println(F("Start Programm")); // Log uint8_t cmd_array[2] = ; // код запуска программы if(send_cmd(cmd_array) == 0) < Serial.println(F("Cmd start programm - OK")); // Log uint8_t ret_adr = send_adress(WRITE_ADDR); if(ret_adr == 0) < Serial.println(F("Start programm - OK")); // Log >else Serial.println(F("Address start programm - ERROR")); // Log > else Serial.println(F("Cmd start programm - ERROR")); // Log > //////////////////////////////////////// Get ID ////////////////////////////////////////// else if(res == 'i') < Serial.println(F("Start Get ID")); // Log uint8_t cmd_array[2] = ; // код Get ID if(send_cmd(cmd_array) == 0) < Serial.println(F("Cmd Get ID - OK")); // Log for(uint16_t i = 0; i < 500; i++) < delay(1); if(Usart_stm32.available()) < uint8_t id[4] = ; for(uint8_t i = 0; i < 4; i++) < id[i] = Usart_stm32.read(); >if(id[3] == 0x79) < uint16_t stm_id = 0; stm_id = id[1]; stm_id = (stm_id else Serial.println(F("Not ID - ERROR")); // Log > > > else Serial.println(F("Cmd Get ID - ERROR")); // Log >Войдите в режим бутлоадера и пошлите символ g или i.
Если же и ардуины нет, то вот то же самое для stm32. В архиве два примера, один с SD-картой, другой без — просто отправка команд. Общение с целевой платой сделано через USART_1, логи и управление через USB. Карта подключается к SPI2. Весь механизм прописан в файле to_stm.c
ESP подключается так же как и ардуина…

С протами вывода здесь всё наоборот, Serial работает с stm, а Serial1 (в ESP у Serial1 есть только TX) печатает логи (они закомментированы).
Код для ESP
#include #include #include #include #include #define WRITE_ADDR 0x08000000 // адрес для заливки прошивки #define SIZE_WRITE 256 #define FIRMWARE "/esptostm.bin" // перед именем должен стоять слеш #define BOOT_PIN D5 #define RESET_PIN D6 #define Usart_stm32 Serial //#define DBG_OUTPUT_PORT Serial1 const char* ssid = "istarik"; const char* password = "SssfsfsfsfDf1"; const char* host = "esptostm32"; // адрес - http://esptostm32.local/ ESP8266WebServer server(80); File fsUploadFile; String getContentType(String filename) < if (server.hasArg("download")) < return "application/octet-stream"; >else if (filename.endsWith(".htm")) < return "text/html"; >else if (filename.endsWith(".html")) < return "text/html"; >else if (filename.endsWith(".css")) < return "text/css"; >else if (filename.endsWith(".js")) < return "application/javascript"; >return "text/plain"; > bool handleFileRead(String path) < //DBG_OUTPUT_PORT.println("handleFileRead: " + path); if (path.endsWith("/")) < path += "edit.htm"; >String contentType = getContentType(path); if(SPIFFS.exists(path)) < File file = SPIFFS.open(path, "r"); server.streamFile(file, contentType); file.close(); return true; >return false; > void handleFileUpload() < if(server.uri() != "/edit") return; HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START) < String filename = upload.filename; if(!filename.startsWith("/")) filename = "/" + filename; //DBG_OUTPUT_PORT.print("handleFileUpload Name: "); //DBG_OUTPUT_PORT.println(filename); fsUploadFile = SPIFFS.open(filename, "w"); filename = String(); >else if(upload.status == UPLOAD_FILE_WRITE) < if(fsUploadFile) fsUploadFile.write(upload.buf, upload.currentSize); >else if(upload.status == UPLOAD_FILE_END) < if(fsUploadFile) fsUploadFile.close(); //DBG_OUTPUT_PORT.print("handleFileUpload Size: "); //DBG_OUTPUT_PORT.println(upload.totalSize); >> void handleFileDelete() < if(server.args() == 0) return server.send(500, "text/plain", "BAD ARGS"); String path = server.arg(0); //DBG_OUTPUT_PORT.println("handleFileDelete: " + path); if(path == "/") return server.send(500, "text/plain", "BAD PATH"); if(!SPIFFS.exists(path)) return server.send(404, "text/plain", "FileNotFound"); SPIFFS.remove(path); server.send(200, "text/plain", ""); path = String(); >void handleFileList() < if (!server.hasArg("dir")) < server.send(500, "text/plain", "BAD ARGS"); return; >String path = server.arg("dir"); //DBG_OUTPUT_PORT.println("handleFileList: " + path); Dir dir = SPIFFS.openDir(path); path = String(); String output = "["; while (dir.next()) < File entry = dir.openFile("r"); if(output != "[") output += ','; bool isDir = false; output += ""; entry.close(); > output += "]"; server.send(200, "text/json", output); > ///////////////////////////////////// Entr_bootloader ////////////////////////////////////////// void handleEntr_bootloader() < //DBG_OUTPUT_PORT.printf("Entr_bootloader\n"); entr_bootloader(); >///////////////////////////////////// Write ////////////////////////////////////////// void handleWrite() < //DBG_OUTPUT_PORT.printf("Write\n"); write_memory(); >///////////////////////////////////// Reset ////////////////////////////////////////// void handleReset() < //DBG_OUTPUT_PORT.printf("Reset\n"); on_reset(); server.send(200, "text/plain", "Reset - OK"); >///////////////////////////////////// On_boot ////////////////////////////////////////// void handleOn_boot() < //DBG_OUTPUT_PORT.printf("On_boot\n"); on_boot(); server.send(200, "text/plain", "On boot - OK"); >///////////////////////////////////// Off_boot ////////////////////////////////////////// void handleOff_boot() < //DBG_OUTPUT_PORT.printf("Off_boot\n"); off_boot(); server.send(200, "text/plain", "Off boot - OK"); >///////////////////////////////////// Event ////////////////////////////////////////// void handleEvent() < // что-то делаем server.send(200, "text/plain", "Event - OK"); >////////////////////////////////////////////////////////////////////////////////////////// void setup(void) < pinMode(BOOT_PIN, OUTPUT); // boot_0 digitalWrite(BOOT_PIN, LOW); pinMode(RESET_PIN, OUTPUT); // reset digitalWrite(RESET_PIN, LOW); Usart_stm32.begin(57600, SERIAL_8E1); //DBG_OUTPUT_PORT.begin(57600); //DBG_OUTPUT_PORT.print("\n"); //DBG_OUTPUT_PORT.setDebugOutput(true); SPIFFS.begin(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while(WiFi.status() != WL_CONNECTED) < delay(500); //DBG_OUTPUT_PORT.print("."); >//DBG_OUTPUT_PORT.println(""); //DBG_OUTPUT_PORT.print("Connected! IP address: "); //DBG_OUTPUT_PORT.println(WiFi.localIP()); MDNS.begin(host); //DBG_OUTPUT_PORT.print("Open http://"); //DBG_OUTPUT_PORT.print(host); //DBG_OUTPUT_PORT.println(".local/edit to see the file browser"); //SERVER INIT server.on("/list", HTTP_GET, handleFileList); server.on("/enter_boot", HTTP_GET, handleEntr_bootloader); server.on("/write", HTTP_GET, handleWrite); server.on("/reset", HTTP_GET, handleReset); server.on("/on_boot", HTTP_GET, handleOn_boot); server.on("/off_boot", HTTP_GET, handleOff_boot); server.on("/event", HTTP_GET, handleEvent); server.on("/edit", HTTP_DELETE, handleFileDelete); server.on("/edit", HTTP_GET, []() < if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound"); >); server.on("/edit", HTTP_POST, []() < server.send(200, "text/plain", ""); >, handleFileUpload); server.onNotFound([]() < if(!handleFileRead(server.uri())) server.send(404, "text/plain", "FileNotFound"); >); server.begin(); //DBG_OUTPUT_PORT.println("HTTP server started"); > void loop(void) < server.handleClient(); >//////////////////////////// entr_bootloader ///////////////////////////////////// void entr_bootloader() < on_off_boot(HIGH); // подтягиваем BOOT_0 к плюсу delay(500); on_reset(); // нажимаем ресет delay(200); while(Usart_stm32.available()) < Usart_stm32.read(); >// в приёмном буфере может быть мусор Usart_stm32.write(0x7F); // первый запрос (для определения скорости) if(ack_byte() == 0) < //DBG_OUTPUT_PORT.println(F("Bootloader - OK")); // Log server.send(200, "text/plain", "Bootloader - OK"); >else < //DBG_OUTPUT_PORT.println(F("Bootloader - ERROR")); // Log server.send(200, "text/plain", "Bootloader - ERROR"); >> //////////////////////////// write_memory ///////////////////////////////////// void write_memory() < if(erase_memory() == 0) < File df = SPIFFS.open(FIRMWARE, "r"); if(df) < uint32_t size_file = df.size(); //DBG_OUTPUT_PORT.print(F("Size file ")); //DBG_OUTPUT_PORT.println(size_file); uint8_t cmd_array[2] = ; // код Write Memory uint32_t count_addr = 0; uint16_t len = 0; uint32_t seek_len = 0; while(true) < if(send_cmd(cmd_array) == 0) < uint8_t ret_adr = send_adress(WRITE_ADDR + count_addr); count_addr = count_addr + SIZE_WRITE; if(ret_adr == 0) < uint8_t write_buff[SIZE_WRITE] = ; len = df.read(write_buff, SIZE_WRITE); seek_len++; df.seek(SIZE_WRITE * seek_len); uint8_t cs, buf[SIZE_WRITE + 2]; uint16_t i, aligned_len; aligned_len = (len + 3) & ~3; cs = aligned_len - 1; buf[0] = aligned_len - 1; for(i = 0; i < len; i++) < cs ^= write_buff[i]; buf[i + 1] = write_buff[i]; >for(i = len; i < aligned_len; i++) < cs ^= 0xFF; buf[i + 1] = 0xFF; >buf[aligned_len + 1] = cs; Usart_stm32.write(buf, aligned_len + 2); uint8_t ab = ack_byte(); if(ab != 0) < //DBG_OUTPUT_PORT.println(F("Block not Write Memory - ERROR")); server.send(200, "text/plain", "Block not Write Memory - ERROR"); break; >if(size_file == df.position()) < Usart_stm32.begin(57600, SERIAL_8N1); while(Usart_stm32.available()) < Usart_stm32.read(); delay(1); >boot_off_and_reset(); String reciv = "
"; for(uint16_t i = 0; i < 2000; i++) < if(Usart_stm32.available()) < while(Usart_stm32.available()) < char c = Usart_stm32.read(); reciv += c; delay(1); >break; > delay(1); > server.send(200, "text/plain", "Done Write Memory - OK" + reciv); //DBG_OUTPUT_PORT.println(F("Done Write Memory - OK")); Usart_stm32.begin(57600, SERIAL_8E1); break; > > else < server.send(200, "text/plain", "Address Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Address Write Memory - ERROR")); // Log break; >> else < server.send(200, "text/plain", "Cmd cod Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Cmd cod Write Memory - ERROR")); // Log break; >> // end while df.close(); > else server.send(200, "text/plain", "Not file - ERROR"); //DBG_OUTPUT_PORT.println(F("Not file - ERROR")); > else server.send(200, "text/plain", "Not erase Write Memory - ERROR"); //DBG_OUTPUT_PORT.println(F("Not erase Write Memory - ERROR")); > ////////////////////////////// erase_memory //////////////////////////////////// uint8_t erase_memory() < uint8_t cmd_array[2] = ; // команда на стирание if(send_cmd(cmd_array) == 0) < uint8_t cmd_array[2] = ; // код стирания (полное) if(send_cmd(cmd_array) == 0) < //DBG_OUTPUT_PORT.println(F("Erase Memory - OK")); // Log return 0; >//else DBG_OUTPUT_PORT.println(F("Cmd cod Erase Memory - ERROR")); // Log > //else DBG_OUTPUT_PORT.println(F("Cmd start Erase Memory - ERROR")); // Log return 1; > ////////////////////////////// send_cmd //////////////////////////////////// uint8_t send_cmd(uint8_t *cmd_array) < Usart_stm32.write(cmd_array, 2); if(ack_byte() == 0) return 0; else return 1; >/////////////////////////////// ack_byte //////////////////////////////////// uint8_t ack_byte() < for(uint16_t i = 0; i < 500; i++) < if(Usart_stm32.available()) < uint8_t res = Usart_stm32.read(); if(res == 0x79) return 0; >delay(1); > return 1; > ///////////////////////////// send_adress //////////////////////////////////// uint8_t send_adress(uint32_t addr) < uint8_t buf[5] = ; buf[0] = addr >> 24; buf[1] = (addr >> 16) & 0xFF; buf[2] = (addr >> 8) & 0xFF; buf[3] = addr & 0xFF; buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3]; Usart_stm32.write(buf, 5); if(ack_byte() == 0) return 0; else return 1; > ////////////////////////////// on_boot /////////////////////////////////// void on_boot() < digitalWrite(BOOT_PIN, HIGH); >////////////////////////////// off_boot /////////////////////////////////// void off_boot() < digitalWrite(BOOT_PIN, LOW); on_reset(); >////////////////////////////// on_reset ////////////////////////////////////// void on_reset() < digitalWrite(RESET_PIN, HIGH); // reset delay(50); digitalWrite(RESET_PIN, LOW); >////////////////////////////// on_off_boot /////////////////////////////////// void on_off_boot(uint8_t i) < digitalWrite(BOOT_PIN, i); >//////////////////////////// boot_off_and_reset ////////////////////////////// void boot_off_and_reset() < on_off_boot(LOW); delay(500); on_reset(); //DBG_OUTPUT_PORT.println(F("Boot off and reset")); // Log >Всё что касается работы с сетью, взято из стандартного примера FSBrowser , и немного переработано. Код из ардуины полностью перенесён в ESP с той лишь разницей, что управление и загрузка прошивки осуществляется через веб-интерфейс, а вместо SD-карты используется SPIFFS . Про работу с SPIFFS в частности, и про ESP в целом, я подробно писал здесь.
Скачиваем архив и загружаем папку data в ESP как это описано по ссылке выше. В программе вписываем название прошивки, имя своей точки доступа и пароль. Прошиваем ESP и заходим в браузер по аресу esptostm32.local/ …

Загружаем файл прошивки — Обзор ⇨ Upload, ждём когда он появится в левой колонке, жмём Enter boot и при положительном ответе жмём Write. МК прошьётся и перейдёт в обычный режим работы. При последующем обновлении нужно удалить прежний файл правой кнопкой мышки и загрузить новый.
Если в прошивке добавить, перед бесконечным циклом, вывод какой-либо инфы в USART, например так…
/* USER CODE BEGIN 2 */ HAL_UART_Transmit(&huart1, (uint8_t*)"Ver 1.3", 12, 100); /* USER CODE END 2 */… тогда после прошивки это будет выводится в веб-интерфейс, сообщая об успешном обновлении. При дальнейших обновлениях будете изменять версию.
Как вы помните, USART системного загрузчика работает с битом чётности, однако в прошивке учитывать это не нужно, настраивайте как обычно…

… а в функции write_memory() специально для этого сделана переинициализация Serial’а .
С помощью кнопок Reset , On boot и Off boot можно обресетить МК или включить/отключить бутлоадер, но по большому счёту они и не нужны, так, на всякий случай. Кнопка Event вообще ничего не делает (только возвращает «ОК»), мало ли кто-то захочет какую-то функцию прикрутить.
На этом всё.

Всем спасибо


- 5 мая 2019, 14:21
- stD
- 25758
—>
Поддержать автора
Задать вопрос по статье
Известит Вас о новых публикациях
- да уже понял. Но мне потом UART и в штатном режиме нужен. А при этом PA9 у меня — выход таймера (TIM1_CH2), которого нигде больше нет. И как я их разрулю? Для загрузки перемычки менять? Вроде можно для UART использовать РА14,15. Но там ведь SWCLK.Лагунов (99 знак., 27.01.2015 06:49 )
- у F030 UART1 на PA9/PA10 — zeleny (27.01.2015 06:16 — 06:35 )
- вот дурдом, STM32F031 есть а STM32F030 нет. И получается (к примеру для STM32F031), что не все ножки USART опрашиваются. — Лагунов (27.01.2015 05:58 )