STM32Cube: библиотека устройства USB |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Инициатива STMCube™ была разработана компанией STMicroelectronics (далее просто ST) для упрощения жизни разработчикам в процессе разработки, снижая их усилия, время и цену работ. Библиотека STM32Cube версии 1.x включает: • STM32CubeMX, графическая утилита конфигурирования, которая позволяет генерировать код инициализации на языке C. это делается с помощью графических визардов. • Продвинутая программная платформа, нацеленная на каждую из серий микроконтроллеров (такую как STM32CubeF4 для серии STM32F4). – STM32Cube HAL, слой абстракции встроенного программного обеспечения, гарантирующий максимальное портирование между семействами STM32. Шина USB - самое полезное в истории персональных компьютеров средство для подключения различных устройств, таких как мышь, геймпады, джойстики, сканеры, принтеры, цифровые камеры и т. д. USB также встраивается в большинство потребительской и мобильной электроники. Это руководство (перевод UM1734 [1]) описывает библиотеку устройств USB STM32Cube, которая входит как часть в пакет STM32Cube. Этот пакет можно бесплатно загрузить с сайта ST (http://www.st.com/stm32cube). Здесь описывается, как начать разрабатывать и реализовать приложения устройства USB основных классов (HID, MSC, Audio, CDC...) на базе стека устройства USB, поддерживаемого всеми сериями микроконтроллеров STM32. Примечание: этот документ применим ко всем сериям STM32, которые имеют на борту периферийное устройство USB. Однако для упрощения изложения в качестве образцовой платформы выбрана разновидность микроконтроллеров STM32F4xx и пакет STM32CubeF4. Чтобы получить дополнительную информацию по примерам реализации на Вашем STM32, обратитесь к файлу readme, предоставленному в соответствующем пакете STM32Cube. API Application Programming Interface (программный интерфейс доступа к функциям библиотеки). BOT Bulk Only Transfer, передача только блоками. CBW Command Block Wrapper, блок команды SCSI, который содержит информацию длины команды, направление, логический юнит (LUT), куда отправляется команда, и уникальный идентификатор, который будет возвращен в ответ от устройства, который может использоваться для проверки соответствия ответа запросу хоста. Command Block Wrapper отправляется устройству с использованием первого направления Out конечной точки Block интерфейса перед тем, как любые данные будут отправлены устройству или приняты из него. Затем хосту устройством отправляется финальный CSW, содержащий результаты операции. CDC Communication Device Class. CSW Command Status Wrapper, блок протокола SCSI. DFU Device Firmware Upgrade, класс и протокол для обновления программного обеспечения (firmware) встраиваемого устройства. firmware встраиваемое программное обеспечение микроконтроллера, так называемая "прошивка". FS Full Speed, полная скорость (12 мегабит/сек). HAL Hardware Abstraction Layer, программный уровень библиотек (драйверов) для абстрагирования от версии аппаратуры. HCD Host Controller Driver. HID Human Interface Device. HS High Speed, высокая скорость (480 мегабит/сек). HSI high-speed internal oscillator, внутренний генератор на 8 МГц. I2C популярная последовательная шина для обмена данными в электронной аппаратуре. I2S Inter-IC Sound, последовательный стандартный интерфейс для соединения друг с другом звуковых устройств. Альтернативно вместо I2S используют аббревиатуру IIS. LBA Logical Block Address. LUN Logical Unit, термин протокола SCSI. Mbps Megabit per second, мегабит/сек. MCU MicroController Unit, микроконтроллер. MSC Mass Storage Class, класс устройства хранения данных. Иногда этот класс называют аббревиатурой MSD (Mass Storage Device). MSP MCU Support Package. MUN Max Unit Number, максимальный номер LUN. OTG On-The-Go: периферийное устройство, поддерживающее OTG, может на лету переключаться между ролями HOST/DEVICE. PCD Peripheral Controller Driver, драйвер контроллера периферийного устройства. PID USB Product Identifier, идентификатор продукта устройства USB. PSTN Public Switched Telephone Network, телефонная коммутационная сеть общего пользования. RCC Reset and Clock Control, система управления сбросом и тактированием. SCSI Small Computer System Interface, популярный стандарт шины и одноименный протокол обмена. SOF Start Of Frame, сигнал начала фрейма. VID USB Vendor Identifier, идентификатор производителя устройства USB. USB Universal Serial Bus. ZLP Zero Length Packet, пакет нулевой длины. [Документация от ST] • USB Host library user manual (UM1720), руководство библиотеки реализации хоста USB. [Внешние ссылки] • Universal Serial Bus Specification, Revision 2.0, http: //www.usb.org [Описание библиотеки устройства USB] ST предоставляет своим клиентам новые стеки USB (стек устройства и стек хоста) которые поддерживают все STM32 MCU, вместе с многими инструментами разработки, такими как Atollic® TrueSTUDIO, IAR Embedded Workbench for ARM® и Keil uVision®. Этот документ фокусируется на стеке устройства USB. Для стека хоста обратитесь к соответствующему руководству. Библиотека устройства USB является стандартной для всех микроконтроллеров STM32, и только слой HAL адаптируется для каждого отдельного устройства STM32. Библиотека устройства USB работает поверх HAL-драйвера STM32Cube для USB устройства, и предоставляет все необходимое API для разработки приложения устройства USB. Представленный документ описывает промежуточный модуль библиотеки STM32Cube устройства USB, и иллюстрирует, как пользователь может с помощью этого модуля просто разработать собственное приложение устройства USB. Библиотека устройства USB является частью пакета STM32Cube для каждой серии STM32. Она содержит: • Низкоуровневый драйвер USB. Библиотека устройства USB предоставляет как минимум по одному демонстрационному примеру firmware для каждого типа передач USB: • Human Interface Device HID. Демонстрируется HID-джойстик, основанный на оценочных платах (EVAL board), также есть примеры пользовательского устройства (Custom HID example). Рассматриваются следующие темы: • Архитектура библиотеки устройства USB. Функции библиотеки. Основные функции библиотеки устройства USB: • Поддержка функции многопакетной передачи, что позволяет отправлять большое количество данных без разделения из на пакеты максимально допустимого размера. Замечание: библиотека устройства USB может использоваться как вместе с RTOS, так и отдельно; используется обертка (CMSIS RTOS wrapper) для реализации абстракции с ядром операционной системы. Примеры устройства USB не отображают сообщения лога. Рис. 1. STM32Cube USB device library (библиотека устройства USB). На рисунке выше приложение пользователя показано зеленым цветом, ядро библиотеки USB желтым и драйвер USB Device HAL синим. [Архитектура библиотеки устройства USB] Библиотека устройства USB разделена на три основных слоя. Приложение разрабатывается над самым верхним слоем, как показано на рис. 2. Рис. 2. Архитектура USB device library. На этом рисунке блоки ядра библиотеки USB показаны желтым цветом, конфигурация устройства USB (USB Device Configuration) фиолетовым, и синим показан драйвер USB HAL. Первый слой состоит из драйверов ядра и драйверов класса USB. Драйверы ядра (Core drivers). Ядро библиотеки составлено из 4 основных блоков: – Модуль ядра USB, который предоставляет полный набор API для управления внутренним состоянием библиотеки машины устройства USB и процессами обратных вызовов (callback) из прерываний USB. Драйверы класса. Классы устройств USB представлены набором драйверов, которые могут быть состыкованы с ядром USB через подпрограмму USBD_RegisterClass(). Библиотека устройства USB это стандартный стек, совместимый с USB 2.0 и со всеми ядрами STM32 USB. Он может быть просто состыкован с любым драйвером USB HAL благодаря конфигурационному файлу обертки, который обходит зависимости между библиотекой USB и драйверами низкого уровня. [Слой USB OTG HAL] Рис. 3. Обзор архитектуры драйвера. Архитектура драйвера: • Нижний слой (Low Layer USB driver) предоставляет общее API для режимов устройства и OTG. Он выполняет инициализацию ядра в каждом режиме и управляет потоком передач. Примечание: для дополнительной информации по использованию драйвера PCD пожалуйста обратитесь к документу UM1725, где описаны все вызовы API драйвера PCD. [Программирование драйвера USB] Конфигурирование структуры драйвера, инициализация. Инициализация устройства. Устройство инициализируется использованием следующей функции, содержащейся в файле stm32fxxx_hal_pcd.c: HAL_StatusTypeDef HAL_PCD_Init(PCD_HandleTypeDef *hpcd); Конфигурация конечной точки. Как только ядро USB инициализировано, верхний слой может вызвать драйвер низкого уровня, чтобы открыть или закрыть активную конечную точку с целью перемещения данных. Могут использоваться следующие 2 API-вызова: HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint16_t ep_mps, uint8_t ep_type); HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); Здесь ep_addr, ep_mps и ep_type это адрес конечной точки, максимум передачи данных и тип передачи соответственно. Структура ядра устройства. Основная структура, используемая в библиотеке устройства, это дескриптор устройства (device handle) типа USBD_HandleTypedef: typedef struct _USBD_HandleTypeDef { uint8_t id; uint32_t dev_config; uint32_t dev_default_config; uint32_t dev_config_status; USBD_SpeedTypeDef dev_speed; USBD_EndpointTypeDef ep_in[15]; USBD_EndpointTypeDef ep_out[15]; uint32_t ep0_state; uint32_t ep0_data_len; uint8_t dev_state; uint8_t dev_old_state; uint8_t dev_address; uint8_t dev_connection_status; uint8_t dev_test_mode; uint32_t dev_remote_wakeup; USBD_SetupReqTypedef request; USBD_DescriptorsTypeDef *pDesc; USBD_ClassTypeDef *pClass; void *pClassData; void *pUserData; void *pData; } USBD_HandleTypeDef; Глобальная структура устройства USB содержит все переменные и структуры, чтобы хранить информацию реального времени, относящуюся к устройству, а также для хранения состояний машины управления передачами(control transfer state machine), и информации/статуса конечной точки. В этой структуре dev_config хранит текущую конфигурацию устройства USB, и ep0_state управляет машиной, которая принимает следующие состояния: /* Состояние управляющей конечной точки (EP0 State) */
#define USBD_EP0_IDLE 0
#define USBD_EP0_SETUP 1
#define USBD_EP0_DATA_IN 2
#define USBD_EP0_DATA_OUT 3
#define USBD_EP0_STATUS_IN 4
#define USBD_EP0_STATUS_OUT 5
#define USBD_EP0_STALL 6
В этой структуре dev_state определяет соединение, конфигурацию и состояние питания: /* Состояние устройства */
#define USBD_DEFAULT 1
#define USBD_ADDRESSED 2
#define USBD_CONFIGURED 3
#define USBD_SUSPENDED 4
Спецификация USB определяет 6 состояний устройства USB: Attached: устройство подключено к шине USB, однако не получает питание. Поток перемещения данных USB. Слой PCD предоставляет все API-вызовы, которые требуются для запуска и управления потоком передачи. Это делает следующий набор функций: HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len); HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len); HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); Слой PCD содержит одну функцию, которая должна быть вызвана прерыванием USB: void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd); Файл stm32fxxx_hal_pcd.h содержит прототипы функций, вызываемых из слоя ядра библиотеки, чтобы обработать события USB. Почти все библиотечные функции возвращают состояние типа USBD_StatusTypeDef. Приложение пользователя должно всегда проверять возвращенное состояние. Ниже показано определение этого типа (перечисление значений), и в таблице 3 описаны возможные возвращаемые состояния. typedef enum { USBH_OK = 0, USBH_BUSY, USBH_FAIL, }USBH_StatusTypeDef; Таблица 3. Состояния устройства USB.
[Обзор библиотеки устройства USB] Библиотека устройства USB основана на стандартном драйвере USB низкого уровня. ПО было разработано для поддержки режимов Full speed и High speed. Реализована машина состояний устройства USB, как это было определено спецификацией USB 2.0. Библиотечные функции находятся в файлах папки Core из пакета USB device library firmware package (см. рис. 5). Модули класса USB (папка Class) разработаны для обеспечения совместимости со спецификацией протокола. Рис. 5. Структура каталогов библиотеки устройства USB. Обработка control endpoint. Спецификация USB определяет 4 типа передач: control, interrupt, bulk и isochronous. Хост USB отправляет запросы к устройству через управляющую точку, control endpoint (в этом случае control endpoint является конечной точкой с номером 0). Запросы отправляются в устройство как пакеты SETUP. Эти запросы можно классифицировать на 3 категории: standard, class-specific и vendor-specific. Поскольку стандартные запросы являются общими для всех видов устройств USB, библиотека принимает и обрабатывает все стандартные запросы на control endpoint 0. Формат и смысл специфичных для класса запросов и специфичных для вендора запросов не совпадают для всех устройств USB. Все запросы SETUP обрабатываются в машине состояний, реализованной в модели прерывания. Прерывание генерируется по окончании корректной передачи USB. Код библиотеки принимает это прерывание. В подпрограмме обработки прерывания (ISR) идентифицируется триггер конечной точки. Если событие это настройка (setup) на endpoint 0, то полезная нагрузка принятой настройки сохраняется, и запускается машина состояний. Транзакции на других конечных точках. Ядро, специфическое для класса, использует конечные точки, не относящиеся к конечной точке управления путем вызова набора функций для отправки или приема данных через callback-стадии data IN и data OUT. Структура данных для пакета SETUP. Когда поступает новый пакет SETUP, все 8 байт пакета SETUP копируются во внутреннюю структуру USB_SETUP_REQ req, чтобы во время обработки следующий пакет SETUP не мог перезаписать предыдущий. Эта внутренняя структура определена следующим образом: typedef struct usb_setup_req { uint8_t bmRequest; uint8_t bRequest; uint16_t wValue; uint16_t wIndex; uint16_t wLength; }USBD_SetupReqTypedef; Стандартные запросы. Большинство запросов спецификации USB, показанных в таблице 4, обрабатываются библиотекой как стандартные. Таблица 4 перечисляет все эти стандартные запросы и их допустимые в библиотеке параметры. Запросы, которых нет в таблице 4, считаются нестандартными. Таблица 4. Стандартные запросы USB.
Примечание: в столбце State применены следующие обозначения состояний. D = Default state (состояние по умолчанию), A = Address state (состояние адресации), C = Configured state (состояние "сконфигурировано"). Значение EP: D0-D3 = адрес конечной точки, D4-D6 = эти биты зарезервированы как нули, D7 = 0: OUT endpoint, 1: IN endpoint. Нестандартные запросы. Все нестандартные запросы через callback-функции передаются в код, специфичный для класса. • SETUP stage Библиотека передает все нестандартные запросы в специфичный для класса код через callback-функцию pdev->pClass->Setup (pdev, req). Нестандартные запросы включают интерпретируемые пользователем запросы и неправильные запросы (invalid requests). Интерпретируемые пользователем запросы это запросы, специфические для класса, специфические для вендора или запросы, которые библиотека считает недопустимыми (invalid), так что приложение может захотеть обработать их для себя как допустимые. Invalid-запросы это такие запросы, которые не являются стандартными, и поэтому не могут быть интерпретированы библиотекой. Поскольку pdev->pClass->Setup (pdev, req) вызывается после SETUP stage и перед DATA stage, то код пользователя в pdev->pClass->Setup (pdev, req) отвечает за парсинг содержимого пакета SETUP (req). Если запрос недопустим, то код пользователя должен вызвать USBD_CtlError(pdev, req) и вернуть управление в код, который вызвал pdev->pClass->Setup (pdev, req). Для запроса, интерпретируемого пользователем, код пользователя подготавливает буфер данных для последующего DATA stage, если в запросе есть стадия данных; иначе пользовательский код выполнит запрос и вернет управление в код, который вызывал pdev->pClass->Setup (pdev, req). • DATA stage Слой класса использует стандартные USBD_CtlSendData и USBD_CtlPrepareRx для отправки или приема данных. Поток переноса данных обрабатывается внутренним кодом библиотеки, и пользователю не нужно делить данные в пакеты размера ep_size. • Status stage Стадия статуса обрабатывается библиотекой после возврата из callback-функции pdev->pClass->Setup (pdev, req). Рис. 7. Обработка в библиотеке устройства USB. Примечание: красный текст идентифицирует конфигурацию устройства USB. Как показано на рис. 7, для программирования USB нужны только следующие модули: библиотека USB, класс устройства USB и основное приложение. Главное приложение выполняет программу пользователя. Модули main.c, stm32fxx_it.c, usbd_conf.c и usbd_desc.c вместе с их заголовочными файлами - все, что пользователю нужно для разработки своего собственного приложения. Пользователь может изменить из в соответствии с требованиями своего приложения (драйвер класса). Делаются только простые API-вызовы. Они позволяют установить взаимосвязь между слоем приложения и модулем библиотеки USB, который обрабатывает инициализацию USB и получает текущее состояние USB. Чтобы инициализировать драйвер USB HAL, библиотеку устройства USB и аппаратуру на используемой плате (BSP), и запустить библиотеку, приложение пользователя должно сделать 3 вызова API: • USBD_Init(): эта функция инициализирует стек устройства и загружает драйвер класса. Дескриптор устройства сохраняется в файлах usbd_desc.c и usbd_desc.h (используется для конфигурации типа дескриптора). Например, пользователь может добавить дополнительные конечные точки в файле usbd_conf, в зависимости от требований класса. Это делается вызовом функции USBD_LL_Init(). В переменной dev_endpoints должно содержаться количество требуемых конечных точек по спецификации класса USB. Благодаря файлу usbd_conf.h библиотека устройства USB предоставляет несколько конфигураций (для дополнительной информации см. далее секцию "Конфигурирование firmware библиотеки устройства USB"). Замечание: инициализация библиотеки HAL осуществляется вызовом HAL_Init() API в модуле stm32fxxx_hal.c. Эта функция выполняет следующие операции: - Сбрасывает все периферийные устройства. Поток данных устройства USB. Библиотека USB (USB core и слой USB class) обрабатывают данные на конечной точке 0 (EP0) через слой запроса I/O, когда необходима обертка для управления функцией multi-packet на control endpoint, или напрямую из слоя stm32fxxx_hal_pcd, когда используются другие конечные точки, поскольку ядро USB OTG поддерживает функцию multi-packet. Рис. 8 показывает эту схему потока данных. Рис. 8. Схема потока данных в устройстве (USB device data flow). Интерфейс ядра с драйвером низкого уровня. Как упоминалось ранее, библиотека устройства USB подключается к низкоуровневому драйверу STM32Cube™ HAL с помощью слоя низкоуровневого интерфейса, который действует как слой соединения (link layer) с STM32Cube™ HAL. Этот низкоуровневый интерфейс реализует API-функции низкого уровня и вызывает некоторые callback-функции ядра библиотеки при некоторых событиях USB. В пакете кода STM32Cube™ реализация низкоуровневого интерфейса предоставлена как часть примеров устройства USB, поскольку некоторые части низкоуровневого интерфейса зависят от платы и системы. Таблица 5 перечисляет низкоуровневые функции API. Таблица 5. Описание API низкого уровня.
Примечание: это API предоставлено файлов конфигурации устройства USB (usbd_conf.c). Оно должно быть реализована в файлах пользователя и адаптировано для драйвера контроллера устройства USB (USB Device Controller Driver). Пользователь может начать создавать свой файл конфигурации, беря в качестве примера файл usbd_conf.c из пакета STM32Cube™. Этот файл можно также скопировать в папку приложения и изменить в зависимости от требований приложения. Модель интерфейса библиотеки устройства USB. Библиотека устройства USB построена вокруг стандартных центральных портируемых модулей класса. Рис. 9. Модель интерфейса библиотеки устройства USB. Таблица 6 показывает все callback-функции библиотеки устройства, которые вызываются из низкоуровневого интерфейса в ответ на некоторые события USB. Таблица 6. Callback-функции событий низкого уровня.
Конфигурирование firmware библиотеки устройства USB. The USB device library can be configured using the usbd_conf.h file. Файл usbd_conf.h - специальный конфигурационный файл, используемый для определения некоторых глобальных параметров и специальных конфигураций. Этот файл используется для подключения библиотеке верхнего уровня вместе с драйверами HAL и драйверами BSP. Таблица 7. Конфигурация библиотеки USB.
Примечание: пользователь может начать работу с файла usbd_conf.c, предоставленного в пакете STM32Cube™. Этот файл также может быть скопирован в папку разрабатываемого приложения, и изменен в зависимости от нужд приложения. По умолчанию для примеров устройств USB библиотечные и пользовательские сообщения не отображаются на LCD. Однако пользователь может реализовать свои собственные сообщения. Для перенаправления библиотечных сообщений на экран LCD к фалам исходного кода приложения должен быть добавлен драйвер lcd_log.c. Он может выбрать, какие сообщения отображать, путем изменения значений #define в конфигурационном файле usbd_conf.h, который должен находиться среди подключаемых файлов проекта. Например: 0: не выводить сообщения лога/отладки. Функции управления USB Device reset. Когда устройство получает сигнал сброса от шины USB, библиотека сбрасывает и инициализирует и программное обеспечение, и аппаратуру приложения. Эта функция - часть подпрограммы обработки прерывания. Device suspend. Когда устройство определяет состояние (suspend condition) приостановки на шине USB, библиотека останавливает все происходящие операции и переводит систему в состояние приостановки, suspend state (если разрешен режим пониженного энергопотребления в файле usbd_conf.c). Device resume. Когда устройство определяет сигнал возобновления работы (resume) на шине USB, библиотека восстанавливает тактирование ядра USB и переводит систему в состояние ожидания, idle state (если разрешен режим пониженного энергопотребления в файле usbd_conf.c). [Функции библиотеки устройства USB] В папке Core содержатся машины библиотеки устройства USB, как это определено в спецификации USB ревизии 2.0. Таблица 8. Файлы ядра устройства USB.
Папка Class содержит все файлы, относящиеся к реализации класса, и они соответствуют спецификации протокола этих классов. Таблица 9. Файлы драйверов классов устройств USB.
Ниже приведено краткое описание назначения функций в различных модулях библиотеки. Таблица 10. usbd_core.c, usbd_core.h.
Прототипы функций таблицы 10: USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id); USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass); USBD_StatusTypeDef USBD_Start (USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_Stop (USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup); USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev, uint8_t epnum, uint8_t *pdata); USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef *pdev, USBD_SpeedTypeDef speed); USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev); Таблица 11. usbd_ioreq.c, usbd_ioreq.h.
Прототипы функций таблицы 11: USBD_StatusTypeDef USBD_CtlSendData (USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint16_t len); USBD_StatusTypeDef USBD_CtlContinueSendData (USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint16_t len); USBD_StatusTypeDef USBD_CtlPrepareRx (USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint16_t len); USBD_StatusTypeDef USBD_CtlContinueRx (USBD_HandleTypeDef *pdev, uint8_t *pbuf, uint16_t len); USBD_StatusTypeDef USBD_CtlSendStatus (USBD_HandleTypeDef *pdev); USBD_StatusTypeDef USBD_CtlReceiveStatus (USBD_HandleTypeDef *pdev); uint16_t USBD_GetRxCount (USBD_HandleTypeDef *pdev, uint8_t ep_addr); Таблица 12. usbd_ctrlq.c, usbd_ctrlq.h.
Прототипы функций таблицы 12: USBD_StatusTypeDef USBD_StdDevReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); USBD_StatusTypeDef USBD_StdItfReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); USBD_StatusTypeDef USBD_StdEPReq (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_GetDescriptor(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_SetAddress (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_SetConfig (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_GetConfig (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_GetStatus (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_SetFeature (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void USBD_ClrFeature (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); void USBD_CtlError (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); void USBD_GetString (uint8_t *desc, uint8_t *unicode, uint16_t *len); static uint8_t USBD_GetLen (uint8_t *buf); void USBD_ParseSetupRequest (USBD_SetupReqTypedef *req, uint8_t *pdata); [Интерфейс класса устройства USB] Структура USB Class callback. Класс USB выбирается при инициализации библиотеки устройства USB путем выбора соответствующей callback-структуры класса. Эта структура класса определена следующим образом: typedef struct _Device_cb { uint8_t (*Init) (struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); uint8_t (*DeInit) (struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx); /* Обработка управляющей конечной точки (Control Endpoint, EP0) */ uint8_t (*Setup) (struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); uint8_t (*EP0_TxSent) (struct _USBD_HandleTypeDef *pdev); uint8_t (*EP0_RxReady) (struct _USBD_HandleTypeDef *pdev); /* Конечные точки, специфичные для класса устройства USB */ uint8_t (*DataIn) (struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*DataOut) (struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*SOF) (struct _USBD_HandleTypeDef *pdev); uint8_t (*IsoINIncomplete) (struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t (*IsoOUTIncomplete) (struct _USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t *(*GetHSConfigDescriptor)(uint16_t *length); uint8_t *(*GetFSConfigDescriptor)(uint16_t *length); uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length); uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length); } USBD_ClassTypeDef; В этой структуре определены указатели на следующие callback-функции: Init: вызывается, когда устройство принимает USB-запрос установки конфигурации; в этой функции открываются конечные точки, используемые в интерфейсе класса. Структура дескрипторов устройства USB. Библиотека также предоставляет callback-структуру дескрипторов, что позволяет управлять устройством и строковыми дескрипторами во время работы приложения (run time). Эта структура дескрипторов определена следующим образом: typedef struct { uint8_t *(*GetDeviceDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetLangIDStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetManufacturerStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetProductStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetSerialStrDescriptor)(USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetConfigurationStrDescriptor)( USBD_SpeedTypeDef speed, uint16_t *length); uint8_t *(*GetInterfaceStrDescriptor)( USBD_SpeedTypeDef speed, uint16_t *length); } USBD_DescriptorsTypeDef; GetDeviceDescriptor: этот callback вернет дескриптор устройства. Примечание: в файле usbd_desc.c, предоставляемом с примерами устройства USB, реализованы тела этих callback-функций. [Модуль класса библиотеки устройства USB] Модуль класса содержит все файлы, относящиеся к реализации какого-либо класса устройства USB. Они соответствуют спецификации протокола, встроенного в эти классы. Таблица 13 показывает файлы класса устройства USB для классов MSC, HID, DFU, Audio, CDC. Таблица 13. Файлы классов устройства USB.
[Класс USB HID] Реализация класса HID. Этот модуль содержит HID class V1.11, в соответствии со спецификацией "Device Class Definition for Human Interface Devices (HID) Version 1.11 June 27, 2001". Примечание: спецификацию HID можно найти поиском по ключевому слову hidpage на сайте www.st.com. Этот драйвер реализует следующие аспекты спецификации: • Подкласс интерфейса загрузки (boot interface subclass). Пользовательский интерфейс HID. Входные репорты (input reports) отправляются через канал прерывания IN (Interrupt In pipe, см. пример HID mouse). Репорты Feature и Output должны инициироваться хостом через управляющий канал (Control pipe) или канал прерывания OUT (Interrupt Out pipe, см. пример Custom HID). Примечание: направления IN и OUT обозначаются по отношению к хосту USB (хост это обычно компьютер или смартфон). Таким образом, канал IN это канал для данных от устройства USB к хосту, а канал OUT это канал для данных от хоста к устройству USB. Приложением HID mouse для отправки HID-репортов может использоваться функция USBD_HID_SendReport. В этом релизе драйвер HID обрабатывает только трафик IN. Ниже показан пример использования этой функции: static __IO uint32_t counter=0; HAL_IncTick();/* Проверка состояния джойстика каждые 10 мс */if (counter++ == 10) { GetPointerData(HID_Buffer); /* Отправка данных через конечную точку IN */ if((HID_Buffer[1] != 0) || (HID_Buffer[2] != 0)) { USBD_HID_SendReport(&USBD_Device, HID_Buffer, 4); } counter =0; } Toggle_Leds(); HID Class Driver API. Ниже в таблице суммарно показаны API-функции драйвера HID-класса, определенные в модуле usbd_hid.c. Таблица 14. Функции в файлах usbd_hid.c, usbd_hid.h.
Прототипы функций таблицы 14: static uint8_t USBD_HID_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_HID_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); uint8_t USBD_HID_SendReport (USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len); Стек HID инициализируется вызовом USBD_HID_Init(), затем приложение должно вызывать функцию USBD_HID_SendReport() для отправки репортов HID. Через конечную точку 0 (EP0, Control, конечная точка управления) реализованы следующие запросы, специфичные для HID: #define HID_REQ_SET_PROTOCOL 0x0B
#define HID_REQ_GET_PROTOCOL 0x03
#define HID_REQ_SET_IDLE 0x0A
#define HID_REQ_GET_IDLE 0x02
#define HID_REQ_SET_REPORT 0x09
#define HID_REQ_GET_REPORT 0x01
Адрес конечной точки IN и максимальное количество байт, которое можно отправить, задаются определениями: #define HID_EPIN_ADDR 0x81
#define HID_EPIN_SIZE 0x04
[Класс Mass Storage (USB MSC)] Реализация класса Mass Storage. Этот модуль обслуживает MSC class V1.0 в соответствии со спецификацией "Universal Serial Bus Mass Storage Class (MSC) Bulk-Only Transport (BOT) Version 1.0 Sep. 31, 1999". Драйвер реализует следующие аспекты спецификации: • Протокол транспорта Bulk-only (только пакетные передачи). Класс USB mass storage построен вокруг Bulk Only Transfer (BOT). Он использует прозрачный набор команд SCSI. Стандартная BOT-транзакция основана на простой машине состояний. Она начинается с ready state (idle state). Если от хоста принят CBW, могут обслуживается 3 случая: • DATA-OUT-STAGE: когда флаг направления установлен в "0", устройство должно быть подготовлено к приему количества данных, показанного полем cbw.dDataLength блока CBW. По окончанию передачи данных возвращается CSW с оставшейся длиной данных и поле STATUS. Рис. 13. Архитектура протокола BOT. В следующей таблице показаны поддерживаемые команды SCSI. Таблица 15. Команды SCSI.
Реализован запрос сброса устройства хранения (Bulk-only mass storage reset request, это запрос, специфический для класса), как того требует спецификация BOT. Это запрос используется для сброса устройства хранения (mass storage device) и связанного с ним интерфейса. Этот специфический для класса запрос должен подготовить устройство для поступления от хоста следующего CBW. Чтобы генерировать BOT Mass Storage Reset, хост должен отправить запрос устройству на канале по умолчанию (default pipe): • bmRequestType: тип запроса Class, interface, от хоста к устройству Get Max MUN (специфический для класса запрос). Устройство может реализовать несколько логических юнитов (LUN), которые используют общие характеристики устройства. Хост использует bCBWLUN, чтобы показать, какой LUN устройства является пунктом назначения для CBW. Запрос к устройству Get Max LUN используется для того, чтобы определить количество LUN, поддерживаемых устройством. Для генерации запроса Get Max LUN для устройства, хост отправляет запрос на канале по умолчанию (default pipe): • bmRequestType: тип запроса Class, Interface, от устройства к хосту Файлы ядра MSC. Ниже в таблицах показаны модули класса устройства хранения и их функции с кратким описанием. Таблица 16. Функции в файлах usbd_msc.c, usbd_msc.h.
Прототипы функций таблицы 16: uint8_t USBD_MSC_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx); uint8_t USBD_MSC_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx); uint8_t USBD_MSC_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); uint8_t USBD_MSC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t USBD_MSC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum); Таблица 17. Функции в файлах usbd_msc_bot.c, usbd_msc_bot.h.
Прототипы функций таблицы 17: void MSC_BOT_Init (USBD_HandleTypeDef *pdev); void MSC_BOT_Reset (USBD_HandleTypeDef *pdev); void MSC_BOT_DeInit (USBD_HandleTypeDef *pdev); void MSC_BOT_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); void MSC_BOT_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum); static void MSC_BOT_CBW_Decode (USBD_HandleTypeDef *pdev); static void MSC_BOT_SendData (USBD_HandleTypeDef *pdev, uint8_t* buf, uint16_t len); void MSC_BOT_SendCSW (USBD_HandleTypeDef *pdev, uint8_t CSW_Status); static void MSC_BOT_Abort (USBD_HandleTypeDef *pdev); void MSC_BOT_CplClrFeature (USBD_HandleTypeDef *pdev, uint8_t epnum); Таблица 18. Функции в файлах usbd_msc_scsi.c, usbd_msc_scsi.h.
Прототипы функций таблицы 18: int8_t SCSI_ProcessCmd (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_TestUnitReady (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_Inquiry (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_ReadCapacity10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_ReadFormatCapacity (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_ModeSense6 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_ModeSense10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_RequestSense (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); void SCSI_SenseCode (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t sKey, uint8_t ASC); static int8_t SCSI_StartStopUnit (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_Read10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_Write10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_Verify10 (USBD_HandleTypeDef *pdev, uint8_t lun, uint8_t *params); static int8_t SCSI_CheckAddressRange (USBD_HandleTypeDef *pdev, uint8_t lun, uint32_t blk_offset, uint16_t blk_nbr); static int8_t SCSI_ProcessRead (USBD_HandleTypeDef *pdev, uint8_t lun); static int8_t SCSI_ProcessWrite (USBD_HandleTypeDef *pdev, uint8_t lun); Структура для операций диска. USBD_StorageTypeDef USBD_DISK_fops = {
STORAGE_Init,
STORAGE_GetCapacity,
STORAGE_IsReady,
STORAGE_IsWriteProtected,
STORAGE_Read,
STORAGE_Write,
STORAGE_GetMaxLun,
STORAGE_Inquirydata,
};
Примечание: MicroSD - интерфейс носителя памяти по умолчанию (default media interface), предоставленный в библиотеке. Однако Вы можете добавить другие носители данных (память Flash, SDRAM, и т. п.), используя файл шаблона usbd_msc_storage_template.c. Функция callback хранилища для класса MSC добавляется в приложение пользователя путем вызова USBD_MSC_RegisterStorage(&USBD_Device, &USBD_DISK_fops). Данные стандартного запроса предоставляются пользователем в массиве STORAGE_Inquiry. Он должен быть определен следующим образом: int8_t STORAGE_Inquirydata[] = { /* 36 */ /* LUN 0 */ 0x00, 0x80, 0x02, 0x02, (STANDARD_INQUIRY_DATA_LEN - 5), 0x00, 0x00, 0x00, 'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Производитель: 8 байт */ 'P', 'r', 'o', 'd', 'u', 'c', 't', ' ', /* Продукт: 16 байт */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0', '.', '0','1', /* Версия: 4 байта */ }; Таблица 19. Функции дисковых операций.
Прототипы функций таблицы 19: int8_t STORAGE_Init (uint8_t lun); int8_t STORAGE_GetCapacity (uint8_t lun, uint32_t *block_num, uint16_t *block_size); int8_t STORAGE_IsReady (uint8_t lun); int8_t STORAGE_IsWriteProtected (uint8_t lun); int8_t STORAGE_Read (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); int8_t STORAGE_Write (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len); int8_t STORAGE_GetMaxLun (void); [Класс обновления прошивки (DFU)] Ядро класса DFU обслуживает протокол в соответствии со стандартом "Device Class Specification for Device Firmware Upgrade Version 1.1 Aug 5, 2004". Реализованы следующие аспекты спецификации: • Обслуживание дескриптора устройства. Примечание: разновидность протокола DFU от компании ST (ST DFU sub-protocol) совместима с протоколом DFU. Она использует sub-запросы для обслуживания адресации памяти, обработки команд, специфических операций с памятью (стирание памяти, чтение, запись, и т. д.). Как требует спецификация DFU, в этом приложении используется только конечная точка 0 (EP0, endpoint 0, или конечная точка управления). В приложение могут быть добавлены другие конечные точки и функции (HID, и т. п.). Эти аспекты могут быть расширены или изменены для определенного пользовательского приложения. Драйвер USB не реализует режим Manifestation Tolerant, определенный в спецификации. Однако есть возможность обслуживать эту функцию путем модификации драйвера. Реализация класса DFU. Транзакции DFU основаны на endpoint 0 (EP0, управляющая конечная точка). Все запросы и управление состоянием отправляются/принимаются через EP0. Машина DFU основывается на следующих состояниях: Таблица 20. Состояния DFU.
Разрешенные переходы между состояниями описаны в документе спецификации DFU. Рис. 16. Диаграмма перехода состояний интерфейса DFU. Чтобы защитить приложение от случайных доступов перед инициализацией, начальное состояние ядра DFU (после запуска) будет dfuERROR. Тогда хост должен очистить это состояние путем отправки запроса DFU_CLRSTATE перед генерацией любого другого запроса. Ядро DFU обслуживает все поддерживаемые запросы (см. таблицу 21). Таблица 21. Поддерживаемые запросы DFU.
Каждая транзакция к EP0 одной из двух категорий. • Data transfer: эти транзакции используются для: – получения некоторых данных из устройства (DFU_GETSTATUS, DFU_GETSTATE и DFU_UPLOAD). • No-Data transfer: эти транзакции используются для отправки запросов управления от хоста к устройству (DFU_CLRSTATUS, DFU_ABORT и DFU_DETACH). Файлы ядра класса DFU. Реализация этого драйвера находится в модуле usbd_dfu.c и его заголовочном файле usbd_dfu.h. Здесь обслуживаются все запросы DFU и машина состояний DFU. Модуль не работает напрямую с носителем памяти (это осуществляется драйверами низкого уровня). Таблица 22. Функции в файлах usbd_dfu.c, usbd_dfu.h.
Примечание: внутренняя память Flash это память по умолчанию, которая обслуживается библиотекой. Однако Вы можете добавить другие носители памяти, используя файл шаблона usbd_dfu_media_template.c. Прототипы функций таблицы 22: static uint8_t USBD_DFU_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_DFU_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_DFU_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t USBD_DFU_EP0_TxReady (USBD_HandleTypeDef *pdev); static uint8_t USBD_DFU_EP0_RxReady (USBD_HandleTypeDef *pdev); static uint8_t* USBD_DFU_GetUsrStringDesc (USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length); static void DFU_Detach (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void DFU_Download (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void DFU_Upload (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void DFU_GetStatus (USBD_HandleTypeDef *pdev); static void DFU_ClearStatus (USBD_HandleTypeDef *pdev); static void DFU_GetState (USBD_HandleTypeDef *pdev); static void DFU_Abort (USBD_HandleTypeDef *pdev); static void DFU_Leave (USBD_HandleTypeDef *pdev); Чтобы использовать драйвер DFU: 1. Задайте в файле usbd_conf.h следующие параметры: – количество поддерживаемых носителей (видов памяти) в #define USBD_DFU_MAX_ITF_NUM. 2. Вызовите функцию USBD_DFU_Init() для инициализации всех интерфейсов памяти и машины состояний DFU. 3. Все операции управления/запроса (control/request) выполняются через EP0 с помощью функций USBD_DFU_Setup() и USBD_DFU_EP0_TxReady(). Эти функции можно использовать для вызова callback каждой памяти (read / write /erase / get state, ...), в зависимости от сгенерированных запросов DFU. Для этих операций от пользователя не требуется никаких действий. 4. Чтобы закрыть коммуникацию, вызовите функцию USBD_DFU_DeInit(). Примечание: когда запускается приложение DFU, то по умолчанию состояние его машины DFU_STATE_ERROR. Это состояние установлено, чтобы защитить приложение от случайных операций перед тем, как будет установлена корректная конфигурация устройства. [Класс USB Audio] Драйвер реализует Audio Class 1.0 в соответствии со стандартом "USB Device Class Definition for Audio Devices V1.0 Mar 18, 98". Реализованы следующие аспекты спецификации: • Обслуживание дескриптора устройства. Примечание: Audio Class 1.0 основан на USB 1.0, и поэтому поддерживаются только режимы скоростей Low Speed и Full Speed (транзакции High Speed не поддерживаются). За подробностями обращайтесь к стандарту "USB Device Class Definition for Audio Devices V1.0 Mar 18, 98". Перечисленные аспекты могут быть расширены или модифицированы для специфичного приложения пользователя. В этом драйвере не реализованы следующие аспекты спецификации (однако их можно обслуживать путем некоторых модификаций драйвера): • Управление Audio Control endpoint. Реализация класса Audio. Транзакции звука основаны на isochronous endpoint. Запросы управления (Audio control request) также обслуживаются через control endpoint (endpoint 0). В каждом фрейме передается пакет данных звука, и он должен быть обработан (преобразован в звук) в течение этого фрейма (перед поступления следующего фрейма). Качество звука зависит от синхронизации между транзакциями данных и воспроизведением данных в качестве звука. Этот драйвер реализует простой механизм синхронизации процессов передачи и воспроизведения, полагаясь на точность передаваемой тактовой частоты I2S. В каждом начале фрейма драйвер проверяет, были ли израсходованы данные предыдущего пакета, обрывает запрос передачи, если предыдущие данные все еще воспроизводятся. Для предотвращения перезаписи данных используются 2 основных механизма защиты: • DMA для перемещения данных между буфером USB и регистрами устройства вывода (I2S). На основе этого механизма, если точность тактов или частота воспроизведения недостаточно высока, то получится плохое качество звука. Этот механизм может быть улучшен путем реализации более гибкого управления потоком звуковых данных наподобие режима USB feedback, динамической коррекции тактирования звука или генерации/управления тактами звука на основе события начала фрейма (SOF event). Этот драйвер также поддерживает базовые запросы управления звуком (Audio Control). Чтобы упростить драйвер, реализована обработка только двух запросов. Однако другие запросы могут быть поддержаны путем незначительной модификации драйвера. Таблица 23. Запросы Audio control.
Файлы класса Audio. Основной код драйвера реализован в модуле usbd_audio_core.c и его заголовочном файле usbd_audio_core.h. Он управляет транзакциями audio-данных и запросами управления (control request). Драйвер не работает напрямую с аппаратурой звука (это обслуживается низкоуровневыми драйверами). Таблица 24. Функции в файлах usbd_audio_core.c, usbd_audio_core.h.
Прототипы функций таблицы 24: static uint8_t USBD_AUDIO_EP0_RxReady (USBD_HandleTypeDef *pdev); static uint8_t USBD_AUDIO_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); static uint8_t USBD_AUDIO_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum); static uint8_t USBD_AUDIO_SOF (USBD_HandleTypeDef *pdev); static void AUDIO_REQ_GetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static void AUDIO_REQ_SetCurrent(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); Интерфейс низкоуровневого слоя аппаратуры обслуживается через его соответствующую структуру драйвера: typedef struct { int8_t (*Init) (uint32_t AudioFreq, uint32_t Volume, uint32_t options); int8_t (*DeInit) (uint32_t options); int8_t (*AudioCmd) (uint8_t* pbuf, uint32_t size, uint8_t cmd); int8_t (*VolumeCtl) (uint8_t vol); int8_t (*MuteCtl) (uint8_t cmd); int8_t (*PeriodicTC) (uint8_t cmd); int8_t (*GetState) (void); }USBD_AUDIO_ItfTypeDef; Каждый звуковой аппаратный интерфейс драйвер должен предоставить указатель на структуру типа USBD_AUDIO_ItfTypeDef. Функции и константы, на которые указывает эта структура, перечислены в следующих секциях. Если какая-то функциональность не поддерживается в имеющемся интерфейсе памяти, то соответствующее поле устанавливается в значение NULL. usbd_audio_if.c, usbd_audio_if.h. Этот драйвер на низком уровне управляет аппаратурой звука - интерфейсом Audio Out (обслуживание преобразования данных USB в звук динамика / наушников). Пользователь может вызвать низкоуровневый драйвер кодека (Codec driver, например stm324xg_eval_audio.c, stm324xg_eval_audio.h) для базовых операций со звуком (play / pause / volume control, ...). Этот драйвер предоставляет указатель на структуру: extern USBD_AUDIO_ItfTypeDef USBD_AUDIO_fops;
Таблица 25. Функции usbd_audio_if.c, usbd_audio_if.h.
Примечание: файлы шаблона (usbd_audio_if_template.c, usbd_audio_if_template.h) предоставляют базовый пример драйвера, который позволит Вам реализовать дополнительные функции для своего приложения Audio. Прототипы функций таблицы 25: static int8_t Audio_Init (uint32_t AudioFreq, uint32_t Volume, uint32_t options); static int8_t Audio_DeInit (uint32_t options); static int8_t Audio_PlaybackCmd (uint8_t* pbuf, uint32_t size, uint8_t cmd); static int8_t Audio_VolumeCtl (uint8_t vol); static int8_t Audio_MuteCtl (uint8_t cmd); static int8_t Audio_PeriodicTC (uint8_t cmd); static int8_t Audio_GetState (void); Обслуживаются состояния приложения Audio player, показанные в таблице ниже. Таблица 26. Состояния Audio player.
Как использовать драйвер USB Audio. Этот драйвер использует слой абстракции от драйвера аппаратуры (например аппаратный кодек, интерфейс I2S, интерфейс управления через I2C, и т. п.). Эта абстракция выполняется через модуль нижнего слоя (например usbd_audio_if.c), который Вы можете модифицировать в зависимости от аппаратуры, доступной для приложения. Для использования драйвера выполните следующие действия: 1. Сконфигурируйте в файле usbd_conf.h необходимую частоту выборок звука, audio sampling rate (#define USBD_AUDIO_FREQ). 2. При запуске приложения вызовите функцию USBD_AUDIO_Init() для конфигурирования всех необходимых программных и аппаратных компонентов (функции конфигурирования аппаратуры, специфичные для приложения, также вызываются из этой функции). Аппаратные компоненты управляются через низкоуровневый интерфейс (например usbd_audio_if.c), и это может быть изменено пользователем в зависимости от требований приложения. 3. Транзакция полностью обслуживается функциями (пользователю не нужно вызывать никакую функцию для out-транзакций) usbd_audio_DataIn() и usbd_audio_DataOut(), которые обновляют буферы звука принятыми или переданными данными. Для Out-транзакций, когда принимаются данные, они напрямую копируются в звуковой буфер, и инкрементируется указатель буфера записи (wr_ptr). 4. Запросы управления Audio Control обслуживаются функциями USBD_AUDIO_Setup() и USBD_AUDIO_EP0_RxReady(). Эти функции перенаправляют запросы Audio Control на нижний уровень (например, usbd_audio_if.c). В текущей версии драйвера поддерживаются только запросы SET_CUR и GET_CUR для управления функцией выключения звука (mute control). Известные ограничения: • Если сконфигурирована низкая частота выборок (#define USBD_AUDIO_FREQ ниже 24 кГц), то могут быть помехи в звуке при операциях pause / resume / stop. Причина в интервале времени между остановкой тактов I2S и отправкой команды mute внешнему кодеку (external Codec). [Класс USB CDC] Драйвер класса коммуникационного устройства USB (Communication device class, CDC) работает в соответствии со стандартом "Universal Serial Bus Class Definitions for Communications Devices Revision 1.2 November 16, 2007" и sub-протоколом "Universal Serial Bus Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007". Этот драйвер реализует следующие аспекты спецификации: • Обслуживание дескриптора устройства. Эти аспекты могут быть расширены и улучшены для специфического приложения пользователя. Примечание: для поддержки Abstract Control Model эта реализация только передает запросы на диспетчер нижнего уровня (например usbd_cdc_vcp.c/usbd_cdc_vcp.h), которые должен обслуживать каждый запрос и выполняет соответствующие действия. Этот драйвер не реализует следующие аспекты спецификации (однако можно обслужить эти функции путем некоторых модификаций кода драйвера): • Любой специфический для класса аспект, относящийся к коммуникационному классу, должен обрабатываться приложением пользователя. Ядро CDC использует два типа конечных точек/транзакций: • Конечные точки Bulk endpoint для передач данных (1 OUT endpoint и 1 IN endpoint). Передачи данных для транзакций IN и OUT обслуживаются по-разному. Обслуживание Data IN transfer (от устройства к хосту). Транзакция данных обслуживается периодически, в зависимости от запроса хоста (устройство задает интервал между пакетами запроса). По этой причине для сохранения отправляемых в терминал данных используется статический буфер (например USART в случае терминала Virtual COM Port). Обслуживание Data OUT transfer (от хоста к устройству). Обычно канал передачи данных USB намного быстрее, чем вывод терминала (например, максимальная скорость бит для USART составляет 115.2 Kbps, в то время как скорость USB составляет 12 Mbps для режима Full Speed и 480 Mbps в режиме High Speed). Соответственно перед отправкой новых пакетов хост ждет, пока устройство не завершит обработку данных, которые ранее передал хост. Таким образом, не требуется кольцевой буфер, когда данные принимаются от хоста: драйвер вызывает функцию низкого уровня для OUT транзакции и ждет, пока эта функция не завершится, перед тем, как будут разрешены новые транзакции на OUT endpoint (при этом пакеты OUT будут отклоняться, NACK). Обслуживание Command request. В этом драйвере управляющая конечная точка (control endpoint, endpoint 0, EP0) используется для обслуживания запросов управления. Однако для обслуживания команд также может использоваться data interrupt endpoint. Если размер данных запроса не превышает 64 байт, то размер endpoint 0 достаточен для обслуживания этих запросов. Драйвер CDC не делает парсинг командных запросов. Вместо этого он вызывает функцию низкоуровневого драйвера управления с кодом запроса, длиной и буфером данных. Тогда эта функция должна обработать запрос, и выполнить соответствующие требуемые действия. Файлы драйвера USB CDC. Ядро драйвера находится в файлах usbd_cdc.c, usbd_cdc.h. Этот код обслуживает транзакции данных и запросы управления. Он не работает напрямую с аппаратурой CDC (это обслуживается драйверами низкого уровня). Таблица 27. Функции в файлах usbd_cdc.c, usbd_cdc.h.
Прототипы функций таблицы 27: static uint8_t USBD_CDC_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_CDC_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx); static uint8_t USBD_CDC_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev); static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum); static uint8_t USBD_CDC_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum); uint8_t USBD_CDC_RegisterInterface (USBD_HandleTypeDef *pdev, USBD_CDC_ItfTypeDef *fops); uint8_t USBD_CDC_SetTxBuffer (USBD_HandleTypeDef *pdev, uint8_t *pbuff, uint16_t length); uint8_t USBD_CDC_SetRxBuffer (USBD_HandleTypeDef *pdev, uint8_t *pbuff); uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev); uint8_t USBD_CDC_ReceivePacket (USBD_HandleTypeDef *pdev); Низкоуровневые аппаратные интерфейсы обслуживаются через соответствующую структуру драйвера, где поля представляют указатели на callback-функции: typedef struct _USBD_CDC_Itf { int8_t (* Init) (void); int8_t (* DeInit) (void); int8_t (* Control) (uint8_t, uint8_t *, uint16_t); int8_t (* Receive) (uint8_t *, uint32_t *); }USBD_CDC_ItfTypeDef; Каждый драйвер аппаратного интерфейса должен предоставить указатель на структуру типа USBD_CDC_ItfTypeDef. Функции, на которые указывают поля в этой структуры, перечислены в последующих секциях. Если какая-то функция не поддерживается, то соответствующее поле должно быть установлено в значение NULL. Замечание: чтобы достичь самой высокой производительности, рекомендуется вычислить значения, необходимые для следующих параметров (все они конфигурируются в файлах usbd_cdc.h и usbd_cdc_interface.h), см. таблицу 28. Таблица 28. Конфигурируемые параметры CDC.
usbd_cdc_interface.c, usbd_cdc_interface.h. Этот драйвер может быть частью приложения пользователя. Он не предоставлен в библиотеке, однако можно использовать шаблон (usbd_cdc_if_template.c, usbd_cdc_if_template.h), чтобы создать собственный драйвер, и предоставлен пример для интерфейса USART. Он обслуживает нижний слой аппаратуры CDC. Драйвер usbd_cdc_interface.c / usbd_cdc_interface.c.h управляет интерфейсом терминала и обменом данными (например конфигурацией интерфейса USART и передачей / приемом данных). Этот драйвер предоставляет указатель на структуру: USBD_CDC_ItfTypeDef USBD_CDC_fops =
{
CDC_Itf_Init,
CDC_Itf_DeInit,
CDC_Itf_Control,
CDC_Itf_Receive
};
Таблица 29. Функции usbd_cdc_interface.c, usbd_cdc_interface.h.
Прототипы функций таблицы 29: static int8_t CDC_Itf_Init (void); static int8_t CDC_Itf_DeInit (void); static int8_t CDC_Itf_Control (uint8_t cmd, uint8_t* pbuf, uint16_t length); static int8_t CDC_Itf_Receive (uint8_t* pbuf, uint32_t *Len); Чтобы ускорить обслуживание данных для транзакций IN/OUT, низкоуровневый драйвер использует следующие глобальные переменные (находятся в usbd_cdc_interface.c / usbd_cdc_interface.h): Таблица 30. Переменные, используемые в usbd_cdc_xxx_if.c, / usbd_cdc_xxx_if.h.
Как использовать драйвер USB CDC. В драйвере применен уровень абстракции для низкоуровневого драйвера аппаратуры CDC (например, интерфейс управления USART). Эта абстракция низкого уровня (например stm32fxxx_hal_msp.c) позволяет Вам модифицировать адаптировать приложение к имеющейся аппаратуре. Чтобы использовать драйвер, выполните следующее. 1. В файлах usbd_cdc.h и usbd_cdc_interface.h сконфигурируйте параметры: – размеры пакетов Data IN, OUT и команд (определения #define CDC_DATA_XX_IN_PACKET_SIZE, CDC_DATA_XX_OUT_PACKET_SIZE). Здесь вместо "XX" надо подставить FS или HS, в зависимости от режима скорости Fast Speed или High Speed. 2. Вызовите функцию USBD_CDC_Init() при запуске приложения, чтобы сконфигурировать все необходимые программные и аппаратные компоненты (эта функция также вызовет зависящие от приложения функции конфигурации оборудования). Компоненты аппаратуры управляются низкоуровневым интерфейсом (например usbd_cdc_interface.c) и это может быть изменено пользователем в зависимости от требований приложения. 3. CDC-транзакции данных IN и OUT управляются двумя функциями: – USBD_CDC_SetTxBuffer должна быть вызвана из приложения пользователя каждый раз, когда имеются данные для отправки хосту USB. 4. Запросы управления (CDC control requests) должны быть обработаны функцией Controllability(). Эта функция вызывается каждый раз, когда принят запрос от хоста, и доступны все соответствующие данные, если таковые имеются. Эта функция должна парсить запрос, и выполнять требуемые запросом действия. 5. Чтобы закрыть обмен, вызовите функцию USBD_CDC_DeInit(). Она закрывает используемые конечные точки, и вызывает низкоуровневые функции, отменяющие сделанные ранее инициализации. Известные ограничения CDC. Когда этот драйвер используется с ядром OTG HS, разрешение режима DMA (#define USB_OTG_HS_INTERNAL_DMA_ENABLED в файле usb_conf.h) приведет к тому, что количество отправляемых данных всегда будет нацело делиться на 4. Причина в том, что фактически USB DMA не позволяет передавать данные, не выровненные по адресу на границу слова. Для этого специфичного приложения не рекомендуется разрешать эту опцию без специальной необходимости. [Добавление пользовательского класса USB] В этой секции описывается, как создать новый пользовательский класс USB (custom class), основанный на существующем классе USB. Чтобы создать новый custom Class, выполните следующие шаги: 1. Добавьте USBD_CustomClass_cb (чтобы принимать различные события шины USB) как это описано выше в разделе "Интерфейс класса устройства USB", в usbd_template.c / usbd_template.h, находящемся в каталоге Class/Template. Этот шаблон содержит все функции, которые должны быть адаптированы к потребностям приложения, и он может использоваться для реализации любого типа класса устройства USB. 2. Настройте дескрипторы. Эти дескрипторы запрашивает хост, и они должны быть сконфигурированы, чтобы описать устройство, в зависимости от спецификации класса устройства приложения. Следующий список не полный, однако дает обзор различных дескрипторов, которые могут потребоваться: – стандартный дескриптор устройства 3. Программа должна сконфигурировать STM32, чтобы разрешить транзакции USB (isochronous, Bulk, Interrupt или Control), в зависимости от приложения пользователя: – В функциях DataIn и DataOut пользователь может реализовать внутренний протокол или машину состояний. 4. Процесс выделения памяти: память выделяется приложением с использованием malloc (USBD_malloc): – USBD_malloc(sizeof (USBD_CUSTOM_CLASS_HandleTypeDef)): это динамически выделяемая память для структуры класса. [Оптимизация кода библиотеки USB] В этой секции мы рассмотрим некоторые базовые советы (возможно банальные) для оптимизации памяти программ и данных и быстродействия, когда программа использует библиотеку устройства USB. Уменьшение размера используемой памяти в примерах USB всегда является важным вопросом для микроконтроллеров STM32, особенно для тех, у которых маленькие объемы памяти Flash/RAM, таких как серии STM32L0 и F0. Уменьшение размера кучи и стека (настройка в файле линкера). Стек это область оперативной памяти, где программа сохраняет: • Локальные переменные, аргументы функции Куча это специальная область памяти, из которой можно динамически (во время выполнения программы, run time) выделять блоки памяти нужного размера. Во многих встраиваемых приложениях куча вообще не используется. Если Ваша конфигурация линкера резервирует слишком много памяти для кучи и стека, то Вы можете уточнить количество памяти, которое на самом деле требуется программе. Старайтесь всегда, если это возможно, использовать локальные переменные вместо глобальных. Если переменная используется только в одной функции, и её значение не нужно сохранять между отдельными вызовами этой функции, то такая переменная должна быть определена как локальная переменная. Константы должны выделяться из памяти flash. Рекомендуется все глобальные переменные, которые не изменяются, определить как константы в секции памяти, предназначенной только для чтения. Например, дескрипторы USB это хорошие кандидаты для декларации в виде констант, используя ключевое слово const языка C. Пример декларации дескрипторов USB как const: /* Стандартный дескриптор устройства USB */
const uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC]= { 0x12, /* bLength */ USB_DESC_TYPE_DEVICE, /* bDescriptorType */ 0x00, /* bcdUSB */ 0x02, 0x00, /* bDeviceClass */ 0x00, /* bDeviceSubClass */ 0x00, /* bDeviceProtocol */ USB_MAX_EP0_SIZE, /* bMaxPacketSize */ LOBYTE(USBD_VID), /* idVendor */ HIBYTE(USBD_VID), /* idVendor */ LOBYTE(USBD_PID), /* idVendor */ HIBYTE(USBD_PID), /* idVendor */ 0x00, /* bcdDevice rel. 2.00 */ 0x02, USBD_IDX_MFC_STR, /* Индекс строки описания производителя */ USBD_IDX_PRODUCT_STR, /* Индекс строки описания продукта */ USBD_IDX_SERIAL_STR, /* Индекс строки серийного номера */ USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */ }; /* USB_DeviceDescriptor */ Используйте статическую память вместо malloc. Библиотека устройства USB использует динамическую память для выделения памяти под структуру класса (class handle structure), чтобы можно было поддерживать несколько экземпляров объектов (в случае работы в двухядерной среде). Это означает, к примеру, что мы можем иметь иметь два экземпляра одного и того же класса USB (HS и FS). Вторая причина использования динамического выделения памяти - возможность её освобождения, когда USB больше не используется. Однако динамическое выделение вносит некоторые дополнительные затраты как памяти кода, так и памяти данных, особенно в памяти кода. По этой причине рекомендуется использовать статическое выделение памяти вместо динамического для устройств STM32 с малым объемом памяти, или когда не требуется поддержка несколько экземпляров (вариантов) стека USB. Для этого необходимо декларировать статический буфер достаточного размера, чтобы в нем разместилась структура класса USB (class handle structure). Ниже показан пример реализации: 1. В файле usbd_conf.h определите статическое выделение памяти и подпрограммы для этого: USBD_static_malloc()and USBD_static_free() #define MAX_STATIC_ALLOC_SIZE 4 /* HID Class structure size */ #define USBD_malloc (uint32_t *)USBD_static_malloc
#define USBD_free USBD_static_free
2. Ниже показана готовая реализация в файле usbd_conf.c: /** * @brief Одиночное статическое выделение памяти. * @param size: размер выделяемой памяти
* @retval вернет указатель на выделенную память */
void *USBD_static_malloc(uint32_t size) { static uint32_t mem[MAX_STATIC_ALLOC_SIZE]; return mem; } /** * @brief Заглушка для "освобождения" памяти
* @param *p указатель на выделенную память
* @retval нет возвращаемого значения
*/
void USBD_static_free(void *p) { } [FAQ] 1. Как можно на лету модифицировать дескриптор устройства и строковые дескрипторы? В файле usbd_desc.c дескриптор, относящийся к устройству и строки могут быть изменены в помощью callback-вызовов Get Descriptor. Приложение может вернуть корректный буфер дескриптора относительно индекса приложения, используя оператор switch/case. 2. Как драйверу класса устройства хранения (mass storage class, MSC) поддержать больше одного логического юнита (LUN)? В файле usbd_msc_storage_template.c определено все API, необходимое для использования физического носителя данных (physical media). Каждая функция поставляется с параметром LUN для выбора адресованного носителя. Количество поддерживаемых The LUN можно поменять с помощью изменения #define STORAGE_LUN_NBR в файле usbd_msc_storage_xxx.c (здесь xxx обозначает используемый носитель). Для запроса данных буфер STORAGE_Inquirydata содержит стандартные данные запроса для каждого LUN. Ниже показан пример использования двух LUN. const int8_t STORAGE_Inquirydata[] = { /* LUN 0 */ 0x00, 0x80, 0x02, 0x02, (USBD_STD_INQUIRY_LENGTH - 5), 0x00, 0x00, 0x00, 'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Производитель: 8 байт */ 'm', 'i', 'c', 'r', 'o', 'S', 'D', ' ', /* Продукт: 16 байт */ 'F', 'l', 'a', 's', 'h', ' ', ' ', ' ', '1', '.', '0' ,'0', /* Версия: 4 байт */ /* LUN 0 */ 0x00, 0x80, 0x02, 0x02, (USBD_STD_INQUIRY_LENGTH - 5), 0x00, 0x00, 0x00, 'S', 'T', 'M', ' ', ' ', ' ', ' ', ' ', /* Производитель: 8 bytes */ 'N', 'a', 'n', 'd', ' ', ' ', ' ', ' ', /* Продукт: 16 Bytes */ 'F', 'l', 'a', 's', 'h', ' ', ' ', ' ', '1', '.', '0' ,'0', /* Версия: 4 Bytes */ }; 3. Где определены адреса конечных точек? Адреса конечных точек определены в файле заголовка драйвера класса. В случае MSC demo, например, адреса конечных точке IN/OUT определены в файле usbd_msc.h следующим образом: #define MSC_EPIN_ADDR 0x81 // для Endpoint 1 IN #define MSC_EPOUT_ADDR 0x01 // для Endpoint 1 OUT 4. Может ли библиотека устройства USB быть сконфигурированной для работы либо в режиме High Speed, либо в режиме Full Speed? Да, библиотека может обработать USB OTG HS и USB OTG FS, и если ядро USB OTG FS может работать только в Full Speed mode, то USB OTG HS может работать и режимах High Speed и Full Speed. Для выбора подходящего ядра To USB, с которым нужно работать, пользователь должен добавить следующие макроопределения для препроцессора компилятора (это уже сделано в заранее сконфигурированных проектах примеров): - "USE_USB_HS" когда используется ядро USB High Speed (HS) 5. Как можно использовать измененные конечные точки в драйвере класса устройства USB? Для изменения конечных точке или добавления новой конечной точки: a) Выполните инициализацию конечной точки с помощью USBD_LL_OpenEP(). Для серий STM32F2 и STM32F4 (ядра FS и HS): HAL_PCD_SetRxFiFo() HAL_PCD_SetTxFiFo() Общий размер Rx FIFO и Tx FIFO должен быть меньше, чем общий размер FIFO, используемого ядром, 320 x 32 бит (1.25 килобайт) для ядра USB OTG FS и 1024 x 32 бит (4 килобайта) для ядра USB OTG HS. Для серий STM32F0, STM32L0, STM32F1 и STM32F3 (только ядро FS): HAL_PCD_PMA_Config() 6. Совместима ли библиотека устройства USB с операционной системой реального времени (RTOS)? Да, библиотека устройства USB может использоваться вместе с RTOS, обертка CMSIS RTOS wrapper используется при создании абстракции для ядра операционной системы (OS kernel). [Ссылки] 1. UM1734 STM32Cube USB device library site:st.com. |