Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прошивка STM32F7 через другой STM32F7 (UART)
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
AVStech
Добрый день! Повторюсь в этой ветке, так как в основном работа с ARM

Возникла необходимость прошивки микроконтроллера STM32F7 другим микроконтроллером STM32F7 подключенный к нему через UART, а также прошивки Atmega328P подключенного по SPI. Необходимость возникла по причине того, что SoM на Allwinere страдает кучей недостатков в том числе отсутствием необходимого количества UART.
Постараюсь описать кратко идею:
Есть несколько микроконтроллеров STM32F7 общающихся между собой по UART и один Atmega328P подключенный к STMке по SPI, из них:

а) 1-й Микроконтроллер STM32F7: Концентратор на базе STM32F7 он собирает данные с разных микроконтроллеров по UART пакует их в пакет и отправляет по Wi-Fi пользователю через модуль ESP-01 или ESP-06 (UART+2xGPIO). Также данный контроллер имеет слот под microSD подключенный по SDIO и FRAM по SPI.

б) 2-е...5-е Микроконтроллеры STM32F7 (4 штуки): собирают различную цифровую и аналоговую информацию, обрабатывают ее и отправляют на концентратор (см. пункт а)), который в свою очередь ее упаковывает и отправляет в сеть.

в) Микроконтроллер Atmega328P: Следит за уровнем заряда батареи, управляет вкл и выкл систем питания, и в теории программирует по i2c две микросхемы BQ25892 (устанавливает настройки).

Как все это должно работать в теории:

1. Вставляем в microSD слот карту памяти с папкой, например, Firmware, и контроллер (концентратор stm32 из пункта а)) просматривает файлы и в случае, если находит в ней прошивку старше загруженной шьется сам и шьет все остальные контроллеры включая Atmega328P. Затем удаляет все исходники из папки Firmware. Все папка пуста.

2. Подключается по wifi к сети интернет, заходит по ссылке в сети интернет смотрит версии прошивки и если есть прошивки старше загруженной качает ее на microSD карту в папку Firmware, перезагружается, и затем как в варианте с microSD шьется сам и шьет все остальные контроллеры включая Atmega328P. Затем удаляет все исходники из папки Firmware. Все папка пуста.

Теперь вопросы - это возможно реализовать на практике?

И "ДА" - Естественно все микроконтроллеры имеют необходимый bootloader и все прошивки скомпилированы в бин или хекс.

У меня одна проблема - я не могу учится по учебникам, то ли мозг так заточен, то ли уситчивости не хватает. Я не нашел ни одного примера. Я могу взять чужой пример и переделать его. С нуля не получается, тем более что нужно быстро.
Поэтому спрашиваю это физически возможно?
Если да то прошу помощи - пример как это сделать, включая схему подключения между контроллерами. Ведь голый uart не пойдет нужно с концентратора еще и gpio подводить к ногам boot0 и boot1 и RST ведомых микроконтроллеров (для avr также)

П.С. Если кто-то готов помочь, но нет времени я готов оплатить это время. Но вот только в бюджетах я ограничен. Если кому интересно пишите в личку, может договоримся.

Спасибо.
zombi
Цитата
Прошивка ведомых ведомым.

Может прошивка ведомых ведущим? не?
AVStech
Цитата(zombi @ May 23 2018, 19:44) *
Может прошивка ведомых ведущим? не?


Исчерпывающий ответ на мой вопрос sm.gif

Поправил. Спасибо!
_pv
мелкий 8ми битный МК, за питанием следить и рубить его остальным, кто спать сам нормально не умеет - ещё как-то понять можно, если бы не:
4 штуки stm32f7, да ещё один который просто данные с уартов собирает - вот это уже слишком.
вы что-то явно делаете не так, особенно учитывая уровень остальных вопросов.

а физически сделать конечно можно, даже без boot0/1 и ресета.
читайте AN4657
AVStech
Цитата(_pv @ May 23 2018, 20:10) *
вы что-то явно делаете не так, особенно учитывая уровень остальных вопросов.
а физически сделать конечно можно, даже без boot0/1 и ресета.
читайте AN4657


