Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: ПО для перепрошивки 8051
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Все остальные микроконтроллеры > MCS51
Jevys
Встала задача по созданию программного обеспечения для обновления ПО 8051. Особенностью создаваемого устройства является то, что в процессе эксплуатации может потребоваться неоднократное обновление программного обеспечение. В связи с этим хочу спросить две вещи:
1. Можно ли вообще написать модуль ПО перепрошивки на Си? В проекте стоит опция максимальной оптимизации по размеру кода, при которой Кеil по идее может сам находить похожие куски кода и выделять их в подпрограммы. И тогда код, расположенный в ПО перепрошивки, будет вызывать код, который лежит за пределами области памяти программ перепрошивщика, что недопустимо. Остается только ассемблер?
2. Если все же писать на Си, то как жестко разместить программу перепрошивки с заданного адреса? Например 0xF800. И как это сделать в варианте на ассемблере.
barabek
Цитата(Jevys @ Sep 23 2009, 20:40) *
Встала задача по созданию программного обеспечения для обновления ПО 8051. Особенностью создаваемого устройства является то, что в процессе эксплуатации может потребоваться неоднократное обновление программного обеспечение. В связи с этим хочу спросить две вещи:
1. Можно ли вообще написать модуль ПО перепрошивки на Си? В проекте стоит опция максимальной оптимизации по размеру кода, при которой Кеil по идее может сам находить похожие куски кода и выделять их в подпрограммы. И тогда код, расположенный в ПО перепрошивки, будет вызывать код, который лежит за пределами области памяти программ перепрошивщика, что недопустимо. Остается только ассемблер?
2. Если все же писать на Си, то как жестко разместить программу перепрошивки с заданного адреса? Например 0xF800. И как это сделать в варианте на ассемблере.

1. Можно, какая разница на чем писать.
2. В кейле project->options for target->bl51 locate. В asm это делается с помощью например ORG или смотрите далее. Найдите ее описание.

Но в общем Вы ничего не написали о том как хотите обновлять прошивку. Если предположить, что Вы хотите по UART присылать новую прошиву и полностью переписывать содержимое внутренней flash-памяти программ, относящейся к основной программе. Следует прочитать "AN-112 UART IN-APPLICATION CODE LOADING EXAMPLES" у silabs. Я делал следующим образом. Создал 2 проекта - основной и bootloader. Основная прога ложится в память начиная, например, с адреса 0x100 (задается установками линкера). Естественно, вектора прерывания находятся по своим местам ниже этой точки. В проекте бутлоадера делаем установки линкера размещать код с выбранного Вами адреса 0xf800. Далее, в оба проекта явно добавляем копии стандартного startup.a51. Далее немного модифицируем эти копии. Для основного проекта:
Код
        PUBLIC    ?C_STARTUP

        CSEG    AT    100h      <-измененный адрес!
?C_STARTUP:    LJMP    STARTUP1

        RSEG    ?C_C51STARTUP

для бутлоадера:
Код
        CSEG    AT    0;делаем переход на нужный адрес 0xf800
      LJMP    0f800h
        CSEG    AT    0f800h; теперь прога начинается с этой позиции
?C_STARTUP:    LJMP    STARTUP1

Теперь при старте начинает выполняться бутлоадер. Если для него работы нет, то он передает управление основной программе вызовом application(); имеющей только прототип void (*application)(void)=(void code *)0x0100;
Основная программа при выполнении получает новую прошиву, размещает ее во внешней памяти, выставляет какой нибудь флаг. После этого сбрасывает контроллер или переходит на выполнение строки по 0-му адресу, бутлоадер видит флаг и начинается ...
Можно и самим бутлоадером получать прошиву. Зависит от Ваших требований. Описывайте подробнее желаемое. Можно и основную прогу начинать по 0-му адресу, а из нее вызывать бутлоадер. Но в моем варианте критическим является переписывание только 0-го сектора. Даже если в процессе перезаписи произошел сбой, пропадание питания или еще что-нибудь, бутлоадер начнет сначала.
Для обоих проектов создаем HEX-файлы и объеденяем их в один, не забывая удалить в одном из них строку окончания файла. Получившийся файл загружаем в камень при первой прошивке.
Ну и еще можно добавить в бутлоадер функции по расшифровке полученного перед записью во флэш и тогда можно прошивки передавать через третьих лиц smile.gif
Jevys
Обновление ПО представляю так: От компьютера по стандартному протоколу передачи данных через UART подается определенная команда, после которой происходит переход в программу перепрошивки, которая уже далее принимает по своему специальному протоколу пакеты с данными, содержащими код программы, и по мере поступления происывает их в память программ. Аварийный выход в программу перепрошивки только при включении питания при определенном нажатии кнопок (есть еще и такие).

