./../
  I D :    ¾ÏÈ£ :    IDÀúÀå      ȸ¿ø°¡ÀÔ    ³×°Ô(ÁåÀå) ÂÊÁöº¸³»±â    ¸Þ¸ðÀå    °¢Á¾°è»ê±â    Ä÷¯ºü·¿    À̸ðƼÄÜ(ÇÁ·¹ÀÓ, »õâ)    º¸À¯Àåºñ
¸ñ·ÏÀ¸·Î °¡±â..
STM32 °³¹ß ¶§ ´Ù¾çÇÑ USB CDCs »ç¿ëCDC, CP210x, FTDI, CH34x, PL2303) ±èÁ¾È£ | »èÁ¦Çϱâ

# USB Serial Unified Driver (CDC, CP210x, FTDI, CH34x, PL2303) ÀÌ ¹®¼­´Â STM32 USB Host Library¿¡¼­ ´ÙÁß USB ½Ã¸®¾ó Ĩ¼Â(CDC, CP2102, FT232, CH340, PL2303)À» Ç÷¯±× ¾Ø Ç÷¹ÀÌ·Î Áö¿øÇϱâ À§ÇØ ÀÛ¼ºµÈ **ÅëÇÕ µå¶óÀ̹ö(Unified Driver)**¿¡ ´ëÇÑ °¡ÀÌµå ¹× Àüü ¼Ò½º ÄÚµå ¹é¾÷ÀÔ´Ï´Ù. --- ## 1. Çʼö ¶óÀ̺귯¸® ÆÐÄ¡ (Core Patches) ### 1.1. usbh_core.c ÆÐÄ¡ Middlewares/ST/STM32_USB_Host_Library/Core/Src/usbh_core.cÀÇ HOST_CHECK_CLASS ºÎºÐÀ» ´ÙÀ½°ú °°ÀÌ º¯°æÇØ¾ß ÇÕ´Ï´Ù. ```c case HOST_CHECK_CLASS: if (phost->ClassNumber == 0U) { USBH_UsrLog("No Class has been registered."); } else { phost->pActiveClass = NULL; for (idx = 0U; idx < USBH_MAX_NUM_SUPPORTED_CLASS; idx++) { if (phost->pClass[idx] != NULL && phost->pClass[idx]->ClassCode == phost->device.CfgDesc.Itf_Desc[0].bInterfaceClass) { phost->pActiveClass = phost->pClass[idx]; if (phost->pActiveClass->Init(phost) == USBH_OK) { phost->gState = HOST_CLASS_REQUEST; USBH_UsrLog("%s class started.", phost->pActiveClass->Name); phost->pUser(phost, HOST_USER_CLASS_SELECTED); break; } phost->pActiveClass = NULL; } } if (phost->pActiveClass == NULL) { phost->gState = HOST_ABORT_STATE; USBH_UsrLog("No registered class for this device."); } } ``` ### 1.2. usbh_conf.h ÆÐÄ¡ CM7/USB_HOST/Target/usbh_conf.h¿¡¼­ ´ÙÀ½ ¼³Á¤µéÀ» ¹Ýµå½Ã Àû¿ëÇØ¾ß ÇÕ´Ï´Ù. (½ºÅà ¿À¹öÇ÷οì·Î ÀÎÇÑ Hard Fault ¹æÁö) ```c // µî·ÏÇÒ Å¬·¡½º ¼ö¸¦ 5°³·Î ´Ã¸² #define USBH_MAX_NUM_SUPPORTED_CLASS 5U // USB ó¸® ½º·¹µåÀÇ ½ºÅà »çÀÌÁ 1024(word = 4096 bytes)·Î Áõ°¡ #define USBH_PROCESS_STACK_SIZE ((uint16_t)1024) ``` --- ## 2. ÅëÇÕ µå¶óÀ̹ö ÆÄÀÏ Àüü ¼Ò½º ÄÚµå ¾Æ·¡´Â EEPROM µî¿¡¼­ ÀÐÀº ¼³Á¤°ªÀ¸·Î Åë½Å ¼Óµµ ¹× Æ÷¸ËÀ» µ¿ÀûÀ¸·Î Àû¿ëÇÒ ¼ö ÀÖ´Â ±â´ÉÀÌ Æ÷ÇÔµÈ ÅëÇÕ µå¶óÀ̹öÀÇ Àüü ÄÚµåÀÔ´Ï´Ù. ### 2.1. usbh_serial.h ```c #ifndef USBH_SERIAL_H #define USBH_SERIAL_H #ifdef __cplusplus extern "C" { #endif #include "usbh_core.h" #include "usbh_cdc.h" extern USBH_ClassTypeDef CP210x_Class; extern USBH_ClassTypeDef FTDI_Class; extern USBH_ClassTypeDef CH34x_Class; extern USBH_ClassTypeDef PL2303_Class; USBH_StatusTypeDef USBH_Serial_RegisterClasses(USBH_HandleTypeDef *phost); USBH_StatusTypeDef USBH_Serial_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length); // Call this before USB device connection (e.g., at boot after reading EEPROM) // baudrate: e.g. 115200, 9600 // databits: 5, 6, 7, 8 // parity: 0:None, 1:Odd, 2:Even, 3:Mark, 4:Space // stopbits: 0:1 bit, 1:1.5 bits, 2:2 bits void USBH_Serial_InitSettings(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits); #ifdef __cplusplus } #endif #endif ``` ### 2.2. usbh_serial.c ```c #include "usbh_serial.h" static uint32_t Serial_BaudRate = 115200; static uint8_t Serial_DataBits = 8; static uint8_t Serial_Parity = 0; static uint8_t Serial_StopBits = 0; void USBH_Serial_InitSettings(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits) { Serial_BaudRate = baudrate; Serial_DataBits = databits; Serial_Parity = parity; Serial_StopBits = stopbits; } // ========================================== // CP210X DRIVER // ========================================== typedef enum { CP210X_INIT_IFC_ENABLE, CP210X_INIT_SET_BAUDDIV, CP210X_INIT_SET_LINE_CTL, CP210X_INIT_DONE } CP210x_State; typedef struct { uint8_t InEp, OutEp, InPipe, OutPipe; uint16_t InEpSize, OutEpSize; CP210x_State state; } CP210x_HandleTypeDef; static CP210x_HandleTypeDef CP210x_Handle; static USBH_StatusTypeDef CP210x_Init(USBH_HandleTypeDef *phost) { if (phost->device.DevDesc.idVendor != 0x10C4) return USBH_FAIL; printf("[CP210x] Vendor ID Matched!\r\n"); uint8_t interface = USBH_FindInterface(phost, 0xFF, 0x00, 0x00); if (interface == 0xFF) { printf("[CP210x] Interface failed!\r\n"); return USBH_FAIL; } printf("[CP210x] Interface found: %d\r\n", interface); USBH_SelectInterface(phost, interface); phost->pActiveClass->pData = &CP210x_Handle; CP210x_HandleTypeDef *CP210x = (CP210x_HandleTypeDef *)phost->pActiveClass->pData; for (uint8_t i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; i++) { if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80) { CP210x->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; CP210x->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } else { CP210x->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; CP210x->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } } CP210x->OutPipe = USBH_AllocPipe(phost, CP210x->OutEp); CP210x->InPipe = USBH_AllocPipe(phost, CP210x->InEp); USBH_OpenPipe(phost, CP210x->OutPipe, CP210x->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, CP210x->OutEpSize); USBH_OpenPipe(phost, CP210x->InPipe, CP210x->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, CP210x->InEpSize); USBH_LL_SetToggle(phost, CP210x->OutPipe, 0); USBH_LL_SetToggle(phost, CP210x->InPipe, 0); CP210x->state = CP210X_INIT_IFC_ENABLE; printf("[CP210x] Init OK!\r\n"); return USBH_OK; } static USBH_StatusTypeDef CP210x_DeInit(USBH_HandleTypeDef *phost) { CP210x_HandleTypeDef *CP210x = (CP210x_HandleTypeDef *)phost->pActiveClass->pData; if (CP210x->OutPipe) { USBH_ClosePipe(phost, CP210x->OutPipe); USBH_FreePipe(phost, CP210x->OutPipe); } if (CP210x->InPipe) { USBH_ClosePipe(phost, CP210x->InPipe); USBH_FreePipe(phost, CP210x->InPipe); } return USBH_OK; } static USBH_StatusTypeDef CP210x_CtlReq(USBH_HandleTypeDef *phost, uint8_t req, uint16_t value) { phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_INTERFACE; phost->Control.setup.b.bRequest = req; phost->Control.setup.b.wValue.w = value; phost->Control.setup.b.wIndex.w = 0; phost->Control.setup.b.wLength.w = 0; return USBH_CtlReq(phost, NULL, 0); } static USBH_StatusTypeDef CP210x_Requests(USBH_HandleTypeDef *phost) { CP210x_HandleTypeDef *CP210x = (CP210x_HandleTypeDef *)phost->pActiveClass->pData; USBH_StatusTypeDef status; switch (CP210x->state) { case CP210X_INIT_IFC_ENABLE: status = CP210x_CtlReq(phost, 0x00, 0x0001); if (status == USBH_OK) { printf("[CP210x] IFC_ENABLE OK\r\n"); CP210x->state = CP210X_INIT_SET_BAUDDIV; } break; case CP210X_INIT_SET_BAUDDIV: { uint32_t bauddiv = 3686400 / Serial_BaudRate; status = CP210x_CtlReq(phost, 0x01, bauddiv); if (status == USBH_OK) { printf("[CP210x] SET_BAUDDIV (%lu) OK\r\n", Serial_BaudRate); CP210x->state = CP210X_INIT_SET_LINE_CTL; } break; } case CP210X_INIT_SET_LINE_CTL: { uint16_t line_ctl = (Serial_DataBits << 8); if (Serial_Parity == 1) line_ctl |= 0x0010; else if (Serial_Parity == 2) line_ctl |= 0x0020; else if (Serial_Parity == 3) line_ctl |= 0x0030; else if (Serial_Parity == 4) line_ctl |= 0x0040; if (Serial_StopBits == 2) line_ctl |= 0x0002; status = CP210x_CtlReq(phost, 0x03, line_ctl); if (status == USBH_OK) { printf("[CP210x] SET_LINE_CTL OK\r\n"); CP210x->state = CP210X_INIT_DONE; } break; } case CP210X_INIT_DONE: return USBH_OK; } return USBH_BUSY; } static USBH_StatusTypeDef CP210x_BgndProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } static USBH_StatusTypeDef CP210x_SOFProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } USBH_ClassTypeDef CP210x_Class = { "CP210x_Class", 0xFF, CP210x_Init, CP210x_DeInit, CP210x_Requests, CP210x_BgndProcess, CP210x_SOFProcess, NULL }; USBH_StatusTypeDef USBH_CP210x_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length) { CP210x_HandleTypeDef *CP210x = (CP210x_HandleTypeDef *)phost->pActiveClass->pData; if (phost->gState != HOST_CLASS || phost->device.is_disconnected) return USBH_FAIL; USBH_URBStateTypeDef urb = USBH_LL_GetURBState(phost, CP210x->OutPipe); if (urb == USBH_URB_IDLE || urb == USBH_URB_DONE || urb == USBH_URB_NOTREADY || urb == USBH_URB_ERROR || urb == USBH_URB_STALL) { USBH_BulkSendData(phost, pbuff, length, CP210x->OutPipe, 1); return USBH_OK; } return USBH_BUSY; } // ========================================== // FTDI DRIVER // ========================================== typedef enum { FTDI_INIT_RESET, FTDI_INIT_SET_BAUD, FTDI_INIT_SET_DATA, FTDI_INIT_SET_FLOW, FTDI_INIT_SET_MODEM, FTDI_INIT_DONE } FTDI_State; typedef struct { uint8_t InEp, OutEp, InPipe, OutPipe; uint16_t InEpSize, OutEpSize; FTDI_State state; } FTDI_HandleTypeDef; static FTDI_HandleTypeDef FTDI_Handle; static USBH_StatusTypeDef FTDI_Init(USBH_HandleTypeDef *phost) { if (phost->device.DevDesc.idVendor != 0x0403) return USBH_FAIL; printf("[FTDI] Vendor ID Matched!\r\n"); uint8_t interface = USBH_FindInterface(phost, 0xFF, 0xFF, 0xFF); if (interface == 0xFF) { printf("[FTDI] FindInterface Failed!\r\n"); return USBH_FAIL; } printf("[FTDI] Interface found: %d\r\n", interface); USBH_SelectInterface(phost, interface); phost->pActiveClass->pData = &FTDI_Handle; FTDI_HandleTypeDef *FTDI = (FTDI_HandleTypeDef *)phost->pActiveClass->pData; for (uint8_t i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; i++) { if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80) { FTDI->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; FTDI->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } else { FTDI->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; FTDI->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } } FTDI->OutPipe = USBH_AllocPipe(phost, FTDI->OutEp); FTDI->InPipe = USBH_AllocPipe(phost, FTDI->InEp); USBH_OpenPipe(phost, FTDI->OutPipe, FTDI->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, FTDI->OutEpSize); USBH_OpenPipe(phost, FTDI->InPipe, FTDI->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, FTDI->InEpSize); USBH_LL_SetToggle(phost, FTDI->OutPipe, 0); USBH_LL_SetToggle(phost, FTDI->InPipe, 0); FTDI->state = FTDI_INIT_RESET; return USBH_OK; } static USBH_StatusTypeDef FTDI_DeInit(USBH_HandleTypeDef *phost) { FTDI_HandleTypeDef *FTDI = (FTDI_HandleTypeDef *)phost->pActiveClass->pData; if (FTDI->OutPipe) { USBH_ClosePipe(phost, FTDI->OutPipe); USBH_FreePipe(phost, FTDI->OutPipe); } if (FTDI->InPipe) { USBH_ClosePipe(phost, FTDI->InPipe); USBH_FreePipe(phost, FTDI->InPipe); } return USBH_OK; } static USBH_StatusTypeDef FTDI_CtlReq(USBH_HandleTypeDef *phost, uint8_t req, uint16_t value, uint16_t index) { phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE; phost->Control.setup.b.bRequest = req; phost->Control.setup.b.wValue.w = value; phost->Control.setup.b.wIndex.w = index; phost->Control.setup.b.wLength.w = 0; return USBH_CtlReq(phost, NULL, 0); } static USBH_StatusTypeDef FTDI_Requests(USBH_HandleTypeDef *phost) { FTDI_HandleTypeDef *FTDI = (FTDI_HandleTypeDef *)phost->pActiveClass->pData; USBH_StatusTypeDef status; switch (FTDI->state) { case FTDI_INIT_RESET: status = FTDI_CtlReq(phost, 0x00, 0x0000, 0); if (status == USBH_OK || status == USBH_NOT_SUPPORTED || status == USBH_FAIL) { printf("[FTDI] RESET status: %d\r\n", status); FTDI->state = FTDI_INIT_SET_BAUD; } break; case FTDI_INIT_SET_BAUD: { uint32_t divisor = 3000000 / Serial_BaudRate; status = FTDI_CtlReq(phost, 0x03, divisor, 0); if (status == USBH_OK || status == USBH_NOT_SUPPORTED || status == USBH_FAIL) { printf("[FTDI] BAUD (%lu) status: %d\r\n", Serial_BaudRate, status); FTDI->state = FTDI_INIT_SET_DATA; } break; } case FTDI_INIT_SET_DATA: { uint16_t data = (Serial_DataBits & 0x0F); if (Serial_Parity == 1) data |= (1 << 8); else if (Serial_Parity == 2) data |= (2 << 8); else if (Serial_Parity == 3) data |= (3 << 8); else if (Serial_Parity == 4) data |= (4 << 8); if (Serial_StopBits == 2) data |= (2 << 11); status = FTDI_CtlReq(phost, 0x04, data, 0); if (status == USBH_OK || status == USBH_NOT_SUPPORTED || status == USBH_FAIL) { printf("[FTDI] DATA status: %d\r\n", status); FTDI->state = FTDI_INIT_SET_FLOW; } break; } case FTDI_INIT_SET_FLOW: status = FTDI_CtlReq(phost, 0x02, 0x0000, 0); if (status == USBH_OK || status == USBH_NOT_SUPPORTED || status == USBH_FAIL) { printf("[FTDI] FLOW status: %d\r\n", status); FTDI->state = FTDI_INIT_SET_MODEM; } break; case FTDI_INIT_SET_MODEM: status = FTDI_CtlReq(phost, 0x01, 0x0303, 0); if (status == USBH_OK || status == USBH_NOT_SUPPORTED || status == USBH_FAIL) { printf("[FTDI] MODEM status: %d\r\n", status); FTDI->state = FTDI_INIT_DONE; } break; case FTDI_INIT_DONE: return USBH_OK; } return USBH_BUSY; } static uint8_t FTDI_RxBuffer[64]; static USBH_StatusTypeDef FTDI_BgndProcess(USBH_HandleTypeDef *phost) { FTDI_HandleTypeDef *FTDI = (FTDI_HandleTypeDef *)phost->pActiveClass->pData; static uint8_t rx_state = 0; if (phost->gState == HOST_CLASS && !phost->device.is_disconnected) { if (rx_state == 0) { USBH_BulkReceiveData(phost, FTDI_RxBuffer, FTDI->InEpSize, FTDI->InPipe); rx_state = 1; } else { USBH_URBStateTypeDef urb = USBH_LL_GetURBState(phost, FTDI->InPipe); if (urb == USBH_URB_DONE || urb == USBH_URB_NOTREADY || urb == USBH_URB_ERROR || urb == USBH_URB_STALL) { rx_state = 0; } } } else { rx_state = 0; } return USBH_OK; } static USBH_StatusTypeDef FTDI_SOFProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } USBH_ClassTypeDef FTDI_Class = { "FTDI_Class", 0xFF, FTDI_Init, FTDI_DeInit, FTDI_Requests, FTDI_BgndProcess, FTDI_SOFProcess, NULL }; USBH_StatusTypeDef USBH_FTDI_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length) { FTDI_HandleTypeDef *FTDI = (FTDI_HandleTypeDef *)phost->pActiveClass->pData; if (phost->gState != HOST_CLASS || phost->device.is_disconnected) return USBH_FAIL; USBH_URBStateTypeDef urb = USBH_LL_GetURBState(phost, FTDI->OutPipe); if (urb == USBH_URB_IDLE || urb == USBH_URB_DONE || urb == USBH_URB_NOTREADY || urb == USBH_URB_ERROR || urb == USBH_URB_STALL) { USBH_BulkSendData(phost, pbuff, length, FTDI->OutPipe, 1); return USBH_OK; } return USBH_BUSY; } // ========================================== // CH34X DRIVER // ========================================== typedef enum { CH34X_INIT_SETUP, CH34X_INIT_BAUD, CH34X_INIT_DONE } CH34x_State; typedef struct { uint8_t InEp, OutEp, InPipe, OutPipe; uint16_t InEpSize, OutEpSize; CH34x_State state; } CH34x_HandleTypeDef; static CH34x_HandleTypeDef CH34x_Handle; static USBH_StatusTypeDef CH34x_Init(USBH_HandleTypeDef *phost) { if (phost->device.DevDesc.idVendor != 0x1A86) return USBH_FAIL; uint8_t interface = USBH_FindInterface(phost, 0xFF, 0x01, 0x02); if (interface == 0xFF) interface = 0; USBH_SelectInterface(phost, interface); phost->pActiveClass->pData = &CH34x_Handle; CH34x_HandleTypeDef *CH34x = (CH34x_HandleTypeDef *)phost->pActiveClass->pData; for (uint8_t i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; i++) { if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80) { CH34x->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; CH34x->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } else { CH34x->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; CH34x->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } } CH34x->OutPipe = USBH_AllocPipe(phost, CH34x->OutEp); CH34x->InPipe = USBH_AllocPipe(phost, CH34x->InEp); USBH_OpenPipe(phost, CH34x->OutPipe, CH34x->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, CH34x->OutEpSize); USBH_OpenPipe(phost, CH34x->InPipe, CH34x->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, CH34x->InEpSize); USBH_LL_SetToggle(phost, CH34x->OutPipe, 0); USBH_LL_SetToggle(phost, CH34x->InPipe, 0); CH34x->state = CH34X_INIT_SETUP; return USBH_OK; } static USBH_StatusTypeDef CH34x_DeInit(USBH_HandleTypeDef *phost) { CH34x_HandleTypeDef *CH34x = (CH34x_HandleTypeDef *)phost->pActiveClass->pData; if (CH34x->OutPipe) { USBH_ClosePipe(phost, CH34x->OutPipe); USBH_FreePipe(phost, CH34x->OutPipe); } if (CH34x->InPipe) { USBH_ClosePipe(phost, CH34x->InPipe); USBH_FreePipe(phost, CH34x->InPipe); } return USBH_OK; } static USBH_StatusTypeDef CH34x_CtlReq(USBH_HandleTypeDef *phost, uint8_t req, uint16_t value, uint16_t index) { phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_TYPE_VENDOR | USB_REQ_RECIPIENT_DEVICE; phost->Control.setup.b.bRequest = req; phost->Control.setup.b.wValue.w = value; phost->Control.setup.b.wIndex.w = index; phost->Control.setup.b.wLength.w = 0; return USBH_CtlReq(phost, NULL, 0); } static USBH_StatusTypeDef CH34x_Requests(USBH_HandleTypeDef *phost) { CH34x_HandleTypeDef *CH34x = (CH34x_HandleTypeDef *)phost->pActiveClass->pData; switch (CH34x->state) { case CH34X_INIT_SETUP: if (CH34x_CtlReq(phost, 0xA1, 0, 0) == USBH_OK) CH34x->state = CH34X_INIT_BAUD; break; case CH34X_INIT_BAUD: if (CH34x_CtlReq(phost, 0x9A, 0x1312, 0xCC1A) == USBH_OK) CH34x->state = CH34X_INIT_DONE; break; case CH34X_INIT_DONE: return USBH_OK; } return USBH_BUSY; } static USBH_StatusTypeDef CH34x_BgndProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } static USBH_StatusTypeDef CH34x_SOFProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } USBH_ClassTypeDef CH34x_Class = { "CH34x_Class", 0xFF, CH34x_Init, CH34x_DeInit, CH34x_Requests, CH34x_BgndProcess, CH34x_SOFProcess, NULL }; USBH_StatusTypeDef USBH_CH34x_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length) { CH34x_HandleTypeDef *CH34x = (CH34x_HandleTypeDef *)phost->pActiveClass->pData; if (phost->gState != HOST_CLASS || phost->device.is_disconnected) return USBH_FAIL; USBH_URBStateTypeDef urb = USBH_LL_GetURBState(phost, CH34x->OutPipe); if (urb == USBH_URB_IDLE || urb == USBH_URB_DONE || urb == USBH_URB_NOTREADY || urb == USBH_URB_ERROR || urb == USBH_URB_STALL) { USBH_BulkSendData(phost, pbuff, length, CH34x->OutPipe, 1); return USBH_OK; } return USBH_BUSY; } // ========================================== // PL2303 DRIVER // ========================================== typedef enum { PL2303_INIT_SETUP, PL2303_INIT_DONE } PL2303_State; typedef struct { uint8_t InEp, OutEp, InPipe, OutPipe; uint16_t InEpSize, OutEpSize; PL2303_State state; } PL2303_HandleTypeDef; static PL2303_HandleTypeDef PL2303_Handle; static USBH_StatusTypeDef PL2303_Init(USBH_HandleTypeDef *phost) { if (phost->device.DevDesc.idVendor != 0x067B) return USBH_FAIL; uint8_t interface = USBH_FindInterface(phost, 0xFF, 0x00, 0x00); if (interface == 0xFF) interface = 0; USBH_SelectInterface(phost, interface); phost->pActiveClass->pData = &PL2303_Handle; PL2303_HandleTypeDef *PL2303 = (PL2303_HandleTypeDef *)phost->pActiveClass->pData; for (uint8_t i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; i++) { if (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80) { PL2303->InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; PL2303->InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } else { PL2303->OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; PL2303->OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } } PL2303->OutPipe = USBH_AllocPipe(phost, PL2303->OutEp); PL2303->InPipe = USBH_AllocPipe(phost, PL2303->InEp); USBH_OpenPipe(phost, PL2303->OutPipe, PL2303->OutEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PL2303->OutEpSize); USBH_OpenPipe(phost, PL2303->InPipe, PL2303->InEp, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, PL2303->InEpSize); USBH_LL_SetToggle(phost, PL2303->OutPipe, 0); USBH_LL_SetToggle(phost, PL2303->InPipe, 0); PL2303->state = PL2303_INIT_SETUP; return USBH_OK; } static USBH_StatusTypeDef PL2303_DeInit(USBH_HandleTypeDef *phost) { PL2303_HandleTypeDef *PL2303 = (PL2303_HandleTypeDef *)phost->pActiveClass->pData; if (PL2303->OutPipe) { USBH_ClosePipe(phost, PL2303->OutPipe); USBH_FreePipe(phost, PL2303->OutPipe); } if (PL2303->InPipe) { USBH_ClosePipe(phost, PL2303->InPipe); USBH_FreePipe(phost, PL2303->InPipe); } return USBH_OK; } static USBH_StatusTypeDef PL2303_Requests(USBH_HandleTypeDef *phost) { PL2303_HandleTypeDef *PL2303 = (PL2303_HandleTypeDef *)phost->pActiveClass->pData; switch (PL2303->state) { case PL2303_INIT_SETUP: { static uint8_t line_coding[7] = {0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08}; // 115200 8N1 phost->Control.setup.b.bmRequestType = 0x21; phost->Control.setup.b.bRequest = 0x20; phost->Control.setup.b.wValue.w = 0; phost->Control.setup.b.wIndex.w = 0; phost->Control.setup.b.wLength.w = 7; if (USBH_CtlReq(phost, line_coding, 7) == USBH_OK) PL2303->state = PL2303_INIT_DONE; } break; case PL2303_INIT_DONE: return USBH_OK; } return USBH_BUSY; } static USBH_StatusTypeDef PL2303_BgndProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } static USBH_StatusTypeDef PL2303_SOFProcess(USBH_HandleTypeDef *phost) { return USBH_OK; } USBH_ClassTypeDef PL2303_Class = { "PL2303_Class", 0xFF, PL2303_Init, PL2303_DeInit, PL2303_Requests, PL2303_BgndProcess, PL2303_SOFProcess, NULL }; USBH_StatusTypeDef USBH_PL2303_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length) { PL2303_HandleTypeDef *PL2303 = (PL2303_HandleTypeDef *)phost->pActiveClass->pData; if (phost->gState != HOST_CLASS || phost->device.is_disconnected) return USBH_FAIL; USBH_URBStateTypeDef urb = USBH_LL_GetURBState(phost, PL2303->OutPipe); if (urb == USBH_URB_IDLE || urb == USBH_URB_DONE || urb == USBH_URB_NOTREADY || urb == USBH_URB_ERROR || urb == USBH_URB_STALL) { USBH_BulkSendData(phost, pbuff, length, PL2303->OutPipe, 1); return USBH_OK; } return USBH_BUSY; } // ========================================== // UNIFIED WRAPPER // ========================================== USBH_StatusTypeDef USBH_Serial_RegisterClasses(USBH_HandleTypeDef *phost) { if (USBH_RegisterClass(phost, USBH_CDC_CLASS) != USBH_OK) return USBH_FAIL; if (USBH_RegisterClass(phost, &CP210x_Class) != USBH_OK) return USBH_FAIL; if (USBH_RegisterClass(phost, &FTDI_Class) != USBH_OK) return USBH_FAIL; if (USBH_RegisterClass(phost, &CH34x_Class) != USBH_OK) return USBH_FAIL; if (USBH_RegisterClass(phost, &PL2303_Class) != USBH_OK) return USBH_FAIL; return USBH_OK; } USBH_StatusTypeDef USBH_Serial_Transmit(USBH_HandleTypeDef *phost, uint8_t *pbuff, uint16_t length) { if (phost->gState != HOST_CLASS) return USBH_FAIL; if (phost->pActiveClass == USBH_CDC_CLASS) return USBH_CDC_Transmit(phost, pbuff, length); else if (phost->pActiveClass == &CP210x_Class) return USBH_CP210x_Transmit(phost, pbuff, length); else if (phost->pActiveClass == &FTDI_Class) return USBH_FTDI_Transmit(phost, pbuff, length); else if (phost->pActiveClass == &CH34x_Class) return USBH_CH34x_Transmit(phost, pbuff, length); else if (phost->pActiveClass == &PL2303_Class) return USBH_PL2303_Transmit(phost, pbuff, length); return USBH_FAIL; } ``` --- ## 3. ¿¹Á¦ ÄÚµå (main.c Àû¿ë ¹æ¹ý) ```c #include "usbh_serial.h" extern USBH_HandleTypeDef hUsbHostHS; // MX_USB_HOST_Init()¿¡¼­ »ý¼ºµÈ Àü¿ª º¯¼ö void myTaskFunc01(void *argument) { // 1. EEPROM ¶Ç´Â »ç¿ëÀÚ ¼³Á¤¿¡¼­ ºÒ·¯¿Â °ªÀ» Àû¿ë (USB Åë½Å ½ÃÀÛ Àü) uint32_t saved_baud = 9600; USBH_Serial_InitSettings(saved_baud, 8, 0, 0); // 2. USB È£½ºÆ® ¸ðµâ ÃʱâÈ­ (³»ºÎÀûÀ¸·Î ½º·¹µå ±¸µ¿) MX_USB_HOST_Init(); uint32_t cnt = 0; char txBuffer[64]; for(;;) { // 3. µð¹ÙÀ̽º°¡ Àνĵǰí Åë½Å Áغñ°¡ ³¡³­ »óÅÂ(HOST_CLASS)ÀÏ ¶§ µ¥ÀÌÅÍ Àü¼Û if (hUsbHostHS.gState == HOST_CLASS) { int len = snprintf(txBuffer, sizeof(txBuffer), "Tick: %lu\r\n", cnt++); USBH_Serial_Transmit(&hUsbHostHS, (uint8_t *)txBuffer, len); } osDelay(1000); } } // 4. µå¶óÀ̹ö µî·Ï (usb_host.c ³»ºÎ) void MX_USB_HOST_Init(void) { if (USBH_Init(&hUsbHostHS, USBH_UserProcess, HOST_HS) != USBH_OK) { Error_Handler(); } // ±âÁ¸ USBH_RegisterClass ´ë½Å Unified ÇÔ¼ö È£Ãâ if (USBH_Serial_RegisterClasses(&hUsbHostHS) != USBH_OK) { Error_Handler(); } if (USBH_Start(&hUsbHostHS) != USBH_OK) { Error_Handler(); } } ```