Ну собственно другого ответа я и не ждал. Судя по вашему ответу вы не особенно то и вчитывались в проблему и вопросы. Мне сейчас не до учебников. Есть что предложить конкретно? Со схемой и кодом? Я даже заплатить готов, если цена и результат устроит обоих.

За AN4657 правда спасибо, если не найду решения почитаю!
mantech
Цитата(AVStech @ May 23 2018, 17:58) *
Необходимость возникла по причине того, что SoM на Allwinere страдает кучей недостатков в том числе отсутствием необходимого количества UART.


Не понятно, какой аллвиннер имеется ввиду, например в А10 уартов аж 8 штук!
Arlleex
Цитата
Теперь вопросы - это возможно реализовать на практике?

Реализовать можно, соединять микроконтроллеры между собой можно как угодно (желательно меньше уровней иерархии). Мне, например, приходилось писать многоуровневые bootloader-ы, в которых до кучи и интерфейсные связи были разными.

Цитата
У меня одна проблема

Цитата
Я не нашел ни одного примера

Не правда, в интернетах полно примеров загрузчиков.

Цитата
Я могу взять чужой пример и переделать его

Скажем так. Любая задача не имеет универсального подхода к решению. Все равно придется подпиливать для себя. Поэтому, даже если Вы найдете рабочий загрузчик в интернете, или тут кто-нибудь скинет (хотя загрузчик на UART-е пишется за пару часов работы с Reference Manual-ом и компилятором), Вы вряд ли так сходу адаптируете его под свою функциональность и нужды. Я почитал, что Вам требуется и могу сказать, что это делается за полдня, без выкачки по сети Интернет. Над второй частью придется посидеть подольше. Однако криминала не вижу все равно.

P.S. Но браться не буду.
AVStech
Цитата(mantech @ May 23 2018, 20:40) *
Не понятно, какой аллвиннер имеется ввиду, например в А10 уартов аж 8 штук!

Добрый!
Был взят H2+ но не только в uart-ах была проблема. А в временном лаге между сигналом датчика и отображения данных на экране пользователя, а тауже температурных режимах. И мы не работали с камнем. Мы работали с модулем. А там их было 5 один ушел на дебаг второй на wifi. Соответственно одного не хватало. Сделали mux но это оказалось тупиков. При последующих опытах выяснили что приемлемый лаг получается с использование stm32f7, при условии загрузки до 70%.

Цитата(Arlleex @ May 23 2018, 20:50) *
Реализовать можно, соединять микроконтроллеры между собой можно как угодно (желательно меньше уровней иерархии). Мне, например, приходилось писать многоуровневые bootloader-ы, в которых до кучи и интерфейсные связи были разными.



Не правда, в интернетах полно примеров загрузчиков.


Скажем так. Любая задача не имеет универсального подхода к решению. Все равно придется подпиливать для себя. Поэтому, даже если Вы найдете рабочий загрузчик в интернете, или тут кто-нибудь скинет (хотя загрузчик на UART-е пишется за пару часов работы с Reference Manual-ом и компилятором), Вы вряд ли так сходу адаптируете его под свою функциональность и нужды. Я почитал, что Вам требуется и могу сказать, что это делается за полдня, без выкачки по сети Интернет. Над второй частью придется посидеть подольше. Однако криминала не вижу все равно.

P.S. Но браться не буду.


Как бы так себя правильно спозиционировать, чтобы было понятно мой уровень в программировании и не только.
Скажу так, я не программист. Мой уровень в программировании ноль, а уровень общения с микроконтроллерами понять можно по количеству сообщений на форуме и вообще вопросах которые я задаю.
Я больше инженер-конструктор, технолог, разрабатываю общую концепцию устройств, описываю алгоритмы динамики движения и управления различными машинами в математических формулах. В радиоэлектронике с 2014 года примерно. За 4 года успел освоить только Азы и освоить некоторую компонентную базу. Могу прочитать даташит сделать схему развести плату, спаять и собрать рабочее устройство, отладить его на физическом уровне с асцилографом и анализатором. Максимум могу написать ну очень простенькую программку, руководствуясь разными примерами, в том числе даташитом. Но в данный момент и текущего состояния проекта понимаю, что нужно самому входить в тему, хотя бы на том уровне на котором это необходимо. Уперся в вопрос который задал, потому что для меня это в новинку и подсказать мне не кому.
x893
а посему нельзя использовать собственные загрузчики для 2...5
с 1-го можно управлять только RST на 2..5
точно также можно и AVR прошивать.
И не связываться с BOOT0 и протоколом от ST

