AVR287: демонстрация хоста USB HID и USB Mass Storage Печать
Добавил(а) microsin   

Перевод даташита AVR287 [1] компании Atmel, описывающий пример реализации хоста для устройств USB HID (клавиатуры, мыши и другие устройства ввода/вывода) и устройств USB Mass Storage (флешки, внешние жесткие диски). Другие примеры хоста на чипах AVR USB можно найти в библиотеке LUFA [2].

• Пример основан на библиотеке AVR® USB OTG Reduced Host
• Работает на чипах AT90USB647/AT90USB1287
• Поддерживает загружаемые и не загружаемые стандартные мыши USB
• Поддерживает функцию хаба USB (только для устройств хранения USB Mass Storage)
• Mass Storage:
- обрабатывается сокращенный набор команд блока (Reduced Block Commands, RBC)
- обрабатываются команды блока SFF-8020i или MMC-2 (ATAPI)
- рассчитан обычно на UFI, устройство внешнего гибкого диска (floppy disk drive, FDD)
- обрабатываются команды блока SFF-8070i
- прозрачный набор команд SCSI

AVR287-USB-host-pic1 1

Рис. 1-1. Приложение хоста HID и Mass Storage

[1. Введение]

Сейчас все больше периферийных устройств используют для подключения интерфейс USB. Наличие функции хоста для встраиваемых приложений дает им возможность поддерживать взаимодействие со стандартным устройством USB, и такое приложение получает большие преимущества на рынке. Цель этого документа - описать, как начать реализацию приложения хоста USB на базе класса USB HID (мышь USB) и класса USB Mass Storage (устройство хранения данных, флешка, внешний диск). В завершении будет показан простой пример одновременного управления микроконтроллером AT90USB (серия чипов Atmel Series-7 с поддержкой USB) классов устройств USB HID и USB Mass Storage, с поддержкой файловой системы (FAT12/FAT16/FAT32). Подразумевается, что пользователь знаком с AVR USB firmware framework компании Atmel, а также стандартами HID и MassStotage, которые можно найти на сайте usb.org.

Для запуска примера рекомендуется использовать плату USBKEY, но можно применить и любую другую плату, на которой установлен микроконтроллер AT90USB647 или AT90USB1287 (например userial [3]).

[2.1 Немного теории: как работает класс USB HID]

2.1.1. Конфигурация HID. Класс устройств USB HID требует наличия одной управляющей конечной точки (Ctrl endpoint, ep0. Строго говоря, наличие такой конечной точки требуется в любом устройстве USB), одной конечной точки Interrupt IN (для передачи данных от устройства USB к хосту) и одной необязательной конечной точки Interrupt OUT (передача данных от хоста к устройству USB). Обычно ep0 задействована только для процесса энумерации устройства USB. Конечная точка Interrupt IN предназначена для передачи репортов IN от устройства USB к хосту (основной трафик мыши, описывающий перемещения курсора и нажатия на кнопки). Конечная точка Interrupt OUT применяется для передачи репортов OUT (например, для команд зажечь нужный светодиод NumLock, CapsLock, ScrollLock на клавиатуре) от хоста к устройству USB. По стандарту для мыши USB обязательно наличие одной управляющей конечной точки Ctrl endpoint, ep0 и одной конечной точки Interrupt IN.

2.1.2. Передача данных. Приложение USB HID (мышь USB) использует простой обмен данными между хостом и мышью. Хост опрашивает мышь через определенные интервалы времени P (polling interval time, интервал опроса) на предмет наличия новых данных, и мышь в ответ посылает данные, если они доступны, иначе посылает маркер NAK (No Acknowledge) чтобы сказать хосту, что данных пока нет. Данные, посылаемые хосту, называются репортом. По сути это просто блок байт определенного формата. Структура этого репорта показана на рисунке ниже:

AVR270-USB-mouse-report-structure

Каждый раз, когда пользователь мыши переместил курсор на экране, покрутил колесико или нажал на одну из кнопок, этот репорт будет отправлен к хосту. Байты репорта будут прочитаны в порядке от байта 0 до байта 3.

[2.2. Немного теории: как работает класс USB Mass Storage]

