Программирование AVR: работа с USB Загрузчик USB DFU компании Atmel Tue, March 19 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Загрузчик USB DFU компании Atmel Печать
Добавил(а) microsin   

Перевод документации 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 раза меньше байтового адреса, это иногда создает некоторую путаницу.

USB-DFU-Physical-Environment-fig21

Рис. 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.

USB-DFU-Boot-Process-fig31

Рис. 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 байт, добавленных после последнего байта программы. Суффикс зарезервирован для возможного использования в будущем.

USB-DFU-DFU DNLOAD-Request-fig41

Рис. 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 в поле данных.

USB-DFU-Reading-Flash-or-EEPROM-req

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 с указанного начального адреса до указанного конечного адреса.

USB-DFU-Reading-Flash-or-EEPROM-answ

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 байта).

USB-DFU-Reading-Configuration-or-Manufacturer-Information-req

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 к устройству, чтобы получить значение запрашиваемого поля.

USB-DFU-Reading-Configuration-or-Manufacturer-Information-answ

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 Запрос от хоста

USB-DFU-Starting-Application-req

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).
• Загрузка слова в промежуточный буфер страницы.

USB-DFU-bootloader-API-calls-fig61

Рис. 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.

 

Комментарии  

 
0 #1 Владимир 13.04.2021 16:26
Поясните, через какие функции WinApi хост отправляет команду см. пункт 4.11. Возможно ли использование для передачи setuppacket функций WriteFile, ReadFile.

microsin: см. реализации хоста DFU с открытым исходным кодом. Строка для поиска в Google: Atmel DFU host github
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page