cancel
Showing results for 
Search instead for 
Did you mean: 

Audio async USB attempt not working - feedback not read and/or no effect

jmf1
Senior
Posted on September 04, 2016 at 21:45

Hello,

I would really need some help to get working an USB audio Async interface on the stm32F4 discovery board, with explicit feedback. I'm working on that topic since several weeks and cant't get it work. This is a showstopper for my project if I can't get it work.

I developped my application based on the ST usb device class audio class firmware. I started from previous attempts as presented

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a//my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Audio%20streaming%20clock%20sync&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=2366

and here

http://we.easyelectronics.ru/electro-and-pc/asinhronnoe-usb-audio-na-stm32.html

, and ported the code to the newer HAL libs. But didn't succeeded to get the thing work.

I finally stripped down my application to a minimum implementation, to help solve the issue. It is attached to this message

The main part is a modified usbd_audio.c (modification of the audio class source). This code adds the feedback end point to the descriptor. in the dataout function, I read the incoming data from the PC et get the number of samples. Different leds are used to show:

- 48 samples in the frame => expected value without feedback

- more samples => feedback has an effect

- less samples => feedback has an effect

Every 4 SOF (feedback rate in the descriptor: 2), I send through the feedback EP a fixed value 0x0C0666, which corresponds to 48.1 instead of the normal expected value of 48 (0x0C0000). This should pull the number of received samples per frame toward a different value from 48. But this never happens.

The feedback is only sent if a dedicated flag has been reset by the DataIn function, which I think means that the previous feedback has been read by the host. 

In fact, it seems that I can send the feedback 1 or 2 times, and then the datain function is not called anymore (which could mean that the feedback value is not read by the PC anymore?). I still receive the data stream and music...

I don't have any other idea to debug the thing. Help would be warmly welcomed.

Note that once this will be working, I will rework on a proper measurement of the feedback using a timer gated by the SOF interrupt. 

I attach my streamlined project.

More general information are also available here:

https://github.com/jmf13/Const_DSP_I2S_DAC/wiki/Async-USB-inspiration

https://github.com/jmf13/USB_Async_DAC

Best regards,

JM

#asynchronous #audio #audio #usb #usb #usb-audio-async #asynchronous
9 REPLIES 9
jmf1
Senior
Posted on September 05, 2016 at 23:13

Could my issue come from bit parity isuues? I currently don't manage bit parity of the Feedback data. I looked at the USB spec and found no requirement on that topic (I maybe didn't looked everywhere)

I also have some examples of code for AVR32, and they do something like this (using RTOS), that I can't exactly map to Stm32 USB lib:

if (Is_usb_in_ready(EP_AUDIO_OUT_FB)) {    // Endpoint buffer free ?

    ...

    Usb_ack_in_ready(EP_AUDIO_OUT_FB);    // acknowledge in ready

    Usb_reset_endpoint_fifo_access(EP_AUDIO_OUT_FB);

    ...

    Usb_write_endpoint_data(EP_AUDIO_OUT_FB, 8, sample_LSB);

    Usb_write_endpoint_data(EP_AUDIO_OUT_FB, 8, sample_SB);

    Usb_write_endpoint_data(EP_AUDIO_OUT_FB, 8, sample_MSB);

    Usb_write_endpoint_data(EP_AUDIO_OUT_FB, 8, sample_HSB);

    ...                    

    Usb_send_in(EP_AUDIO_OUT_FB);

} // end if (Is_usb_in_ready(EP_AUDIO_OUT_FB)) // Endpoint buffer free ?

How to check if EP is ready with Stm32? What could be this Usb_ack_in_ready ? Usb_reset_endpoint_fifo_access ?

No management of parity bits here...

I understand that what we have to do, is provide a feedback frame every time the feedback EP is empty. But why in my case would the host stop reading the feedback EP after 1 or 2 times?

JMF 

tsuneo
Senior
Posted on September 06, 2016 at 02:45

A) Descriptor problems

With quick look in your descriptors, it has a couple of conflicts. Correct them first. 1) bNrChannels conflict In your ''Type III Format'' descriptor, bNrChannels is set to 2 But in your input terminal desc, bNrChannels is 1 Also, wChannelConfig is wrong, even if it was mono

usb_audio.c
/* USB AUDIO device Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_AUDIO_CfgDesc[USB_AUDIO_CONFIG_DESC_SIZ] __ALIGN_END =
{
...
line: 238
/* USB Speaker Input Terminal Descriptor */
AUDIO_INPUT_TERMINAL_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_CONTROL_INPUT_TERMINAL, /* bDescriptorSubtype */
0x01, /* bTerminalID */
0x01, /* wTerminalType AUDIO_TERMINAL_USB_STREAMING 0x0101 */
0x01,
0x00, /* bAssocTerminal */
0x01, /* bNrChannels */ <----- 0x02
0x00, /* wChannelConfig 0x0000 Mono */ <----- 0x03
0x00,