А вообще спасибо за столь объемный ответ. Действительно, стало понятнее. Но надо будет еще самому подумать и все проверить.
62256
Цитата(barabek @ Sep 24 2009, 02:59) *
Можно и самим бутлоадером получать прошиву.

не можно, а даже нужно! Следует исходить из того, что основная часть прошивки может испортиться как угодно. Поэтому для живучести надо стартовать в бутлоадер (для силабсов - в последний сектор), там посчитать контрольную сумму и проверить спецножку контроллера. Если сумма испорчена или ножка зажата в ноль - аварийно ждать прошивку и шить. При нормальной сумме и свободной ноге - переходить на основную программу, оттуда тоже должна быть возможность перейти на перепрошивку - это штатный способ обновления FW.
Еще важный трюк: по адресу 0x0200 следует расположить еще один ljmp на бутлоадер. Для того, чтобы сброс контроллера при стертом первом секторе вывел на аварийную загрузку прошивки. А стереться первый сектор может как штатно (при неоконченной его перешивке), так и случайно по сбою.
редактор
Я размещаю загрузчик с 0 адреса, тогда он гарантировано стартует при сбросе.
С векторами прерываний заморочек нет, поскольку НА ВСЕ вектора (кроме того, по которому загрузчик работает)
ставлю команды перехода (LJMP $+Offset где Offset - адрес начала приложения).
В опциях проекта Приложения указываю смещение векторов прерывания и начальный адрес приложения.
Таким образом обработка прерывания происходит всего на одну команду перехода дольше.
barabek
Цитата(62256 @ Oct 6 2009, 08:04) *
не можно, а даже нужно! Следует исходить из того, что основная часть прошивки может испортиться как угодно.

И Вы тоже правы. Но я описывал свой алгоритм, не указав, что прошивку я получаю основной прогой через GPRS. Соответственно обучать бутлоадер работать с модемом накладно.Однако, при указанной позже топикстартером вводной , Ваш алгоритм становится единственно правильным. А за переход 0x200 спасибо, сам не догадался.
Goodvin
Доброго времени суток!!!

Столкнулся с такой-же задачей - программно прошивать CC1010. У контроллера CC1010 есть функция перепрошивки себя любимого, но, в отличае от Атмелов, у него нет "аппаратного" bootloader'а.
Поделюсь своими мыслями по повод решения данной задачи и с удовольствием вышлушаю Вашу критику.
Загрузчин думаю разместить с 0-го адреса, тогда при загрузки сразу будет запускаться bootloader. После загрузки он в течении некоторого времени "слушает" UART. Если по UART'у пришла команда перехода в режим программирования, то по тому-же UART'у кусками принемаем прошивку и шьем ее во флеш начиная с определенного адреса. Т.к. прошивается флеш по страницам в 128 байт, то адрес начала основной программы будет определяться так: адрес начала следующей, после конца bootloader'а, страницы (если bootloader заканчивается на середине третьей страницы, то основная программа начинается с четвертой). Если по UART'у команда перехода в режим программирования не пришла, пришла не та команда или уже все прошили, то прыгаем на адрес начала основной программы программы.
Если действительно можно приказать Кейлу, чтобы он собирал hex начиная с определенного адреса так, чтобы вся программа, включая таблицу векторов прерывания, была тупо смещена, то проблем не должно быть.