То есть проблема явно надуманная. Хотя если книг не читать, то может и сложно будет.
kolobok0
Цитата(AVStech @ May 23 2018, 17:58) *
...Возникла необходимость прошивки...


спрошу аккуратно.
- Это гипотетическая задача или есть такое железо, где это требуется (если можно, для общего развития, обрисуйте в двух словах)?
Ну и классический вопрос
- что произойдёт если допустили ошибку в прошивке прошивальщика при очередном агрэйде кристалла?


(круглый)
AVStech
Цитата(x893 @ May 23 2018, 21:55) *
а почему нельзя использовать собственные загрузчики для 2...5
с 1-го можно управлять только RST на 2..5
точно также можно и AVR прошивать.
И не связываться с BOOT0 и протоколом от ST

То есть проблема явно надуманная. Хотя если книг не читать, то может и сложно будет.


Bootloader'ы будут конечно собственные. Об этом писал в начале. Правильно ли я понимаю, что подключаю только UART между концентратором и ведомым и от концентратора GPIO к RST ведомого?
Судя по всему буду читать книжки...
Спасибо!

Цитата(kolobok0 @ May 23 2018, 22:04) *
спрошу аккуратно.
- Это гипотетическая задача или есть такое железо, где это требуется (если можно, для общего развития, обрисуйте в двух словах)?
Ну и классический вопрос
- что произойдёт если допустили ошибку в прошивке прошивальщика при очередном агрэйде кристалла?


(круглый)


Постараюсь также аккуратно ответить:
- это реальное железо, пока в двух экземплярах. Также один ведомый модуль на STM32F7 сейчас в проработке, вот его даже на бумаге нет. В двух словах пользовательское устройство, где все 4 ведомых и концентратор на RTOS. Для понимания Концентратор выполняет еще некоторые вычисления кроме того, чтобы собирать пакеты и отправлять их в сеть. Более сказать не могу.
- для каждого камня выведен отдельный разъем JTAG, в случае сбоя можно перепрошить через него. Сейчас так и делаем, прошиваем каждый модуль отдельно, что не удобно и не возможно для самостоятельной прошивки (обновления) пользователем. Поэтому продолжая Вашу мысль, сначала планируем отработать прошивку на двух эталонах, только после чего выкладывать обновление в сеть. Но если все же произошел сбой, то пользователю придется отправлять устройство изготовителю. Сейчас вообще любая прошивка возможна только изготовителем.
x893
Когда загрузчики свои - вообще не проблема.
UART + RST хватит для обновлений всех подчинённых.
_pv
ресет-то зачем?
AVStech
Цитата(_pv @ May 24 2018, 09:10) *
ресет-то зачем?


Я так понимаю чтобы выполнить основной принцип IAP :
Принцип из документа:
"Когда происходит сброс (reset), счетчик программ настроен на выполнение драйвера IAP. Это должен быть компактный код, который проверяет конкретное условие, например, что комбинация клавиш нажата, или в моем случае сверяет версии прошивки. Когда стартовое условие выполняется, код драйвера IAP либо обновляет пользовательское приложение (прошивку) или запускает её."
Правда в документе AN4657 указано, что прошивка качается на внутреннюю память камня, мне же желательно работа напрямую с карты памяти, так как если он сначала качнет, если вообще места хватит себе на внутренюю флеш прошивку, потом прошьется, потом проверит все ли норм и удалит старую.
В принципе по данному алгоритму можно сделать собственный bootloader, который будет шить собственный камень. Но как это сделать для камня, который далеко от слота карты памяти ведущего, и у которого нет собственного накопителя, кроме флеша, который к тому же почти под завязку заполнен?

