Загрузчик для dsPIC33

в 13:02, , рубрики: bootloader, dsPIC33, загрузчик, программирование микроконтроллеров, метки:

Загрузчик (bootloader) — очень удобный инструмент работы с микроконтроллерами (далее — МК). Это маленькая программа, которая позволяет МК «самопрограммироваться» (self-programming). Обычно, при подаче питания на МК, управление сначала получает загрузчик, которые проверят заранее заданные условия (определенное состояние на ножке МК, флаг в EEPROM, подходящий файл прошивки на SD-карте и т.д.). Если условия не выполняются, то управление передается основной программе. Если же условия выполняются, то загрузчик переключается в режим программирования, получая данные новой прошивки по предопределенному интерфейсу. Это позволяет обновить прошивку МК не прибегая к паяльнику, программатору или внутрисхемному программированию.

Обычный алгоритм использования загрузчика для МК, только что вынутого из упаковки:

  1. с помощью программатора/отладчика прошивается загрузчик
  2. МК монтируется в плату
  3. используя загрузчик по заранее определенному интерфейсу загружается основная прошивка

Это вполне приемлемо для опытных образцов изделий или для мелкосерийного производства. А что делать, если производство крупносерийное? Или сборка проводится автоматами (а может людьми с функционалом автоматов) — прошил, впаял? Тогда разумно убрать из алгоритма 3го пункт — объединить в одной прошивке и основную программу и загрузчик.

В своей практике я столкнулся с довольно жутким методом получения такой прошивки — в HEX-файл основной прошивки просто дописывался HEX-файл с кодом загрузчика. Конечно. такой подход имеет право быть — как ни крути, но итоговые «прошивки имени др. Франкенштейна» работали как надо. Но чувство, что для решения этой задачи должны быть более корректные методы, меня не оставляло.

Когда Я поискал решения в Интернете, то был неприятно удивлен, что простого и понятного описания решения нет. Собственно, именно это побудило меня написать публикацию, описывающую мое решение этой ситуации. Возможно мое видение решения этой проблемы отличается от максимально правильного, но оно гораздо более логичней, чем сшивание HEX-файлов.

Прежде чем перейти к самой теме публикации, хочу привести список упрощений и инструментов, которые были использованы:

  • MPLAB IDE v8.85 (да, весьма устаревшая IDE)
  • Microchip C30 Toolsuite v3.12
  • объектные файлы в формате COFF (т.е. по умолчанию для этого toolchain-a)
  • расположение загрузчика и основной программы статичны
  • загрузчик располагается с адреса 0х0400
  • основная программа располагается с адреса 0х2000
  • память по адресам с 0х0200 по 0х0400 — неиспользуемая

И самое главное — конкретных исходников загрузчика в это публикации не будет.

Компоновка загрузчика в основной проект

Просто скомпилировать…

Начнем с самого простого случая. Добрый Дедушка Мороз прислал Вам на Новый Год готовый загрузчик. Причем он уже потрудился на славу и скомпилировал и скомпоновал его для Вас. Итак, у Вас в руках (на флешке/в сети/на жестком диске) есть файлик – UltraBoot3000.blob. Дальше алгоритм очень простой – просто добавь его к себе в проект.

Касаемо MPLAB IDE его надо добавить в категорию «Object Files». К сожалению, по умолчанию в эту категорию можно добавить только файлы с расширением «o». Отмечу так же, что файлы с расширением «o» получаются так же в процессе компиляции Вашей программы. Чтобы нечаянно не перепутать и не забыть о файле загрузчика, рекомендую держать его с другим расширением, например blob – binary linked object. Чтобы IDE положило файл blob в категорию «Object Files», этой категории нужно скорректировать настройки фильтров. Жмем правой кнопкой мыши на этой категории и выбираем пункт «Filter…». В появившемся окне в поле через точку-с-запятой дописываем необходимый нам шаблон фильтра. В нашем случае в поле должно быть в итоге следующее описание фильтров:

*.o;*.blob

После настройки фильтров можно добавить файл загрузчика в проект.

Запускаем процесс компи… НЕТ! СТОП!

Чтобы корректно собрать прошивку с нашим загрузчиком, нужен правильный скрипт компоновщика. Конечно, если Дедушка Мороз был настолько добр, что и этот скрипт Вам прислал, то просто добавляем его себе в проект (MPLAB IDE поддерживает файлы с расширением «gld»), запускаем процесс сборки проекта и на выходе получаем корректный файл прошивки с уже встроенным кодом загрузчика.

Но что делать, если Дедушка забыл про это скрипт или может быть именно Вы и являетесь тем, кто сделал этот загрузчик и Вам надо встроить его в свой/чужой проект? Читаем дальше…

Подготовка скрипта компоновщика

Собственно не стоит писать скрипт с нуля. Достаточно чуть-чуть переделать скрипт, который устанавливается в комплекте с компилятором. Лежит этот скрипт в папке «${ToolChainPath}supportdsPIC33Fgld». Примечание: здесь и далее ${ToolChainPath} – это путь, куда был установлен компилятор С30, по умолчанию это – «c:Program FilesMicrochipMPLAB C30». Скопируем оттуда скрипт по умолчанию для нашего устройства, например для МК dsPIC33FJ128GP802 это будет файл «p33FJ128GP802.gld».

Первым делом надо вписать два символа, описывающих начало области загрузчика и основной программы. Например, так:

_Booter = 0x000400;
_mainFW = 0x002000;

