cancel
Showing results for 
Search instead for 
Did you mean: 

HAL USB MIDI Host code, MIDI stays in ''Idle'' mode, no data received.

Christopher Pappas
Senior II
Posted on September 13, 2016 at 01:03

Everything in the code seems to be working properly in Debug Mode. As soon as a keyboard is plugged into the USB-OTG port on the board,  the USB-Debug log shows the MIDI class is started, the Init routines run, the endpoints are linked to MIDI In and MIDI Out, and MIDI Process seems to be armed and ready to accept MIDI input from the keyboard.

However, when I play a note on the keyboard, I notice no data is being received. The MIDI core is idling, as if nothing is being output from the keyboard.

Does anyone have any thoughts about what could be causing this? 

Thank you!!

Christopher

#usb #host #hal-usb-midi-host-f7-nucleo-144 #bulk-in #synth-usb-midi-nucleo-144
1 ACCEPTED SOLUTION

Accepted Solutions
tsuneo
Senior
Posted on September 13, 2016 at 06:36

> However, when I play a note on the keyboard, I notice no data is being received.

1) The Major state machine in USBH_MIDI_Process() Unless your firmware calls USBH_BulkReceiveData(), no bulk IN transaction occurs on the bus. Your MIDI class implementation calls it in MIDI_ProcessReception():case MIDI_RECEIVE_DATA (usbh_MIHI.c). But it doesn't reach to this case while the major state machine (USBH_MIDI_Process()) stays in MIDI_IDLE_STATE. Do you really need MIDI_IDLE_STATE at this state machine ? The host bulk IN pipe should poll the EP repeatedly, to get ''asynchronous'' input from the device - so called polling bulk IN. While no data to send on the device, it returns NAK. And then, your firmware should call USBH_BulkReceiveData(), periodically. There is no ''idle'' state on this endpoint. For the host bulk OUT pipe, Of course, would stay in idle, while the host app doesn't have any data to send. This idle state should be processed in the independent state machine for the bulk OUT pipe, apart from the bulk IN state machine. 2) Problem of the host engine on bulk IN-NAK Ideally for USB host engine, it should process bulk IN-NAK without any firmware intervention - it should automatically re-send bulk IN transaction, until the firmware explicitly stops it. Unfortunately, the USB host engines on STM32, both of OTG_FS and OTG_HS, require firmware intervention to re-send IN transaction after NAK (**1). On the ST's Cube/HAL USB host library, this re-prime process is coded in USB NAK interrupt. Immediately after the re-activate of the host channel, another bulk IN-NAK occurs. And then, the host firmware suffers USB interrupt flood, while target device is NAKing. 50% or more of MCU time is eaten up by this interrupt process. This problem had discussed in

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32Cube%20USB%20Host%20Driver%20Interrupt%20Flood

. Here is the host process of the bulk IN-NAK on Cube/HAL. When the NAK interrupt occurs, the channel is deactivated by the host engine (OTG_HCCHARx.CHENA = 0). This code re-activates the channel at (**2) lines.

stm32f7xx_hal_hcd.c
static void HCD_HC_IN_IRQHandler (HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
...
line:916
else if ((USBx_HC(chnum)->HCINT) & USB_OTG_HCINT_NAK)
{ 
if(hhcd->hc[chnum].ep_type == EP_TYPE_INTR)
{
__HAL_HCD_UNMASK_HALT_HC_INT(chnum); 
USB_HC_Halt(hhcd->Instance, chnum); 
}
else if ((hhcd->hc[chnum].ep_type == EP_TYPE_CTRL)||
(hhcd->hc[chnum].ep_type == EP_TYPE_BULK))
{
/* re-activate the channel */ <----- (**2)
tmpreg = USBx_HC(chnum)->HCCHAR;
tmpreg &= ~USB_OTG_HCCHAR_CHDIS;
tmpreg |= USB_OTG_HCCHAR_CHENA;
USBx_HC(chnum)->HCCHAR = tmpreg;
}
hhcd->hc[chnum].state = HC_NAK;
__HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);
}
}

Your options are, A) Complete the transaction at NAK Separate EP_TYPE_BULK from EP_TYPE_CTRL in above ''if'' clause. Don't re-activate the channel for EP_TYPE_BULK. Instead, force the channel to halt, as if the transaction would successfully complete (XFRC) with Zero-Length packet.

__HAL_HCD_UNMASK_HALT_HC_INT(chnum); 
USB_HC_Halt(hhcd->Instance, chnum); 
hhcd->hc[chnum].state = HC_XFRC;
hhcd->hc[chnum].ErrCnt = 0;
__HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);

Your class code should repeat USBH_BulkReceiveData() calls periodically. B) Assign the bulk IN EP as interrupt IN EP As of the handle of periodical interrupt IN call, refer to host HID example. (**1) I'm not sure this is feature or bug of the engine. The latter revision of USB IP by Synopsys (ex. on EFM32) works ''ideally'' without any firmware intervention. Tsuneo

View solution in original post

3 REPLIES 3
tsuneo
Senior
Posted on September 13, 2016 at 06:36

> However, when I play a note on the keyboard, I notice no data is being received.

