Реле Tianbo - ресурс 10 млн переключений
РадиоЛоцман - Все об электронике

Популярно о USB. Часть 4

Часть 1

Часть 2

Часть 3

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

Немоляев А. В, г. Екатеринбург

Транзакции

Из пакетов строятся транзакции. Транзакция – это логическая единица обмена данными. Транзакции, как и пакеты, имеют структуру и находятся на более высокой степени абстракции. Транзакции совершаются в рамках временных окон, о которых уже говорилось. Каждая транзакция должна начинаться и завершаться в рамках единственного окна, и недопустимо, чтобы транзакция началась в одном временном окне и закончилась в другом. Напомним, что начало временнóго окна отмечается пакетом SOF и завершается особым состоянием шины EOF.

Существует четыре типа транзакций: прерывания, массовой передачи, изохронных передач, и управления. Уже говорилось о каналах, связывающих конечную точку с хостом. Поток транзакций и составляет канал. Транзакции для различных типов передач имеют протокольные различия, обусловленные гарантированием или не гарантированием пропускной способности, временем отклика, надежностью доставки и синхронизацией ввода и вывода. К примеру, транзакции изохронных передач гарантируют скорость обмена, но не обеспечивают надежности доставки. При приеме поврежденных пакетов повторная передача поврежденного пакета не осуществляется, в то время как транзакции прерываний обеспечивают гарантированное время реакции USB-устройства на запрос хоста, но не предназначены для передачи больших объемов данных.

Рассмотрим транзакцию прерывания чтения. Любая транзакция инициируется хостом посредством передачи пакета маркера, который ранее описывался. Пакет маркера описывает тип транзакции, адрес USB-устройства, номер конечной точки. Адрес распознается USB-устройством, и оно возвращает пакет данных. Затем хост отвечает квитанцией. В транзакции прерывания может быть только один пакет данных. Для передачи нескольких пакетов используются дополнительные транзакции. Пакет маркера защищен от искажений, и если USB-устройством будет принят искаженный пакет маркера, USB-устройство выдерживает тайм-аут, сигнализируя хосту о необходимости повторной передачи маркерного пакета. В случае неготовности данных для передачи посылается пакет NAK или STALL, в зависимости от ситуации. Если пакет данных, переданный USB-устройством, принят хостом с искажениями, то хост выдерживает тайм-аут, сигнализируя USB-устройству о необходимости повторной передачи. Рисунок 7 поясняет сказанное.

 Популярно о USB
Рисунок 7.

В транзакции прерывания записи хост посылает пакет маркера транзакции записи и пакет данных. В случае искажения маркерного пакета или пакета данных, переданных хостом, USB-устройство выдерживает тайм аут, свидетельствуя об ошибке. При успешном принятии данных USB-устройство возвращает пакет положительного квитирования ACK. Если буфер еще не пуст, возвращается NAK.

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

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

Запросы

В терминологии USB команды, которые посылает хост USB-устройству, называются запросами, или requests на английском. Для передачи запросов используется канал управления. В подавляющем большинстве случаев для приема запросов используется нулевая конечная точка. Но стандартом допускается существование нескольких точек управления. По умолчанию все запросы управления принимаются нулевой конечной точкой. Запросы могут менять конфигурацию и некоторые свойства USB-устройства, запрашивать служебную информацию. В примере программы, который был приведен, хост посылает специальный запрос USB-устройству на получение строкового дескриптора от USB-устройства. В ответ USB-устройство возвращает строковый дескриптор.

Стандартом USB определен набор запросов, которые должно обрабатывать любое USB-устройство. Эти запросы называются стандартными запросами, и всего их 11. Стандартом предусмотрена возможность создания разработчиком своих собственных запросов. А для USB-устройств, относящихся к стандартизованным классам, например мыши USB, кроме стандартных запросов имеется своя система запросов, которую рассматривать не будем, но о существовании которой знать надо. Средствами библиотеки libusb можно посылать любой мыслимый запрос USB-устройству.

Для подачи запроса USB-устройству используется механизм управляющих передач. Это последовательность обмена пакетами, похожая на рассмотренные ранее транзакции, но только сложнее. Кратко рассмотрим стандартные запросы.

Запрос GET_DESCRIPTOR позволяет получить дескриптор устройства, конфигурации, конечной точки или строки. Во время процесса энумерации хост получает сведения об USB-устройстве с помощью этого запроса.

Запрос SET_DESCRIPTOR позволяет добавить новый дескриптор или расширить имеющийся.

Запрос SET_ADDRESS присваивает адрес USB-устройству. Каждое USB-устройство имеет уникальный адрес на шине. Адрес назначает хост в процессе энумерации.