2.2.1. Конфигурация Mass Storage. Приложение Mass Storage использует 2 конечные точки типа bulk (одна IN, другая OUT) для реализации передач данных и состояния. Конечная точка 0 (Ctrl endpoint, ep0) используется только в целях энумерации, обработки ошибок и для того, чтобы определить значение LUN (Logic Unit Number).

2.2.2. Передача данных. Обмен данными через USB для приложения Mass Storage основано на командах SCSI. Другими словами, приложение Mass Storage управляется набором команд SCSI, посылаемых хостом для управления передачей файлов. Благодаря наличию LUN класс Mass Storage позволяет одному устройству поддерживать несколько юнитов хранения данных.

AVR273-USB-Mass-Storage-Application-Overview

Рис. 2-2. Обзор приложения USB Mass Storage. В качестве хоста обычно фигурирует компьютер, но в нашем же примере хостом будет работать демонстрационная плата с микроконтроллером AVR.

К сожалению протокол SCSI слишком сложен, чтобы его можно было рассмотреть в рамках этой статьи. Для получения подробной информации обратитесь к стандарту "USB Mass Storage Class Bulk-Only Transport specification" на сайте usb.org.

[3. Архитектура программного обеспечения Atmel]

Ниже на рисунке показана архитектура firmware хоста HID и Mass Storage, где отображены файлы кода, используемые в проекте.

AVR287-USB-host-firmware-architecture-pic3 1

Рис. 3-1. Архитектура хоста HID и Mass Storage.

Управление классом HID (мышь) реализовано в модуле host_mouse_task.c, в то время как поддержка класса Mass Storage реализована в модуле host_ms_task.c. Шедулер (процедура, постоянно вызываемая в бесконечном цикле main) периодически вызывает подпрограммы usb_task(void), host_mouse_task(void) и host_ms_task(void). Работа этих трех функций описана ниже.

• usb_task(void)
- Детектирование режима USB.
- Энумерация устройства USB / хоста USB.
• host_mouse_task(void)
- Проверяет, подключена ли мышь.
- Получает данные от мыши, и выполняет приложение пользователя.
- Проверяет отключение мыши от хоста.
• host_ms_task(void)
- Проверяет, подключено ли устройство хранения Mass Storage.
- Выполняет приложение пользователя.
- Проверяет отключение устройства Mass Storage от хоста.

[3.1. Энумерация]

Когда устройство USB подключается к хосту, запускается процедура энумерации. Кратко энумерацию можно описать как процесс опознания хостом устройства USB. Если firmware хоста USB примет подключение интерфейса устройства на низком уровне, то будут проверены дескрипторы устройства на соответствие списку поддерживаемых интерфейсов (список определен в файле conf_usb.h), и далее процедуры host_mouse_task() и host_ms_task() увидят оповещение соединения (макрос Is_new_device_connection_event() вернет TRUE).

Текущее число принятых устройств будет возвращен функцией Get_nb_device(), если это разрешено опцией USB_HUB_SUPPORT в файле conf_usb.h, и число принятых интерфейсов будет возвращено функцией Get_nb_supported_interface(). Для каждого интерфейса каждого устройства возможен программный доступ к кодам класса, подкласса и протокола благодаря макросам Get_class(i), Get_subclass(i) и Get_protocol(i).

Программа проверяет - имеет ли подключенное устройство поддерживаемый интерфейс класса, и если интерфейс устройства принят, программа конфигурирует каналы функцией host_auto_configure_endpoint или функцией User_configure_endpoint(), в зависимости от того, была ли разрешена опция HOST_AUTO_CFG_ENDPOINT в файле conf_usb.h, или нет.

Когда конфигурирование завершено, хост посылает запрос Set_configuration() к устройству USB, и перейдет к состоянию DEVICE_READY (устройство USB готово к работе).

Для задачи хоста мыши, когда она получит оповещение о подключении, будет проверены класс и протокол, чтобы убедиться, что подключена мышь. Если это так, задача разморозит канал IN и подготовит его для передачи данных. Ниже приведен соответствующий код детектирования мыши хостом HID:

