Добавление серийного номера к устройству USB Печать
Добавил(а) microsin   

Серийный номер для микроконтроллеров AVR со встроенным аппаратным USB вместе со стеком USB от Atmel дает следующие возможности:

• Автоматическое получение 20 символов уникального серийного номера
• Уникальный идентификатор (ID) для Вашего устройства
• Серийный номер разрешается и запрещается во время компиляции

Несмотря на наличие уникальной пары VID (Vendor ID, идентификатор производителя) и PID (Product ID, идентификатор продукта), устройству USB может потребоваться уникальный серийный номер. Продукция AVR® USB предоставляет Вам эту возможность, и для этого Вам не нужны никакие внешние данные для построения серийного номера. Все, что нужно - уникальный идентификатор, который записывается в память FLASH микроконтроллера AVR. Для чтения этого документа нужно ознакомиться с даташитом USB Software Library for AT90USBxxx Microcontrollers [2] (Doc 7675, который можно найти на CD-ROM & сайте компании Atmel®) и спецификацией USB (сайт usb.org).

AVR922-serial-number-USB-device

[Функции, которые нужны для чтения серийного номера]

В пакете библиотек для построения USB firmware, предоставляемом Atmel, есть библиотечный драйвер для доступа к FLASH, доступный в файле flash_drv.h. Макрос Flash_read_sn(pos) позволяет Вам читать байт из памяти FLASH в позиции pos. Вызов этого макроса 10 с позициями от 0 до 9 предоставлять Вам уникальный ID, состоящий из 10 байт (порядок чтения по позициям не имеет значения). Эти 10 байт будут использоваться для генерирования серийного номера.

После того, как Вы собрали 10 байт, можно построить серийный номер для Вашего устройства USB. Этот серийный номер будет содержать 20 символов. При этом каждый байт будет преобразован в 2 символа. Например, байт 0x1A даст 2 символа "1" и "A".

[Когда серийный номер будет отправлен хосту USB]

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

Этот запрос обрабатывается файлом usb_standard_request.c. Имейте в виду, что генерация серийного номера может быть разрешена или запрещена кодом Вашего firmware микроконтроллера. Чтобы разрешить или запретить генерацию серийного номера, нужно корректно задать конфигурацию в заголовочном файле conf_usb.h (в этом примере генерация серийного номера разрешена):

#define USB_DEVICE_SN_USE ENABLE
#define USE_DEVICE_SN_UNIQUE ENABLE

Если параметр USB_DEVICE_SN_USE запрещен, то серийный номер не будет оправляться к хосту USB. Если же этот параметр разрешен, то возможны 2 случая:

• USB_DEVICE_SN_UNIQUE запрещен. Тогда для устройства USB будет установлен серийный номер по умолчанию (он будет одинаков для всех устройств).
Серийный номер по умолчанию задан в заголовочном файле usb_descriptors.h:

#define USB_SN_LENGTH 0x05
#define USB_SERIAL_NUMBER \
{ Usb_unicode('0') \
 ,Usb_unicode('0') \
 ,Usb_unicode('0') \
 ,Usb_unicode('0') \
 ,Usb_unicode('0') \
}

• USB_DEVICE_SN_UNIQUE разрешен. В этом случае уникальный серийный номер будет установлен и отправлен хосту, как это было описано ранее.

На скриншотах ниже показаны свойства устройства USB, которые можно просмотреть с помощью Диспетчера Устройств Windows. Если у Вас на компьютере установлено несколько устройств USB, то выберите нужное (Вы можете протестировать пример Atmel "AVR USB MOUSE DEMO", найдите нужное устройство по его имени), сделайте на устройстве правый клик мышью и выберите из контекстного меню Свойства (Properties).

На этом скриншоте серийный номер назначен для устройства USB:

AVR922-serial-number-assigned

На этом скриншоте для устройства USB назначен серийный номер по умолчанию:

AVR922-serial-number-default

Здесь для устройства USB серийный номер не назначен:

AVR922-no-serial-number

Обратите внимание, что серийный номер, показанный на последнем скриншоте, автоматически установлен операционной системой Windows®, он не отправляется устройством USB (этот серийный номер не сохраняется в Вашем устройстве USB).

Для создания устройств USB на микроконтроллерах AVR используются также популярные библиотеки V-USB и LUFA. В них тоже можно задать для устройства USB серийный номер.

[Как задается серийный номер устройства USB в библиотеке V-USB]