Запрос SET_FEATURE позволяет установить значение свойства или состояния. Данные не возвращаются. Запрос может быть адресован устройству, интерфейсу или конечной точке. Адресовав запрос к конечной точке, можно перевести ее в блокированное состояние. Конечная точка, находящаяся в блокированном состоянии, на попытки обращения хоста отвечает пакетом STALL.

Хост может перейти в режим с пониженным энергопотреблением (suspended mode ), а USB-устройство может выводить хост из этого состояния специальной сигнализацией на шине. Это свойство USB-устройства называется «удаленное пробуждение» (remote wakeup). Специальным запросом SET_FEATURE можно дать возможность USB-устройству пробуждать хост.

Запрос CLEAR_FEATURE позволяет сбросить значение свойства или состояния. Запрос аналогичен запросу SET_FEATURE и противоположен по действию. Этим запросом выводится из блокированного состояния конечная точка, если она была заблокирована. Соответственно, сбрасывается свойство пробуждать хост remote wakeup. Кроме того, запросом CLEAR_FEATURE можно переводить USB-устройство в тестовый режим, когда для целей диагностики оно будет генерировать на шине повторяющуюся последовательность сигналов. Для возврата USB-устройства в нормальный режим его потребуется перезагрузить.

Запрос GET_STATUS позволяет определить состояние устройства, интерфейса или конечной точки. Если запрос направлен к устройству, то можно получить информацию, установлено или сброшено свойство wakeup USB-устройства. Дополнительно можно определить способ питания USB-устройства – от шины или от собственного источника.

При направлении запроса GET_STATUS к конечной точке можно определить состояние конечной точки, заблокирована она или нет.

Запросы GET_CONFIGURATION и SET_CONFIGURATION используются хостом для работы с USB-устройствами, имеющими несколько конфигураций. Соответственно, можно получить номер текущей конфигурации и установить альтернативную конфигурацию. Установка конфигурации выполняется на стадии конфигурирования USB-устройства.

Запросы GET_INTERFACE и SET_INTERFACE позволяют работать с альтернативными установками интерфейсов. Альтернативные установки интерфейсов позволяют менять некоторые параметры интерфейса после стадии конфигурирования в процессе нормальной работы. Альтернативные настройки интерфейсов в данной статье не рассматриваются.

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

На данном этапе нужно понять, что существует набор запросов, которые посылает хост для управления USB-устройством и для конфигурирования на стадии энумерации. В библиотеке libusb имеется функция usb_control_msg(), с помощью которой можно посылать запросы USB-устройству.

Микроконтроллер с USB

На стороне хоста имеется инструмент для программирования в виде библиотеки libusb; настала пора всерьез заняться USB-устройством. Для работы мной использовалась простенькая плата USB162_kit, которую я заказал на сайте microsin.ru. Необходимый минимум пакетов для компиляции: gcc-avr, binutils-avr, avr-libc и dfu-programmer. Для полноценной работы я установил Eclipse Kepler и AVR Eclipse plugin.

Основой представленных демонстрационных проектов послужили скачанный с сайта фирмы Atmel проект «AVR USB Series2 software library template» и его описание в документе «Application note AVR276».

В архиве mcu_inter.tar.gz, в подкаталоге /project находится файл project.hex – скомпилированный образ для загрузки в микроконтроллер. Там же имеется сценарий для прошивки «prg.sh». Подключив плату к USB, нажмите кнопку RESET на плате, не отпуская ее, нажмите кнопку HWB. Отпустите кнопку RESET, отпустите кнопку HWB. Плата готова для программирования. Выполните сценарий «prg.sh», плата стала мигать. Найдите плату среди подключенных USB устройств с помощью lsusb. Определите VID:PID и посмотрите все дескрипторы на плате. Как можно убедиться, сконфигурирована единственная конечная точка прерывания ввода, как у мыши.

В архиве host_inter.tar.gz находится вторая программа, выполняемая на стороне хоста. Программа периодически читает содержимое конечной точки. Плата считает все запросы чтения от хоста и возвращает в первом байте считанного из платы буфера значение этого счетчика. Для работы применяется функция usb_interrupt_read() библиотеки libusb. Программа посылает нашему USB-устройству, запрос на чтение и ждет данных, затем выводит, и так далее в бесконечном цикле. Программа USB-устройства каждую секунду заполняет буфер и ждет, когда он будет прочитан.