if(Is_new_device_connection_event()) //подключение устройства
{
   mouse_connected=0;
   for(i=0; i < Get_nb_supported_interface(); i++)
   {
      if(Get_class(i)==HID_CLASS
         && Get_protocol(i)==HID_PROTOCOL_MOUSE)
      {
         mouse_connected=1;
         host_hid_set_idle();
         host_get_hid_report_descriptor();
         LOG_STR_CODE(log_mouse_connect); 
         PIPE_MOUSE_IN=host_get_hwd_pipe_nb(Get_ep_addr(i,0));
         Host_select_pipe(PIPE_MOUSE_IN);
         Host_continuous_in_mode();
         Host_unfreeze_pipe();
         break;
      }
   }
}

Когда задача mass storage увидит событие подключение нового устройства, то для поддержки возможности поддержки нескольких устройств mass storage она ищет каждый интерфейс для каждого устройства. Если текущий интерфейс принадлежит классу mass storage, то текущий индекс устройства сохраняется в массиве устройств mass storage dms[n] и максимальное количество устройств записывается в переменную dms_connected. После этого, задача конфигурирует канал IN и канал OUT, и инициализирует все приводы USB (устройство Mass Storage). Ниже показан код хоста для детектирования устройства Mass Storage.

if(Is_new_device_connection_event())
{
   for(k=0; k < Get_nb_device(); k++)
   {
      Host_select_device(k);
      new_dms=TRUE;
      for(i=0; i < Get_nb_supported_interface(); i++)
      {
         if(Get_class(i)==MS_CLASS)
         {
            LOG_STR_CODE(log_ms_connect);
            if (dms_connected!=0) // Подключено другое DMS?
            {
               for(n=0;n<USB_MAX_DMS_NUMBER;n++)
               {
                  if(dms[n].device_index==k)
                  {
                     new_dms=FALSE;
                  }
               }
            }
            if(new_dms)
            {
               dms_connected++; // TODO: проверить USB_MAX_DMS_NUMBER
               dms[dms_connected-1].device_index=k;
               //Получение корректных физических каналов, связанных
               // с конечными точками IN/OUT устройства DMS.
               if(Is_ep_addr_in(Get_ep_addr(i,0)))
               { //Да, привяжем это к каналу MassStorage IN
                  dms[dms_connected-1].pipe_in=usb_tree.device[k].interface[i].ep[0].pipe_number;
                  dms[dms_connected-1].pipe_out=usb_tree.device[k].interface[i].ep[1].pipe_number;
               }
               else
               { //Нет, инверсия...
                  dms[dms_connected-1].pipe_in=usb_tree.device[k].interface[i].ep[1].pipe_number;
                  dms[dms_connected-1].pipe_out=usb_tree.device[k].interface[i].ep[0].pipe_number;
               }
               //Получение номера lun в подключенном DMS
               Select_dms(dms_connected-1);
               dms[dms_connected-1].nb_lun=host_get_lun();
               //Вычислить номер USB LUN
               lun=0;
               for(n=0;n<dms_connected-1;n++)
               {
                  lun +=dms[n].nb_lun;
               }
               //Инициализация всех приводов USB
               for(n = 0; n < dms[dms_connected-1].nb_lun; n++)
               {
                  host_ms_inquiry();
                  //host_read_format_capacity(lun); //некоторые устройства требуют
                                                      //эту команду перед командой чтения
                  host_read_capacity(lun, &capacity);
                  host_ms_request_sense();
                  while (CTRL_GOOD != host_test_unit_ready(lun));
                  host_read_capacity( lun, &capacity );
                  lun++;
               }
               break;
            }
         }
      }
   }
}

[3.2. Передача данных]

3.2.1. Задача хоста HID, работающая с мышью. Если подключена мышь, то задача каждый раз сначала проверяет, принят ли допустимый пакет IN. Когда данные доступны, программа читает эти данные функцией Host_read_byte() и выполняет алгоритм по обработки действий пользователя (что нужно делать, чтобы обработать новые данные положения курсора мыши и нажатий кнопок). После этого хост посылает IN-команду функцией Host_read_byte(), после чего задача готова к приему следующего пакета данных. Ниже приведен код обработки хостом передачи данных от мыши. Индикационные светодиоды Led3..Led0 показывают состояние обработки данных мыши (эмулирует приложение пользователя).

