2025-03-03 4:25 AM
I'm trying to code a USB host on my stm32h743 board, and I would like more than one active IN endpoint.
I'm basing my code on the STM32Cube_FW_L0_V1.12.1 STM32H743I-EVAL application USB_Host CDC_Standalone.
I have a modified version of USBH_CDC_InterfaceInit that (I think) sets up two sets of CDC endpoints, and I can send characters on either of them and receive interrupts fine. When a character is due to be sent back on the first endpoint, I receive that character. But on the second endpoint, I just receive a NULL character.
It's a little hackish in that one of the endpoint sets is hardcoded from the configuration descriptor because we're after an interface subclass and protocol both of 0xff and USBH_FindInterface treats 0xff as a wildcard. But it doesn't matter which order I initialise them, the first one I initialise works and the second one only returns NULL character(s).
static Danish_HandleTypeDef Danish_Interface; static USBH_StatusTypeDef USBH_Danish_InterfaceInit(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status; uint8_t interface; Danish_HandleTypeDef *Danish_Handle; phost->pActiveClass->pData = &Danish_Interface; Danish_Handle = (Danish_HandleTypeDef *)phost->pActiveClass->pData; if (Danish_Handle == NULL) { USBH_DbgLog("Cannot allocate memory for Danish Handle"); return USBH_FAIL; } USBH_memset(Danish_Handle, 0, sizeof(Danish_HandleTypeDef)); /* Initialize cdc handler */ for (unsigned iPort = 0; iPort < NUM_PORTS; ++iPort) { if (iPort == 0) { interface = USBH_FindInterface(phost, 0xff, 0xfe, 0xff); if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) { /* No Valid Interface */ USBH_DbgLog("Cannot Find the interface for %s.", phost->pActiveClass->Name); return USBH_FAIL; } } else if (iPort == 1) { interface = 1; } status = USBH_SelectInterface(phost, interface); if (status != USBH_OK) { return USBH_FAIL; } for (unsigned i = 0; i < phost->device.CfgDesc.Itf_Desc[interface].bNumEndpoints; ++i) { if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) != 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 3)) { // Interrupt In Danish_Handle->port[iPort].CommItf.NotifEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; Danish_Handle->port[iPort].CommItf.NotifEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; Danish_Handle->port[iPort].InterruptInterval = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bInterval; } else if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) != 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 2)) { // Bulk In Danish_Handle->port[iPort].DataItf.InEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; Danish_Handle->port[iPort].DataItf.InEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } else if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress & 0x80U) == 0U && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bmAttributes == 2)) { // Bulk Out Danish_Handle->port[iPort].DataItf.OutEp = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].bEndpointAddress; Danish_Handle->port[iPort].DataItf.OutEpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[i].wMaxPacketSize; } } if (Danish_Handle->port[iPort].InterruptInterval < 16) Danish_Handle->port[iPort].InterruptInterval = 16; // Override if too short Danish_Handle->port[iPort].CommItf.NotifPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].CommItf.NotifEp); /*Allocate the length for host channel number in*/ (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].CommItf.NotifPipe, Danish_Handle->port[iPort].CommItf.NotifEp, /* Open pipe for Notification endpoint */ phost->device.address, phost->device.speed, USB_EP_TYPE_INTR, Danish_Handle->port[iPort].CommItf.NotifEpSize); (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].CommItf.NotifPipe, 0U); Danish_Handle->port[iPort].DataItf.OutPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].DataItf.OutEp); /*Allocate the length for host channel number out*/ Danish_Handle->port[iPort].DataItf.InPipe = USBH_AllocPipe(phost, Danish_Handle->port[iPort].DataItf.InEp); /*Allocate the length for host channel number in*/ (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].DataItf.OutPipe, Danish_Handle->port[iPort].DataItf.OutEp, /* Open channel for OUT endpoint */ phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, Danish_Handle->port[iPort].DataItf.OutEpSize); (void)USBH_OpenPipe(phost, Danish_Handle->port[iPort].DataItf.InPipe, Danish_Handle->port[iPort].DataItf.InEp, /* Open channel for IN endpoint */ phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, Danish_Handle->port[iPort].DataItf.InEpSize); if (iPort == 0) { Danish_Handle->port[iPort].chosenInterface = 1; } else if (iPort == 1) { Danish_Handle->port[iPort].chosenInterface = 2; } Danish_Handle->port[iPort].state = Danish_Startup_state; (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].DataItf.OutPipe, 0U); (void)USBH_LL_SetToggle(phost, Danish_Handle->port[iPort].DataItf.InPipe, 0U); printf("iPort %d interface %d Interrupt EP %02x, Bulk Out EP %02x, Bulk In EP %02x\n", iPort, Danish_Handle->port[iPort].chosenInterface, Danish_Handle->port[iPort].CommItf.NotifEp, Danish_Handle->port[iPort].DataItf.InEp, Danish_Handle->port[iPort].DataItf.OutEp); } return USBH_OK; }
What makes me think this might be a bug in the stm32h7xx_ll_usb.c is because reception is routed through USB_ReadPacket which always reads from USBx_DFIFO(0), whereas USB_WritePacket writes to USBx_DFIFO((uint32_t)ch_ep_num) which comes from ep->num or hc->ch_num
Has anyone had success using stm32 as a host with more than 1 IN endpoint?
Did you base your code on the stm32 example(s)?
Any recollection as to any changes you had to make?
(Backstory: I'm trying to communicate control a cellular modem and have simultaneous PPP and AT-commands. I can do this with one brand of modem over the UART serial port using CMUX protocol, but there's another brand of modem with built-in GPS that I'd like to use, only I can't get CMUX to work. So I'm trying USB with stm32 as host and two live USB CDC interfaces. In this case they are ECM not ACM)
2025-03-03 7:13 AM
Before anyone picks me up on this, I have just spotted the error in my printf, where I had the in and out endpoint numbers swapped (IN ones have bit 7 set). But that's just a diagnostic to show me that the system has picked up unique and credible endpoint numbers from the configuration descriptor.
I have looked further in the Reference Manual and I am struggling to understand it. (That's why I decided to experiment with how HAL and other ST examples drive the USB peripheral).
I read there is one* FIFO for incoming packets in host mode, and that it has control word(s) to tell you about where each packet should be routed. But I don't see the reading of those words in USB_ReadPacket, so maybe the example I was trying to build from uses a simpler control modem that can't cope with multiple IN endpoints. Which begs the question: is there an example of using the stm32 as a host where it can have multiple IN endpoints?
Trying to debug the example code, I am confused by the IN endpoints repeatedly being disabled (USB_HC_Halt being called from HCD_HC_IN_IRQHandler) with each NAK, and then I guess being re-enabled elsewhere (not traced where that happens). That might be necessary to force the peripheral to poll the next active IN endpoint.
*So the stm32yyxx_ll_usb.c code does look credible. And my problem is likely to lie elsewhere.
2025-03-03 7:32 AM
I don't use Cube nor the 'Hxx, but perhaps you may want to read what I wrote about the Synopsys OTG's RXFLVL interrupts here - that's from Device perspective, but Host in this regard is analogous.
JW