2016-09-04 12:45 PM
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¤tviews=2366
and herehttp://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 #asynchronous2016-09-05 02:13 PM
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? JMF2016-09-05 05:45 PM
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 monousb_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. Tsuneo2016-09-06 01:14 AM
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
2016-09-06 02:26 PM
2016-09-07 04:31 AM
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
2016-09-07 01:15 PM
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, JM2016-09-08 01:24 PM
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, JM2016-09-10 10:52 AM
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=false2016-09-11 12:30 AM