|
STM32 сделать bootloader в Keil, помогите разобраться |
|
|
|
Nov 2 2014, 15:27
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
у нас сделано так бутлоадер в начале флеши, прям так как лежит самая обычная программа, прошивается жетагом и сразу запускается.
при старте он проверяет 1. не нажата ли кнопка остаться в бутлоадере 2. нет ли специального флага остаться в бутлоадере (у нас через флешь, видел варианты в РАМ пихают флаг и делают софт ресет, то есть при старте с подачи питания флага нет, а так есть). Это для входа в режим бутлодера по команде езернет из боевой прошивки. 3.1 не пуст ли регион с прошивкой 3.2 сходиться ли контрольная сумма прошивки.
если через все проверки прошли, то передает управление боевой прошивке, если же нет, остается в бутлоадере и по езернету может принять прошивку и записать ее в нужное место. При это сам следит чтобы этой прошивкой ничего не испортить в себе. Таким образом получается не убиваемый прибор. Чтобы вы ни сделали, в самом плохом случае можно будет нажать спец кнопку и перепрошить прибор.
схема предложена коллегой и нахожу ее весьма разумной, многие на форуме по обсуждению в соседних темах делают так же
|
|
|
|
|
Nov 2 2014, 16:27
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(smk @ Nov 2 2014, 19:10)  Как передать управление от бутлоадера программе и наоборот Читайте руководство на процессор - там это есть. Цитата(smk @ Nov 2 2014, 19:10)  как разместить в нужном месте... Как это в кейле делается? Читайте руководство на кейл - там это есть. Цитата(smk @ Nov 2 2014, 19:10)  Про таблицы векторов тоже не все ясно. Как реализовать чтоб макисмально удобно в эксплуатации было ну и т.п. Читайте этот форум - там это есть.
|
|
|
|
|
Nov 2 2014, 16:40
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
у вас должно быть 2 пути сброс -> загрузчик -> основная программа - это путь для основной работы и сброс -> загрузчик - это путь для обновления прошивки ну и как следствие, если вы хотите перейти основной режим работы -> загрузчик, то просто через сброс на второй укороченный путь основной режим работы -> сброс -> загрузчик в основной программе получая команду перейти в режим обновления, вы ставите какой либо флаг, который после сброса загрузчик проверит. Можно во флэш данные записать какие-то. А загрузчик когда закончит обновлять прошивку, сотрет эти данные. Нет никакого смысла менять программу из программы, перейдите в режим обновления и оттуда меняйте. Если что-то не получится, то вы просто останетесь в режиме обновления. Если всегда грузиться через него, то сбой питания или еще что, все равно вас выкинет в загрузчик который может прошить верную программу, как бы прошлый процесс прошивки не закончился. отладка собственно отдельная тема, зачем отлаживать программу через загрузчик? МЫ отлаживаем программу, потом собираем ее для версии загрузчика и все. Не надо усложнять и делать отладку программы вместе с загрузчиком. Потому что по сути прошивку обновляют клиенты, зачем им отладка? А вы у себя можете программу и без загрузчика погонять, и если удачная копия залить. Это если отладка с жетагом, а если отладка через порт, то разницы нет с загрузчиком или без, а если все же с жетагом, то нафига загрузчик%  ?
|
|
|
|
|
Nov 2 2014, 17:21
|
практикующий тех. волшебник
    
Группа: Участник
Сообщений: 1 190
Регистрация: 9-09-05
Пользователь №: 8 417