Или я опять читаю одно, а понимаю все не верно?
_pv
Туда (в свой бутлоадер, а не встроенный) и программно, по команде от уарта попасть можно, без аппаратного ресета.
И скорее во внутреннюю память(ram) складываются килобайтные куски, так уж ymodem устроен который в данном конкретном случае они использовали.
И куски эти потом сразу пишутся во флэш поверх старой прошивки.
Рама меньше чем флэша, всё не сохранишь перед прошивкой флэша.
Можно ещё флэш пополам поделить и при обновлении писать в другую половину и только после успешного завершения и верификации менять флаг, какую половину использовать. Так старая прошивка не испортится при неудачном обновлении.
Arlleex
Да ресет тут будет нужен для поддержки встроенного загрузчика от STMicroelectronics. Подтягиваете BOOT0 к 1, даете импульс сброса - все, микроконтроллер начнет хавать прошивку по UART, например. Но я бы не стал впиливать их загрузчик в свое устройство. Достаточно UART-а и никаких reset-ов и GPIO лишних.
x893
Можно и без ресета, но если ваша расчудесная программа (или загрузчик) завинет - будете бежать питание передергивать ?
Про WDG можно не писать.
Естественно в загрузчик можно и через команду попасть.
AVStech
Цитата(x893 @ May 24 2018, 11:18) *
Можно и без ресета, но если ваша расчудесная программа (или загрузчик) завинет - будете бежать питание передергивать ?
Про WDG можно не писать.
Естественно в загрузчик можно и через команду попасть.


Да однозначно RST подтяну к ведущему. IWDG также присутствует в некоторых модулях.
Резюмируя, я так понимаю, что любые МК, например DSPIC33 в качестве субведомых (управляется ведомым) также можно шить по UART ведущим? Просто каскадный bootloadr?

Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.
Если есть у кого пример - кусок кода или ссылка будут очень благодарен!
Самое главное что я хотел этой веткой для себя решить - это наличие такой возможности. Соответсвенно она есть и теперь я могу двигаться дальше.
Всем спасибо!
Baser
Цитата(AVStech @ May 24 2018, 09:43) *
В принципе по данному алгоритму можно сделать собственный bootloader, который будет шить собственный камень. Но как это сделать для камня, который далеко от слота карты памяти ведущего, и у которого нет собственного накопителя, кроме флеша, который к тому же почти под завязку заполнен?

Цитата(AVStech @ May 24 2018, 12:39) *
Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.

Вам всего-то нужно понять простую мысль:
Вы нашли примеры как прошить от ПК через различные интерфейсы конечный контроллер.

Так вот в вашем случае под ПК нужно понимать ваш первый МК с флеш-картой как диском-хранилищем.
Т.е. в первом МК должен быть функционал прошивальщика через УАРТ ведомых МК.

А уж какие вы примените алгоритмы и протоколы - это уже детали реализации.
AVStech
Цитата(HardEgor @ May 24 2018, 12:57) *


Спасибо огромное!

Цитата(Baser @ May 24 2018, 12:57) *
Вам всего-то нужно понять простую мысль:
Вы нашли примеры как прошить от ПК через различные интерфейсы конечный контроллер.

Так вот в вашем случае под ПК нужно понимать ваш первый МК с флеш-картой как диском-хранилищем.
Т.е. в первом МК должен быть функционал прошивальщика через УАРТ ведомых МК.

А уж какие вы примените алгоритмы и протоколы - это уже детали реализации.


Вот теперь всё более менее становится на свои места! СПАСИБО!!!
kolobok0
Цитата(_pv @ May 24 2018, 10:24) *
...Можно ещё флэш пополам поделить...


это подходит для 128 меги. А для стм32 - вполне можно сделать чтоб сам себя шил. единственный трабл - динамические адреса.

(круглый)
Arlleex
Цитата(kolobok0 @ May 24 2018, 20:10) *
единственный трабл - динамические адреса.

О чем речь?
leocat
Цитата(Arlleex @ May 24 2018, 18:49) *
О чем речь?

Да это (круглый) опять отлаживает свой ПД ( приход дури ) регулятор. Видать словил...
kolobok0
Цитата(Arlleex @ May 24 2018, 20:49) *
О чем речь?


