Перевод документации USB DFU Bootloader Datasheet [1], посвященной бутлоадеру (загрузчику) USB DFU, который прошит во все чипы AVR с аппаратной поддержкой USB.
Рассмотрены следующие возможности и особенности загрузчика:
• Протокол обмена [3] работает поверх физического подключения через USB. – устройство USB реализует класс USB DFU. – есть автоподстройка под частоту кварца (кварц может быть 8 или 16 МГц). • In-System Programming (программирование чипа прямо в системе). – Поддерживается чтение/запись памяти FLASH (энергонезависимая память кода программы) и EEPROM (энергонезависимая память для хранения данных). – Чтение идентификатора микроконтроллера (Device ID). – Полное стирание памяти чипа (chip Erase). – Есть команда для запуска приложения пользователя (Start application). • In-Application Programming (программирования памяти в программе пользователя) – Есть программная точка входа в подпрограммы драйвера FLASH.
8-битные микроконтроллеры mega AVR с аппаратной поддержкой интерфейса USB поставляются с завода сконфигурированными загрузчиком USB (USB bootloader), который размещен в памяти программ FLASH, в пределах специальной секции загрузки (flash boot section) микроконтроллера. Это касается микроконтроллеров моделей AT90USB128x, AT90USB64x, AT90USB162, AT90USB82, ATmega32U4, ATmega16U4.
USB-загрузчик позволяет выполнить программирование чипа прямо в той системе, где он работает (In-System Programming), через USB от хоста (компьютера PC). Эта процедура происходит без извлечения чипа из системы, где он работает, и при этом не используется никакой дополнительный интерфейс для программирования (типа ISP или JTAG), и не нужен внешний программатор.
В этом документе описывается функционал USB bootloader и затрагивается описание его протокола [3] - рассмотрены вопросы эффективного выполнение операций над памятью чипа (FLASH и EEPROM).
[2. Рабочее окружение загрузчика]
Бутлоадер размещен в секции загрузки (boot section) которая расположена во встроенной в чип памяти FLASH (память программ). Бутлоадер обрабатывает обмен данными через протокол USB и выполняет операции чтения/записи памяти чипа (Flash/EEPROM).
Секция памяти бутлоадера "Bootloader Flash Section" находится в старших адресах памяти FLASH, начиная с заранее известного адреса. Этот адрес может быть выбран из нескольких вариантов фьюзами микроконтроллера, и он влияет на размер секции бутлоадера. Размер секции выбирается так, чтобы код бутлоадера целиком поместился в неё (см. таблицу 2-1). Все необходимые для этого настройки фьюзов и программирование секции загрузки заранее сделаны на заводе Atmel, так что загрузчик поставляется готовым к работе прямо "из коробки".
Таблица 2-1. Параметры USB Bootloader.
Модель чипа AVR USB |
Используемый размер секции загрузки |
VID / PID |
Адрес запуска загрузчика (в словах) |
AT90USB1287 |
4 К слов |
0x03EB / 0x2FFB |
0xF000 |
AT90USB1286 |
AT90USB647 |
2 К слов |
0x03EB / 0x2FF9 |
0x7800 |
AT90USB646 |
AT90USB162 |
0x03EB / 0x2FFA |
0x1800 |
AT90USB82 |
0x03EB / 0x2FF7 |
0x0800 |
ATmega32U4 |
0x03EB / 0x2FF4 |
0x3800 |
ATmega16U4 |
0x03EB / 0x2FF3 |
0x0800 |
Примечание к таблице: словом называются 2 байта, которые составляют минимальный размер одной инструкции AVR. Адрес в словах всегда в 2 раза меньше байтового адреса, это иногда создает некоторую путаницу.
Рис. 2-1. Физическое окружение, в котором работает загрузчик USB.
[3. Запуск загрузчика (Bootloader Activation)]
Как указано в даташите на чип серии AT90USB, bootloader может быть активизирован через выполнение одного из следующих условий:
• Regular application execution. Прямой запуск из программы пользователя: передача управления (выполнение инструкции jump или call) из секции приложения (Application Section). Это может быть инициировано через команду, принятую по USB, USART или SPI, которая была декодирована приложением пользователя.
• Boot Reset Fuse. Фьюз Boot Reset (BOOTRST) может быть запрограммирован так, что вектор сброса (Reset) будет указывать на секцию загрузчика (Boot Flash section), так что загрузчик запустится после сброса или включения питания. Как только загрузчик получил и записал в память приложение пользователя, командой бутлоадера "start application" можно запустить на выполнение загруженную программу пользователя. Имейте в виду, что значение самих фьюзов не может быть изменено самим MCU AVR (командами ассемблера). Это означает, что как только запрограммирован Boot Reset Fuse, вектор сброса всегда будет указывать на начало секции загрузки (адрес Bootloader Reset), и фьюз не может быть изменен другим способом, кроме как через последовательное или параллельное программирование (с помощью специальных программаторов типа JTAGICE mkII или AVRISP mkII). В заводской конфигурации по умолчанию фьюз BOOTRST не активен.
• External Hardware conditions. Внешнее аппаратное условие для запуска загрузчика. Отдельный фьюз Hardware Boot Enable (HWBE) может быть запрограммирован, чтобы отслеживать специальное аппаратное условие запуска загрузчика после сброса чипа. Это условие анализируется по лог. уровню ножки порта GPIO, которая имеет дополнительную функцию HWB. Если после сброса на этой ножке лог. 0, то происходит принудительный переход на секцию загрузчика.
Различные условия запуска загрузчика суммарно отображены на рис. 3-1.
Рис. 3-1. Процесс запуска загрузчика (Boot Process).
Пояснения к рисунку: HWB - специальный вывод порта микроконтроллера, который может использоваться для управления загрузкой (работа ножки HWB должна быть разрешена отдельным фьюзом). jmp BOOT - программный переход по адресу начала бутлоадера (либо вызов подпрограмм API для манипуляций с содержимым FLASH).
[4. Протокол]
4.1 Что такое Device Firmware Upgrade
Device Firmware Upgrade (DFU, обновление программного обеспечения микроконтроллера) - механизм, реализованный для модификаций программного обеспечения (firmware) встраиваемых систем (embedded, т. е. электронные устройства, где применен микроконтроллер). В любом устройстве USB может использоваться такая возможность, если оно отвечает требованиям, описанным в этом документе.
Очевидно, что непрактично для устройства одновременно поддерживать и операции DFU, и нормальное рабочее функционирование, так как в общем случае операции загрузчика DFU могут замедлить реакцию основной программы, и работа устройства USB может стать ненадежной. Например, принтер не будет работать как положено, если вовлечено обновление firmware. Кроме того, устройство должно поддерживать функционирование определенного, заранее заданного класса USB (HID, CDC, Audio), что несовместимо с функционированием устройства DFU, так как оно реализовано в виде отдельного класса USB со своим специальным драйвером. Поэтому устройства, поддерживающие DFU, сделаны так, что они не могут сами войти в режим обновления firmware, для этого необходимо специальное внешнее вмешательство человека или хоста с операционной системой.
4.2 Специфичные запросы протокола DFU
В дополнение к стандартным запросам USB, используются дополнительно 7 DFU запросов, специфичных для класса DFU (class-specific requests). Эти запросы применяются для поддержки операций обновления, см. таблицу 4-1.
Таблица 4-1. DFU Class-specific Requests.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
0010 0001b |
DFU_DETACH (0) |
wTimeout |
Interface (4) |
0 |
нет данных |
0010 0001b |
DFU_DNLOAD (1) |
wBlock |
Interface (4) |
длина |
firmware |
1010 0001b |
DFU_UPLOAD (2) |
wBlock |
Interface (4) |
длина |
firmware |
1010 0001b |
DFU_GETSTATUS (3) |
0 |
Interface (4) |
6 |
статус |
0010 0001b |
DFU_CLRSTATUS (4) |
0 |
Interface (4) |
0 |
нет данных |
1010 0001b |
DFU_GETSTATE (5) |
0 |
Interface (4) |
1 |
состояние |
0010 0001b |
DFU_ABORT (6) |
0 |
Interface (4) |
0 |
нет данных |
4.3 Набор дескрипторов DFU
Устройство экспортирует для хоста USB набор дескрипторов, который содержит:
• Дескриптор устройства DFU (DFU device descriptor). • Один дескриптор устройства (configuration descriptor). • Один дескриптор интерфейса (interface descriptor), включая дескрипторы для альтернативных установок, если они есть.
4.3.1 DFU Device Descriptor
Дескриптор устройства USB DFU, этот дескриптор имеется только в наборе дескрипторов режима DFU. Код класса DFU поставляется в поле bDeviceClass этого дескриптора.
Таблица 4-2. DFU Mode Device Descriptor.
Смещение |
Поле |
Размер |
Значение |
Описание |
0 |
bLength |
1 |
12h |
Размер этого дескриптора в байтах. |
1 |
bDescriptorType |
1 |
01h |
Функциональный тип дескриптора DFU. |
2 |
bcdUSB |
2 |
0100h |
Номер поддерживаемой спецификации USB, закодированный в двоично-десятичном коде (формат BCD). |
4 |
bDeviceClass |
1 |
FEh |
Код класса, специфичный для приложения (Application Specific Class Code). |
5 |
bDeviceSubClass |
1 |
01h |
Код DFU (Device Firmware Upgrade Code). |
6 |
bDeviceProtocol |
1 |
00h |
На этом интерфейсе устройство не использует протокол, специфичный для класса. |
7 |
bMaxPacketSize0 |
1 |
32 |
Максимальный размер пакета для конечной точки 0 (размер ограничен 32 из-за ограничений драйвера на стороне хоста). |
8 |
idVendor |
2 |
03EBh |
Идентификатор вендора (Vendor ID, VID). |
10 |
idProduct |
2 |
2FFxh |
Идентификатор продукта (Product ID, PID). В зависимости от разновидности модели чипа PID может меняться в последней тетраде (см. таблицу 2-1). |
12 |
bcdDevice |
2 |
0x0000 |
Номер релиза устройства, закодированный в двоично-десятичном виде (формат BCD). |
14 |
iManufacturer |
1 |
0 |
Индекс строкового дескриптора производителя устройства (описатель отсутствует). |
15 |
iProduct |
1 |
0 |
Индекс строкового дескриптора для описания продукта (описатель отсутствует). |
16 |
iSerialNumber |
1 |
0 |
Индекс строкового дескриптора серийного номера (описатель отсутствует). |
17 |
bNumConfigurations |
1 |
01h |
Количество конфигураций - только одна конфигурация для устройства DFU. |
4.3.2 DFU Configuration Descriptor
Дескриптор конфигурации DFU, этот дескриптор идентичен стандартному дескриптору конфигурации, описанному в спецификации USB DFU версии 1.0, за исключением того, что поле bNumInterfaces должно содержать значение 01h.
4.3.2.1 DFU Interface Descriptor
Дескриптор интерфейса DFU, этот дескриптор доступен только когда устройство работает в режиме DFU. Таким образом, значение поля bInterfaceNumber всегда равно 0.
Таблица 4-3. DFU Mode Interface Descriptor
Смещение |
Поле |
Размер |
Значение |
Описание |
0 |
bLength |
1 |
09h |
Размер этого дескриптора в байтах. |
1 |
bDescriptorType |
1 |
04h |
Тип дескриптора - INTERFACE. |
2 |
bInterfaceNumber |
1 |
00h |
Номер этого интерфейса. |
3 |
bAlternateSetting |
1 |
00h |
Альтернативная установка(1). |
4 |
bNumEndpoints |
1 |
00h |
Используется только канал управляющих передач (только конечная точка 0). |
5 |
bInterfaceClass |
1 |
FEh |
Код класса, специфичный для приложения (Application Specific Class Code). |
6 |
bInterfaceSubClass |
1 |
01h |
Код DFU (Device Firmware Upgrade Code). |
7 |
bInterfaceProtocol |
1 |
00h |
Устройство не использует для этого интерфейса протокол, специфичный для класса. |
8 |
iInterface |
1 |
00h |
Индекс строкового дескриптора для этого интерфейса (описатель отсутствует). |
Примечание (1): альтернативные установки могут использоваться в приложении для доступа к дополнительным сегментам памяти. В этом случае предлагается, чтобы каждая альтернативная установка использовала строковый дескриптор, чтобы указать целевой сегмент памяти, например "EEPROM". Детали, касающиеся использования альтернативных установок для других возможных применений, выходят за рамки этого документа. Однако их использование специально ничем не ограничено, поскольку авторы ожидают, что творчески настроенные пользователи разработают варианты использования альтернативных настроек для своих целей.
4.4 Описание команд
Протокол, реализованный в бутлоадере AT90USB, позволяет:
• Начать процедуру обмена данными. • Программировать данные FLASH (память программ) или EEPROM • Читать данные Flash или EEPROM • Программировать конфигурационную информацию. • Считывать конфигурацию и информацию производителя. • Очищать память FLASH/ • Запустить на выполнение программу пользователя.
Обзор протокола приведен в Приложении A, см. также [3].
4.5 Состояние устройства (Device Status)
4.5.1 Get Status
Команда запроса состояния устройства. Хост применяет запрос DFU_GETSTATUS для установки синхронизации с устройством. Состояние дает информацию о результате выполнения предыдущего запроса: в_процессе_выполнения/OK/Ошибка/... и т. д.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
1010 0001b |
DFU_GETSTATUS (3) |
0 |
Interface (4) |
6 |
статус |
0010 0001b |
DFU_CLRSTATUS (4) |
0 |
Interface (4) |
0 |
нет данных |
Устройство отвечает на запрос пакетом полезной нагрузки, который содержит следующие данные:
Таблица 4-4. Ответ на команду DFU_GETSTATUS.
Смещение |
Поле |
Размер |
Значение |
Описание |
0 |
bStatus |
1 |
число |
Показывает статус, который стал результатом выполнения последнего запроса хоста. |
1 |
bwPollTimeOut |
3 |
число |
Таймаут - минимальное время в миллисекундах, которое хост должен ждать перед тем, как он может послать следующий запрос DFU_GETSTATUS. Назначение этого поля - предоставить устройству DFU динамически подстраивать количество времени, которое устройство предполагает, что хост будет находиться в ожидании между фазой статуса следующего запроса DFU_DNLOAD и последующим запросом статуса через DFU_GETSTATUS. |
4 |
bState |
1 |
число |
Показывает состояние устройства, в которое оно перейдет сразу после отправки этого ответа. |
5 |
iString |
1 |
индекс |
Индекс описания статуса в таблице строковых описателей. |
Таблица 4-5. Значения поля bStatus.
Статус |
Значение |
Описание |
OK |
0x00 |
Нет ошибок, операция завершилась успешно. |
errTARGET |
0x01 |
Файл не предназначен для использования с этим устройством. |
errFILE |
0x02 |
Файл для этого устройства, однако он не прошел некоторые тесты проверки, специфичные для вендора. |
errWRITE |
0x03 |
Устройство не может ничего записать в память. |
errERASE |
0x04 |
Функция очистки памяти завершилась неудачей. |
errCHECK_ERASED |
0x05 |
Проверка на тест чистоты памяти завершился с отрицательным результатом (память не очищена). |
errPROG |
0x06 |
Программирование памяти завершилось ошибкой. |
errVERIFY |
0x07 |
Верификация памяти и буфера не прошла (они не совпадают). |
errADDRESS |
0x08 |
Невозможно запрограммировать данные в память, потому что принятый адрес выходит за допустимые пределы. |
errNOTDONE |
0x09 |
Принят запрос DFU_DNLOAD с установленным полем wLength == 0, но устройство не может понять, что это за данные. |
errFIRMWARE |
0x0A |
Данные firmware устройства повреждены. Устройство не может вернуться к нормальному выполнению программы (run-time operations). |
errVENDOR |
0x0B |
Поле iString укажет на строку описателя ошибки, определенной вендором. |
errUSBR |
0x0C |
Устройство обнаружило неожиданную сигнализацию сброса по шине USB. |
errPOR |
0x0D |
Устройство обнаружило неожиданный сброс, связанный с включением питания (Power On Reset). |
errUNKNOWN |
0x0E |
Что-то пошло не так, но устройство не знает, что это было. |
errSTALLEDPK |
0x0F |
Устройство перешло в состояние приостанова (STALL, специальное состояние по шине USB) в ответ на неожиданный запрос. |
Таблица 4-6. Значения поля bState.
Состояние |
Значение |
Описание |
appIDLE |
0 |
Устройство работает нормально в рабочем режиме. |
appDETACH |
1 |
Устройство работает нормально, оно приняло запрос DFU_DETACH, и ожидает события сброса по шине USB. |
dfuIDLE |
2 |
Устройство работает в режиме DFU и ожидает поступления запросов. |
dfuDNLOAD-SYNC |
3 |
Устройство приняло блок данных и ожидает от хоста запроса статуса через DFU_GETSTATUS. |
dfuDNBUSY |
4 |
Устройство занято записью блока в энергонезависимую память. |
dfuDNLOAD-IDLE |
5 |
Устройство обрабатывает операцию загрузки и ожидает поступления запросов DFU_DNLOAD. |
dfuMANIFEST-SYNC |
6 |
Устройство приняло последний блок firmware от хоста и ожидает приема DFU_GETSTATUS, чтобы начать фазу манифестации, или устройство завершило фазу манифестации и ожидает приема запроса DFU_GETSTATUS. |
dfuMANIFEST |
7 |
Устройство находится в состоянии манифестации. |
dfuMANIFEST-WAITRESET |
8 |
Устройство запрограммировало свои области памяти и ожидает события сброса по шине USB или сброса при включении питания (Power On Reset). |
dfuUPLOAD-IDLE |
9 |
Устройство обрабатывает операцию выгрузки и ожидает поступления запросов DFU_UPLOAD. |
dfuERROR |
10 |
Произошла ошибка. Устройство ожидает запроса DFU_CLRSTATUS. |
Прим. переводчика: bStatus содержит код, который сигнализирует о каком-то событии или ошибке, произошедшей с устройством. bState характеризует текущее рабочее состояние устройства, его режим. Для bStatus в этом переводе я решил оставить термин "bStatus", а для bState термин "состояние".
4.5.2 Clear Status
Очистка статуса устройства. Каждый раз, когда устройство детектирует ошибку и сообщает о ней хосту по запросу DFU_GETSTATUS, устройство входит в состояние dfuERROR. После сообщения о любой ошибке устройство не может выйти из состояния dfuERROR, пока не получит запрос DFU_CLRSTATUS. Как только устройство получит запрос DFU_CLRSTATUS, оно установит свой статус в OK, и перейдет в состояние dfuIDLE. Как только устройство попало в состояние dfuIDLE, оно может перейти в другие возможные рабочие состояния.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
0010 0001b |
DFU_CLRSTATUS (4) |
0 |
Interface (4) |
0 |
нет данных |
4.5.3 Device State
Рабочее состояние устройства. Оно говорит о том, в каком текущем режиме находилось устройство до передачи ответа на запрос. Значения состояния, указанные в поле bState, идентичны тем, что содержатся в ответе на запрос DFU_GETSTATUS.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
1010 0001b |
DFU_GETSTATE (5) |
0 |
Interface (4) |
1 |
состояние |
4.5.4 DFU_ABORT request
Запрос на прерывание операции. Запрос DFU_ABORT принуждает устройство выйти из любого другого состояния, отличающегося от DFU_IDLE, после этого устройство переходит в состояние DFU_IDLE. При поступлении запроса DFU_ABORT устройство устанавливает свой статус на OK. Для получения дополнительной информации см. соответствующую сводку изменения состояния.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
1010 0001b |
DFU_ABORT (6) |
0 |
Interface (4) |
0 |
нет данных |
4.6 Программирование данных FLASH или EEPROM
Образ firmware загружаются через передачи control-write, инициированные запросом DFU_DNLOAD, специфичным для класса DFU. Хост отправляет к устройству некоторое количество байт в интервале между bMaxPacketSize0 и wTransferSize в передаче control-write. После каждого загруженного блока хост запрашивает статус устройства запросом DFU_GETSTATUS.
Как описано в спецификации USB DFU, "Образы программ (firmware) для специфичных устройств (микроконтроллеров) являются, по определению, зависимыми от вендора. Таким образом требуется, чтобы целевые адреса, размеры записей и любая другая информация, относящаяся к поддержке обновления, была инкапсулирована в файл образа firmware (прим. переводчика: всем этим требованиям удовлетворяет формат файла Intel HEX, который использует для загрузки утилита FLIP). Это зона ответственности как производителя чипа (в данном случае Atmel), так и разработчика firmware - нужно убедиться, что его устройства (в данном случае бутлоадер DFU и утилита FLIP) могут обработать эти инкапсулированные встроенные данные (т. е. если перевести на русский язык, нужно удостовериться, что после обработки образа firmware программа пользователя попадет в те адреса программной памяти, которые нужно, и программа не будет испорчена). За исключением расширения файла DFU (например *.HEX или *.a90) содержимое файла для самого хоста не имеет никакого значения."
Как передается образ firmware в протоколе DFU:
• 32 байта: команда • X байт: X это номер байта (00h), добавленный перед первым значимым байтом firmware. Число X вычисляется для выравнивания начала firmware на начало страницы памяти FLASH. X = start_address [32]. Например, если стартовый адрес 00AFh (175d), X = 175 [32] = 15 (15 это остаток от деления числа 175 на 32). • Код firmware. • Суффикс DFU из 16 байт.
Таблица 4-7. Суффикс файла DFU.
Смещение |
Поле |
Размер |
Значение |
Описание |
-0 |
dwCRC |
4 |
число |
Контрольная сумма всего файла, исключая поле dwCRC. |
-4 |
bLength |
1 |
16 |
Длина суффикса DFU, включая dwCRC. |
-5 |
ucDfuSignature |
3 |
5: 44h 6: 46h 7: 55h |
Поле уникальной сигнатуры DFU. |
-8 |
bcdDFU |
2 |
0100h |
Номер спецификации DFU в двоично-десятичном коде (формат BCD). |
-10 |
idVendor |
2 |
ID |
Идентификатор вендора, связанный с этим файлом. Либо FFFFh, либо должен совпадать с VID устройства USB. |
-12 |
idProduct |
2 |
ID |
Идентификатор продукта, связанный с этим файлом. Либо FFFFh, либо должен совпадать с PID устройства USB. |
-14 |
bcdDevice |
2 |
BCD |
Номер релиза устройства, связанный с этим файлом. Либо FFFFh, либо номер релиза firmware в формате BCD, либо номер версии. |
4.6.1 Запрос от хоста USB
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
0010 0001b |
DFU_DNLOAD (1) |
wBlock |
Interface (4) |
длина |
firmware |
4.6.1.1 Write Command (команда записи)
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_prog_start 01h |
00h |
start_address |
end_address |
Инициализация программирования FLASH. |
01h |
Инициализация программирования EEPROM. |
Команда записи имеет длину 6 байт. Чтобы удовлетворить спецификации USB касательно передач управляющего типа (Control type transfer, передача должна уложиться в пакет 32 байта), команда записи дополняется 26 (32 - 6 = 26) ничего не значащими байтами. Таким образом, общая длина команды получается 32 байта, это размер конечной точки по умолчанию (Default Control Endpoint, конечная точка с номером 0).
4.6.1.2 Firmware
Теперь могут быть переданы в устройство данные firmware. Чтобы соответствовать размеру страницы FLASH (128 байт), X ничего не значащих байт добавляются перед первым байтом для программирования. Число X вычисляется для выравнивания начала firmware на начало страницы FLASH. X = start_address [32]. Например, если стартовый адрес 00AFh (175d), X = 175 [32] = 15 (15 это остаток от деления числа 175 на 32).
4.6.1.3 DFU Suffix (суффикс DFU)
DFU suffix - это 16 байт, добавленных после последнего байта программы. Суффикс зарезервирован для возможного использования в будущем.
Рис. 4-1. Пример запроса DFU_DNLOAD нулевой длины для загрузки firmware.
Хост отправляет запрос DFU_DNLOAD с пакетов нулевой длины (Zero Length Packet, ZLP), чтобы обозначить завершение передачи файла образа firmware. Это последний пакет с полезной нагрузкой для операции передачи данных прошивки.
4.6.1.4 Ответы от бутлоадера
После каждого запроса программирования хост может отравить запрос на состояние и статус устройства путем отправки сообщения DFU_GETSTATUS. Если статус устройства содержит ошибку, то хост должен отправить устройству запрос DFU_CLRSTATUS.
4.7 Чтение данных FLASH или EEPROM
Процедура, описанная ниже, позволяет пользователю прочитать данные памяти FLASH или EEPROM. Команда проверки на чистоту также может быть выполнена с помощью этой процедуры. Вся операция выполняется за 2 шага:
• Запрос DFU_DNLOAD с командой чтения (6 байт). • Запрос DFU_UPLOAD, который соответствует предыдущей команде.
4.7.1 Первый запрос от хоста
Хост отправляет запрос DFU Download с командой Display в поле данных.
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_display_data 03h |
00h |
start_address |
end_address |
Отобразить данные FLASH. |
01h |
Проверить данные FLASH на чистоту. |
02h |
Отобразить данные EEPROM |
4.7.2 Второй запрос от хоста
Хост отправляет запрос DFU Upload.
4.7.3 Ответы от устройства
Устройство отправляет к хосту данные firmware с указанного начального адреса до указанного конечного адреса.
4.7.4 Ответы от устройства на команду Blank Check
Хост отправляет запрос GET_STATUS к устройству. Как только внутренний тест на чистоту завершится, устройство отправляет свой статус.
• Если статус устройства "OK", то память устройства чистая, и устройство будет ожидать нового запроса от хоста. • Если статус устройства "errCHECK_ERASED", то память устройства не очищена. Устройство ждет запроса DFU_UPLOAD, чтобы отправить адрес первого байта, который не равен 0xFF.
4.8 Чтение конфигурационной информации (Configuration Information) или информации производителя (Manufacturer Information)
Алгоритм процесса, позволяющий пользователю читать конфигурационную информацию и информацию производителя, описан далее.
4.8.1 Запросы от хоста
Для начала операции программирования хост отправляет запрос DFU_DNLOAD с командой Read (чтение) в поле данных (2 байта).
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_read_command 05h |
00h |
00h |
|
|
|
Прочитать версию загрузчика. |
01h |
|
|
|
Прочитать boot ID1 устройства. |
02h |
|
|
|
Прочитать boot ID2 устройства. |
01h |
30h |
|
|
|
Прочитать код производителя. |
31h |
|
|
|
Прочитать код семейства. |
60h |
|
|
|
Прочитать имя продукта. |
61h |
|
|
|
Прочитать ревизию продукта. |
4.8.2 Ответы от бутлоадера
Устройство имеет 2 возможные ответа на запрос DFU_GETSTATUS:
• Если чип защищен от доступа из памяти программ, хосту будет возвращен статус "err_VENDOR". • Иначе статус устройства будет "OK". Хост может отправить запрос DFU_UPLOAD к устройству, чтобы получить значение запрашиваемого поля.
4.9 Очистка (Erasing) памяти FLASH
Ниже описан процесс, позволяющий пользователю очистить память FLASH. При этом по команде Full Chip erase вся область памяти программ (Application Section), которая не относится к секции загрузки, будет приведена в чистое состояние (изначальное состояние памяти FLASH, когда каждый байт равен 0xFF). Код загрузчика, находящийся в секции загрузки, остается нетронутым.
4.9.1 Запрос от хоста
Для начала операции очистки хост отправляет запрос DFU_DNLOAD с командой Write (запись) в поле данных (2 байта).
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_write_command 04h |
00h |
FFh |
|
|
|
Полная очистка памяти чипа (данные становятся FFh). |
4.9.2 Ответы от бутлоадера
Устройство имеет 2 возможные ответа на запрос DFU_GETSTATUS:
• Если чип защищен от доступа из памяти программ, хосту будет возвращен статус "err_WRITE". • Иначе статус устройства будет "OK".
4.10 Запуск на выполнение приложения пользователя
Ниже описана процедура, позволяющая пользователю запустить на выполнение программу firmware, находящуюся в Application section, напрямую из бутлоадера. Это делается при приеме специальной команды.
Возможны два варианта опций:
• Запуск программы пользователя с внутренним аппаратным сбросом, который генерирует сторожевой таймер (watchdog). Когда устройство принимает эту команду, то watchdog остается разрешенным, и бутлоадер входит бесконечный цикл ожидания, который приводит к сбросу сторожевым таймером устройства. • Запуск приложения без сброса. При этом просто осуществляется переход по адресу 0 (выполняется команда ассемблера jump 0000h), что приводит к запуску приложения без аппаратного сброса. Имейте в виду, что при этом втором варианте запуска некоторые ресурсы микроконтроллера, которые были использованы в бутлоадере (например, аппаратура USB) могут остаться в состоянии, отличающемся от состояния по умолчанию, которое бывает после аппаратного сброса или включения питания.
Чтобы запустить приложение пользователя, хост отправляет запрос DFU_DNLOAD с указанным типом запуска в поле данных (3 или 5 байт). За этим запросом немедленно следует второй запрос DFU_DNLOAD без поля данных, который приводит к старту приложения по одному из выбранных вариантов.
Внимание: бутлоадер выполняет watchdog reset (сброс, генерируемый сторожевым таймером) для генерации "аппаратного сброса" (hardware reset), что позволяет выполнить код из секции приложения (application section). После того, как произойдет watchdog reset, AVR watchdog останется в работающем состоянии, так что приложение пользователя должно его запретить при своем старте, если watchdog не нужен. Иначе приложение, которое не позаботится о работающем сторожевом таймере (т. е. не будет его обнулять в бесконечном цикле), будет снова сброшено сторожевым таймером.
4.11 Запрос от хоста
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_write_command 04h |
03h |
00h |
|
|
|
Аппаратный сброс (hardware reset). |
01h |
адрес |
|
Запуск программы пользователя командой LJMP 0. |
4.12 Ответы от бутлоадера
Устройство не будет выдавать никакого ответа.
[5. Защита данных программы (Security)]
Когда инициировано USB-соединение с загрузчиком, то загрузчик автоматически входит в режим защиты программы от чтения/записи (независимо от установленных битов защиты продукта lock bits). Это позволяет защитить содержимое встроенной в чип памяти FLASH от доступа на чтение/запись через интерфейс USB. Таким образом, допускается выполнение только одной команды DFU, команды полной очистки "Full Chip Erase".
После того, как команда "Full Chip Erase" была принята и должным образом выполнена, становятся доступными все команды DFU, и память FLASH может быть перепрограммирована и проверена.
[6. Получение доступа к драйверам FLASH низкого уровня (Low level Flash Drivers)]
USB-загрузчик AT90USB размещен в секции загрузки (boot section) встроенной в чип памяти FLASH, причем секция загрузки имеет уникальное свойство, что из этих ячеек памяти программ допускается выполнение операций записи во встроенную память FLASH (только в секции загрузки декодируется и выполняется инструкция SPM). Подробнее см. [4].
Таким образом приложения, которым требуется доступ на запись встроенной в чип памяти FLASH, должны выполнять вызовы подпрограмм драйвера Flash по специальным адресам в пределах кода загрузчика USB.
Загрузчик USB предоставляет несколько интерфейсов программного доступа (Application Programming Interfaces, API) которые позволяют получить доступ к драйверам низкого уровня FLASH, находящимся в секции загрузки (boot section, там размещено тело загрузчика). Эти интерфейсы API позволяют выполнять следующие операции с памятью FLASH:
• Очистка страницы (Page Erase). • Запись страницы (Page Write). • Загрузка слова в промежуточный буфер страницы.
Рис. 6-1. Вызовы API бутлоадера USB.
API расположены по заранее известным абсолютным адресам в коде firmware бутлоадера USB, и принимает в качестве параметров значения из заранее известных регистров. Эти параметры совместимы с соглашением о вызовах (calling convention) компилятора C, так что вызовы подпрограмм драйвера можно осуществлять через указатель на функцию, как показано в примере ниже:
#if (FLASH_END==0x1FFFF)
//чипы с памятью FLASH на 128 килобайт
#define LAST_BOOT_ENTRY 0xFFFE
#elif (FLASH_END==0xFFFF)
//чипы с памятью FLASH на 64 килобайт
#define LAST_BOOT_ENTRY 0x7FFE#else
#error Вы должны указать значение FLASH_END в байтах.
#endif
// Эти указатели на функции используются для вызова функций драйвера FLASH,
// находящихся в теле загрузчика.
void (*boot_flash_page_erase_and_write)(unsigned long adr) =
(void (*)(unsigned long))(LAST_BOOT_ENTRY-12);
U8 (*boot_flash_read_sig) (unsigned long adr) =
(U8 (*)(unsigned long))(LAST_BOOT_ENTRY-10);
U8 (*boot_flash_read_fuse) (unsigned long adr) =
(U8 (*)(unsigned long))(LAST_BOOT_ENTRY-8);
void (*boot_flash_fill_temp_buffer) (unsigned int data,unsigned int adr) =
(void (*)(unsigned int, unsigned int))(LAST_BOOT_ENTRY-6);
void (*boot_flash_prg_page) (unsigned long adr) =
(void (*)(unsigned long))(LAST_BOOT_ENTRY-4);
void (*boot_flash_page_erase) (unsigned long adr) =
(void (*)(unsigned long))(LAST_BOOT_ENTRY-2);
void (*boot_lock_wr_bits) (unsigned char val) =
(void (*)(unsigned char))(LAST_BOOT_ENTRY);
// Эта функция записывает 0x55AA по адресу 0x1200 во встроенную в чип
// память FLASH с помощью использования драйверов FLASH, размещенных
// в теле USB bootloader.
void basic_flash_access(void)
{
unsigned long address;
unsigned int temp16;
temp16=0x55AA;
address=0x12000;
(*boot_flash_fill_temp_buffer)(temp16,address);
(*boot_flash_page_erase)(address);
(*boot_flash_prg_page)(address);
}
Полный код на языке ассемблера для API драйверов FLASH приведен в Приложении B.
[7. Использование бутлоадера USB для программирования "прямо в системе" (In System Programming)]
Для связи с бутлоадером и перепрошивки программы пользователя на компьютере PC может использоваться программа Flip (её можно свободно скачать с сайта компании Atmel website). Подробные инструкции по использованию Flip и загрузчика USB см. в [2] (там же в разделе Ссылки можно найти ссылку на загрузку старых версий Flip).
[8. История развития загрузчика USB]
В следующей таблице показаны разные ревизии бутлоадера и связанные с ними изменения.
Таблица 8-1. История версий USB Bootloader.
Чип |
Ревизия загрузчика |
Какие сделаны изменения |
AT90USB1287 AT90USB1286 AT90USB647 AT90USB646 |
1.0.1 |
Начальная ревизия. |
AT90USB162 AT90USB82 |
1.0.0 |
Начальная ревизия. |
1.0.1 |
Позволяет использовать кварц 16 МГц при напряжении питания 3.3V и фьюз CKDIV8. |
1.0.5 |
Улучшен процесс автоопределения скорости USB. |
ATmega32U4 ATmega16U4 |
1.0.0 |
Начальная ревизия. |
[9. Приложение A]
Таблица 9-1. Краткий обзор фреймов от хоста.
ID команды |
data[0] |
data[1] |
data[2] |
data[3] |
data[4] |
Описание |
Id_prog_start 01h |
00h |
start_address |
end_address |
Инициализация программирования FLASH. |
01h |
Инициализация программирования EEPROM |
Id_display_data 03h |
00h |
start_address |
end_address |
Отобразить данные FLASH. |
01h |
Проверка FLASH на чистоту. |
02h |
Отобразить данные EEPROM. |
Id_write_command 04h |
00h |
FFh |
|
|
|
Полная очистка чипа (все байты данных памяти становятся FFh). |
03h |
00h |
|
|
|
Аппаратный сброс. |
01h |
address |
|
Переход по указанному адресу командой LJMP. |
Id_read_command 05h |
00h |
00h |
|
|
|
Чтение версии бутлоадера. |
01h |
|
|
|
Прочитать boot ID1 устройства. |
02h |
|
|
|
Прочитать boot ID2 устройства. |
01h |
30h |
|
|
|
Чтение кода производителя. |
|
31h |
|
|
|
Чтение кода семейства. |
|
60h |
|
|
|
Чтение имени продукта. |
|
61h |
|
|
|
Чтение ревизии продукта. |
Id_change_base_address 06h |
03h |
0 |
"PP" |
|
|
Выбирает номер PP страницы 64 кбайт. |
[10. Приложение B]
;*A**************************************************************************
; $RCSfile: flash_boot_drv.s90,v $
;----------------------------------------------------------------------------
; Copyright (c) Atmel.
;----------------------------------------------------------------------------
; RELEASE: $Name: $
; REVISION: $Revision: 1.7 $
; FILE_CVSID: $Id: flash_boot_drv.s90,v 1.7 2005/10/03 15:50:12 $
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ:
; Этот файл содержит код драйверов низкого уровня для получения доступа
; к памяти FLASH (low level flash driver) из приложения пользователя.
;****************************************************************************
NAMEflash_drv(16)
;_____ I N C L U D E S ______________________________________________________
#define ASM_INCLUDE
#include "config.h"
;****************************************************************************
; Здесь находится таблица абсолютных адресов входа в подпрограммы драйвера
; (low level flash drivers). Эта таблица задает точки входа, которые могут
; быть вызваны из секции приложения пользователя (application section),
; чтобы выполнять с памятью FLASH чипа следующие операции:
;
; entry_flash_page_erase_and_write (очистка и запись страницы FLASH):
; R18:17:R16: три байта, содержащие адрес страницы;
; entry_flash_fill_temp_buffer (заполнение промежуточного буфера FLASH):
; data16 : R16/R17: слово для загрузки в промежуточный буфер.
; address: R18/R19: адрес слова в промежуточном буфере.
;
; entry_flash_prg_page (байтовый адрес страницы):
; R18:17:R16: Байтовый адрес страницы FLASH.
;
; entry_flash_page_erase (очистка страницы FLASH):
; R18:17:R16: Байтовый адрес страницы FLASH.
;
;****************************************************************************
ASEG FLASH_END-0x0001Bentry_flash_page_erase_and_write:
JMP flash_page_erase_and_writeentry_flash_read_sig:
JMP flash_read_sigentry_flash_read_fuse:
JMP flash_read_fuseentry_flash_fill_temp_buffer:
JMP flash_fill_temp_bufferentry_flash_prg_page:
JMP flash_prg_pageentry_flash_page_erase:
JMP flash_page_erase_publicentry_lock_wr_bits:
JMP lock_wr_bits
RSEGBOOT ;*F**************************************************************************
; ИМЯ: flash_page_erase_and_write
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: R18:17:R16: байтовый адрес страницы FLASH
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: Эта функция может быть вызвана из кода приложения пользователя,
; она выполняет операцию очистки выбранной страницы и запускает
; последовательность программирования той же самой страницы.
; Эта функция позволяет записать во FLASH 256 байт промежуточного буфера
; программы (software temporary buffer) из секции приложения пользователя.
;****************************************************************************
flash_page_erase_and_write:
PUSH R18
RCALL flash_page_erase
POP R18
RCALL flash_prg_page
RET
;*F**************************************************************************
; ИМЯ: flash_prg_page
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: R18:17:R16: байтовый адрес страницы FLASH
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: запуск последовательности программирования страницы FLASH.
;****************************************************************************
flash_prg_page:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
MOV R31,R17
MOV R30,R16 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
OUT RAMPZ, R18
LDI R20,$05 ;(1 << PGWRT) + (1 << SPMEN))
OUT SPMCSR,R20 ; аргумент 2 функции (r18)
SPM ; Store program memory (сохранить в памяти программ)
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
RCALL flash_rww_enable
RET
;*F**************************************************************************
; ИМЯ: flash_page_erase
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: R18:17:R16: байтовый адрес страницы FLASH
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: запуск последовательности очистки страницы FLASH.
;----------------------------------------------------------------------------
; ПРИМЕЧАНИЕ: эта функция не устанавливает бит RWWSE после очистки. Таким
; образом, она не очищает аппаратный промежуточный буфер страницы.
; Эта функция предназначена для использования в загрузчике.
;****************************************************************************
flash_page_erase:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
MOV R31,R17
MOV R30,R16 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
OUT RAMPZ, R18
LDI R20,$03 ;(1 << PGERS) + (1 << SPMEN)))
OUT SPMCSR, R20 ; аргумент 2 функции (r18)
SPM ; Store program memory (сохранить в памяти программ)
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
;RCALL flash_rww_enable ВНИМАНИЕ, НЕ АКТИВИРУЙТЕ ЗДЕСЬ, или
; потеряете все содержимое промежуточного буфера !!!
RET
;*F**************************************************************************
; ИМЯ: flash_page_erase_public
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: R18:17:R16: байтовый адрес страницы FLASH
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: запускает последовательность очистки страницы FLASH.
;----------------------------------------------------------------------------
; ПРИМЕЧАНИЕ: !!!! Эта функция устанавливает бит RWWSE после очистки. Так что
; после очистки будет очищен также и промежуточный аппаратный буфер.
;****************************************************************************
flash_page_erase_public:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
MOV R31,R17
MOV R30,R16 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
OUT RAMPZ, R18
LDI R20,$03 ;(1 << PGERS) + (1 << SPMEN)))
OUTSPMCSR, R20 ; аргумент 2 функции (r18)
SPM ;Store program memory (сохранить в памяти программ)
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
RCALL flash_rww_enable
RET
;*F**************************************************************************
; ИМЯ: flash_rww_enable
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: отсутствуют
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: устанавливает бит RWSE. Это позволяет выполнить код в секции
; приложения пользователя (application section) после программирования
; FLASH (очистка или запись страницы).
;****************************************************************************
flash_rww_enable:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
LDI R20,$11 ;(1 << WWSRE) + (1 << SPMEN)))
OUT SPMCSR, R20 ; аргумент 2 функции (r18)
SPM ;Store program memory (сохранить в памяти программ)
RJMP WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
;*F**************************************************************************
; ИМЯ: flash_read_sig
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: адрес байта сигнатуры
; Возвращает R16: значение сигнатуры
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: читает аппаратный байт сигнатуры чипа (его идентификатор).
; Какой байт прочитать - выбирается через аргумент (см. даташит на чип).
;****************************************************************************
flash_read_sig:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
MOV R31,R17
MOV R30,R16 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
OUT RAMPZ, R18
LDI R20,$21 ;(1 << SPMEN) | (1 << SIGRD))
OUT SPMCSR, R20 ; аргумент 2 функции (r18)
LPM ;Load program memory (загрузить из памяти программ).
MOV R16, R0 ;Сохранить возвращаемое значение (1 байт -> регистр R16)
RJMP WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
;*F**************************************************************************
; ИМЯ: flash_read_fuse
;----------------------------------------------------------------------------
; Возвращает R16: значение фьюза
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: читает байт фьюза. Байт фьюзов выбирается через адрес,
; передаваемый в качестве аргумента (см. даташит на чип, чтобы узнать
; значение адреса).
;****************************************************************************
flash_read_fuse:
RCALL WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
MOV R31,R17
MOV R30,R16 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
OUT RAMPZ, R18
LDI R20,$09 ;(1 << SPMEN) | (1 << BLBSET))
OUT SPMCSR, R20 ; аргумент 2 функции (r18)
LPM ;Load program memory (загрузить из памяти программ).
MOV R16, R0 ;Сохранить возвращаемое значение (1 байт -> регистр R16)
RJMP WAIT_SPMEN ;ожидание, когда очистится флаг SPMEN
/*F**************************************************************************
* ИМЯ: flash_fill_temp_buffer
*----------------------------------------------------------------------------
* ПАРАМЕТРЫ:
* data16 : R16/R17: слово для загрузки в промежуточный аппаратный буфер.
* address: R18/R19: адрес слова.
* return: ничего не возвращает
*----------------------------------------------------------------------------
* НАЗНАЧЕНИЕ: эта функция позволяет загрузить слово в аппаратный
* промежуточный буфер страницы.
*----------------------------------------------------------------------------
* ПРИМЕР:
* fill_temp_buffer(data16, address);
*----------------------------------------------------------------------------
* ПРИМЕЧАНИЕ: первый параметр использует регистры R16, R17. Второй параметр
* использует регистры R18, R19.
*****************************************************************************/
flash_fill_temp_buffer:
MOV R31,R19 ;перемещение адреса в указатель z (R31=ZH R30=ZL)
MOV R30,R18
MOV R0,R17 ;переместить data16 в регистры 0 и 1
MOV R1,R16
LDI R20,(1 << SPMEN)
OUT SPMCSR, R20 ; r18 выбирает функцию
SPM ; Store program memory (сохранить в памяти программ)
RJMP WAIT_SPMEN ; ожидание, когда очистится флаг SPMEN
;*F**************************************************************************
; ИМЯ: lock_wr_bits
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: R16 значение для записи
;----------------------------------------------------------------------------
; PURPOSE: записывает биты защиты
;****************************************************************************
lock_wr_bits:
RCALL WAIT_SPMEN ; ожидание, когда очистится флаг SPMEN
MOV R0,R16
LDI R18,((1 << BLBSET)|(1 << SPMEN))
OUT SPMCSR, R18 ; r18 выбирает функцию
SPM ; запись битов защиты (lockbits)
RJMP WAIT_SPMEN ; ожидание, когда очистится флаг SPMEN
;*F**************************************************************************
; ИМЯ: wait_spmen
;----------------------------------------------------------------------------
; ПАРАМЕТРЫ: нет параметров
;----------------------------------------------------------------------------
; НАЗНАЧЕНИЕ: выполняет ожидание флага SPME
;****************************************************************************
WAIT_SPMEN:
MOVR0, R18
INR18, SPMCSR ; получить SPMCR в регистр r18
SBRC R18,SPMEN
RJMP WAIT_SPMEN ; ожидание, когда очистится флаг SPMEN
MOVR18, R0
RET END
[Ссылки]
1. USB DFU Bootloader Datasheet site:atmel.com. 2. AVR282: обновление firmware через USB. 3. AVR4023: протокол FLIP USB DFU. 4. AVR109: самопрограммирование AVR. |
Комментарии
microsin: см. реализации хоста DFU с открытым исходным кодом. Строка для поиска в Google: Atmel DFU host github
RSS лента комментариев этой записи