С программой на стороне хоста все ясно, теперь займемся программой USB-устройства. Файлы проекта можно открывать любым редактором. Весь процесс сборки программы производится утилитой make; в подкаталоге gcc требуется выполнить make all. Я считаю, что современные проекты с десятками файлов трудно изучать без какой-либо системы навигации по коду, поэтому я использовал Eclipse. Но это не принципиально, можно работать с проектом в любом редакторе, как уже говорилось.

В микроконтроллер AT90USB162 встроена SIE (serial interface engine) – подсистема, отвечающая за реализацию протокола USB. Формирование пакетов, генерирование пакетов квитирования, анализ целостности пакетов, генерирование прерываний после выполнения транзакций и многое другое выполняет SIE. Задача разработчика разобраться в архитектуре SIE конкретного микроконтроллера, сконфигурировать его, залив нужные константы в нужные регистры и написать обработчики прерываний. Но для этого необходимо понимание протокола USB, без этого не обойтись.

Другой путь – использовать шаблонные проекты и частично обойти необходимость детального разбора устройства аппаратуры.

В каталоге conf содержатся файлы, отвечающие за настройки проекта. В файле config.h глобальные настройки. В файле conf_scheduler.h описаны задачи, которые заданы в проекте. Создатели проекта применили некоторое подобие простейшего планировщика для поочередного вызова функций. Как видим, определены четыре задачи. Каждая задача определена в собственном файле. Задачи с суффиксом init выполняются один раз при старте, остальные в бесконечном цикле.

В файле conf_usb.h находятся глобальные настройки USB, в том числе, количество используемых конечных точек и номера конфигурируемых конечных точек. В этом же файле предусмотрена секция из макросов, где можно задать реакцию на некоторые события на шине USB. При приеме каждого пакета SOF от хоста будет вызываться функция sof_action(). Так как эти пакеты передаются с интервалом в 1 мс, то каждую миллисекунду будет вызываться функция sof_action(). Реализация этой функции описана в файле device_template_task.c; статическая переменная cpt_sof монотонно увеличивается при каждом вызове.

Процедура отправки данных хосту находится в этом же файле. Отсчитав секунду, она мигает светодиодом и отправляет значение счетчика и набор пользовательских данных. Для определения готовности устройства к передаче данных здесь не используются прерывания, а только опрос флагов некоторых регистров устройства. Макрос Is_usb_write_enabled() выполняет проверку состояния бита RWAL регистра UEINTX. Этот бит устанавливается и сбрасывается аппаратно, сообщая о готовности буфера к передаче. Особенность программных текстов Atmel – это их кажущаяся сложность. Много файлов, макросов, вызовов, а в результате видишь, что выполняется пара операторов. Это вызвано тем, что фирмачи пишут демонстрационные программы сразу под несколько процессоров, а конкретная модель выбирается заданием нескольких значений в макроопределении. Ситуация усложняется еще и тем, что иногда исходный текст пишется и под разные компиляторы, да еще и для компиляторов под разные операционные системы. Препроцессор кромсает тексты, и в результате остается совсем мало. Но читаемость демонстрационных программ сильно страдает. Макрос Usb_write_byte() записывает байт данных в буфер FIFO конечной точки и реализует запись байта в регистр UEDATX. Для операций с конечной точкой ее нужно предварительно выбрать, для чего служит макрос Usb_select_endpoint(), засылающий номер конечной точки в регистр UENUM. После всех операций загрузки данных в буфер их нужно отослать хосту. Для этого сбрасываются сначала бит TXINI, а затем бит FIFOCON регистра UEINTX. Можно сконфигурировать конечную точку, чтобы после опустошения буфера FIFO генерировалось прерывание, при этом устанавливается флаг TXINI, который для подтверждения прерывания необходимо сбросить. Флаг устанавливается независимо от того, разрешено данное прерывание или нет. Затем требуется сбросить бит FIFOCON, давая сигнал на передачу данных в шину.

В файле usb_descriptors.h заданы значения полей дескрипторов, здесь можно поменять содержимое строковых дескрипторов на свои значения.

Второй проект в архивах mcu_bulk.tar.gz и host_bulk.tar.gz, для хоста и USB-устройства, реализует обмен с двумя конечными точками типа массовой передачи (bulk). Хост передает в USB-устройство массив, USB-устройство принимает, увеличивает каждый член на 3 и возвращает хосту.

Заключение

В работе остались незатронутыми обширные темы энумерации USB-устройств, передача команд по каналу управления, использование прерываний микроконтроллера в обработке событий USB и неохватная тема устройств, относящихся к классам. Возможно, что в следующих публикациях эти темы будут раскрыты.

Электронные компоненты. Бесплатная доставка по России
Для комментирования материалов с сайта и получения полного доступа к нашему форуму Вам необходимо зарегистрироваться.
Имя