о том, что тот случай который описал человек которому я отвечал, можно расширить и stm32 может успешно шить сам себя(при соблюдении ряда условий конечно-же). одна из задач которую придётся решать в рукопашную тогда - позднее связывание адресов.
так понятнее или опять мимо?

(круглый)
khach
Система с кучей STM32 в том числе F7 имеет право на жизнь, и отдельный F7 отвечающий за библиотеки прошивки это тоже нормально. Но в определенных случаях. Например это удаленная система без непосредственного доступа оператора с плохим каналом связи. Т.е прошивка для апгреда заливается медленно и печально, часами или неделями, с обрывами связи. Процессор-менеджер фирмвари этим и занимается. Когда прошивка залита во внешний флешь, проверены ее чексуммы и соответствие акуальной версии харда остальных процессоров, то планируется действия по накатыванию фирмвари в безопасный для техпроцесса период. При этом во флеши менеджера прошивок лежит как минимум две копии фирмвари, в том числе и для даунгрейда в случае проблем. Т.е если вылезит баг при накате прошивки то всегда можно быстро откатиться без передачи новой-старой фирмвари по ненадежному каналу связи. Ну а мультипроцессороность позволяет функционировать управляющему блоку даже при неудачной прошивке. Т.е новая фирмварь вводится в опытную эксплуатацию в режиме надзора без включения в управляющий контур и только после некоторого пробного периода управление переключается на нее. Менеджер фирмвари также может принимать, хранить и высылать журнал эксплуатации системы, журналы срабатывания ватчдогов, трапов и ловушек подчиненных систем итд. Возможен спец-вариант принудительного наката дефолтных прошивок по срабатыванию ватчдога всей системы низкого уровня на "мелком 8-битном процессоре" но с этим надо очень осторожно чтобы не крашнуть систему при проблемах питания. Самоапдейт менеджера фирмвари тоже возможен, но это очень особый случай типа частичной порчи флеша от длительной эксплуатации. Или если эта вся система расположена совсем не на родной планете....
Это все из области ультранадежных дублированных управляющих систем.
jcxz
Цитата(khach @ May 26 2018, 16:25) *
Это все из области ультранадежных дублированных управляющих систем.

Вот только вопрос: а место ли STM32 в этой области? biggrin.gif

Цитата(kolobok0 @ May 26 2018, 00:01) *
о том, что тот случай который описал человек которому я отвечал, можно расширить и stm32 может успешно шить сам себя(при соблюдении ряда условий конечно-же). одна из задач которую придётся решать в рукопашную тогда - позднее связывание адресов.
так понятнее или опять мимо?

Совершенно непонятно.... wacko.gif В чём проблема чтобы STM32 "шить сам себя"? Что такое "позднее связывание адресов" и на кой оно?

Цитата(AVStech @ May 23 2018, 17:58) *
Теперь вопросы - это возможно реализовать на практике?
И "ДА" - Естественно все микроконтроллеры имеют необходимый bootloader и все прошивки скомпилированы в бин или хекс.

Конечно возможно. На прошлой работе работе мы много лет все изделия строили по такой схеме (и сейчас их у заказчиков функционируют десятки если не сотни тысяч и обновляются по необходимости). Мы сделали возможность обновления через любой рабочий интерфейс (устройства имеют несколько их разных, в зависимости от аппаратного исполнения): UART, оптопорт, RS-485, CAN, Ethernet, радиоканал, ZigBee, GSM, PLC - обновляться могут через любой из них. Обновление безопасное: прошивка сначала полностью скачивается во внутреннюю энергонезависимую память (рабочим ПО), потом прошивается во флешь программ (бутлоадером). Эти устройства как правило имели в своём составе кроме главного МК несколько программируемых модулей (GSM, ZigBee, радиомодуль, PLC, ...) и для всех из них возможно обновление их внутренней прошивки (для этого каждый сделан с бутлоадером, для обеспечения безопасного обновления если оно идёт по каналу, который поднят на данном модуле).
В большинстве систем наши устройства работали группами с ретрансляцией между устройствами. Например: один внешний канал для связи с центром - GSM на одно из устройств, остальные устройства в группе подключаются к устройству с GSM через ZigBee. Естественно все эти устройства в ZigBee видны из центра и их любой адресно можно перешить. Также делали системы с резервированием канала к центру (в сети ZigBee два узла с GSM; или GSM и Ethernet).

