/* STM32G474 USB FS device, dedicated PA11/PA12 pins, single-buffer PMA. */ #include "usbd_core.h" #include "usbd_cdc.h" static PCD_HandleTypeDef pcd; /* Единственный статический объект CDC вместо heap. Контракт предполагает один экземпляр класса. */ static USBD_CDC_HandleTypeDef class_memory; /* Проверяет размер запроса и возвращает статический объект либо NULL. Не является универсальным malloc. */ void *BoardUsb_Alloc(uint32_t size) { return size<=sizeof(class_memory)?&class_memory:NULL; } /* Освобождение не требуется: объект существует до перезапуска MCU. */ void BoardUsb_Free(void *ptr) { (void)ptr; } /* Согласует enum HAL и USB: OK -> OK, BUSY -> BUSY, остальные результаты -> FAIL. */ static USBD_StatusTypeDef status(HAL_StatusTypeDef s) { return s==HAL_OK?USBD_OK:s==HAL_BUSY?USBD_BUSY:USBD_FAIL; } /* Единственная точка входа USB ISR; события декодирует HAL_PCD_IRQHandler. */ void USB_LP_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd); } /* Передаёт 8-байтовый SETUP control-запрос стандартному обработчику USB Device. */ void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_SetupStage(h->pData,(uint8_t *)h->Setup); } /* OUT: хост отправил данные устройству; передаём номер endpoint и его принятый буфер. */ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_DataOutStage(h->pData,ep,h->OUT_ep[ep].xfer_buff); } /* IN: завершена передача данных от устройства к хосту; уведомляем USB Device. */ void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_DataInStage(h->pData,ep,h->IN_ep[ep].xfer_buff); } /* Передаёт событие начала USB-кадра; в текущей конфигурации SOF-прерывания отключены. */ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_SOF(h->pData); } /* При reset шины фиксируем Full Speed и сбрасываем состояние USB Device. */ void HAL_PCD_ResetCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_SetSpeed(h->pData,USBD_SPEED_FULL); (void)USBD_LL_Reset(h->pData); } /* Сообщает middleware о приостановке шины; аппаратный low-power режим здесь не включается. */ void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_Suspend(h->pData); } /* Сообщает middleware о возобновлении обмена после suspend. */ void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_Resume(h->pData); } /* Уведомление верхнего уровня о подключении контроллера. */ void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_DevConnected(h->pData); } /* Уведомление верхнего уровня об отключении контроллера. */ void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *h) { (void)USBD_LL_DevDisconnected(h->pData); } /* Переадресует незавершённую изохронную IN-передачу; обычный CDC использует bulk endpoints. */ void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_IsoINIncomplete(h->pData,ep); } /* Переадресует незавершённую изохронную OUT-передачу для полноты порта PCD. */ void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *h,uint8_t ep) { (void)USBD_LL_IsoOUTIncomplete(h->pData,ep); } USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *d) { /* Dedicated USB pins: no GPIO alternate-function selection on G474. */ __HAL_RCC_USB_CLK_ENABLE(); pcd.Instance = USB; pcd.Init.dev_endpoints = 8; pcd.Init.speed = PCD_SPEED_FULL; pcd.Init.phy_itface = PCD_PHY_EMBEDDED; pcd.Init.Sof_enable = DISABLE; pcd.Init.low_power_enable = DISABLE; pcd.Init.lpm_enable = DISABLE; pcd.Init.battery_charging_enable = DISABLE; pcd.pData = d; d->pData = &pcd; if (HAL_PCD_Init(&pcd) != HAL_OK) return USBD_FAIL; /* BTABLE: 0x00..0x3f; EP0 RX/TX 64 bytes each; CDC data RX/TX 64; * CDC notification 16. Byte offsets are disjoint within the 1 KB PMA. */ if (HAL_PCDEx_PMAConfig(&pcd, 0x00, PCD_SNG_BUF, 0x40) != HAL_OK || HAL_PCDEx_PMAConfig(&pcd, 0x80, PCD_SNG_BUF, 0x80) != HAL_OK || HAL_PCDEx_PMAConfig(&pcd, CDC_OUT_EP, PCD_SNG_BUF, 0xc0) != HAL_OK || HAL_PCDEx_PMAConfig(&pcd, CDC_IN_EP, PCD_SNG_BUF, 0x100) != HAL_OK || HAL_PCDEx_PMAConfig(&pcd, CDC_CMD_EP, PCD_SNG_BUF, 0x140) != HAL_OK) return USBD_FAIL; HAL_NVIC_SetPriority(USB_LP_IRQn, 7, 0); HAL_NVIC_EnableIRQ(USB_LP_IRQn); return USBD_OK; } /* Запрещает USB IRQ до деинициализации контроллера, чтобы callbacks не работали с разобранным состоянием. */ USBD_StatusTypeDef USBD_LL_DeInit(USBD_HandleTypeDef *d) { HAL_NVIC_DisableIRQ(USB_LP_IRQn); return status(HAL_PCD_DeInit(d->pData)); } /* Включает USB device через HAL после завершения настройки класса и endpoints. */ USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *d) { return status(HAL_PCD_Start(d->pData)); } /* Останавливает контроллер USB device; память состояния остаётся статической. */ USBD_StatusTypeDef USBD_LL_Stop(USBD_HandleTypeDef *d) { return status(HAL_PCD_Stop(d->pData)); } /* Открывает endpoint: ep содержит номер и направление (бит 7), type — тип, mps — максимальный пакет в байтах. */ USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *d,uint8_t ep,uint8_t type,uint16_t mps) { return status(HAL_PCD_EP_Open(d->pData,ep,mps,type)); } /* Закрывает endpoint по USB-адресу ep, включая бит направления. */ USBD_StatusTypeDef USBD_LL_CloseEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_Close(d->pData,ep)); } /* Очищает FIFO указанного endpoint через HAL. */ USBD_StatusTypeDef USBD_LL_FlushEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_Flush(d->pData,ep)); } /* Устанавливает STALL: endpoint отклоняет неподдерживаемую операцию хоста. */ USBD_StatusTypeDef USBD_LL_StallEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_SetStall(d->pData,ep)); } /* Снимает STALL по запросу верхнего уровня USB Device. */ USBD_StatusTypeDef USBD_LL_ClearStallEP(USBD_HandleTypeDef *d,uint8_t ep) { return status(HAL_PCD_EP_ClrStall(d->pData,ep)); } /* Применяет адрес устройства, назначенный хостом при перечислении USB. */ USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *d,uint8_t address) { return status(HAL_PCD_SetAddress(d->pData,address)); } /* Запускает IN-передачу size байтов из buf. Буфер должен жить до callback завершения; копии здесь нет. */ USBD_StatusTypeDef USBD_LL_Transmit(USBD_HandleTypeDef *d,uint8_t ep,uint8_t *buf,uint32_t size) { return status(HAL_PCD_EP_Transmit(d->pData,ep,buf,size)); } /* Взводит OUT endpoint для приёма в buf ёмкостью size байтов; завершение придёт через callback PCD. */ USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *d,uint8_t ep,uint8_t *buf,uint32_t size) { return status(HAL_PCD_EP_Receive(d->pData,ep,buf,size)); } /* Читает состояние endpoint из HAL handle: bit7 выбирает IN, младшие 7 бит — номер. */ uint8_t USBD_LL_IsStallEP(USBD_HandleTypeDef *d,uint8_t ep) { PCD_HandleTypeDef *h=d->pData; return ep&0x80?h->IN_ep[ep&0x7F].is_stall:h->OUT_ep[ep].is_stall; } /* Возвращает фактическое число принятых байтов последней OUT-передачи. */ uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *d,uint8_t ep) { return HAL_PCD_EP_GetRxCount(d->pData,ep); } /* Блокирующая задержка ST middleware в миллисекундах HAL; требует работающего SysTick. */ void USBD_LL_Delay(uint32_t ms) { HAL_Delay(ms); }