AVR276: USB Software Library for AT90USBxxx Microcontrollers |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
В этой статье опубликован разбор документа "AVR276: USB Software Library for AT90USBxxx Microcontrollers" (doc7675.pdf с сайта Atmel). В документе есть мелкие ошибки (например, в количестве и названиях полей в дескрипторах), которые я постарался здесь исправить. Библиотека предназначена для облегчения разработки firmware USB-устройства на основе чипов AT90USBxxx (может применяться макетная плата Atmel STK526 или макетная плата AVR-USB162 [1]. и программного обеспечения компьютера (ПО хоста), работающего с этим устройством. [Библиотека AT90USBxxx] Может быть сконфигурирована для работы в 3 режимах: - USB device - подчиненное устройство, работающее по командам ПО хоста При замыкании на землю ножки ID (подсоединение к MiniA вилке) AT90USBxxx входит в режим USB reduced host mode. Если ножка ID не подсоединена (подсоединение к MiniB вилке), то AT90USBxxx работает как подчиненное устройство USB device. [Дескрипторы USB] Во время процесса энумерации хост запрашивает из устройства некоторые значения дескриптора (descriptor), чтобы идентифицировать его и загрузить подходящие драйвера. Каждое устройство USB должно иметь хотя бы минимальный набор дескрипторов, указанный на рисунке: Рис. 3-3. USB Descriptors Наиболее сложная часть приложения USB - определение, какие должны быть дескрипторы. Каждое устройство USB передает свои требования к хосту в особом процессе, называемом энумерацией (enumeration). Библиотека AT90USBxxx software library полностью поддерживает процесс энумерации для режимов USB device и USB reduced host. Во время энумерации дескрипторы устройства (device descriptors) передаются хосту, который назначает уникальный адрес устройству. [Device Descriptor] Устройство USB может иметь только один дескриптор устройства (DD), который описывает устройство целиком. Он передает информацию о версии USB, максимальный размер пакета для конечной точки (endpoint) 0, vendor ID, product ID, версию устройства (product version), количество возможных конфигураций для устройства и т. д. В таблице отображен формат DD: Таблица 3-1. Device descriptor
Пример декларации DD можно найти в файле STK526-series2-hidio-2_0_1-doc\demo\STK526-series2-hidio\usb_descriptors.h. [Configuration Descriptor] Устройство USB может иметь несколько дескрипторов конфигурации (CD), но обычно имеется только один CD. Этот дескриптор задает режим питания (power-supply mode) - отдельное питание или питание от шины USB, максимальная потребляемая мощность, интерфейсы, принадлежащие устройству, общий размер всех дескрипторов данных (data descriptors) и т. д. Например, устройство может иметь две конфигурации - при питании от шины USB и при отдельном внешнем питании. Можно представить себе также конфигурации с разными режимами передачи. Таблица 3-2. Configuration descriptor
Пример значений полей CD также можно увидеть в файле STK526-series2-hidio-2_0_1-doc\demo\STK526-series2-hidio\usb_descriptors.h. [Interface Descriptor] Одно устройство может иметь несколько интерфейсов. Общая информация, выдаваемая дескриптором Interface Descriptor (IntD) - количество конечных точек для интерфейса и USB class и subclass Таблица 3-3. Interface descriptor
Пример значений полей IntD также можно увидеть в файле STK526-series2-hidio-2_0_1-doc\demo\STK526-series2-hidio\usb_descriptors.h. [Endpoint Descriptor] (ED). Описатель, предоставляющий параметры для конечной точки - направление передачи (IN или OUT), поддерживаемый режим передачи (Interrupt, Bulk, Isochronous), размер конечной точки, интервал передачи данных в случае interrupt transfer mode и т. д. Таблица 3-4. Endpoint descriptor
Пример значений полей ED также можно увидеть в файле STK526-series2-hidio-2_0_1-doc\demo\STK526-series2-hidio\usb_descriptors.h. Библиотека поддерживает следующие возможности: 1. Доступны скорости Low Speed (1.5Mbit/s) и Full Speed (12Mbit/s) Библиотека AT90USBxxx USB software library предназначена для облегчения написания разработчиками firmware для USB-устройств. В частности, всю заботу о процедурах энумерации (для тех, кто в танке - энумерацией называют процесс определения параметров USB устройства хостом в момент подключения USB устройства к хосту. При этом хост пытается подобрать нужный режим обмена с устройством, выбрать драйвер для устройства, а если драйвера нет - предлагает его установить) берет на себя библиотека, разработчику нужно только сконфигурировать параметры устройства (рассмотрено далее), определить каналы для передачи данных, а иногда и написать/поправить обработчики событий (прерываний). Есть также примеры ПО хоста (см. статью [2]). [Архитектура firmware] Архитектура firmware (см. рисунок 4-1. AT90USBxxx USB Firmware Architecture for dual role application) спроектирована так, чтобы разработчик как можно меньше соприкасался с железом (регистрами чипа AT90USBXXX) - слой drivers layer никак не должен модифицироваться разработчиком. Рисунок 4-1. AT90USBxxx USB Firmware Architecture for dual role application Firmware может работать либо как USB устройство, либо как USB хост (на картинке часть, относящаяся к хосту, показана розовеньким; нас это не интересует). Режим переключается (если двойная роль разрешена) с помощью специального контакта разъема USB (в пятиконтактной версии разъема) - сигнала USB ID. Такая "двойная роль" основывается на выполнении 3-х задач: - usb_task (usb_task.c), задача выполнения процесса энумерации USB низкого уровня (либо в режиме устройства, либо в режиме хоста). После выполнения этой задачи обновляются различные флаги статуса, которые могут быть проверены на более высоком уровне программы, и USB-соединение становится полностью работоспособным. [Описание примера firmware] Пример приложения firmware показывает, как USB software library может работать в двух режимах (device или host). Примеры ищите на сайте Atmel: - Пример, относящийся к этому документу (doc7675.pdf), есть в апноуте Atmel "AVR USB Series2 software library template". Режим устройства USB имеет 2 интерфейса: - первый интерфейс (first interface) bulk IN/OUT interface, работающий как "обратная петля" (loop back data) - все данные, принятые от хоста через конечную точку OUT endpoint отправляются хосту обратно через конечную точку IN endpoint. Рисунок 4-2. Обзор примера firmware Замечание. Дескрипторы устройства B device, используемые в этом примере firmware, не могут быть напрямую использованы для энумерации стандартной подсистемой PC хоста (для ПО хоста, работающего на компьютере). Ищите на сайте Atmel "реальные" примеры приложений для устройств (HID mouse, HID keyboard, MassStorage, CDC ... ). [Исходные файлы кода и их назначение] lib_mcu\usb\usb_drv.c demo\template\conf\conf_usb.h demo\template\conf\conf_scheduler.h demo\template\main.c demo\template\usb_specific_request.c demo\template\device_template_task.c demo\template\host_template_task.c demo\template\usb_descriptors.c modules\usb\device_chap9\usb_device_task.c modules\usb\device_chap9\usb_standard_request.c modules\usb\host_chap9\usb_host_task.c modules\usb\host_chap9\usb_host_enum.c modules\usb\host_chap9\usb_task.c lib_board\stk_525\stk_525.c [Конфигурирование USB software library] Пример firmware сконфигурирован для поддержки двух режимов - reduced USB host и USB device. В зависимости от выбранного режима в качестве задачи USB (USB task) вызывается либо usb_host_task, либо usb_device_task для обработки запросов согласно стандарту USB org chapter 9. При этом соответствующие задачи template_device_task либо template_host_task могут быть удалены из scheduled tasks. #define Scheduler_task_1_init usb_task_init #define Scheduler_task_1 usb_task #define Scheduler_task_2_init device_template_task_init #define Scheduler_task_2 device_template_task #define Scheduler_task_4_init host_template_task_init #define Scheduler_task_4 host_template_task Функции scheduler_task_X_init выполняются один раз при запуске планировщика, тогда как функции Scheduler_task_X вызываются снова и снова в бесконечном цикле. 3. Конфигурация USB library - файл conf_usb.h. Этот файл содержит установки для обоих режимов USB host и USB device. Файл разбит на 3 секции - глобальные параметры, параметры device и параметры host. В глобальной секции можно выбрать либо режим USB host, либо USB device, либо и то, и другое, а также включение внутреннего регулятора сигналов USB (internal USB pads regulator), который должен быть включен или нет для firmware (в зависимости от используемого диапазона напряжения питания). Запросы энумерации от хоста обрабатываются в режиме опроса файлами usb_device_task.c и usb_standard_request.c. Задача usb_task принадлежит к задачам планировщика, которая периодически проверяет новые управляющие запросы (control requests) от хоста. Конфигурация USB library, относящаяся к USB device. Для включения обработки библиотекой режима USB device mode должно быть задано макроопределение USB_DEVICE_FEATURE в значении ENABLED. Секция файла conf_usb.h, относящаяся к устройству USB, содержит определения физических номеров конечных точек (physical endpoints numbers definition), используемых firmware устройства USB и набор специфических действий, которые пользователь может привязать к специальным событиям обмена USB. Для приложения-примера firmware: #define NB_ENDPOINTS 4 //Общее количество конечных точек (EP) // в приложении, включая EP0. #define EP_TEMP_IN 1 #define EP_TEMP_OUT 2 #define EP_TEMP_INT_IN 3 //Укажите здесь действие, ассоциируемое с каждым событием USB. //Будьте внимательны с расходом времени выполнения этих функций // (они должны завершаться как можно скорее). #define Usb_sof_action() sof_action(); #define Usb_wake_up_action() #define Usb_resume_action() #define Usb_suspend_action() #define Usb_reset_action() #define Usb_vbus_on_action() #define Usb_vbus_off_action() #define Usb_set_configuration_action() Действия пользователя задают выполняемое поведение firmware на высоком уровне путем вызова нужных функций. Например, пользователь может привязать функцию к каждому событию USB start of frame event (SOF) или событию USB bus reset. Для поддержки работы композитного USB device (которое работает как сразу несколько периферийных устройств, например как USB HID и USB MSD) необходимо задать несколько интерфейсов (более одного) в дескрипторе конфигурации (configuration descriptor). Каждый интерфейс имеет свой собственный протокол Class/SubClass/Protocol и связанное поведение приложения на высоком уровне (associated high level application behavior). В примере задано композитное устройство со следующими интерфейсами: - первый интерфейс имеет две конечные точки типа bulk IN/bulk OUT Конфигурация USB library - дескрипторы устройства Дескрипторы устройства (device descriptors) используются в процессе энумерации, и содержатся в файлах usb_descriptors.c и usb_descriptors.h (декларация структуры). Здесь пользователь должен объявить все параметры энумерации для конфигурации своего устройства USB. Тип конфигурации дескриптора задан в конце файла usb_descriptors.h: // Шаблон конфигурации дескриптора (Configuration descriptor template) // Устройство имеет 2 интерфейса // - первый имеет 2 bulk endpoints // - второй имеет 1 interrupt IN endpoint typedef struct { S_usb_configuration_descriptor cfg_temp; S_usb_interface_descriptor ifc_temp; S_usb_endpoint_descriptor ep1_temp; S_usb_endpoint_descriptor ep2_temp; S_usb_interface_descriptor ifc_second_temp; S_usb_endpoint_descriptor ep3_temp; }S_usb_user_configuration_descriptor; Связанные интерфейсы и параметры конечных точек заданы в начале файла usb_descriptors.h. // USB Device descriptor #define USB_SPECIFICATION 0x0200 #define DEVICE_CLASS 0 //! каждая конфигурация имеет свой класс #define DEVICE_SUB_CLASS 0 //! каждая конфигурация имеет свой подкласс #define DEVICE_PROTOCOL 0 //! каждая конфигурация имеет свой протокол #define EP_CONTROL_LENGTH 64 #define VENDOR_ID 0x03EB // Atmel vendor ID = 03EBh #define PRODUCT_ID 0x0000 #define RELEASE_NUMBER 0x1000 #define MAN_INDEX 0x01 #define PROD_INDEX 0x02 #define SN_INDEX 0x03 #define NB_CONFIGURATION 1 // CONFIGURATION #define NB_INTERFACE 2 //! Номер интерфейса для этой конфигурации #define CONF_NB 1 //! Номер этой конфигурации #define CONF_INDEX 0 #define CONF_ATTRIBUTES USB_CONFIG_SELFPOWERED #define MAX_POWER 50 // 100 mA (в единицах 2mA) // USB Interface descriptor gen #define INTERFACE_NB_TEMP 0 //! Номер этого интерфейса #define ALTERNATE_TEMP 0 //! Альтернативная установка nb для этого интерфейса #define NB_ENDPOINT_TEMP 2 //! Количество EP, которые имеет интерфейс #define INTERFACE_CLASS_TEMP 0x00 //! Класс #define INTERFACE_SUB_CLASS_TEMP 0x00 //! Подкласс #define INTERFACE_PROTOCOL_TEMP 0x00 //! Протокол #define INTERFACE_INDEX_TEMP 0 // USB Endpoint 1 descriptor FS #define ENDPOINT_NB_TEMP1 (EP_TEMP_IN | 0x80) #define EP_ATTRIBUTES_TEMP1 0x02 // BULK = 0x02, INTERRUPT = 0x03 #define EP_IN_LENGTH_TEMP1 64 #define EP_SIZE_TEMP1 EP_IN_LENGTH_TEMP1 #define EP_INTERVAL_TEMP1 0x00 //Интервал опроса хостом (Interrupt // polling interval from host). // USB Endpoint 2 descriptor FS #define ENDPOINT_NB_TEMP2 EP_TEMP_OUT #define EP_ATTRIBUTES_TEMP2 0x02 // BULK = 0x02, INTERRUPT = 0x03 #define EP_IN_LENGTH_TEMP2 64 #define EP_SIZE_TEMP2 EP_IN_LENGTH_TEMP2 #define EP_INTERVAL_TEMP2 0x00 //Интервал опроса хостом (Interrupt // polling interval from host) // USB Second Interface descriptor gen #define INTERFACE_NB_SECOND_TEMP 1 //! Номер этого интерфейса #define ALTERNATE_SECOND_TEMP 0 //! Альтернативная установка nb для него #define NB_ENDPOINT_SECOND_TEMP 1 //! Количество EP, которые имеет интерфейс #define INTERFACE_CLASS_SECOND_TEMP 0x00 //! Класс #define INTERFACE_SUB_CLASS_SECOND_TEMP 0x55 //! Подкласс #define INTERFACE_PROTOCOL_SECOND_TEMP 0xAA //! Протокол #define INTERFACE_INDEX_SECOND_TEMP 0 // USB Endpoint 2 descriptor FS #define ENDPOINT_NB_TEMP3 (EP_TEMP_INT_IN | 0x80) #define EP_ATTRIBUTES_TEMP3 0x03 // BULK = 0x02, INTERRUPT = 0x03 #define EP_IN_LENGTH_TEMP3 64 #define EP_SIZE_TEMP3 EP_IN_LENGTH_TEMP2 #define EP_INTERVAL_TEMP3 20 //Интервал опроса хостом (Interrupt // polling interval from host). Все эти параметры энумерации используются для заполнения полей дескриптора, заданном в файле usb_descriptors.c. Когда контроллер хоста выполняет процесс энумерации, он запрашивает и декодирует параметры благодаря функциям энумерации standard_request.c и предварительно заданным дескрипторам, данные которых отправляются контроллеру хоста. 5. Reduced host configuration Device application void device_template_task(void) { //.. Первая проверка состояния энумерации устройства if (Is_device_enumerated()) { //.. Тут запускается код приложения USB DEVICE } } Файл device_template_task.c, включенный в приложение-пример firmware показывает, как использовать совместно конечные точки bulk IN/OUT и interrupt, связанные с интерфейсами, объявленными в configuration descriptors. Host application VID (Vendor ID) и PID (Product ID) позволяют идентифицировать устройство программе ПО хоста. Каждый производитель должен иметь собственный VID, который будет одинаков для всех его продуктов (он назначается организацией USB org по специальному запросу). Каждый отдельный продукт должен иметь свой собственный PID (он назначается производителем). Величины VID и PID заданы в usb_descriptor.h. Чтобы их поменять, отредактируйте следующие параметры: // USB Device descriptor (дескриптор устройства USB) #define VENDOR_ID 0x03EB // Atmel vendor ID = 03EBh #define PRODUCT_ID 0x201C 7.1.2 Как поменять значение строковых описателей (string descriptors value)? Значения строковых описателей заданы в usb_descriptor.h. Например, чтобы поменять значение имени продукта, поменяйте следующие параметры. //Длина строки: #define USB_PN_LENGTH 18 //Сама строка: #define USB_PRODUCT_NAME { Usb_unicode('A'), Usb_unicode('V'), Usb_unicode('R'), Usb_unicode(' '), Usb_unicode('U'), Usb_unicode('S'), Usb_unicode('B'), Usb_unicode(' '), Usb_unicode('M'), Usb_unicode('O'), Usb_unicode('U'), Usb_unicode('S'), Usb_unicode('E'), Usb_unicode(' '), Usb_unicode('D'), Usb_unicode('E'), Usb_unicode('M'), Usb_unicode('O') } 7.1.3 Как можно сконфигурировать режим питания устройства - self-powered или bus-powered? Это параметр задается в файле usb_descriptor.h. Ниже дано определение каждого режима. // USB Configuration descriptor, bus-power mode
// (дескриптор конфигурации, режим питания от шины USB)
#define CONF_ATTRIBUTES USB_CONFIG_BUSPOWERED // USB Configuration descriptor, self-power mode // (дескриптор конфигурации, питание от дополнительного собственного источника)
#define CONF_ATTRIBUTES USB_CONFIG_SELFPOWERED
7.1.4 Как добавить новый дескриптор? Процесс по шагам: 1. Задайте значение параметров дескриптора и его структуру в файле usb_descriptors.h. /* ____HID descriptor___________________________________________________*/ #define HID 0x21 #define REPORT 0x22 #define SET_REPORT 0x02 #define HID_DESCRIPTOR 0x21 #define HID_BDC 0x1001 #define HID_COUNTRY_CODE 0x00 #define HID_CLASS_DESC_NB 0x01 #define HID_DESCRIPTOR_TYPE 0x22 /*_____ USB HID DESCRIPTOR______________________________________________*/ typedef struct { U8 bLength; /* Размер этого дескриптора в байтах */ U8 bDescriptorType; /* HID descriptor type */ U16 bscHID; /* закодированный двоично Decimal Spec. release */ U8 bCountryCode; /* Страна для использования устройства */ U8 bNumDescriptors; /* Количество следующих далее дескрипторов HID class */ U8 bRDescriptorType; /* Report descriptor type */ U16 wDescriptorLength; /* Общая длина Report descriptor */ }S_usb_hid_descriptor; 2. Добавьте новый дескриптор в структуру s_usb_user_configuration_descriptor, в файле usb_descriptors.h: typedef struct { S_usb_configuration_descriptor cfg_mouse; S_usb_interface_descriptor ifc_mouse; S_usb_hid_descriptor hid_mouse; S_usb_endpoint_descriptor ep1_mouse; }S_usb_user_configuration_descriptor; 3. В файле usb_descriptors.c добавьте размер нового дескриптора к параметру wTotalLength дескриптора конфигурации configuration descriptor и добавьте значение дескриптора (см. пример ниже): code S_usb_user_configuration_descriptor usb_conf_desc = { { sizeof(S_usb_configuration_descriptor), CONFIGURATION_DESCRIPTOR, Usb_write_word_enum_struc(sizeof(S_usb_configuration_descriptor) +sizeof(S_usb_interface_descriptor) +sizeof(S_usb_hid_descriptor) +sizeof(S_usb_endpoint_descriptor)), NB_INTERFACE, CONF_NB, CONF_INDEX, CONF_ATTRIBUTES, MAX_POWER }, { sizeof(S_usb_interface_descriptor), INTERFACE_DESCRIPTOR, INTERFACE_NB_MOUSE, ALTERNATE_MOUSE, NB_ENDPOINT_MOUSE, INTERFACE_CLASS_MOUSE, INTERFACE_SUB_CLASS_MOUSE, INTERFACE_PROTOCOL_MOUSE, INTERFACE_INDEX_MOUSE }, { sizeof(S_usb_hid_descriptor), HID_DESCRIPTOR, HID_BDC, HID_COUNTRY_CODE, HID_CLASS_DESC_NB, HID_DESCRIPTOR_TYPE, Usb_write_word_enum_struc(sizeof(S_usb_hid_report_descriptor_mouse)) }, { sizeof(S_usb_endpoint_descriptor), ENDPOINT_DESCRIPTOR, ENDPOINT_NB_1, EP_ATTRIBUTES_1, Usb_write_word_enum_struc(EP_SIZE_1), EP_INTERVAL_1 } }; 4. Не забудьте добавить все связанные функции для обработки нового дескриптора. 7.1.5 Как добавить новую конечную точку? // USB Interface descriptor (дескриптор интерфейса USB) #define INTERFACE_NB xx #define ALTERNATE xx #define NB_ENDPOINT xx //Этот параметр = числу endpoints // интерфейса. Его меняем. #define INTERFACE_CLASS xx #define INTERFACE_SUB_CLASS xx #define INTERFACE_PROTOCOL xx #define INTERFACE_INDEX xx 2. Следующий шаг - задание величин EP дескриптора. Они заданы в in usb_descriptors.h. #define ENDPOINT_NB_1 (EP_MOUSE_IN | 0x80) #define EP_ATTRIBUTES_1 0x03 // BULK = 0x02, INTERRUPT = 0x03 #define EP_IN_LENGTH_1 8 #define EP_SIZE_1 EP_IN_LENGTH_1 #define EP_INTERVAL_1 0x02 // Интервал опроса конечной точки хостом EP_MOUSE_IN задано в conf_USB.h для указания номера EP, используемого приложением. 3. Добавьте новый endpoint descriptor к configuration descriptor (как описано в предыдущем вопросе FAQ). 4. Добавьте вызов инициализации железа (hardware initialization call) в usb_specific_request.c: void usb_user_endpoint_init(U8 conf_nb) { usb_configure_endpoint(EP_MOUSE_IN, TYPE_INTERRUPT, DIRECTION_IN, SIZE_8, ONE_BANK, NYET_ENABLED); } [Ссылки] 1. Макетная плата AVR-USB162. |