cancel
Showing results for 
Search instead for 
Did you mean: 

Gaps when playing audio file with DAC

Lars Beiderbecke
Senior III

I'm trying to play a sound file with raw PCM data using the DAC:

uint8_t buffer[16384];  // 4-aligned
uint8_t *bufptr[2] = {buffer, buffer + sizeof(buffer) / 2};
int curr_buffer = 0;
volatile int audio_dma_half_complete = 0;
 
f_read(fh, buffer, sizeof(buffer), &br);
 
HAL_TIM_Base_Start(&htim6);
 
sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  return 1;
 
if (HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)buffer, file_size,
                      DAC_ALIGN_8B_R) != HAL_OK)
  return 2;
 
while (1) {
  while (!audio_dma_half_complete);
  f_read(fh, bufptr[curr_buffer], sizeof(buffer) / 2, &br);
  if (br == 0)
    break;
  audio_dma_half_complete = 0;
  curr_buffer = !curr_buffer;  // toggle buffer half
}
 
HAL_TIM_Base_Stop(&htim6);
HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_2);

Note that the file contains raw PCM data that as already been reduced to 8 or 12 bits, resp.

This kind of works, but there are regular gaps of about 600 ms of silence, followed by 2-3 s of the signal.

0690X000008ASjiQAG.png

While the signal itself makes me think I have an endianness problem, the main issue are the gaps. (This diagram shows a gap for 12R data; for byte data, the gaps are more than 1 second wide.)

My first explanation were underruns, but doubling the SDMMC speed had absolutely no effect.

The second explanation were external interrupts, which in theory should be regular but run very shortly. But disabling all EXTI interrupts had again no effect.

What else might cause these gaps? Is it a priority problem between different peripherals?

1 ACCEPTED SOLUTION

Accepted Solutions
S.Ma
Principal

Maybe first use the DAC self waveform generation to see if the timer is ok.

You probably also need to make sure to process both half transfer and transfer complete events if DMA is cyclical: If the interrupt or SW fails, the HW won't wait and loop through the buffer. Time preserved, output data won't.

Which STM32? DMAs are not the same for all of the STM32 family. some do double buffer DMA like STM32F437

View solution in original post

6 REPLIES 6
AIM65
Associate III

It may be related to the configuration of the DMA channel, is it well set to Circular mode ?

During the ‘gap’, it looks like data are send to the dac (the bursts at the center of the screen), the dma may be ‘free’ running at unexpected memory location.

S.Ma
Principal

Maybe first use the DAC self waveform generation to see if the timer is ok.

You probably also need to make sure to process both half transfer and transfer complete events if DMA is cyclical: If the interrupt or SW fails, the HW won't wait and loop through the buffer. Time preserved, output data won't.

Which STM32? DMAs are not the same for all of the STM32 family. some do double buffer DMA like STM32F437

Thanks for your suggestion. I didn't realize that the half interrupt will not trigger at the end of the buffer (that would make things much simpler).

Please note that generating audio from a fixed circular array (i.e., not using FatFs to load more data) works perfectly fine.

But I still don't understand the length parameter of HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)buffer, length, DAC_ALIGN_8B_R). If length is the total length of the data to send (like the declaration states), how does the HAL know how large the buffer is? Or is it the length of the buffer instead, and I have to detect the end of the data myself?

Also, is the correct callback called

void HAL_DACEx_ConvCpltCallbackCh2(DAC_HandleTypeDef* hdac)

or

void HAL_DAC_TxCpltCallback(DAC_HandleTypeDef* hdac)

I'd favor the first one, but I also saw the second name in examples.

S.Ma
Principal

I don't think you have a choice, At the declaration of both do you see a #ifdef xxxx compile option?

USE_HAL_DAC_REGISTER_CALLBACKS or something like this.

The switch case make finer granularity at the expense of the interrupt routine latency/speed.

Please note that all interrupt enable sources of the peripheral usually are enabled even if you don't need it.

DMA half and complete interrupt will fire nevertheless.

Lars Beiderbecke
Senior III

Alright, I got it working now. HAL_DACEx_ConvCpltCallbackCh2 is the right name, and length is the buffer length.