cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 USB Host communicating with a custom class device with multiple interrupt out endpoints.

RyanKnowsNothing
Associate II

Hi all,

I am using the Nucleo-L476RG dev board and am using it as a USB Host to communicate with a custom vendor device which has 4 interrupt IN endpoints and 4 interrupt OUT endpoints.

My code has been generated using CubeMX and I have generated a custom class for this device. The custom class is loosely based on the HID keyboard/mouse class example provided in the package.

I can successfully enumerate my device and initiate my custom class driver. Communication with the 4 IN pipes works fine, however when writing data to the interrupt OUT pipes, my program crashes as soon as I send to the 3rd of 4th endpoint. If I don’t send any data to the 3rd or 4th endpoint my code work fine.

If I USBH_ClosePipe + USBH_FreePipe the out pipe after each transaction, then USBH_AllocPipe + USBH_OpenPipe the corresponding pipe before the transaction, the code works with all 4 out endpoints, but this doesn’t seem like the right thing to do.

By ‘crash’ It’s difficult to tell what it is doing exactly, however my main while loop gets stuck within the MX_USB_HOST_Process function, and the debugger seems to indicate that it’s just stuck in the HAL_HCD_IRQHandler function.

Hoping anyone can provide any suggestions on what I might be missing or if this is expected when dealing with multiple interrupt endpoints?

I'm not particularly knowledgeable in the low level USB details so apologies if it's obvious.

My Class Init Function:

static USBH_StatusTypeDef USBH_DEVICE_InitDevice(USBH_HandleTypeDef *phost) {
interface = USBH_FindInterface(phost, phost->pActiveClass->ClassCode, 0xFF, 0xFF);
USBH_SelectInterface(phost, interface);
phost->pActiveClass->pData = (DEVICE_HandleTypeDef *) USBH_malloc(sizeof(DEVICE_HandleTypeDef));
DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
DEVICE_Handle->state = DEVICE_INIT;
 
//We know what all the addresses are without parsing the descriptor so just allocate them
DEVICE_Handle->InEp[0] = 0x81;
DEVICE_Handle->InEp[1] = 0x83;
DEVICE_Handle->InEp[2] = 0x85;
DEVICE_Handle->InEp[3] = 0x87;
 
DEVICE_Handle->OutEp[0] = 0x01;
DEVICE_Handle->OutEp[1] = 0x03;
DEVICE_Handle->OutEp[2] = 0x05;
DEVICE_Handle->OutEp[3] = 0x07;
 
DEVICE_Handle->InEpSize = 0x0020;
DEVICE_Handle->OutEpSize = 0x0020;
 
 
//Create a Pipe to each IN end point
for (uint8_t i = 0; i < 4; i++) {
	DEVICE_Handle->InPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->InEp[i]);
	USBH_OpenPipe(phost, DEVICE_Handle->InPipe[i], DEVICE_Handle->InEp[i],
		phost->device.address, phost->device.speed,
		USB_EP_TYPE_INTR, DEVICE_Handle->InEpSize);
	
	USBH_LL_SetToggle(phost, DEVICE_Handle->InPipe[i], 0);
	printf("InEPaddr 0x%02x allocated to pipe 0x%02x\n",
		DEVICE_Handle->InEp[i], DEVICE_Handle->InPipe[i]);
}
	
//Create a Pipe to each OUT end point
for (uint8_t i = 0; i < 4; i++) {
	DEVICE_Handle->OutPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->OutEp[i]);
	USBH_OpenPipe(phost, DEVICE_Handle->OutPipe[i], DEVICE_Handle->OutEp[i],
		phost->device.address, phost->device.speed,
		USB_EP_TYPE_INTR, DEVICE_Handle->OutEpSize);
	USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i], 0);
	printf("OutEPaddr 0x%02x allocated to pipe 0x%02x\n", DEVICE_Handle->OutEp[i], DEVICE_Handle->OutPipe[i]);
}
 
DEVICE_Handle->state = DEVICE_POLL;
}

My SOFProcess Function

static USBH_StatusTypeDef USBH_DEVICE_SOFProcess(USBH_HandleTypeDef *phost) {
	DEVICE_HandleTypeDef *DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
 
	if (DEVICE_Handle->state == DEVICE_POLL) {
		if ((phost->Timer - DEVICE_Handle->InTimer) > 1) {
			DEVICE_Handle->state = DEVICE_GET_DATA;
		}
		if ((phost->Timer - DEVICE_Handle->OutTimer) > 8) {
			DEVICE_Handle->state = DEVICE_SEND_DATA;
		}
	}
	return USBH_OK;
}

My Process Function:

static USBH_StatusTypeDef USBH_DEVICE_Process(USBH_HandleTypeDef *phost) {
DEVICE_HandleTypeDef *DEVICE_Handle = (DEVICE_HandleTypeDef *) phost->pActiveClass->pData;
switch (DEVICE_Handle->state) {
case DEVICE_INIT:
	DEVICE_Handle->Init(phost);
	DEVICE_Handle->state = DEVICE_POLL;
	break;
case DEVICE_POLL:
	/* CHECK IF INPUT DATA HAS BEEN RECEIVED */
	for (uint8_t i = 0; i < 4; i++) {
		USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->InPipe[i]);
		if ((URB == USBH_URB_DONE || URB == USBH_URB_IDLE) && DEVICE[i].inputDataReady == 0) {
			DEVICE[i].inputDataReady = 1;
			USBH_DEVICE_ParseReport(i, DEVICE[i].inputBuffer);
		} else if (URB == USBH_URB_ERROR) {
			DEVICE[i].inputDataReady = 1;
			printf("[%u]: USBH_URB_ERROR",i);
		} else if (URB == USBH_URB_STALL) {
			if (USBH_ClrFeature(phost, DEVICE_Handle->InEp[i]) == USBH_OK) {
				DEVICE_Handle->state = DEVICE_GET_DATA;
			}
			printf("[%u]: USBH_URB_STALL",i);
			DEVICE[i].inputDataReady = 1;
		}
	}
	/* CHECK IF OUTPUT DATA HAS BEEN SENT */
	for (int i = 0; i < 4; i++) {
		USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->OutPipe[i]);
		if (DEVICE[i].outputDataSent == 0 && (URB == USBH_URB_DONE/* || URB == USBH_URB_IDLE*/)) {
			USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i],
				1 ^ USBH_LL_GetToggle(phost, DEVICE_Handle->OutPipe[i]));
			//USBH_ClosePipe(phost, DEVICE_Handle->OutPipe[i]);
			//USBH_FreePipe(phost, DEVICE_Handle->OutPipe[i]);
			//DEVICE_Handle->OutPipe[i] = 0x00;
			DEVICE[i].outputDataSent = 1;
		} else if (URB == USBH_URB_ERROR) {
			DEVICE[i].outputDataSent = 1;
			printf("[%u]: USBH_URB_ERROR", i);
		} else if (URB == USBH_URB_STALL) {
			if (USBH_ClrFeature(phost, DEVICE_Handle->OutEp[i]) == USBH_OK) {
				DEVICE_Handle->state = DEVICE_SEND_DATA;
			}
			printf("[%u]: USBH_URB_STALL", i);
			DEVICE[i].outputDataSent = 1;
		}
	}
	break;
case DEVICE_SEND_DATA:
	//printf("DEVICE_SEND_DATA\n");
	for (int i = 0; i < 4; i++) {
		//DEVICE_Handle->OutPipe[i] = USBH_AllocPipe(phost, DEVICE_Handle->OutEp[i]);
		//USBH_OpenPipe(phost, DEVICE_Handle->OutPipe[i], DEVICE_Handle->OutEp[i],
			phost->device.address, phost->device.speed,
			USB_EP_TYPE_INTR, DEVICE_Handle->OutEpSize);
		//USBH_LL_SetToggle(phost, DEVICE_Handle->OutPipe[i], 0);
		USBH_InterruptSendData(phost, DEVICE[i].outputBuffer, 12, DEVICE_Handle->OutPipe[i]);
		DEVICE_Handle->state = DEVICE_POLL;
		DEVICE_Handle->OutTimer = phost->Timer;
		DEVICE[i].outputDataSent = 0;
	}
	/* no break we want to jump into GET_DATA below */
case DEVICE_GET_DATA:
	for (uint8_t i=0;i<4;i++){
		USBH_URBStateTypeDef URB = USBH_LL_GetURBState(phost, DEVICE_Handle->InPipe[i]);
		if(URB==USBH_URB_IDLE || URB==USBH_URB_DONE){
			USBH_InterruptReceiveData(phost, DEVICE[i].inputBuffer,
				DEVICE_Handle->InEpSize, DEVICE_Handle->InPipe[i]);
			DEVICE[i].inputDataReady = 0;
		}
	}
	DEVICE_Handle->state = DEVICE_POLL;
	DEVICE_Handle->InTimer = phost->Timer;
	break;
default:
	printf("Not meant to be here %u", DEVICE_Handle->state);
	break;
}
return USBH_OK;
}

4 REPLIES 4

You have no other pipes in service?

JW

RyanKnowsNothing
Associate II

Hi JW,

What do you mean?

I am trying to have 8 pipes in service. None in service anywhere else.

Should all other pipes have successfully completed their transactions before sending data on the next pipe?

Thanks for your response.

RW

No. The Synopsys OTG IP in its 'L476 incarnation imposes a limit of maximum 8 transactions at a time in one cathegory (nonperiodic=CTRL+BULK, periodic=ISOC+INTR) due to so called queues implemented in hardware. If I know it correctly, Cube is written with simply ignoring this limit (it should've checked the respective register, OTG_HNPTXSTS/OTG_HPTXSTS, and postpone starting a transaction if respective queue or FIFO full). I may have misread Cube's code though; I don't Cube.

You seem to start to struggle at 6+1 transactions so maybe this is not the root of your problems, although the fact that you got it working through staggering transactions hints towards that.

You've already made sure you have enough FIFO space available, I presume.

JW

RyanKnowsNothing
Associate II

Thanks JW,

Sounds like it certainly could be related. I'll look into it and see how I go!