2) Audio format type conflict You assign Type III format descriptor, but wFormatTag on AS interface desc is set to PCM Usually, wFormatTag of Type III is assigned to IEC1937_AC-3 (0x2001) or another one - refer to ''Table A-3: Audio Data Format Type III Codes'' (p29,

http://www.usb.org/developers/docs/devclass_docs/frmtspdf

)

line:303
/* USB Speaker Audio Streaming Interface Descriptor */
AUDIO_STREAMING_INTERFACE_DESC_SIZE, /* bLength */
AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */
AUDIO_STREAMING_GENERAL, /* bDescriptorSubtype */
0x01, /* bTerminalLink */
0x01, /* bDelay */
0x01, /* wFormatTag AUDIO_FORMAT_PCM 0x0001*/ <---- 0x01
0x00, <---- 0x20

Do you really need Type III? Mac and Linux (ALSA) support it, but Windows don't.

https://msdn.microsoft.com/en-us/library/windows/hardware/ff538676%28v=vs.85%aspx

B) isoc IN EP on OTG_FS/OTG_HS As discussed on

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/oddeven%20bit%20in%20assynchronous#DisplayLink69529

, - It is unknown exactly when host would start the isoc IN transaction - host puts the isoc IN transactions in the interval claimed at bRefresh field And then, your firmware should prime the isoc IN EP, at every frame. If no isoc IN transaction would occur in the frame, ISOIXFR flag should up - the isoc IN EP should deactivate once, re-activate for the next priming. C) Feedback value measurement > Note that once this will be working, I will rework on a proper measurement of the feedback using a timer gated by the SOF interrupt. For OTG_FS/OTG_HS, SOF trigger is provided to Timer2 (TIM2_OR:ITR1_RMP) It gives more precise measurement than SOF interrupt. Tsuneo
jmf1
Senior
Posted on September 06, 2016 at 10:14

Dear Tsuneo,

Thank you very much for your help. I will correct my descriptor and come back to you as fast as possible.

What would happen, if I prime the isoc IN EP, and then wait for the DataIn call back to prime a new value?

The host would pull the feedback at its own rate. I don't expect big changes of the feedback value over time, and my buffer could accommodate some inertia. I imagine that it wouldn't be a too big impact if my feedback value is read only after 2 or 3 frames. CAn this work or is it potentially a blocking issue.

I would also be very happy to understand the bit parity constraints on the feedback value data.  I looked at the USB spec and found no requirement on that topic (I maybe didn't looked everywhere)

Best regards,

JM

jmf1
Senior
Posted on September 06, 2016 at 23:26

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6kU&d=%2Fa%2F0X0000000bua%2FcrV_pD0FG.2AZvGnpPp7ZDc4r.8C2Q8b1ahcewZ.dUs&asPdf=false
jmf1
Senior
Posted on September 07, 2016 at 13:31

Up to now, I have mainly studied the USBD Audio Class implementation, and some of the USBD_LL_xxxx USB device library. I would have questions about how to implement the below process.

B) isoc IN EP on OTG_FS/OTG_HS

As discussed on

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/oddeven%20bit%20in%20assynchronous#DisplayLink69529

, - It is unknown exactly when host would start the isoc IN transaction - host puts the isoc IN transactions in the interval claimed at bRefresh field And then, your firmware should prime the isoc IN EP, at every frame. If no isoc IN transaction would occur in the frame, ISOIXFR flag should up - the isoc IN EP should deactivate once, re-activate for the next priming.

=> Can I prime my feedback value at each frame in the SOF callback just using:

USBD_LL_Transmit (pdev, AUDIO_IN_EP, (uint8_t *) &feedback_data, 3); ?

Does the above action guaranties a good parity bit for the IN transaction?

Is the USBD_AUDIO_IsoINIncomplete callback ''equivalent'' to ''if no isoc IN transaction would occur in the frame, ISOIXFR flag should up'' ?

Is there a way to achieve ''IN EP should deactivate once, re-activate for the next priming'' with USBD_LL_xxxx library, like:

USBD_LL_CloseEP(pdev,AUDIO_IN_EP);
USBD_LL_OpenEP(pdev, AUDIO_IN_EP, USBD_EP_TYPE_ISOC, 3);

or is it needed to dig to lowest level libraries?

Thanks for your help,

Best regards,

JM

jmf1
Senior
Posted on September 07, 2016 at 22:15

I try to prime the feedback data at each SOF. I have put some breakpoints and LED On commands in both:

- USBD_AUDIO_IsoINIncomplete

- USBD_AUDIO_DataIn

And none is called at all. The music is played, the USBD_AUDIO_SOF function is called... but the EP used for feedback is dead...

