2013-02-13 08:00 AM
I am interested in techniques for synchronizing the STMF4 I2S clock with an incoming audio stream on the USB. The clock on the source and the clock on the STM32 board will always be slightly out of sync causing either its DMA to the codec to be ahead or behind the stream. Dropping / stuffing samples or re-sampling the stream are not preferred solutions.
Thanks, Tony #stm32f4-i2s #streaming2013-02-15 10:26 AM
Hello LowPower,
That is an interesting point on WinXp support. I would prefer that the Client (STM32) adapt to the Host, but if adaption is handled on the host end for Win7 on up it is sufficient. I will have to check IOS too. Regards, AjG2013-02-15 10:29 AM
Update: A better link for USB Audio support is in USB.ORG's approved class specification documents section : http://www.usb.org/developers/devclass_docs#approved
FYI, AjG2013-02-16 03:10 AM
> I think that the host must support the feedback and unfortunately windows XP doesn't support it
Here is an interesting post by a MS engineer, which answered to Async support on Windows.http://www.freelists.org/post/wdmaudiodev/USB-Audio-synch-mechanism
Date: Tue, 13 May 2003 19:11:09 -0700The ''USB Audio and Windows'' whitepaper (available from
http://www.microsoft.com/whdc/hwdev/tech/audio/USBaud.mspx[1]) says:
''Starting with Windows 98, Usbaudio.sys supported the adaptive and
synchronous endpoints, but it did not implement the asynchronous endpoint
correctly. Full support for asynchronous endpoints in Usbaudio.sys is
plannedfor Windows Longhorn.''
However, I have implemented async. isoch. synch. mechanism on two different
devices - and it has worked perfectly - starting from Windows 98 SE.
What's not correct about the implementation?
Thanks.
Devendra.
Date: Wed, 14 May 2003 09:37:31 -0700
Hello Devendra,
The error with async endpoints has to do with the timeliness of the
updating of the sample rate based on the data from the feedback
endpoint. Technically it will work with all OS's back to Win98, but only
recently have I fixed the timing.
Thanks,
DJ Sisolak
Microsoft Corp. Tsuneo
2013-02-16 04:20 AM
As of synchronization methods, the USB audio spec refers to this chapter of the original USB2.0 spec.
5.12.4.1 Synchronization Type The outline of Async Sink is here.|
Host <---|---> Device | Counter +---------- synch IN EP <--- shift to <--- gated by SOF | 10.14 format ^ | | | Master clock (256fs) Feedback | | | | Divider (/256) V | Audio rate V Source ===(conversion)===> isoc OUT EP ===> buffer ==========> DAC ===> Speaker Above isoc OUT EP provides the audio stream data, Synch (feedback) IN EP gives the frequency ratio of the sampling clock / SOF timing. Using this ratio, host tunes the number of samples per packet, so that it matches to the sampling clock on the device. On the device side, the master clock is fed to a counter on a STM32F4, as its external clock source. Gated by SOF trigger, the counter value is captured at SOF timing. The difference of two contiguous captures gives the ''ratio'' of the sampling clock and SOF. The firmware puts this ratio to the feedback EP, after shifting it into 10.14 (10.10) fixed-point format. The principle is so simple. Of course, the devil is in the details ;) Tsuneo2013-02-26 02:35 PM
2013-03-15 04:34 PM
Hi ajg,
While waiting for tsuneo to come back (I hope) let me ask you politely to explain how TIM2 is configured to capture the desynchro in your application? Thanks in advance.2013-03-22 02:52 PM
Hi all,
The desynchro (delay) must be measured between main APBx clock or I2S clock? In STM32 I2S has a dedicated PLL that is why APBx clock is different from I2S clock. If the delay is to be measured is with I2S clock which signal must be considred: WS: word select or MCLK: Master clock or Bit clock2015-11-06 02:52 AM
Hi, all. Let me continue this discussion, as I haven't found any working example for USB Audio on STM32 with explicit feedback.
First of all, we need to measure SOF to MCLK/256 rate. This is done with TIM2.CCR1 channel. This code [CODE]//timer2 is clocked via the MCLK frequency and captures its counter value by SOF eventvoid TMR2_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; // TIM2_CH1_ETR pin (PA.15) configuration GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_TIM2); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM_ICInitStructure; TIM_OCInitTypeDef TIM_OCInitStructure; /* Enable the TIM2 clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_Cmd(TIM2, DISABLE); /* Time base configuration */ TIM_TimeBaseStructure.TIM_Period = 0xffffffff; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //clock TIM2 via ETR pin TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0); /* TIM2 input trigger selection */ /* At leading edge of SOF signal we need to capture TIM2 value into CCR1 register and reset TIM2 counter itself. In the SOF interrupt handler capture flag is reset and value from TIM2 CCR1 register is accumulated to compute feedback value. As according to the standard, explicit feedback value contains ratio of sample rate to SOF rate and should be sent in 10.14 format, it is accumulated during 2^FEEDBACK_RATE periods. To compute feedback value it should be shifted left by 6 digits (due to the MCLK to sample rate 256 times ratio). */ /* Enable capture*/ TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_TRC; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInit(TIM2, &TIM_ICInitStructure); //program TMR2 to USB SOF capture TIM_RemapConfig(TIM2,TIM2_USBFS_SOF); TIM_SelectInputTrigger(TIM2, TIM_TS_ITR1); TIM_SelectSlaveMode(TIM2,TIM_SlaveMode_Reset); TIM_Cmd(TIM2, ENABLE);}[/CODE]2015-11-06 10:17 PM
At the SOF handler, we need to place feedback calculating routine. Maybe like this:
[CODE]static uint8_t usbd_audio_SOF (void *pdev){ uint8_t res;static uint16_t n; /* Check if there are available data in stream buffer. In this function, a single variable (PlayFlag) is used to avoid software delays. The play operation must be executed as soon as possible after the SOF detection. */if (usbd_audio_AltSet==1){ if (PlayFlag) { /* Start playing received packet */ res=AUDIO_OUT_fops.AudioCmd((uint8_t*)(IsocOutRdPtr), /* Samples buffer pointer */ AUDIO_OUT_PACKET, /* Number of samples in Bytes */ AUDIO_CMD_PLAY); /* Command to be processed */ /* Increment the Buffer pointer or roll it back when all buffers all full */ IsocOutRdPtr += AUDIO_OUT_PACKET; //48 samples*4bytes/sample=192 bytes if (IsocOutRdPtr >= (IsocOutBuff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM))) {/* Roll back to the start of buffer */ IsocOutRdPtr = IsocOutBuff; } }; accum+=(TIM2->CCR1); SOF_num++; if ((!flag)) { if (SOF_RATE>6) {feedback_data+=accum>>(SOF_RATE-6);} else {feedback_data+=accum<<(6-SOF_RATE);}; feedback_data>>=1; DCD_EP_Tx (pdev, AUDIO_IN_EP, (uint8_t *) &feedback_data, 3); accum=0; flag=1; SOF_num=0; }; }return USBD_OK;}static uint8_t usbd_audio_DataIn (void *pdev, uint8_t epnum){ if (epnum == (AUDIO_IN_EP&0x7f)) { flag=0; SOF_num=0; } return USBD_OK;}uint8_t tmpbuf[AUDIO_OUT_PACKET+16] __attribute__ ((aligned(4)));static volatile uint16_t rest;static volatile uint16_t max_length;static uint8_t usbd_audio_DataOut (void *pdev, uint8_t epnum){ uint16_t curr_length; uint8_t flag; uint16_t curr_pos,rest; static uint16_t tmp; if (epnum == AUDIO_OUT_EP) { curr_length=USBD_GetRxCount (pdev,epnum); DCD_EP_PrepareRx(pdev, AUDIO_OUT_EP, (uint8_t*)tmpbuf, curr_length); curr_pos=(IsocOutWrPtr-IsocOutBuff); rest=(AUDIO_OUT_PACKET * OUT_PACKET_NUM)-curr_pos; if (curr_length<AUDIO_OUT_PACKET) {STM_EVAL_LEDToggle(LED3);}; if (curr_length>AUDIO_OUT_PACKET) {STM_EVAL_LEDToggle(LED5);}; if (rest<curr_length) { if (rest>0) {memcpy((uint8_t*)IsocOutWrPtr,tmpbuf,rest); IsocOutWrPtr = IsocOutBuff;}; if ((curr_length-rest)>0) {memcpy((uint8_t*)IsocOutWrPtr,tmpbuf+rest,curr_length-rest); IsocOutWrPtr+=curr_length-rest;}; } else { if (curr_length>0) {memcpy((uint8_t*)IsocOutWrPtr,tmpbuf,curr_length); // Increment the Buffer pointer IsocOutWrPtr += curr_length;}; } //roll it back when all buffers are full if (IsocOutWrPtr >= (IsocOutBuff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM))) IsocOutWrPtr = IsocOutBuff; /* Toggle the frame index */ ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame = (((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame)? 0:1; /* Prepare Out endpoint to receive next audio packet */ /* DCD_EP_PrepareRx(pdev, AUDIO_OUT_EP, (uint8_t*)tmpbuf, 196);*/ /* Trigger the start of streaming only when half buffer is full */ if ((PlayFlag == 0) && (IsocOutWrPtr >= (IsocOutBuff + ((AUDIO_OUT_PACKET * OUT_PACKET_NUM) / 2)))) { /* Enable start of Streaming */ PlayFlag = 1; //DCD_EP_Tx(pdev,AUDIO_IN_EP,(uint8_t*)&feedback_data,3); } } return USBD_OK;}[/CODE]2015-11-06 10:29 PM
Changing the MCLK frequency, you can see that host changes real sample rate of incoming data and LEDs flash more or less frequently.
The only issue I have now is to determine ODD/EVEN PID of data IN token for async feedback EP.