STM32 хост USB CDC и чипы FTDI Печать
Добавил(а) microsin   

Недавно понадобилось запустить хост USB для виртуального последовательного порта (класс USB CDC) на плате STM32F429I-DISC1 (процессор STM32F429ZITx). И неожиданно столкнулся с проблемой взаимодействия с чипами FTDI.

Начал, как обычно, с создания шаблона проекта с помощью STM32CubeMX (STM32F429-USB-CDC-host.ioc [8]). После традиционных правок и исправления ошибок хост USB CDC наконец заработал, но категорически отказывался опознавать чипы FTDI. С другими чипами USB CDC, которые создавали стандартный класс, проблем не было.

Оказалось, что чипы FTDI работают с хостом через свой драйвер, исходный код для которого компания FTDI не предоставляет, и его протокол официально не опубликован. Есть только двоичные библиотеки и описание API, которое позволяет работать с устройствами CDC FTDI на хосте PC (Windows, Linux или других платформ).

Преобразователь USB-CAN CANable AZSMZ-USB2CAN [3]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 60c4h
VID: ad50h
Address (#1) assigned.
Manufacturer : Protofusion Labs
Product : CANable 1205aa6 https://github.com/normaldotcom/cantact-fw
Serial Number : 0029001F4E4E430820353036
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : 2h
SubClass : 2h
Protocol : 1h
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Адаптер USB - TTL UART на FT232RL (CN480661), купленный на AliExpress:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 7523h
VID: 1a86h
Address (#1) assigned.
Manufacturer : N/A
Product : USB2.0-Ser!
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
No registered class for this device.
UP: HOST_USER_DISCONNECTION
USB Device disconnected
UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6001h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : FT232R USB UART
Serial Number : A50285BI
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Device remote wakeup enabled
No registered class for this device.

Адаптер USB - TTL UART на фирменном чипе FT232RL [4]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6001h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : FT232R USB UART
Serial Number : A50285BI
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Device remote wakeup enabled
No registered class for this device.
UP: HOST_USER_DISCONNECTION
USB Device disconnected
UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6001h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : FT232R USB UART
Serial Number : A7006Wml
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Device remote wakeup enabled
No registered class for this device.

Плата на чипе FT2232D [5]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6010h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Dual RS232
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
No registered class for this device.

Плата на основе чипа FT2232H [6]:

HOST_USER_CONNECTION
USB Device Attached
PID: 6010h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Dual RS232-HS
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
No registered class for this device.

Плата на основе чипа FT4232H [6]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6011h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Quad RS232-HS
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
No registered class for this device.

Решить проблему помог проект HOST_CP_FTDI, который нашел на форуме [1]. Усиленное гугление в поиске других источников позволило найти исходный код драйвера FTDI для Linux ftdi_sio.c [2]. Автор проекта HOST_CP_FTDI [1] брал куски кода из этого драйвера.

На основе источников [1, 2] создал рабочий проект на FreeRTOS, который нормально может работать с чипами FTDI. Хост обрабатывает события подключения и отключения устройств, и осуществляет обмен с ними через последовательную консоль на USART1. Все, что передается из устройства к хосту, выводится в эту консоль. Обратно данные можно передавать командой консоли tx. Исходный код проекта для IAR 8.30 можно скачать по ссылке [8].

Адаптер USB - TTL UART на фирменном чипе FT232RL [4]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6001h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : FT232R USB UART
Serial Number : A7006V62
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Device remote wakeup enabled
Switching to Interface (#0)
Class    : ffh
SubClass : ffh
Protocol : ffh
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Плата на чипе FT2232D [5]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6010h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Dual RS232
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : ffh
SubClass : ffh
Protocol : ffh
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Плата на основе чипа FT2232H [6]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6010h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Dual RS232-HS
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : ffh
SubClass : ffh
Protocol : ffh
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Плата на основе чипа FT4232H [6]:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6011h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : Quad RS232-HS
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Switching to Interface (#0)
Class    : ffh
SubClass : ffh
Protocol : ffh
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Чип FT231X:

UP: HOST_USER_CONNECTION
USB Device Attached
PID: 6015h
VID: 403h
Address (#1) assigned.
Manufacturer : FTDI
Product : FT231X USB UART
Serial Number : N/A
Enumeration done.
This device has only 1 configuration.
Default configuration set.
Device remote wakeup enabled
Switching to Interface (#0)
Class    : ffh
SubClass : ffh
Protocol : ffh
CDC class started.
UP: HOST_USER_CLASS_SELECTED
UP: HOST_USER_CLASS_ACTIVE

Добавление поддержки хоста CDC под управлением FreeRTOS на примере платы STM32F407G-DISC1, процесс по шагам:

1. Добавьте в один из потоков, перед входом в его бесконечный цикл, вызов MX_USB_HOST_Init(). Для этого добавьте в проект файлы:

usb_host.c
usbh_conf.c
usbh_platform.c

Также добавьте в пути поиска папки:

$PROJ_DIR$/../Middlewares/ST/STM32_USB_Host_Library/Core/Inc
$PROJ_DIR$/../Middlewares/ST/STM32_USB_Host_Library/Class/CDC/Inc

2. Создайте в проекте новую группу (папку) USB_Host_Library, и добавьте в неё следующие файлы.

Находится в каталоге Middlewares\ST\STM32_USB_Host_Library\Class\CDC\Src\:

usbh_cdc.c

Эти файлы находятся в каталоге Middlewares\ST\STM32_USB_Host_Library\Core\Src\:

usbh_core.c
usbh_ctlreq.c
usbh_ioreq.c
usbh_pipes.c

3. Добавьте в папку Drivers\STM32F4xx_HAL_Driver проекта модули:

stm32f4xx_hal_hcd.c
stm32f4xx_ll_usb.c

В файле конфигурации HAL (stm32f4xx_hal_conf.h) раскомментируйте определение:

#define HAL_HCD_MODULE_ENABLED

4. Проверьте конфигурацию ножек USB в файле usbh_conf.c, они должны соответствовать портам микроконтроллера, подключенным к коннектору microUSB CN5 платы STM32F4DISCOVERY:

// USB_OTG_FS_VBUS, ножка входа GPIO, определяющая наличие
// напряжения +5V VBUS на коннекторе CN5 microUSB:
#define VBUS_FS_Pin              GPIO_PIN_9
#define VBUS_FS_GPIO_Port        GPIOA
// USB_OTG_FS_ID, аппаратный вход идентификации USB OTG.
// Роль хоста определяется по лог. 0 на этом входе:
#define OTG_FS_ID_Pin            GPIO_PIN_10
#define OTG_FS_ID_Port           GPIOA
// Ножки сигналов D- и D+ USB:
#define OTG_FS_DM_Pin            GPIO_PIN_11    // USB_OTG_FS_DM
#define OTG_FS_DM_Port           GPIOA
#define OTG_FS_DP_Pin            GPIO_PIN_12    // USB_OTG_FS_DP
#define OTG_FS_DP_Port           GPIOA
// Ножка активации ключа U6 STMPS2141STR (активный уровень 0),
// который подает на VBUS напряжение питания +5V:
#define OTG_FS_PowerSwitchOn_Pin       GPIO_PIN_0
#define OTG_FS_PowerSwitchOn_GPIO_Port GPIOC

5. Добавьте в MX_GPIO_Init настройку ножки порта OTG_FS_PowerSwitchOn_Pin в режиме выхода, с лог. 1 на выходе по умолчанию:

HAL_GPIO_WritePin(OTG_FS_PowerSwitchOn_GPIO_Port,
                  OTG_FS_PowerSwitchOn_Pin,
                  GPIO_PIN_SET);
GPIO_InitStruct.Pin = OTG_FS_PowerSwitchOn_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(OTG_FS_PowerSwitchOn_GPIO_Port, &GPIO_InitStruct);

6. В файл stm32f4xx_it.c добавьте обработчики прерывания OTG_FS_IRQHandler:

extern HCD_HandleTypeDef hhcd_USB_OTG_FS;
 
void OTG_FS_IRQHandler(void)
{
   HAL_HCD_IRQHandler(&hhcd_USB_OTG_FS);
}

При необходимости настройте приоритет прерывания для OTG_FS_IRQn. Это может быть важным, когда у Вас в системе используются другие прерывания, и необходимо соблюсти нужные приоритеты. В частности, такую настройку необходимо обязательно выполнить для FreeRTOS, потому что иначе будет срабатывать configASSERT в функции проверки правильности установки приоритетов vPortValidateInterruptPriority. Приоритет прерывания OTG_FS_IRQn настраивается в функции HAL_HCD_MspInit, по умолчанию настраивается уровень приоритета 0, т. е. самый высокий приоритет для STM32:

HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);

Для FreeRTOS это работать не будет. Поменяйте уровень приоритета в вызове HAL_NVIC_SetPriority на более низкий:

HAL_NVIC_SetPriority(OTG_FS_IRQn, 9, 0);

7. Отредактируйте опции в файле usbh_conf.h.

Разрешить вывод отладочных сообщений можно редактированием макроса USBH_DEBUG_LEVEL. По умолчанию он определен как 0, что означает отсутствие отладочного вывода хоста USB. Если определить USBH_DEBUG_LEVEL как 3, то будет вывод в консоль терминала IAR (для этого необходимо скомпилировать проект с поддержкой семихостинга, см. [7]):

//#define USBH_DEBUG_LEVEL      0U
#define USBH_DEBUG_LEVEL      3U

Если у Вас используется FreeRTOS, то поменяйте макрос USBH_USE_OS на 1:

//#define USBH_USE_OS      0U
#define USBH_USE_OS      1U 

8. Определитесь с классами устройств USB CDC (Virtual COM Port, VCP), с которыми будет работать Ваш хост. К примеру, стандартное устройство VCP использует код класса 0x02, а устройство VCP компании FTDI использует код класса, определяемого вендором 0xFF. При подключении устройства USB CDC на финальной стадии (usbh_core.c -> USBH_Process -> метка HOST_CHECK_CLASS) проверяется соответствие кода зарегистрированного класса коду класса интерфейса. Если эти коды совпадают, то хост активируется и готов работать с устройством USB CDC.

Код зарегистрированного класса USB CDC определяется в момент вызова MX_USB_HOST_Init -> USBH_RegisterClass. В функцию USBH_RegisterClass вторым параметром передается код класса через структуру USBH_ClassTypeDef (код регистрируемого класса находится в поле ClassCode). Если необходимо работать только с устройствами FTDI, то в ClassCode должно находиться значение 0xFF, если со стандартными устройствами USB CDC, то в ClassCode должно находиться 0x02.

Таким образом, нужно определить структуру USBH_ClassTypeDef для регистрируемого класса USB CDC/VCP. В ней нужно заполнить поле ClassCode, и поле указателя на функцию инициализации класса Init, остальные поля могут быть заполнены одинаково. Указатель на экземпляр этой структуры нужно будет передать в функцию USBH_RegisterClass. Примеры определения структуры показаны ниже:

// Структура для стандартного класса устройств USB CDC:
USBH_ClassTypeDef CDC_Class_Standard =
{
   "CDC Standard",
   USB_CDC_CLASS,                // ClassCode = 0x02
   USBH_CDC_InterfaceInit,       // (*Init)(struct _USBH_HandleTypeDef *phost);
   USBH_CDC_InterfaceDeInit,
   USBH_CDC_ClassRequest,
   USBH_CDC_Process,
   USBH_CDC_SOFProcess,
   NULL,
};
 
// Структура для класса устройств USB CDC компании FTDI:
USBH_ClassTypeDef CDC_Class_Ftdi =
{
   "CDC FTDI",
   VENDOR_SPECIFIC,              // ClassCode = 0xFF
   USBH_CDC_FTDI_InterfaceInit,  // (*Init)(struct _USBH_HandleTypeDef *phost);
   USBH_CDC_InterfaceDeInit,
   USBH_CDC_ClassRequest,
   USBH_CDC_Process,
   USBH_CDC_SOFProcess,
   NULL,
};

9. В файле usbh_conf.h измените настройки, соответствующие FreeRTOS:

//#define USBH_USE_OS      0U
#define USBH_USE_OS      1U
//#define USBH_PROCESS_STACK_SIZE    ((uint16_t)0)
#define USBH_PROCESS_STACK_SIZE    ((uint16_t)512)

10. Передача. Чтобы передать данные от хоста в инициализированное устройство USB CDC, необходимо вызвать функцию USBH_CDC_Transmit, например:

USBH_CDC_Transmit(&hUsbHostHS, (uint8_t*)txbuffer, strlen(txbuffer));

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

11. Прием. Чтобы хосту принять данные от инициализированного устройства USB CDC, необходимо вызвать функцию USBH_CDC_Receive, например:

USBH_CDC_Receive(phost, CDC_RX_Buffer, sizeof(CDC_RX_Buffer));

В качестве параметров в эту функцию передается указатель на дескриптор хоста, указатель на буфер приема и количество принимаемых данных. По завершению передачи будет вызвана функция USBH_CDC_ReceiveCallback, где можно обработать принятые данные, и для продолжения приема нужно снова вызвать USBH_CDC_Receive.

Примеры хоста для процессоров STM32F407 и STM32F429 можно скачать по ссылке [8].

[Ссылки]

1. STM32F4 usb cdc host + cp210x site:forum.easyelectronics.ru.
2. torvalds / linux / linux/drivers/usb/serial/ftdi_sio.c site:github.com.
3. CANable AZSMZ-USB2CAN.
4. Плата FT232R-Breakout.
5. FT2232D-Breakout: плата для гальванической развязки USB.
6. FT2232H Board - макетная плата на высокоскоростном чипе моста USB фирмы FTDI.
7. IAR: использование printf и окна терминала.
8. 210404STM32-USB-CDC-host.zip - исходный код для хоста USB CDC на STM32F429.