I have read a lot about all the conditions to get the feedback data read by the Host, but if I understand well, at least one of USBD_AUDIO_IsoINIncomplete or USBD_AUDIO_DataIn should be called at each frame... No ?

And as long as I don't have a call on the IsoINIncomplete, I can't perform the required ''N EP should deactivate once, re-activate for the next priming''...

Thanks a lot for helping,

Best regards,

JM

jmf1
Senior
Posted on September 08, 2016 at 22:24

After further investigation, using STM32Cube_FW_F4_V1.13.0 and a modified version of usbd_audio.c MCD Application Team V2.4.2 11-December-2015, it appears that the 

USBD_AUDIO_IsoINIncomplete is not called back by usbd_core.c

The code there is:

 USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)

{

  return USBD_OK;

}

Note that it is the same for:

USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)

{

  return USBD_OK;

}

It seems to me that the usbd_core library may not be suitable for the implementation of USB Async audio with feedback, as it doesn't manage as such all the relevant callbacks... I'm going to post a comment on the firmware forum.

Best regards,

JM 

jmf1
Senior
Posted on September 10, 2016 at 19:52

To implement Tsuneo's proposed strategy, I have developped the following code. This is a bare minimum implementation:

- at SOF, prime a fixed feedback value,

- at DataIn, do nothing,

- at IsoIncomplete, deactivate and reactivate the IN EP and reprime the feedback for the next frame.

But it doesn't work. The DataIn and IsocIncomplete callback are not executed except if I play with breakpoints (see other thread)

Thanks in advance if you can say if it looks OK or wrong (something obvioulsy wrong).

Important code below... (and attached the complete project, but less clean because of many trials)

JMF

/**

* @brief USBD_AUDIO_SOF

* handle SOF event

* @param pdev: device instance

* @retval status

* SOF token handler updates the data for the final synchronization point

*/

static uint8_t USBD_AUDIO_SOF (USBD_HandleTypeDef *pdev)

{

uint8_t res;

static uint16_t n;

USBD_AUDIO_HandleTypeDef *haudio;

haudio = (USBD_AUDIO_HandleTypeDef*) pdev->pClassData;

USB_OTG_GlobalTypeDef *USBx = pdev->pData;

if (haudio->alt_setting==1)

{

//?? for test purpose, prime Feedback to Audio_IN_EP every frame

feedbacktable[0]= 0x66;

feedbacktable[1]= 0x06;

feedbacktable[2]= 0x0C;

BSP_LED_Toggle(LED6);

USBD_LL_Transmit(pdev, AUDIO_IN_EP, (uint8_t *) feedbacktable[0], 3);

}

return USBD_OK;

}

/**

* @brief USBD_AUDIO_DataIn

* handle data IN Stage

* @param pdev: device instance

* @param epnum: endpoint index

* @retval status

* If the data is successfully sent - all repeated

*/

static uint8_t USBD_AUDIO_DataIn (USBD_HandleTypeDef *pdev,

uint8_t epnum)

{

BSP_LED_Toggle(LED3);

return USBD_OK;

}

/**

* @brief USBD_AUDIO_IsoINIncomplete

* handle data ISO IN Incomplete event

* @param pdev: device instance

* @param epnum: endpoint index

* @retval status

*/

static uint8_t USBD_AUDIO_IsoINIncomplete (USBD_HandleTypeDef *pdev, uint8_t epnum)

{

USB_OTG_GlobalTypeDef *USBx = pdev->pData;

//?? Not sure that the flushing of the EP is needed

USBD_LL_FlushEP(pdev,AUDIO_IN_EP);

/* EP disable, IN data in FIFO */

USBx_INEP(AUDIO_IN_EP&0x7f)->DIEPCTL = (USB_OTG_DIEPCTL_EPDIS | USB_OTG_DIEPCTL_SNAK);

/* EP enable, IN data in FIFO */

USBx_INEP(AUDIO_IN_EP&0x7f)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);

// reprime the feedback value for next frame

feedbacktable[0]= 0x66;

feedbacktable[1]= 0x06;

feedbacktable[2]= 0x0C;

USBD_LL_Transmit(pdev, AUDIO_IN_EP, (uint8_t *) &feedbacktable, 3);

}

________________

Attachments :

USB_Async_DAC.zip : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I1HV&d=%2Fa%2F0X0000000blf%2F8n207Jd49R4Tp3CciFt9T0K631PO1MA80UVupB8AJjk&asPdf=false
jmf1
Senior
Posted on September 11, 2016 at 09:30

Small good news: yesterday, I started to use a Linux machine to interface with the stm32, instead of the windows machine... and it behaves differently. I now can get shorter or longer USB frames according to the feedback I provide. This is still not perfect as what I get when I ask for more samples is strange...

But this is a step forward :)

JMF