Цитата(AVStech @ May 23 2018, 17:58) *
Если да то прошу помощи - пример как это сделать, включая схему подключения между контроллерами. Ведь голый uart не пойдет нужно с концентратора еще и gpio подводить к ногам boot0 и boot1 и RST ведомых микроконтроллеров (для avr также)

Почему не пойдёт? Никакие доп. пины не нужны, так как перешивать и сбрасывать после прошивки должен бутлоадер. И прошивка возможна по любому каналу.

В том изделии, что разрабатываю сейчас, обновление построено по тому же принципу, но кроме того есть возможность безопасного обновления и самого бутлоадера.
Arlleex
Цитата(jcxz @ May 30 2018, 09:47) *
Совершенно непонятно.... wacko.gif В чём проблема чтобы STM32 "шить сам себя"? Что такое "позднее связывание адресов" и на кой оно?

Я даже побоялся спрашивать дальше, думал может я чего-то не знаю/не понимаю. А нет, не один такой biggrin.gif

Цитата(jcxz @ May 30 2018, 09:47) *
В том изделии, что разрабатываю сейчас, обновление построено по тому же принципу, но кроме того есть возможность безопасного обновления и самого бутлоадера.

В ОЗУ исполняемый бут? А если питание сбросится во время обновления бута? ИМХО должно быть два бута для действительно безопасного обновления самого бутлоадера.
jcxz
Цитата(Arlleex @ May 30 2018, 10:49) *
В ОЗУ исполняемый бут? А если питание сбросится во время обновления бута? ИМХО должно быть два бута для действительно безопасного обновления самого бутлоадера.

Не в ОЗУ, а во флешь естественно. В ОЗУ он копируется при запуске. Да, естественно имеется две копии бута. Обновляется всегда неактивная, а потом переключается активность.
А в ОЗУ, затем, чтобы не было нужды делать позиционно-независимый бутлоадер и всякие "поздние связывания адресов" cool.gif
Arlleex
Цитата(jcxz @ May 30 2018, 12:46) *
Не в ОЗУ, а во флешь естественно. В ОЗУ он копируется при запуске. Да, естественно имеется две копии бута. Обновляется всегда неактивная, а потом переключается активность.
А в ОЗУ, затем, чтобы не было нужды делать позиционно-независимый бутлоадер и всякие "поздние связывания адресов" cool.gif

1. Правильно ли я полагаю, что в ОЗУ Вы копируете код (и обработчики исключений) лишь для того, чтобы не потерять возможные прерывания и события при остановке CPU при стирании или программировании Flash?
2. Копии загрузчиков хранятся в разных секторах Flash и не пересекаются, правильно понимаю? Иначе при стирании одного сектора обновляемого загрузчика, можно зацепить активный и при сбросе питания оба загрузчика будут неработоспособными.
3. Тогда есть все-таки один основной механизм, который никогда не обновляется - в начале Flash смотрится какая-то переменная, показывающая, какой из загрузчиков активен, копирует в ОЗУ содержимое Flash региона нужного загрузчика и все его обработчики прерываний, а потом переходит на загрузчик? Загрузчик уже сам понимает что дальше делать - обновлять кого-то или переходить на приложение. Исходя из этого можно предположить, что регионов в памяти у Вас 3: первый как раз навеки-вечно зашитый механизм просмотра и запуска нужного бута, второй - основной бут, третий - резервный бут (копия, как удобно). И располагаются они в разных секторах Flash: первый обязательно в начале, второй и третий (загрузчики) - где удобно не в одном секторе с первым.
Так? Ну вернее я так вижу, как бы сделал я (на беглый взгляд).
jcxz
Цитата(Arlleex @ May 30 2018, 13:06) *
1. Правильно ли я полагаю, что в ОЗУ Вы копируете код (и обработчики исключений) лишь для того, чтобы не потерять возможные прерывания и события при остановке CPU при стирании или программировании Flash?

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