|
Цитата(smk @ Nov 2 2014, 18:22)  ...Понадобился бутлоадер.... делаю так. есть бутлоадер. он же обладает функциями для прошивки. он же статический код (версионность аппаратуры, проверка и передача управления основной программе, обработка исключений с записью во флэш). основная программа представляет из себя модули (аля форточные дэлеле). каждый модуль имеет понятие номер версии, есть сборка этих версий (инкрементальная, фулл, дифф). во флэше лежат эти модули в разных местах. шьётся на уровне версий(фулл, дифф, инкрементальной). источники приёма версий - пофигу (изернет, модбасс, микросд и т.д.). надо прошить - обращаемся к ядру, он отслеживает адресацию и корректность записи. после ресета - выбираем последнюю рабочую версию(кол-во успешных загрузок, качество версий отслеживается).если всякие црц, заголовки, перекрёстные ссылки всё ок - передаётся управление модулю. в случае возникновения многократных исключений, или с сайта поддержки - версия может отметиться как неработоспособная и будет загружаться предыдущая. минусы такого подхода два. 1) адресация внутри модулей должна быть с учётом любого адреса загрузки. 2) отлаживать модули из среды - только азм.
|
|
|
|
|
Nov 2 2014, 17:29
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Сложновато выходит. Да и нужды нет разбивать на модули. Цитата МЫ отлаживаем программу, потом собираем ее для версии загрузчика и все. В отношении сборки с загрузчиком не совсем понял. Загрузчик получается по сути добавляется в программу?
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Nov 2 2014, 17:34
|
Знающий
   
Группа: Свой
Сообщений: 524
Регистрация: 25-12-08
Из: Москва
Пользователь №: 42 748

|
мы сделали такой прошиватель (на блекфине, но в принципе это не важно) у нас большое количество приборов (~1000) и при изменении программы, поиска и исправления багов подключать JTAG (в нашем случае ICE-100B) это было бы критично. При производстве плтаты с помошью JTAG мы прошиваем во flash саму программу прошивальщика, сама основная программа прошивается так: на SD карте должно быть 2 файла, при 1) вклчении питания прошивальщик смотрит их наличие - если их нет он передает управление основной программе, если эти 2 файла там есть - он прошивает их на flash? стирает эти 2 файла с SD и делает перезагрузку (1) Вобщем все идеально работает около 2-х лет. приборы у нас могут находиться за 1000 км от нас мы просто меняем прошивку и те, кто с нашыми приборами работает сами могут их перешить. да. если прошика сбойная, битая или ошибочная всегда можно заново перешить без всяких JTAG подсказали нам сдесь: https://ez.analog.com/thread/35318
|
|
|
|
|
Nov 2 2014, 18:05
|
Знающий
   
Группа: Свой
Сообщений: 524
Регистрация: 25-12-08
Из: Москва
Пользователь №: 42 748

|
Цитата(AHTOXA @ Nov 2 2014, 20:59)  А почему именно два? это в принципе тока к блекфину относится - там одни функции во flash лежат другие в RAM только из за этого. для ARM будет 1.
|
|
|
|
|
Nov 2 2014, 20:06
|
Участник

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530