Что скажите Господа? Кейл правда умеет такое делать?
Заранее спасибо.
Палыч
Цитата(Goodvin @ Oct 21 2009, 19:33) *
Если действительно можно приказать Кейлу, чтобы он собирал hex начиная с определенного адреса так, чтобы вся программа, включая таблицу векторов прерывания, была тупо смещена, то проблем не должно быть.
Кейл правда умеет такое делать?
Чисто теоритически - такое проделать, имхо, возможно: переопределить начало всех сегментов, размещаемых в памяти программ командами линкера...
Сам я делал иначе. Делал так:
Загрузчик располагал в конце памяти программ. Загрузчик работает исключительно по готовности (не использует прерывания). Тогда в таблице векторов на загрузчик указывает только команда по вектору 0. При записи в память программ прикладной программы загрузчик контролирует запись в этот (нулевой) вектор и сохраняет его неизменным, но "выкусывает" из загружаемай программы адрес перехода из команды по нулевому адресу и сохраняет его в выделенном месте памяти программ. Это "выкусывание" и сохранение адреса используется для последующего запуска прикладной программы.
Приимущества: прикладную программу не нужно "корёжить". Единственное ограничение - прикладная программа должна использовать хоть один вектор прерывания, тогда по нулевому адресу Keil поместит команду JMP, из которой бутлоадер "выкусит" адрес старта прикладной программы.
Goodvin
Доброго времени суток!!!

Вчера попробовал сместить адреса в Кейле. Делается это, как описывает уважаемый barabek: project->options for target->bl51 locate снимаем галачку "Use Memory Layuot..." в поле "Code Range" изменяем первый адрес на тот, который нужно. В моем случае, для CC1010, адрес можно менять на число байт, кратное 128, это следует из страничной организации памяти программ этого контроллера (256 страниц по 128 байт в каждой). Hex генерируется что надо, но таблица векторов прерывания все равно идет с нулегого адреса, что не удивительно:

БЫЛО:

:020000020000FC
:100000000211E1000000000000000002114E4B2F21
:100010000E58000041FC9C0280604844810AFFC0E9
:10002000000000020FB2EF4E70038D9922AF058FD2
:10003000C12200000000000000000002108000004B
:10004000000000020EDEBB010689828A83E0225096
:1000500002E722BBFE02E32289828A83E49322BB69
:10006000010CE58229F582E5833AF583E02250060A
:10007000E92582F8E622BBFE06E92582F8E222E5C0
:100080008229F582E5833AF583E49322BB01068950
:10009000828A83F0225002F722BBFE01F322F8BBD2
:1000A000010DE58229F582E5833AF583E8F02250D7
:1000B00006E92582C8F622BBFE05E92582C8F222A0


СТАЛО:

:020000020000FC
:1000000002121B0000000000000000021188000026
:1000100000000000000000000000000000000000E0
:10002000000000020FEC00000000000000000000D3
:1000300000000000000000000000000210BA0000F4
:10004000000000020F180000000000000000000087
:1000500000000000000000000000000000000000A0
:100060000000000000000000000000000000000090
:100070000000000000000000000000000000000080
:10008000BB010689828A83E0225002E722BBFE027E
:10009000E32289828A83E49322BB010CE58229F55D
:1000A00082E5833AF583E0225006E92582F8E622CC
:1000B000BBFE06E92582F8E222E58229F582E58386

Правда в hex'нике изначальном в начале, где вектора прерывания, еще что-то есть, но я не знаю что это (например адрес 0x000E)

Щас думаю как в сложившейся ситуации разместить bootloader и как что будет работать.