Цитата(Arlleex @ May 30 2018, 13:06) *
2. Копии загрузчиков хранятся в разных секторах Flash и не пересекаются, правильно понимаю? Иначе при стирании одного сектора обновляемого загрузчика, можно зацепить активный и при сбросе питания оба загрузчика будут неработоспособными.

Конечно.

Цитата(Arlleex @ May 30 2018, 13:06) *
Так? Ну вернее я так вижу, как бы сделал я (на беглый взгляд).

Да, примерно так. В начале FLASH есть код (вообще-то у меня именно он и называется BOOTLOADER; а те две копии - они у меня называются UPDATER), который выбирает один из двух UPDATER (активный), копирует его в ОЗУ и передаёт управление ему (сообщая ему его позицию 0 или 1). Уже UPDATER читает внешнюю SPI-FLASH, проверяет наличие в ней прошивки нужного типа и шьёт если надо.
Регионов у меня всего 4, а не 3: BOOTLOADER, 0-й UPDATER, 1-й UPDATER, WORK.
BOOTLOADER сделан максимально простым и на асме и без обращения к какой-либо периферии, чтобы минимизировать возможность багов в нём. Он размером всего около 128 байт.
Arlleex
Цитата(jcxz @ May 30 2018, 14:39) *
Нет, не только. Я же пишу, что:
1) чтобы не создавать позиционно-независимый бутлоадер (у нас же две копии его во флешь!);

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

Цитата(jcxz @ May 30 2018, 14:39) *
Регионов у меня всего 4, а не 3: BOOTLOADER, 0-й UPDATER, 1-й UPDATER, WORK.

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

Цитата(jcxz @ May 30 2018, 14:39) *
BOOTLOADER сделан максимально простым и на асме и без обращения к какой-либо периферии, чтобы минимизировать возможность багов в нём. Он размером всего около 128 байт.

Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше rolleyes.gif
jcxz
Цитата(Arlleex @ May 30 2018, 13:57) *
Ох. Осталось переварить как это понять biggrin.gif

Когда Вы линкуете код для выполнения с каких-то адресов, то он и будет только в тех адресах выполняться. Это называется позиционно-зависимый код. Компоновщик его и делает обычно.
Так что для двух копий UPDATER мне пришлось бы линковать 2 разные прошивки (если запускать во флешь).
Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу).

Цитата(Arlleex @ May 30 2018, 13:57) *
Так в этом и проблема, что, допустим, у меня первый сектор Flash 16кБ, и трется он весь при Erase, так что не разместить тут первую копию UPDATER-а, лишь зря бы были заняты 16кБ - 128байт. Но, насколько я вижу, у Вас размер первого сектора горааааздо меньше и память, таким образом, расходуется красивше rolleyes.gif

В первом секторе у меня BOOTLOADER находится (плюс ещё некоторая уникальная инфа, которая зашивается один раз при изготовлении: MAC-адрес, серийный номер; и которая не должна обновляться).
А размеры секторов в моём МК: 64КБ, 128КБ, 256КБ.
Arlleex
Цитата(jcxz @ May 30 2018, 15:51) *
Но я просто линкую UPDATER для выполнения по адресу ОЗУ (единому адресу).

Дошло, спасибо. На мой взгляд действительно гибко и удобно laughing.gif
Буду и у себя так делать в следующий раз. Хотя прошиваторы у меня редко меняются, от слова почти никогда. Но иметь такую возможность, особенно если памяти всегда хватает, не помешает.
scifi
Цитата(AVStech @ May 24 2018, 12:39) *
Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.

У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:
CODE
#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
Цитата(scifi @ May 30 2018, 19:54) *
У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:

Ай-яй-яй! goto используете! не комильфо rolleyes.gif
Arlleex
Код
PT_THREAD...

А не тот ли это протопоток который Адаму Данкелсу в страшном сне приснился? biggrin.gif
Кстати надо бы разобраться, как по-человечески протопотоки использовать smile3046.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.