Библиотека V-USB [3] без сомнения является шедевром программистского искусства. Она дает уникальную возможность создать устройство USB на самом обычном микроконтроллере AVR (серий ATmega или ATtiny), у которого даже нет для этого специальной аппаратуры (нет встроенного контроллера USB). Физические сигналы D+ и D- шины USB опрашиваются и обрабатываются микроконтроллером программно (!).

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

/*#define USB_CFG_SERIAL_NUMBER 'N', 'o', 'n', 'e' */
/*#define USB_CFG_SERIAL_NUMBER_LEN 0 */
/* Здесь задается строка для серийного номера устройства - аналогично текстовым
 * дескрипторам, показанным ранее. Если Вам не нужен серийный номер для устройства USB
 * не определяйте эти 2 макроса (оставьте их закомментированными).
 */

В этом примере серийный номер не задан, потому что макросы USB_CFG_SERIAL_NUMBER (содержимое серийного номера) и USB_CFG_SERIAL_NUMBER_LEN (длина серийного номера) не заданы. Можно их раскомментировать, и указать произвольную строку для серийного номера устройства USB, например (этот пример взят из проекта [5]):

#define USB_CFG_SERIAL_NUMBER '2', '0', '1', '0', '-', 'j', 'u', 'n', '-', '1', '8'
#define USB_CFG_SERIAL_NUMBER_LEN 11

Обратите внимание, что в макрос USB_CFG_SERIAL_NUMBER_LEN должен точно указывать длину серийного номера, т. е. то количество символов, которое задано в макросе USB_CFG_SERIAL_NUMBER.

Может оказаться полезным предоставить серийный номер устройства не во время компиляции, а во время выполнения программы firmware. См. секцию по свойствам дескрипторов ниже (Fine Control over USB Descriptors), где показано, как тонко управлять дескрипторами USB - такими как строковый дескриптор для серийного номера. В этом случае можно разместить серийный номер не в памяти FLASH, а в памяти RAM микроконтроллера, так что появляется возможность генерировать данные для серийного номера на лету (runtime), с помощью кода, работающего в микроконтроллере.

/* ------------------- Fine Control over USB Descriptors ------------------- */
/* Если Вы не хотите использовать дескрипторы USB по умолчанию для драйвера,
 * то можете предоставить свои собственные. Это можно реализовать с помощью
 * (1) статических данных фиксированной длины в памяти FLASH,
 * (2) статических данных фиксированной длины в памяти RAM, или
 * (3) динамически создаваемых данных во время выполнения кода в функции 
 * usbFunctionDescriptor(). Для получения дополнительной информации по этой
 * функции смю файл usbdrv.h.
 * Обработка дескрипторов конфигурируется с помощью свойств дескриптора. Если
 * Если свойства не заданы, или установлены как 0, то будет использоваться 
 * дескриптор по умолчанию.
 * Возможны свойства:
 * + USB_PROP_IS_DYNAMIC: данные для дескриптора должны быть получены runtime
 * с помощью вызова функции usbFunctionDescriptor(). Если используется
 * механизм usbMsgPtr, то по умолчанию данные находятся в памяти FLASH.
 * Добавьте свойство USB_PROP_IS_RAM, если Вам нужны указатели на RAM.
 * + USB_PROP_IS_RAM: данные, которые возвращает usbFunctionDescriptor(),
 * находятся в статической памяти RAM, не в памяти программ (не во FLASH).
 * + USB_PROP_LENGTH(len): если данные находятся в статической памяти (RAM
 * или FLASH), драйвер должен знать длину дескриптора. Сам дескриптор
 * находится по адресу, известному по его идентификатору (см. ниже).
 * Вот список имен статических дескрипторов (должны быть декларированы с атрибутом
 * PROGMEM, если надо разместить данные дескрипторов во FLASH):
 * char usbDescriptorDevice[];
 * char usbDescriptorConfiguration[];
 * char usbDescriptorHidReport[];
 * char usbDescriptorString0[];
 * int usbDescriptorStringVendor[];
 * int usbDescriptorStringDevice[];
 * int usbDescriptorStringSerialNumber[];
 * Другие дескрипторы не могут быть предоставлены статически, они должны быть
 * предоставлены динамически во время выполнения программы (runtime).
 *
 * Свойства дескриптора могут быть объединены друг с другом как флаги операцией
 * OR (логическая операция ИЛИ), например:
 * #define USB_CFG_DESCR_PROPS_DEVICE (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))
 *
 * Заданы следующие дескрипторы:
 * USB_CFG_DESCR_PROPS_DEVICE
 * USB_CFG_DESCR_PROPS_CONFIGURATION
 * USB_CFG_DESCR_PROPS_STRINGS
 * USB_CFG_DESCR_PROPS_STRING_0
 * USB_CFG_DESCR_PROPS_STRING_VENDOR
 * USB_CFG_DESCR_PROPS_STRING_PRODUCT
 * USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER
 * USB_CFG_DESCR_PROPS_HID
 * USB_CFG_DESCR_PROPS_HID_REPORT
 * USB_CFG_DESCR_PROPS_UNKNOWN (для всех дескрипторов, которые не обрабатываются
 * драйвером)
 *
 * Обратите внимание, что строковые дескрипторы не просто строки, это строки
 * в формате Unicode, снабженные префиксом заголовка из 2 байт. Пример:
 * int serialNumberDescriptor[] = {
 * USB_STRING_DESCRIPTOR_HEADER(6),
 * 'S', 'e', 'r', 'i', 'a', 'l'
 * };
 */
 
