Добавление поддержки хоста 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].