|
Это уже обсуждали вот здесь. Можно хотя бы для приличия первые странички разделов поглядеть...
Сообщение отредактировал menzoda - Nov 2 2014, 20:07
|
|
|
|
|
Nov 2 2014, 20:28
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(smk @ Nov 2 2014, 17:22)  Суть вопроса такова. Понадобился бутлоадер. Многие ответили, но обще. Я приведу примеры и "трюки" из собственного загрузчика, тем более, что предпосылки STM32 и KEIL совпадают. 1. Загрузчик нужно располагать с начала флэша, тогда управление при старте передается сразу к нему. 2. С точки зрения создания и отладки загрузчик представляет собой обычную программу. 3. Пользовательская программа являет собой также обычную программу, которая, однако, располагается с некоего фиксированного адреса после загрузчика. С какого именно, станет ясно, когда размер загрузчика определится более-менее. Этот адрес следует знать затем и загрузчику, чтобы упростить ему задачу поиска приложения в памяти, хотя можно применить и другие методы поиска. 4. Для основной программы необходимо: 4.1. соответствующим образом настроить проект KEIL и, как это сделал я, создать свой файл скаттера для компоновщика, например, когда первые 16К для загрузчика зарезервированы: CODE ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ; ; FlashSize = 0x00040000 (256K): ; ; Boot Loader 16K ; ;LR_IROM1 0x08000000 0x4000 { ; ER_IROM1 0x08000000 0x4000 { ; *.o (RESET, +First) ; } ;} ; ; Main Code from 16K ; LR_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load region size_region ER_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load address = execution address *.o (USER_VECTORS, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x0000C000 { ; RW data .ANY (+RW +ZI) } }
4.2. start-up код (файл *.s) следут модифицировать так, чтобы он сразу переназначал таблицу векторов пользовательской программы: CODE ; Vector Table Mapped to Address 0 at Reset ; AREA RESET, DATA, READONLY, ALIGN=9 ; 2^9=512 ; ;__Boot DCD __initial_sp ; Top of Stack ; DCD Reset_Handler ; Reset Handler
; Future user program vector table at 0x4000
AREA USER_VECTORS, DATA, READONLY, ALIGN=9 ; 2^9=512 EXPORT __Vectors
__Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler ...
AREA |.text|, CODE, READONLY VTOR EQU (0xE000E000 + 0x0D08)
; Reset handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit MOV32 R1, #VTOR ; Load VTOR register address LDR R0, =__Vectors ; Load user program vector table address STR R0, [R1] ; Store to VTOR to relocate the vector table LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP
4.3. Обратите внимание на закомментированные строки в начале скаттера (LR_IROM1) и start-up файла (AREA RESET). Они определяют вектор по адресу 0. Эти строки можно раскомментировать, чтобы отлаживать основную программу, пока загрузчик еще не готов или сам в память не загружен. 5. Для определения версии основной программы я использовал дату и время ее компиляции. 5.1. В любом месте кода можно разместить строки: CODE //------------------------------------------------------------------------------ // // Compilation time stamp // const char _cd[] = __DATE__; const char _ct[] = __TIME__; Эти строки представляют собой генерируемые компилятором дату и время. Встроенные макросы __DATE__ и __TIME__ описаны в помощи KEIL. 5.2. В start-up файле можно поместить некоторый дескриптор со ссылками на эти строки: CODE IMPORT |Load$$LR$$LR_IROM2$$Length|
IMPORT _cd IMPORT _ct DCB "DSC=" DCD __Vectors ; load address DCD |Load$$LR$$LR_IROM2$$Length|; Image length (.bin file length) DCD _cd ; -> date stamp as Jan 01 2012 DCD _ct ; -> time as HH:MM:SS
Фактически, это есть некая структура, которую я располагаю между таблицей векторов и Reset_Handler. Эта структура содержит уникальную строку "DSC=", затем идет указатель на таблицу векторов (фактически те самые 0x4000), затем длина образа программы, после чего указатели на строки даты и времени компиляции. Для загрузчика эту структуру легко найти (уж точно в первых 512 байтах кода пользовательской программы) и почерпнуть оттуда версию и длину приложения. 6. Загрузчик передает управление приложению, загрузив SP из первого слова таблицы векторов, и переходом по адресу из второго слова. Следует помнить, что нужно предварительно остановить периферию, которую использовал загрузчик, и SysTick. 7. Мой загрузчик использует SD карту как источник обновления. При старте он ищет файл с образом приложения, сравнивает версии и, если необходимо, - обновляет приложение.
Сообщение отредактировал KnightIgor - Nov 2 2014, 20:36
|
|
|
|
|
Nov 3 2014, 11:45
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Код ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ; ; FlashSize = 0x00040000 (256K): ; ; Boot Loader 16K ; ;LR_IROM1 0x08000000 0x4000 { ; ER_IROM1 0x08000000 0x4000 { ; *.o (RESET, +First) ; } ;} ; ; Main Code from 16K ; LR_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load region size_region ER_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load address = execution address *.o (USER_VECTORS, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x0000C000 { ; RW data .ANY (+RW +ZI) } } Идея понятна. Пытаюсь понять с практической стороны. Вот адреса бы обсудить где что... Сейчас посмотрел такой же файл в своем тестовом проекте на 407VGT6. Там похоже, но адреса другие. Код ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; *************************************************************
LR_IROM1 0x08000000 0x00100000 { ; load region size_region ER_IROM1 0x08000000 0x00100000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } }
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Nov 3 2014, 13:01
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
CODE ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ; ; FlashSize = 0x00040000 (256K): //размер всей флеши ; ; Boot Loader 16K ; ;LR_IROM1 0x08000000 0x4000 { //начальный адрес и размер под бутлоадер ; ER_IROM1 0x08000000 0x4000 { //то же ; *.o (RESET, +First) ; } ;} ; ; Main Code from 16K ; LR_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load region size_region //начальный адрес и размер программы ER_IROM2 (0x08000000 + 0x4000) (0x40000-0x4000) { ; load address = execution address //то же *.o (USER_VECTORS, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x0000C000 { ; RW data //начальный адрес и размер ОЗУ .ANY (+RW +ZI) } }
я все правильно понял?
Сообщение отредактировал IgorKossak - Nov 3 2014, 14:42
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Nov 3 2014, 13:53
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(smk @ Nov 3 2014, 12:42)  Если я правильно понял, то загрузчик пишется как обычно, а программа с примнением указанных примеров? Загрузчик пишется с применением настроек по-умолчанию. Просто как свежий проект под KEIL. Основная программа по сути тоже, просто ее надо разместить в памяти не с самого начала, а после загрузчика. У меня загрузчик занимает 16К. Следовательно, основную программу размещаем как бы с адреса 0x4000 (ну, фактически с 0x08000000 + 0x4000). Для этого применяется модифицированный скаттер файл. Можно напрямую взять тот, что я привел. В проекте KEIL в закладке Linker нужно будет лишь явно указать имя файла модифицированного скаттера. Например, myscatter.sct. Следует понимать, что в процессе отладки основной программы, если не предпринять действий по записи векторной таблицы по адресу начала флэша (то есть, 0x08000000), управление основной программе не попадет. Если загрузчик не готов или еще не загружен, нужно временно раскомментировать строки секции LR_IROM1, а также что я там указал в старт-ап файле. Тогда после сборки проекта основной программы ее кусочек "заменит" загрузчик, и можно будет работать. Потом, собирая образ программы для загрузчика, нужно будет снова закомментировать, что надо. Цитата(smk @ Nov 3 2014, 15:01)  я все правильно понял? Мне кажется, Вы тут чего-то не дописали. Если применить идею модификации скаттера к Вашему проекту, оставив, скажем, 32K на загрузчик, то будет так: CODE ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* ; ; Boot loader 32K (0x8000) ; ; Uncomment this load region for debugging the main application as long as no boot loader present ; Also remove comments in start-up.s file for RESET area in this case. ; ;LR_IROM1 0x08000000 0x8000 { ; load region size_region ; ER_IROM1 0x08000000 0x8000 { ; load address = execution address ; *.o (RESET, +First) ; } ;} ; ; Main Application begins at 32K. ; LR_IROM2 0x08000000 0x00100000-0x8000 { ; load region size_region, smaller by 32K ER_IROM2 0x08000000 0x00100000-0x8000 { ; load address = execution address, , smaller by 32K *.o (USER_VECTORS, +First) ; <- changed default name RESET to USER_VECTORS, make the same in start-up.s file! *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } }
Сообщение отредактировал IgorKossak - Nov 3 2014, 14:43
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
Nov 4 2014, 11:49
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Код AREA RESET, DATA, READONLY, ALIGN=9 ; 2^9=512 вот эта строка не ясна. у меня наверно 2^10 если мегабайт? Код VTOR EQU (0xE000E000 + 0x0D08) вот это тоже не пойму как Код EXPORT __Vectors_End EXPORT __Vectors_Size Это закомментировать?
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Nov 4 2014, 14:28
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(smk @ Nov 4 2014, 13:49)  Код AREA RESET, DATA, READONLY, ALIGN=9; 2^9=512 > вот эта строка не ясна. у меня наверно 2^10 если мегабайт? ALIGN=9 - это директива выравнивания на границу 512 байт. Комментарий просто поясняет, что 9 означает именно степень для 2. Код VTOR EQU (0xE000E000 + 0x0D08) > вот это тоже не пойму как VTOR - это регистр в ядре Cortex-Mx, который "переназначает" активную таблицу векторов. Он имеет фиксированный адрес в карте памяти. Директива EQU - читайте ассемблер. Код EXPORT __Vectors_End EXPORT __Vectors_Size > Это закомментировать? Они никому не мешают.
|
|
|
|
|
Nov 5 2014, 11:50
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Код error: L6236E: No section matches selector - no section to be FIRST/LAST. Вот такая ошибка. Ругается на строчку Код *.o (RESET, +First) Что нужно предпринять? Код EXPORT __Vectors_End EXPORT __Vectors_Size таки закомментировал т.к. было сообщение об ошибке
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|