#define USB_CFG_DESCR_PROPS_DEVICE 0
#define USB_CFG_DESCR_PROPS_CONFIGURATION USB_PROP_IS_DYNAMIC
#define USB_CFG_DESCR_PROPS_STRINGS 0
#define USB_CFG_DESCR_PROPS_STRING_0 0
#define USB_CFG_DESCR_PROPS_STRING_VENDOR 0
#define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0
#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0
#define USB_CFG_DESCR_PROPS_HID 0
#define USB_CFG_DESCR_PROPS_HID_REPORT 0
#define USB_CFG_DESCR_PROPS_UNKNOWN 0

[Как задается серийный номер устройства USB в библиотеке LUFA]

Библиотека LUFA [4] позиционируется как альтернатива аналогичному стеку библиотек USB от компании Atmel, предназначена она для микроконтроллеров AVR, которые имеют на борту аппаратное периферийное устройство (контроллер) USB.

Библиотека LUFA также предоставляет возможность создания серийного номера для устройства USB. К сожалению, опции для конфигурирования серийного номера для проекта не задаются так же прозрачно, как в библиотеке V-USB. Для того, чтобы разрешить серийный номер, см. содержимое файла LUFA\Drivers\USB\Core\AVR8\Device_AVR8.h. Собственно у Вас есть только возможность запретить использование серийного номера, если Вы зададите макрос NO_INTERNAL_SERIAL в настройках своего проекта.

#if (!defined(NO_INTERNAL_SERIAL) && \
 (defined(USB_SERIES_7_AVR) || defined(USB_SERIES_6_AVR) || \
 defined(USB_SERIES_4_AVR) || defined(USB_SERIES_2_AVR) || \
 defined(__DOXYGEN__)))
   /** Индекс строкового дескриптора для уникального серийного номера устройства, расположенного
 * в устройстве USB. Этот уникальный серийный номер используется хостом для привязки ресурсов
 * к устройству (таких как драйверы, или назначаемые номера для COM-порта) независимо от порта
 * хоста, в который устройство подключено. Некоторые микроконтроллеры содержат внутри себя
 * уникальный серийный номер, и установка индекса строки дескриптора серийного номера в это
 * значение приведет к использованию этого внутреннего серийного номера.
 *
 * На не поддерживающих серийный номер устройствах будет оценено значение макроса \ref NO_DESCRIPTOR,
 * что принудительно отключит серийный номер, так что хост сам будет создавать идентификатор
 * для устройства USB и самостоятельно привязывать к нему ресурсы. Например, для устройств USB CDC
 * номер назначенного COM-порта будет зависеть от того, в какой порт USB хоста Вы подключите свое 
 * устройство USB.
 */
   #define USE_INTERNAL_SERIAL            0xDC
 
   /** Длина уникального внутреннего серийного номера в битах, если он присутствует в выбранной
 * модели микроконтроллера.
 */
   #define INTERNAL_SERIAL_LENGTH_BITS    80
 
   /** Начальный адрес внутреннего серийного номера в соответствующем адресном пространстве, если
 * он присутствует в выбранной модели микроконтроллера.
 */
   #define INTERNAL_SERIAL_START_ADDRESS  0x0E
#else
   #define USE_INTERNAL_SERIAL            NO_DESCRIPTOR
 
   #define INTERNAL_SERIAL_LENGTH_BITS    0
   #define INTERNAL_SERIAL_START_ADDRESS  0
#endif

[Ссылки]

1. AVR922: Adding a Serial Number to a USB Device site:atmel.com.
2. AVR276: USB Software Library for AT90USBxxx Microcontrollers.
3. Библиотека V-USB site:wikipedia.ru.
4. LUFA - бесплатная библиотека USB для микроконтроллеров Atmel AVR.
5. USB консоль для управления радиолюбительскими приборами.