1) The Major state machine in USBH_MIDI_Process() Unless your firmware calls USBH_BulkReceiveData(), no bulk IN transaction occurs on the bus. Your MIDI class implementation calls it in MIDI_ProcessReception():case MIDI_RECEIVE_DATA (usbh_MIHI.c). But it doesn't reach to this case while the major state machine (USBH_MIDI_Process()) stays in MIDI_IDLE_STATE. Do you really need MIDI_IDLE_STATE at this state machine ? The host bulk IN pipe should poll the EP repeatedly, to get ''asynchronous'' input from the device - so called polling bulk IN. While no data to send on the device, it returns NAK. And then, your firmware should call USBH_BulkReceiveData(), periodically. There is no ''idle'' state on this endpoint. For the host bulk OUT pipe, Of course, would stay in idle, while the host app doesn't have any data to send. This idle state should be processed in the independent state machine for the bulk OUT pipe, apart from the bulk IN state machine. 2) Problem of the host engine on bulk IN-NAK Ideally for USB host engine, it should process bulk IN-NAK without any firmware intervention - it should automatically re-send bulk IN transaction, until the firmware explicitly stops it. Unfortunately, the USB host engines on STM32, both of OTG_FS and OTG_HS, require firmware intervention to re-send IN transaction after NAK (**1). On the ST's Cube/HAL USB host library, this re-prime process is coded in USB NAK interrupt. Immediately after the re-activate of the host channel, another bulk IN-NAK occurs. And then, the host firmware suffers USB interrupt flood, while target device is NAKing. 50% or more of MCU time is eaten up by this interrupt process. This problem had discussed in

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32Cube%20USB%20Host%20Driver%20Interrupt%20Flood

. Here is the host process of the bulk IN-NAK on Cube/HAL. When the NAK interrupt occurs, the channel is deactivated by the host engine (OTG_HCCHARx.CHENA = 0). This code re-activates the channel at (**2) lines.

stm32f7xx_hal_hcd.c
static void HCD_HC_IN_IRQHandler (HCD_HandleTypeDef *hhcd, uint8_t chnum)
{
...
line:916
else if ((USBx_HC(chnum)->HCINT) & USB_OTG_HCINT_NAK)
{ 
if(hhcd->hc[chnum].ep_type == EP_TYPE_INTR)
{
__HAL_HCD_UNMASK_HALT_HC_INT(chnum); 
USB_HC_Halt(hhcd->Instance, chnum); 
}
else if ((hhcd->hc[chnum].ep_type == EP_TYPE_CTRL)||
(hhcd->hc[chnum].ep_type == EP_TYPE_BULK))
{
/* re-activate the channel */ <----- (**2)
tmpreg = USBx_HC(chnum)->HCCHAR;
tmpreg &= ~USB_OTG_HCCHAR_CHDIS;
tmpreg |= USB_OTG_HCCHAR_CHENA;
USBx_HC(chnum)->HCCHAR = tmpreg;
}
hhcd->hc[chnum].state = HC_NAK;
__HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);
}
}

Your options are, A) Complete the transaction at NAK Separate EP_TYPE_BULK from EP_TYPE_CTRL in above ''if'' clause. Don't re-activate the channel for EP_TYPE_BULK. Instead, force the channel to halt, as if the transaction would successfully complete (XFRC) with Zero-Length packet.

__HAL_HCD_UNMASK_HALT_HC_INT(chnum); 
USB_HC_Halt(hhcd->Instance, chnum); 
hhcd->hc[chnum].state = HC_XFRC;
hhcd->hc[chnum].ErrCnt = 0;
__HAL_HCD_CLEAR_HC_INT(chnum, USB_OTG_HCINT_NAK);

Your class code should repeat USBH_BulkReceiveData() calls periodically. B) Assign the bulk IN EP as interrupt IN EP As of the handle of periodical interrupt IN call, refer to host HID example. (**1) I'm not sure this is feature or bug of the engine. The latter revision of USB IP by Synopsys (ex. on EFM32) works ''ideally'' without any firmware intervention. Tsuneo
Christopher Pappas
Senior II
Posted on September 17, 2016 at 00:34

Everything seems to be working correctly now,

Thanks again Tsuneo and all users on the forum for your expert help!!

Christopher 

Masoud Nejati
Associate
Posted on February 23, 2018 at 12:48

I found that the behavior of the different USB mass storage flashes are different.

Specially in non-standard devices there are some unexpected response to usb host.

for example I wrote my own usb host stack for MSC and test it with 6 different dongles, one dongle “hp brand 8GB� does not accept port reset with reset duration of exactly 10 ms! If I extend reset time to 12 ms, then it works.

the other one “16GB SAMSUNG ( but vendor ID is some other unfamous company!)� can not be enumerated correctly if the first control transfer be not “Set-address�.

for example at the first my code was enumerating the device as following:

-reset port

-get the first 8 bytes of device descriptor

- reset port

- set address.

-...

This way my code was able to succesfully enumerate 5 usb dongles of my 6 dongles. But one of them need to get  enumeration as following to work properly:

-reset

-set address

- get full 18 bytes device descriptor

-...

after I modified my code to do enumuration as above, then all my 6 dongles work properly.

I see some advanced usb host like Microsoft stack can work with almost all devices.

perhaps they are complicated state machines that have special program for any special errors.

unfortunately RM0090 is not well-explained reference for people who want to accomplish their stack by only reading RM0090.

I have wasted long time of my life for this issue and fond many points out of reference manual.

I hope every one who waste his time and found some error, share it here for other people to not wast their time too.