Далее в структуре MEMORY {…} в поле program указать начальную позицию (origin) и длину (length), соответствующие началу загрузчика и размеру flash-памяти минус начало загрузчика. Примерно так:

program (xr) : ORIGIN = 0x400,LENGTH = (0x15800 - 0x400)

Следующим шагом будет корректировка вектора сброса. В структуре SECTIONS {…} найдем описание «Reset Instruction». Необходимо чтобы она выглядела следующим образом:

.reset :
{
SHORT(ABSOLUTE(_Booter));
SHORT(0x04);
SHORT((ABSOLUTE(_Booter) >> 16) & 0x7F);
SHORT(0);
} >reset

Осталось только добавить описание зоны загрузчика. Зона описывается в структуре SECTIONS {…}. Это описание необходимо вставить перед описанием зоны «.text». Описание следующее:

.boot _Booter :
{
*(.booter);
. = _mainFW - _Booter;
} >program = 0xFFFF

Итак, скрипт готов.

Создание загрузчика

Сделать загрузчик из программы

Первое, что хотелось бы отметить: загрузчик не должен быть самостоятельной программой. Конечно, в процессе отладки загрузчика его можно реализовать как самостоятельную программу. Но как только Вы планируете его встроить в другую программу его необходимо специально подготовить.

Итак, чего лишается программа, превращаясь в загрузчик:

  1. Описание конфигурационных бит
  2. Векторов прерываний
  3. Вектора сброса

Кроме этого, невозможно корректно встроить константы загрузчика, хранимые во flash-памяти, в код основной программы. Поэтому от них тоже придется отказаться. Примечание: на самом деле способ есть, но он настолько нетривиален, что для массового использования проще отказать от констант в загрузчике.

Доработка исходных текстов

Доработка несложная. Убираем все макросы, описывающие конфигурационные биты. Исключаем использование глобальных констант.

Настройка проекта

Так же необходимо проверить и, при необходимости, скорректировать настройки проекта. Все изменения – во вкладке «MPLAB LINK30», категория «General». Установить чек-боксы: don’t pack data template; don’t create hanldes; don’t create default ISR; remove unused sections.

Доработка скрипта компоновщика

Так же как и для основной программы с загрузчиком скрипт будет отличным от скрипта по умолчанию. Итак, берем скрипт по умолчанию и вносим следующие изменения.

Структуру MEMORY {…} уменьшаем до двух позиций: data и program. Причем начало и длина program соответствуют началу и длине области загрузчика:

{
data (a!xr) : ORIGIN = 0x800, LENGTH = 0x4000
program (xr) : ORIGIN = 0x400, LENGTH = 0x1C00
}

Удаляем полностью описание «Reset Instruction» в структуре SECTIONS {…}. В этой же структуре удаляем описание «Configuration Words». Полностью удаляем структуру SECTIONS {…}, которая описывает вектора прерываний (метка «Section Map for Interrupt Vector Tables»).

В структуре SECTIONS {…} дорабатываем описание зоны «.text», заменив название зоны на «.booter» и приведя ее к следующему виду:

.booter 0x400 :
{
*(.init);
*(.user_init);
*(.handle);
*(.libc) *(.libm) *(.libdsp); /* keep together in this order */
*(.lib*);
*(.dinit);
*(.text);
} >program

Естественно, полученный скрипт надо добавить в проект.

Постобработка выходного файла

После осуществления предыдущих действий можно запустить процесс компиляции. В выводе процесса сборки (для MPLAB IDE это будет в окне Output, вкладка Build) можно увидеть результат компоновки. Например, так:

Program Memory [Origin = 0x400, Length = 0x1c00]

section address length (PC units) length (bytes) (dec)
------- ------- ----------------- --------------------
.booter 0x400 0x7d0 0xbb8 (3000)

Total program memory used (bytes): 0xbb8 (3000) 27%

Data Memory [Origin = 0x800, Length = 0x4000]

section address alignment gaps total length (dec)
------- ------- -------------- -------------------
.nbss 0x800 0 0xa2c (2604)
bootdata 0x47c0 0 0x40 (64)

Total data memory used (bytes): 0xa6c (2668) 16%

Если в program memory больше одной секции – то скорей всего вы не до конца выполнили действия описанные выше. Если там именно одна секция с названием «.booter» — то все сделано правильно.

Также надо обратить внимание на количество секций в data memory.

Теперь надо выполнить постобработку выходного файла. Постобработка проводиться с файлом с расширением «cof». Открываем командную строку в папке с этим файлом. Допустим файл имеет имя ultraboot.cof, тогда выполним команду:

"${ToolChainPath}binpic30-strip.exe" -s --remove-section=.nbss --remove-section=bootdata -o ultraboot.blob ultraboot.cof

Не забываем ${ToolChainPath} заменять на реальный путь. Количество опций «--remove-section=…» должно соответствовать количество секций в data memory (из вывода результата работы компоновщика).

Далее надо провести финальную проверку полученного бинарного файла с загрузчиком. Команда:

"${ToolChainPath}binpic30-objdump.exe" -ht ultraboot.blob

Вывод будет примерно следующим:

ultraboot.blob: file format coff-pic30

Sections:
Idx Name Size VMA LMA File off Algn
0 .booter 000007d0 00000400 00000400 00000058 2**1
CONTENTS, ALLOC, LOAD, CODE
SYMBOL TABLE:
no symbols

Если вы увидите, что секций ровно одна — с названием «.booter» и в символьной таблице нет символов, то можно считать, что все сделано корректно.

И в конце ссылки на примеры файлов для линкера:

Автор: jinrou85

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js