Host_select_pipe(PIPE_MOUSE_IN);
if(Is_host_in_received())
{
   if(Is_host_stall()==FALSE)
   {
      i=Host_read_byte();
      new_x=(S8)Host_read_byte();
      new_y=(S8)Host_read_byte();
      if(new_x==0)
      { 
         Led0_off();
         Led1_off();}
      else if(new_x>0)
      {
         Led0_on();
         Led1_off();
      }
      else
      {
         Led0_off();
         Led1_on();
      }
      if(new_y==0)
      {
         Led2_off();
         Led3_off();
      }
      else if(new_y>0)
      {
         Led2_on();
         Led3_off();
      }
      else
      {
         Led2_off();
         Led3_on();
      }
   }
   Host_ack_in_received();
   Host_send_in();
}

Операции обработки данных мыши реализованы только в демонстрационных целях - в зависимости от направления перемещения курсора будут загораться и гаснуть разные индикационные светодиоды на плате USBKEY. Пользователь может использовать эту функцию "как есть", но на самом деле в этом месте должен быть свой обработчик данных от мыши (приложение пользователя).

3.2.2. Задача хоста по обработке Mass Storage. Как упоминалось ранее, приложение Mass Storage использует набор команд SCSI, отправляемых хостом для поддержки чтения и записи файлов на диске MSD. Базовая передача данных выполняется функциями host_get_data() и host_send_data(). Приложение пользователя должно реализовать кодировщик протокола SCSI, декодер файловой системы (см. апноут AVR114 [5]).

[4. Пример реализации]

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

В этой конфигурации демонстрационная плата сначала подключается к компьютеру PC через интерфейс USB, работая как устройство хранения USB Mass Storage device, подготавливая демонстрационные данные. Затем демонстрационная плата отключается от PC, и подключается к устройству хранения Mass Storage device (флешке), чтобы показать передачу данных Mass Storage. Мы также можем подключить стандартную мышь USB к демонстрационной плате,
чтобы показать работу приложения хоста, обрабатывающего протокол USB HID мыши.

Приложение хоста USB HID и Mass Storage может быть реализовано на любом AVR, у которых есть аппаратный интерфейс с поддержкой функции хоста, путем использования пакета программного обеспечения, доступного на сайте Atmel.

Прим. переводчика: на самом деле "на любом AVR" слишком громко сказано. В действительности количество чипов AVR, поддерживающих функцию хоста, весьма ограничено - это только семейство AVR USB Series7, в которое входит 2 чипа: AT90USB647 и AT90USB1287. Причина понятна - для качественной поддержки функций хоста требуется гораздо больше памяти и быстродействия, чем имеется в наличии у классических AVR.

[4.2. Аппаратура]

Оба пакета программного обеспечения могут быть запущены на имеющихся стартер-китах. На момент написания этого документа пакеты поддержки хоста HID и хоста Mass Storage могут быть запущены на платах STK525 и USBKEY (на которых установлены чипы AT90USB647 или AT90USB1287). Устройство USB Mass Storage, которое подключается к хосту Mass Storage, может быть либо обычной флешкой USB, либо стартер-китом, работающим как устройство USB Mass Storage (см. [5, 6]).

Плата хоста должна работать в режиме хоста (подключен коннектор miniA). Для работы платы в режиме хоста требуется конфигурирование и подключение внешнего источника питания. Причина понятна - плата хоста нуждается в питании, и устройство USB (мышь HID или устройство Mass Storage) требует для себя подачи питания через USB.

[4.3. Программное обеспечение]

4.3.1. Описание работы. Для задачи хоста, обрабатывающей протокол USB HID мыши, если Вы подключите стандартную мышь USB к демонстрационной плате, то перемещения мыши будут отображаться зажиганием светодиодов демонстрационной платы LED3..LED0. Как только прошла энумерация, событие получения пакета IN будет срабатывать всякий раз, когда будет перемещаться мышь, ли когда на ней будут нажиматься кнопки. Хост прочитает данные мыши через интерфейс USB. Перемещение по оси X будет управлять горением LED0 и LED1 (при X > 0 включится LED0, при X < 0 включится LED1), а по оси Y перемещение будет отображаться горением LED2 и LED3 (при Y > 0 включится LED2, при Y < 0 включится LED3).

