@bohrok2610 , do you send to SAI data of fixed size or variable size changed according to feedback? I mean if for example stream is 192 kHz @ 32 bits, do all SAI packets have size of 192 bytes or they have variable size of 184, 192 or 200 bytes?
The feedback calculation seem reasonable now. I don't think the issue is with feedback but somehow your implementation is running a bit slow.The best result I have is with the following feedback calculation. But background clicks still present, the sound is not pure. By means of debugger I can see Read pointer reaches Write pointer after 10200 - 10700 SAI transfers.
You should check that incoming / outgoing data rates are matching. E.g. cumulate incoming and outgoing sample count and output those every 1-2 seconds.
Yes, I observe incoming / outgoing data. At the moment when Read / Write pointers are equal the system state is the following one.You should check that incoming / outgoing data rates are matching. E.g. cumulate incoming and outgoing sample count and output those every 1-2 seconds.
Outputted packets count (SAIPack_cnt) = 10210
Received data bytes (RecData) = 1991232
Read pointer = Write pointer = 25152
So as for Received/Outputted packets count, Received packets count is bigger than Outputted packets count, the difference is 10296 - 10210 = 86. Outputting starts when Received packets count is 80, so there is nothing strange with 86 Received packets difference.
SAI always outputs fixed number of bytes - 192 - per transfer. For 10210 transfers it outputs 192 * 10210 = 1960320 bytes. Received bytes - outputted bytes = 1991232 - 1960320 = 30912 bytes. Then minus 80 received packets before outputting starts: 30912 - 80 * 192 = 15552 bytes. So Write pointer is ahead of Read pointer on 15552 bytes or 15552 / 192 = 81 packets. Write pointer changes faster than Read pointer. That is why I think I have wrong feedback value estimation.
UPDATE: the avarage Received packet size is 1991232 / 10296 = 193,3986 bytes, that is PC reacts for feedback and changes packet size.
Last edited:
With Windows 10 host you should be able to run 192k several minutes (maybe hours) without any buffer overruns even without feedback (or with just using nominal rate as feedback). That is why I believe something else than feedback is causing the problem. You mentioned that you have external MCK. How are you setting the SAI clock?
SAI clock settings for external syncHow are you setting the SAI clock?
C:
EXT_SYNC_GPIO->AFR[1] |= (EXT_SYNC_AF << (4 * (EXT_SYNC_PIN - 8)));
EXT_SYNC_GPIO->MODER |= (2 << (2 * EXT_SYNC_PIN));
RCC->DCKCFGR1 |= RCC_DCKCFGR1_SAI1SEL_1;
C:
#define SAI_MCLK_196_608MHz ((2 << RCC_PLLSAICFGR_PLLSAIQ_Pos) | \
(262 << RCC_PLLSAICFGR_PLLSAIN_Pos))
#define SAI_MCLK_180_633MHz ((2 << RCC_PLLSAICFGR_PLLSAIQ_Pos) | \
(241 << RCC_PLLSAICFGR_PLLSAIN_Pos))
enum SAIQDividers
{
SAIQDIV_1xFs = 15,
SAIQDIV_2xFs = 7,
SAIQDIV_4xFs = 3,
SAIQDIV_8xFs = 1,
SAIQDIV_16xFs = 0,
};
void SAI_PLLSAIConfig(uint32_t AudioFrequency)
{
RCC->CR &= ~RCC_CR_PLLSAION;
while((RCC->CR & RCC_CR_PLLSAIRDY) == RCC_CR_PLLSAIRDY);
RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVQ;
switch(AudioFrequency)
{
//48 x n
default:
RCC->PLLSAICFGR = SAI_MCLK_196_608MHz;
break;
//44,1 x n
case USB_AUDIO_CONFIG_FREQ_44_1_K:
case USB_AUDIO_CONFIG_FREQ_88_2_K:
case USB_AUDIO_CONFIG_FREQ_176_4_K:
case USB_AUDIO_CONFIG_FREQ_352_8_K:
case USB_AUDIO_CONFIG_FREQ_705_6_K:
RCC->PLLSAICFGR = SAI_MCLK_180_633MHz;
break;
}
uint32_t SAIQDivValue = 0;
switch(AudioFrequency)
{
//48, 44,1
default:
SAIQDivValue = SAIQDIV_1xFs;
break;
case USB_AUDIO_CONFIG_FREQ_88_2_K:
case USB_AUDIO_CONFIG_FREQ_96_K:
SAIQDivValue = SAIQDIV_2xFs;
break;
case USB_AUDIO_CONFIG_FREQ_176_4_K:
case USB_AUDIO_CONFIG_FREQ_192_K:
SAIQDivValue = SAIQDIV_4xFs;
break;
case USB_AUDIO_CONFIG_FREQ_352_8_K:
case USB_AUDIO_CONFIG_FREQ_384_K:
SAIQDivValue = SAIQDIV_8xFs;
break;
case USB_AUDIO_CONFIG_FREQ_705_6_K:
case USB_AUDIO_CONFIG_FREQ_768_K:
SAIQDivValue = SAIQDIV_16xFs;
break;
}
RCC->DCKCFGR1 |= SAIQDivValue << RCC_DCKCFGR1_PLLSAIDIVQ_Pos;
RCC->CR |= RCC_CR_PLLSAION;
while((RCC->CR & RCC_CR_PLLSAIRDY) != RCC_CR_PLLSAIRDY);
}
In both cases system clock is 216 MHz with the following PLL settings
C:
//Onboard quartz is 24 MHz
RCC->PLLCFGR = (2 << 28) | RCC_PLLCFGR_PLLSRC | \
(9 << RCC_PLLCFGR_PLLQ_Pos) | \
(288 << RCC_PLLCFGR_PLLN_Pos) | \
(16 << RCC_PLLCFGR_PLLM_Pos);
IMHO the time depends on USB-host controller clock vs. DAC clock vs. buffer size in the device. IMO a properly implemented UAC2 driver will always end up passing corresponding number of samples each microframe, on any OS.With Windows 10 host you should be able to run 192k several minutes (maybe hours) without any buffer overruns even without feedback (or with just using nominal rate as feedback).
When troubleshooting the ignored implicit feedback above, the device was RTX6001 analyzer with XMOS. At duplex analog loopback I was getting clicks every 10 seconds. The samplerate difference calculated from the captured packets statistics was 6.5Hz at 48kHz which is a lot. It suggests a 128-frames buffer in the XMOS, half of which would correspond to 6.5 sample every second per 10 seconds.
Another diyaudio user was kind to make the same test on his RTX + linux PC and got clicks every 50 seconds - his clocks deviated 5 times less. Still unusable for longer measurements.
After enabling the implicit feedback the clicks were gone completely.
Yes, without feedback the time to run without over/underruns depends on the implementation. I have an onboard led indicator for buffer over/underruns so I know even without listening if these have occurred.
@EvSap
Your clock configurations seem to be ok. Is the onboard oscillator 25MHz?
For debugging the data rates I output to SWV at every 8000th OUT event (i.e. every second) cumulative number of samples coming from USB and going out through SAI. This makes it very easy to see that the data rates are equal.
...
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
...
Your clock configurations seem to be ok. Is the onboard oscillator 25MHz?
For debugging the data rates I output to SWV at every 8000th OUT event (i.e. every second) cumulative number of samples coming from USB and going out through SAI. This makes it very easy to see that the data rates are equal.
...
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
OUT: 192000 DMA: 192000
...
24 MHz. PLL settings can be seen in my previous post under the last spoiler.Is the onboard oscillator 25MHz?
Good suggestion, thank you.For debugging the data rates I output to SWV at every 8000th OUT event (i.e. every second) cumulative number of samples coming from USB and going out through SAI. This makes it very easy to see that the data rates are equal.
With slight modification of feedback implementation I managed to get rid of audio buffer overruns. Now gap between write/read pointers is always very close to nominal value, +/- 2 packets. But sound still contains clicks and some other distortions. I can observe OVRUDR flag rising in SAI status register after transfer completes. Think this may be the reason of sound distortions. Now SAI DMA is used to transmit only one packet and after transfer completes the next one is initiated in SAI DMA interrupt routine. Maybe that is the reason and SAI DMA should be used in circular mode with double buffer option enabled.
Couldn't you get exactly 192kHz out of PLL with these:
PLLSAIN=384
PLLSAIQ=2
PLLM=24
PLLN=288
PLLQ=6
I use circular mode in SAI DMA.
PLLSAIN=384
PLLSAIQ=2
PLLM=24
PLLN=288
PLLQ=6
I use circular mode in SAI DMA.
Not sure I understand what you mean. For 192 kHz audio frequency SAI input clock should be 192000 * 256 = 49,152 MHz. To achieve this value I use the following settings and the frequency is 49,125 MHz.Couldn't you get exactly 192kHz out of PLL with these:
PLLSAIN=384
PLLSAIQ=2
PLLM=24
PLLN=288
PLLQ=6
After several tests it is found out that SAI behaves strangely. Data is outputted (SD pin activity) only at 192 kHz. For other frequencies SD always has 0 value. When SAI DMA is in circular mode data is not outputted but other 3 signals are active.
Sorry, you're correct. I haven't used the PLL clock configuration for a while since I have ext-MCK clocks onboard. Earlier I used a 25MHz clock for PLL which gets to 192.057kHz with N=295, P=2, Q=3.
Are you using FIFO with DMA?
Are you using FIFO with DMA?
Last edited:
Yes, I am. Have tried 2 variants. 1) DMA in "one shot" mode with new transfer initiated in DMA transfer complete interrupt. In this case OVRUDR flag rises in SAI status register and data is ouputted only for 192 kHz, I can here sound with distortions. For 48 / 96 kHz only MCLK, BCLK and LR signals are valid.Are you using FIFO with DMA?
It seems that you are using half-word data size. Try word data size. Also INCR4 burst mode can be beneficial.
Screenshots are for 48 kHz @ 16 bits. For 192 kHz @ 32 bits word size is used, but SAI behaviour is the same.It seems that you are using half-word data size. Try word data size.
Only full transfer complete interrrupt is used in "one shot" mode to reinitialize transfer again.Have you implemented the DMA half transfer complete and full transfer complete callbacks?
Finally managed to get SAI outputting data for 48 / 96 kHz audio frequencies too. But there is a very strange issue. Half of data is nulled periodically. This happens regardless of audio frequency and resolution. Have tried 48 / 96 / 192 / 384 / 768 kHz @ 16 / 32 bits and DMA "one shot" and circular modes. The result is always the same: half of data is nulled periodically.
Test application is very simple containing just SAI and DMA initialization. For DMA circular mode then SAI starts tranfer, no DMA interrupts activated. For DMA "one shot" mode full transfer complete interrupt is used to reinitialize transfer again. Data are formed only once at the very beginning and stay unchangeable. Data buffer is filled with 0x0F.
Please watch short videos explaining what I am talking about. The first video is for data signal which is nulled periodically. And the second one is for LR signal which is clean meander. Audio stream is 48 kHz @ 16 bits
I have not used DMA normal mode ("one shot").
The normal approach with circular buffers is to treat the DMA buffer as 2 half buffers and use half transfer complete and full transfer complete callbacks to fill appropriate half buffer with new data. In double buffer mode DMA uses 2 buffers (memory pointers). If there are no transfer errors the DMA in circular (or double buffer) mode should run continuously after enabling. So if you have pre-filled the buffer with some data that data should be outputted even without half/full transfer complete callbacks.
The normal approach with circular buffers is to treat the DMA buffer as 2 half buffers and use half transfer complete and full transfer complete callbacks to fill appropriate half buffer with new data. In double buffer mode DMA uses 2 buffers (memory pointers). If there are no transfer errors the DMA in circular (or double buffer) mode should run continuously after enabling. So if you have pre-filled the buffer with some data that data should be outputted even without half/full transfer complete callbacks.
Yes, that is what I expect to see, but now oscilloscope shows that something is wrong with outputted data.So if you have pre-filled the buffer with some data that data should be outputted even without half/full transfer complete callbacks.
- Home
- Source & Line
- Digital Line Level
- UAC2.0 on STM32