÷¾ð ´Þ±â
À̸§ : ¾ÏÈ£ :
¹øÈ£    Á¦¸ñ...(÷¾ð) ÀÛ¼ºÀÏ Á¢¼Ó
xxx [°øÁö]ÀÚÀ¯°Ö·¯¸® µî·Ï ¿À·ù ¼öÁ¤ ¿Ï·á 2017-12-22 684691
xxx [°øÁö]ÀÚ·á½Ç ´Ù¿î·Îµå ¿À·ù ó¸® 2017-12-01 30588
xxx [°øÁö]ºñ³­, ±¤°í °Ô½Ã¹°À» ½Å°í, Â÷´Ü, »èÁ¦µË´Ï´Ù. 2005-06-26 26436
253 [ÀϹÝ]  .net core Web »ý¼ºÇÏ¸é ³ª¿À´Â ±¸Á¶ 2026-09-12(Åä) 10
252 [M D] > STM32 °³¹ß ¶§ ´Ù¾çÇÑ USB CDCs »ç¿ëCDC, CP210x, FTDI, CH34x, PL2303) < 2026-09-11(±Ý) 18
251 [M D]  STM32H747I-DISCO CP2102 USB Host Æ®·¯ºí½´ÆÃ Á¾ÇÕ º¸°í¼­ 2026-09-11(±Ý) 18
250 [ÀϹÝ]  STM32H747I-DISCO¿¡¼­ STM32CubeMX ÅÛÇø´¶§¹®¿¡ USB OTG ¹®Á¦ ¹ß»ýÇÒ¶§ 2026-09-10(¸ñ) 23
249 [M D]  TouchGFX¿¡¼­ ¹öư°°Àº°Å »èÁ¦ ÇßÀ»¶§ make: *** No rule to make target.. ¿À·ù 2026-08-20(¸ñ) 101
248 [M D]  STM TouchGFX¼³°èÇÒ¶§ main¿¡¼­ View¿¡ Á¢±ÙÇÏ´Â ¼ø¼­ 2026-08-16(ÀÏ) 73
247 [M D]  STM TouchGFX¼³°èÇÒ¶§ View°¡ main¿¡ Á¢±ÙÇÏ´Â ¼ø¼­ 2026-08-16(ÀÏ) 127
246 [M D]  MCU Task°ü¸®ÇÒ¶§ ÇÊ¿äÇÑ ¼¼¸¶Æ÷¾î(Semaphore)¿Í ¹ÂÅØ½º(Mutex) 2026-08-09(ÀÏ) 115
245 [M D]  STM32F ¸¶ÀÌÄÄ °øºÎÇÒ¶§ RAM¿¡ ¿Ã·Á¼­ ½ÃÇèÇϱâ.(¼Óµµ ºü¸§) 2026-08-08(Åä) 109
244 [ÀϹÝ]  STM32F767°ú FreeRTOS¸¦ ÀÌ¿ëÇØ ¼³°èÇÒ¶§ Time base Source´Â? 2026-08-08(Åä) 111
243 [ÀϹÝ]  VSCode·Î STM32 °³¹ßÇÒ¶§ printf¿¡¼­ ½Ç¼ö Ãâ·Â ¾ÈµÇ¸é 2026-07-16(¸ñ) 968
242 [ÀϹÝ]  VisualStudioÀ¸·Î ¸¸µç ÇÁ·Î±×·¥ .net ¹öÀü ¿Ã¸®±â À§ÇØ 2026-07-02(¸ñ) 1186
241 [C#]  c# winform¿¡¼­ URL QR»ý¼º 2026-06-30(È­) 1028
240 [ÀϹÝ]  VC ÄÄÆÄÀÏÇÒ¶§ ¼Ö·ù¼Ç Æú´õ¿¡ ÄÄÆÄÀÏµÈ ÆÄÀÏÀÌ »ý¼ºµÉ¶§ 2026-06-29(¿ù) 412
239 [XML]  IIS 8.0 ÀÌ»ó IP Â÷´Ü web.config ¼³Á¤ 2026-06-13(Åä) 143
238 [ÀϹÝ]  C#.net 6ÀÌ»ó ºôÆ®ÇÒ¶§ TargetFramework Æú´õ »ý±â´Â°Å ¾ø¾Ö±â 2026-04-16(¸ñ) 443
237 [ÀϹÝ]  windowsÀÇ °¢Á¾ ¿¡µðÅÍ¿¡¼­ ÇÑ±Û ÀԷ½ÿ¡ ¿Ï¼ºµÇ¾ß Ç¥±âµÇ´Â ¹®Á¦ 2026-04-10(±Ý) 437
236 [ÀϹÝ]  MPLab X 6.2¿¡¼­ ICD3 ¿¬°á ½ÇÆÐ°¡ ³ª¿Ã¶§ 2026-04-07(È­) 454
235 [ÀϹÝ]  Verolog HDL Operation (¿¬»êÀÚ) 2026-03-27(±Ý) 410
234 [ÀϹÝ]  Mac OS Sequoia¸¦ OCLP·Î ¼³Ä¡ÇßÀ»¶§ Chrome ¿À·ù ÇØ°á¹æ¹ý 2026-03-21(Åä) 681
°Ë»ö :
Page : ÀÌÀü 1  2  3  4  5  6  7  8  9  10  11  12  13  ´ÙÀ½




Copyright 2003-2026 ¨Ï ±èÁ¾È£. All rights reserved. kimjongho.co.kr kimjongho.kr kimjongho.net kimjongho.com