Уважаемый Палыч, не понял фразу "загрузчик работает исключительно по готовности" это как, нельзя ли описать по подробнее? И еще, как вы принемаете прошивку, если bootloader работает не по прерываниям?
Думаю, что в моем случае не получится реализовать Ваше решение из-за страничной организации памяти.
редактор
Сместить вектора прерывания в программе не проблема
На вкладке С51 есть галочка "Interrupt vector at adress" напротив нее поле где ввести адрес смещения для векторов прерывания.
Далее необходимо переопределить таблицу прерываний в стартапе.
Если загрузчик в младших адресах, то разместить команды перехода обработки прерываний на новые адреса по принципу
org 0x3 // прерывание INT0
LJMP $+Offset // переход на новый адрес обработки прерывания
и т.д. для всех векторов
Далее в стандартном стартапе приложения расположенного в старшей области памяти при необходимости изменить директиву начального адреса программы (если не менять то загружаться будет приложение, если изменить , то сперва заработает загрузчик)
ORG 0 -> заменит на ORG Offset

Если приложение в младших адресах, а загрузчик в старших, то достаточно переопределить только один вектор с которым работает загрузчик и следить что бы приложение не вылезло за границу отведенной памяти.
Палыч
Цитата(Goodvin @ Oct 22 2009, 23:44) *
Уважаемый Палыч, не понял фразу "загрузчик работает исключительно по готовности" это как, нельзя ли описать по подробнее? И еще, как вы принемаете прошивку, если bootloader работает не по прерываниям?
При работе загрузчика прерывания запрещены. Прошивка принимается по UART. В загрузчике проверяются флаги TI и RI регистра SCON: есть флаг - забираем/передаём очередной байт, сбрасываем флаг; принята очередная порция информации для записи в память программ - производим запись.
Goodvin
Доброго времени суток!!!

А может быть в памяти программ 2 таблицы векторов прерываний (одна bootloader'a вторая прикладной программы)?
Так, что бы пока bootloader работает, выполнялись его обработчики прерываний, а когда выполняется прикладная программа, то исполняются ее обработчики.

2Редактор А нельзя ли поподробнее о STARTUP'е, а то я не догоню что-то. help.gif
barabek
Цитата(Goodvin @ Oct 24 2009, 03:16) *
Доброго времени суток!!!

А может быть в памяти программ 2 таблицы векторов прерываний (одна bootloader'a вторая прикладной программы)?
Так, что бы пока bootloader работает, выполнялись его обработчики прерываний, а когда выполняется прикладная программа, то исполняются ее обработчики.

2Редактор А нельзя ли поподробнее о STARTUP'е, а то я не догоню что-то. help.gif

Было бы хорошо иметь несколько таблиц, но чудес не бывает. В своей программе Вы можете прописать обработчики прерываний по любым адресам ( при большом желании ) . Однако в железе при приходе прерывания переход будет осуществлен на строго определенный НЕИЗМЕНЯЕМЫЙ адрес, прописаный в документации. Не пытайтесь никак это изменить, не получится.
А по второму вопросу. В каждый проект на С добавляется файл startup.a51, который исполняется после сброса контроллера до перехода на Вашу функцию main. Поиском найдете у себя на компе. При желании можете его менять под свои нужды. Как? Найдите недавно был пост типа "не могу записать в xdata". Извините, пишу с телефона и мне нужно бежать :-). Потом если что ...
Goodvin
Доброго времени суток!!!

Думаю поступить так: bootloader расположить в начальных адресах. В стартапе bootloader'а переопределить все вектора прерывания (кроме тех, что используются в bootloader'е) на адреса, где во флеше будет таблица прерываний прикладной программы. Тогда по прерыванию контроллер идет по адресу прописаному в документации, а там ему говорят иди по новому адресу. Придя по новому адресу он обрабатывает прерывание. Но получается, что если я в bootloader'е буду использовать прерывания по UART'у и таймеру, то в прикладной программе их юзать уже на получится?

Еще вариант расположить bootloader в последних адресах. Тогда прикладная программа при прерываниях не будет скакать по адресам, но там другие загвоздки есть.

Я правильно уловил логику "колдования" со стартапом?