Для задачи хоста USB Host Mass Storage, как только прошла энумерация устройства MSD, можно осуществить обмен файлами между данными, записанными на flash-диске платы хоста (это микросхема памяти, установленная на плату), и устройством USB MSD. Правое положение джойстика позволяет прочитать содержимое папки "OUT" на устройстве USB MSD, и записать его на flash-диск платы хоста в папку "IN". И наоборот, левое положение джойстика позволяет переписать данные в обратном направлении, с диска микросхемы dataflash на устройство USB MSD.

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

• Конфигурация USB, в файле conf_usb.h:
- должна быть разрешена опция USB_HOST_FEATURE для поддержки функции хоста USB.
- должна быть разрешена опция USB_DEVICE_FEATURE для соединения с компьютером PC.
- в массиве VID_PID_TABLE должны содержаться значения VID и PID для поддерживаемых устройств, если разрешена опция HOST_STRICT_VID_PID_TABLE.
- массив CLASS_SUBCLASS_PROTOCOL должен включать в себя протокол класса HID class Mouse. Для поддержки стандартных мышей USB должны быть включены загружаемые (bootable) и не загружаемые (non-bootable) подклассы, и для поддержки устройств USB MSD должен быть подключен интерфейс Mass Storage (Mass Storage class, SCSI subclass, протокол только bulk). Все эти элементы class/subclass/protocol должны быть добавлены пользователем, если нужно применение HUB.
- должна быть разрешена опция USB_HUB_SUPPORT, если Вы хотите использовать несколько устройств USB MSD.
- опция HOST_STRICT_VID_PID_TABLE может быть либо разрешена, либо запрещена. Для поддержки разных продуктов от различных производителей рекомендуется запретить этот макрос.
• Конфигурация USB Mass Storage:
- должна быть разрешена опция HOST_SYNC_MODE в файле config.h для запуска программы примера хоста USB Mass Storage.

[Дополнительная документация]

• AVR USB products Datasheet (atmel.com)
• USB HID Class specification (usb.org)
• USB Mass Storage Class specification (usb.org)

[Словарик]

Здесь приведена расшифровка некоторых непонятных терминов и аббревиатур. Объяснение других терминов, касающихся стандарта USB (конечная точка, репорт, энумерация, дескрипторы и т. д.) см. в статье USB in a NutShell [4].

ATAPI Advanced Technology Attachment Packet Interface, стандарт шины для подключения к компьютеру хранилищ данных. Подробнее см. Википедию.

DMS Device Mass Storage, устройство хранения USB.

LUN Logical Unit Number — это адрес диска (в ранних версиях) или дискового устройства в сетях хранения (современное использование).

MMC-2 MultiMedia Card версии 2, тип интерфейса для подключения flash-карт памяти.

MSD Mass Storage Device, устройство хранения USB, то же самое, что и DMS.

OTG On The Go, дословный перевод "на лету". Обозначает интерфейс USB, который может работать как в режиме хоста USB, так и в режиме устройства USB.

RBC Reduced Block Commands, подмножество команд стандарта SCSI.

SCSI Small Computer System Interface, стандартный тип интерфейса для подключения внешних устройств хранения данных, и одноименный протокол.

SFF-8020i стандарт для подключения устройств CD-ROM/DVD на основе интерфейса ATA/ATAPI.

SFF-8070i стандарт для подключения устройств чтения/записи гибких дисков.

UFI Universal File Interface, стандарт для подключения устройств чтения/записи гибких дисков.

[Ссылки]

1. AVR287: USB Host HID and Mass Storage Demonstration site:atmel.com.
2. LUFA - бесплатная библиотека USB для микроконтроллеров Atmel AVR.
3. userial - мост USBI2C/SPI/GPIO/ADC на микроконтроллере AT90USB647.
4. USB in a NutShell - путеводитель по стандарту USB (начало).
5. AVR114: использование файловой системы Atmel.
6. AVR273: реализация флешки USB на AVR с аппаратным USB.
7. AVR276: USB Software Library for AT90USBxxx Microcontrollers.
8. AVR270: демонстрационный проект мыши на AVR с аппаратным USB.
9. 141127AVR287.zip - исходный код проекта.