2023-05-24 09:46 PM
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
2023-05-25 03:39 PM
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.
2023-05-26 08:06 PM
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