Уважаемый barabek, не могли бы Вы написать о стартапе или дать ссылку.
Палыч
Цитата(Goodvin @ Oct 26 2009, 18:24) *
Но получается, что если я в bootloader'е буду использовать прерывания по UART'у и таймеру, то в прикладной программе их юзать уже на получится?
Ну, почему же - не получится... Просто переход по прерыванию в прикладной программе получится длиннее: нужна будет переменная - флаг работы прикладной программы. При прерываниях по UART и таймеру придётся её проверять и переходить на переопределённый вектор или выполнять некие действия для бутлоадера.
Цитата(Goodvin @ Oct 26 2009, 18:24) *
Еще вариант расположить bootloader в последних адресах. Тогда прикладная программа при прерываниях не будет скакать по адресам, но там другие загвоздки есть.
Это какие загвоздки? Работа бутлоадера по-готовности устройств? Проверять циклически несколько флагов в регистрах - это разве так сложно?
Цитата(Goodvin @ Oct 26 2009, 18:24) *
... не могли бы Вы написать о стартапе или дать ссылку.
Что Вы хотите узнать о стартапе? Посмотрите на стартап сами - файлы для разных МК лежат в Keil/C51/Lib
Goodvin
Доброго времени суток!!!

Цитата
"Ну, почему же - не получится... Просто переход по прерыванию".

Вообщето тоже думал об этом. В обработчике прерываний проверять что сейчас работает (bootloader или прикладная программа) и в зависимости от этого прыгать на определенный адрес. Вы это имели ввиду?

По поводу работы bootloader'а "по готовности" еще не размышлял вплотную (все таки кажется правильнее с прерываниями). Загвоздки в другом (даже скорее и не загвоздка; просто не думал пока об этом, т.к. привожу в порядок полученную информацию): при загрузке с нулевого адреса будем уходить на bootloader, но тогда этот нулевой адрес (там у нас адрес начала прикладной программы) нужно сохранять в заранее известное место.

Стартапы смотрел. В принципе все понятно, но было бы круто, если б кто нибудь закоментил стартап какого либо своего проекта...
barabek
Цитата(Goodvin @ Oct 27 2009, 05:49) *
Доброго времени суток!!!


Вообщето тоже думал об этом. В обработчике прерываний проверять что сейчас работает (bootloader или прикладная программа) и в зависимости от этого прыгать на определенный адрес. Вы это имели ввиду?

По поводу работы bootloader'а "по готовности" еще не размышлял вплотную (все таки кажется правильнее с прерываниями). Загвоздки в другом (даже скорее и не загвоздка; просто не думал пока об этом, т.к. привожу в порядок полученную информацию): при загрузке с нулевого адреса будем уходить на bootloader, но тогда этот нулевой адрес (там у нас адрес начала прикладной программы) нужно сохранять в заранее известное место.

Стартапы смотрел. В принципе все понятно, но было бы круто, если б кто нибудь закоментил стартап какого либо своего проекта...

посмотрите,пожалуйста, еще раз внимательно мой первый ответ. Там я как раз и дал стартапы (вернее изменяемые их строки) для бута и основной проги.

Работайте в бутлоадере без прерываний, чистыми полингами. Вам об этом и Палыч говорит. Прога для бутлоадера довольно проста, по сути прерывание нужно только от UART. Если боитесь не успеть принимать - выбирайте небольшую скорость обмена. Сам алгоритм сделайте таким, чтобы комп слал только тогда, когда это ему позволяет бутлоадер.
Палыч
Цитата(Goodvin @ Oct 26 2009, 21:49) *
В обработчике прерываний проверять что сейчас работает (bootloader или прикладная программа) и в зависимости от этого прыгать на определенный адрес. Вы это имели ввиду?
Да - это. Недостаток такой организации прерываний прикладной программы очевиден: задержка в переходе на процедуру обработки прерывания (для прикладной программы это может оказаться критичным). Еще одно маленькое неудобство - флаг работы бутлоадера/программы нужно где-то хранить, при этом необходимо обеспечить, чтобы этот флаг не разрушался прикладной программой. Т.е. необходимо выделить байт или бит, который бы использовался бутлоадером, но не использовался бы прикладной программой. Наверное, для этих целей можно использовать бит F0 регистра PSW (я не замечал, чтобы Keil этот бит использовал, а выделять для этого место в ОЗУ крайне не желательно - имхо, тяжело будет разделять его между бутлоадером и программой).
Мне все-таки кажется более предпочтительным бутлоадер, размещаемый в конце памяти. Место для адреса начала прикладной программы много не нужно - всего три байта (команда LJMP), которые можно отвести в начале бутлоадера. Бутлоадер, скорее всего, получится несколько больше по объёму за счет сохранения неизменным нулевого вектора на бутлоадер и сохранения (формирования команды перехода) адреса начала прикладной программы. Но при этом прикладная программа не будет тормозиться лишними проверками и переходами, что гораздо ценнее, да и прикладная программа не будет "искорёжена" сдвигом на длину бутлоадера.

