cancel
Showing results for 
Search instead for 
Did you mean: 

I2S DMA in NORMAL mode for double-buffering, not halving a single one!

Zaher
Senior II

Hello everyone!

I was wondering what might go wrong with I2S DMA configured in normal mode for audio streaming with double-buffering technique. I'm not talking about circular buffer here (ping-pong) where a single buffer is used in halves in HC and TC interrupts.

I have spent quite some time trying to figure it out, but seems the codec doesn't output any audio when DMA is in 'NORMAL' mode. The init sequence of codec doesn't work, as well, which indicates the first 16 dummy bytes that must be sent for the codec to set/initialize the registers never make it to the codec.

The only thing that comes to my mind now are the HC/TC IRQs, their callbacks, and whether they were registered when DMA is configured in 'NORMAL' mode.

 This is for STM32F413 + WM8994

11 REPLIES 11
Zaher
Senior II

I was wondering if LL driver generation is available in Cube for the STM32F413. Although I get used to HAL for quite some years now, I want to start playing with this I2S DMA thing at register level. At least, this seems easier to understand what's going on. Or maybe a mix of HAL/LL to get the job done.

Zaher
Senior II

I got it! It's working flawlessly. I can confirm now, DMA streaming for I2S is working in double-buffer mode using two separate buffers, waveform1/waveform2, on STM32F413ZHT. I have populated these buffers with sine waves of different frequencies, 440/220 HZ respectively, so I can distinguish them. The DMA controller is alternating between first buffer and second buffer very smoothly. I did not utilize any interrupts for now to keep it simple.

In case someone is interested in doing the same, here's my DMA setup code:

/*
 * Configure the DMA Stream for I2S TX to work in double-buffer mode
 */
void  DMA_DBM_Setup()
{
	static DMA_Stream_TypeDef* DMA_Stream = DMA1_Stream4;
	// Disable the DMA stream first
	DMA_Stream->CR &=  ~DMA_SxCR_EN;
	// Wait until the stream is disabled
	while(DMA_Stream->CR & DMA_SxCR_EN);
 
	// Assign I2S2 DR to DMA stream
	DMA_Stream->PAR = (uint32_t)&haudio_i2s.Instance->DR;
 
	// Configure source audio buffers used in DBM
	DMA_Stream->M0AR = (uint32_t)waveform1;
	DMA_Stream->M1AR = (uint32_t)waveform2;
 
	// Set the length/size of the DATA stream in
	DMA_Stream->NDTR = SINE_LENGTH;
 
	DMA_Stream->CR |= (0 << DMA_SxCR_CHSEL_Pos); // DMA channel selection
	DMA_Stream->CR &= ~DMA_SxCR_MBURST; // Set single memory burst
	DMA_Stream->CR &= ~DMA_SxCR_PBURST; // Set single peripheral burst
	DMA_Stream->CR &= ~DMA_SxCR_CT; 	// Set memory to peripheral mode
	DMA_Stream->CR &= ~DMA_SxCR_PL_1; 	// Set DMA priority to high
	DMA_Stream->CR &= ~DMA_SxCR_PINCOS; // Set peripheral increment offset size to 32-bit
	DMA_Stream->CR |= DMA_SxCR_MINC; 	// Enable memory increment mode
	DMA_Stream->CR &= ~(DMA_SxCR_PINC);  // Disable peripheral increment mode
	DMA_Stream->CR |= DMA_SxCR_MSIZE_0; // Set memory data size to 16-bit
	DMA_Stream->CR |= DMA_SxCR_PSIZE_0; // Set peripheral data size to 16-bit
	DMA_Stream->CR |= DMA_SxCR_CIRC; 	// Enable circular mode
	DMA_Stream->CR |= (DMA_SxCR_DBM);  // Enable double buffer mode
	DMA_Stream->CR |= (DMA_SxCR_TCIE | DMA_SxCR_HTIE);  // Enable TC and HC interrupts
 
	/* Enable DMA stream */
	DMA_Stream->CR |= DMA_SxCR_EN;
}

Zaher