P.S. Кстати, работу по сохранению вектора 0 и адреса начала прикладной программы можно частично (а, может быть - полностью) перенести на программу, которая будет работать "извне" (на ПЭВМ, с которой "заливается" прикладная программа) и взаимодействовать с бутлоадером.
редактор
Цитата(Goodvin @ Oct 26 2009, 19:24) *
Еще вариант расположить bootloader в последних адресах. Тогда прикладная программа при прерываниях не будет скакать по адресам, но там другие загвоздки есть.

Я правильно уловил логику "колдования" со стартапом?


Загвоздки могут быть. Например при работе с SiLabs.
При блокировке области кода от чтения и записи доступ к младшим страницам из старших запрещен (возвращаемый результат неопределен),
что вызывает сложности с подсчетом контрольной суммы приложения расположенного ниже чем загрузчик. Лично не проверял, но описанию склонен верить.
Пример изменений стартапа для ознакомления прилагаю.

Код
                EXTRN CODE (?C_START)
                PUBLIC  ?C_STARTUP
// основные изменения сделаны здесь
                CSEG    AT      0
?C_STARTUP:     LJMP    STARTUP1
//                ORG  3H
Offset SET 800H    // адрес основного приложения
                ISR_JMP 0  // int 0
                ISR_JMP 1  // Timer 0
                //ISR_JMP 2  // int 1
                ISR_JMP 3  // Timer 1
                ISR_JMP 4  // uart 0
                ISR_JMP 5  // Timer 2
                ISR_JMP 6  // spi0
                ISR_JMP 7  // SMBus
                ISR_JMP 8  // ADC 0
                ISR_JMP 9  // PCA
                ISR_JMP 10 // Comarator 0
                ISR_JMP 11 // Comarator 0
                ISR_JMP 12 // Comarator 1
                ISR_JMP 13 // Comarator 1
                ISR_JMP 14 // Timer 3
                ISR_JMP 15 // ADC 0
                ISR_JMP 16 // Timer4
                ISR_JMP 17 // ADC2
                ISR_JMP 18 // ADC2
                ISR_JMP 19 // Reserved
                ISR_JMP 20 // UART1

                CSEG AT 0100h          
EXTRN CODE(LoadCode)             // фиксируем адрес для вызова из основной программы

                MOV     SP,#?STACK-1 // установить стек загрузчика
                LJMP LoadCode        // перейти к выполнению функции

                RSEG    ?C_C51STARTUP              
STARTUP1:
        .... // штатные действия


?My_Exit_Func   SEGMENT   CODE
             RSEG   ?My_Exit_Func
PUBLIC exit
exit:                             // вызывается из загрузчика для передачи управления основному приложению
               MOV SP,#7   // значение стека как при сбросе контроллера
               LJMP Offset

               END
62256
Цитата(редактор @ Oct 27 2009, 09:09) *
Загвоздки могут быть. Например при работе с SiLabs.
При блокировке области кода от чтения и записи доступ к младшим страницам из старших запрещен (возвращаемый результат неопределен), что вызывает сложности с подсчетом контрольной суммы приложения расположенного ниже чем загрузчик. Лично не проверял, но описанию склонен верить.

в Силабсе из последней страницы есть доступ ко всем: она лочится автоматически при закрытии даже одной нижней.
И ещё: эта страница нестираема из кода. Поэтому бутлоадер лучше располагать в ней, и не использовать в нем процедур из остальной флеши (в том числе прерывания).
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.