I am trying to run an I2S ES8388 codec from a stm32h750. Without DMA, the codec is working just fine, and I can output and receive data to and from it respectively. But, I'm experiencing problems with the DMA part.
I've tried first to set up the DMA as in this question: Configuring the DMA request multiplexer on a STM32H7 MCU
And it works, but for some reason I have to "jumpstart" the first DMA transaction by writing to SPI2->TXDR register directly. After that, everything works fine. Now, I am trying to do the same thing, but with a circular buffer.
Here is my i2s initialization code:
void i2s2_init() {
// Clock sourse already set as PLL2P in spi_clock_init()
RCC->APB1LENR |= RCC_APB1LENR_SPI2EN;
i2s2_gpio_init();
SPI2->CFG1 |= SPI_CFG1_TXDMAEN;
SPI2->CFG1 &= ~SPI_CFG1_FTHLV;
SPI2->I2SCFGR |= SPI_I2SCFGR_I2SMOD | SPI_I2SCFGR_MCKOE | (0b101 << SPI_I2SCFGR_I2SCFG_Pos) | (0b00 << SPI_I2SCFGR_I2SSTD_Pos); // I2S/PCM mode, Master clock enable, master full duplex, I2S standard
SPI2->I2SCFGR |= (0b01 << SPI_I2SCFGR_DATLEN_Pos) | (0b1 << SPI_I2SCFGR_CHLEN_Pos) | (0b0 << SPI_I2SCFGR_DATFMT_Pos); // right-aligned TX/RX registers, 24-bit
SPI2->I2SCFGR |= (0b0 << SPI_I2SCFGR_CKPOL_Pos) | (0b0 << SPI_I2SCFGR_FIXCH_Pos) | (0b0 << SPI_I2SCFGR_WSINV_Pos); // Low clock polarity, no word invvertion
SPI2->I2SCFGR |= (2 << SPI_I2SCFGR_I2SDIV_Pos) | (0 << SPI_I2SCFGR_ODD_Pos);
}
DMA initialization and start code:
void i2s2_start(uint32_t* inData, uint32_t* outData, uint32_t buffer_size) {
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
DMA2_Stream0->PAR = (uint32_t) &(SPI2->TXDR);
DMA2_Stream0->M0AR = (uint32_t) outData;
DMA2_Stream0->M1AR = (uint32_t) outData;
DMA2_Stream0->NDTR = buffer_size;
DMA2_Stream0->CR = 0;
DMA2_Stream0->CR |= (1u << DMA_SxCR_DIR_Pos); // Memory to peripheral
DMA2_Stream0->CR |= DMA_SxCR_MINC; // Memory increment mode
DMA2_Stream0->CR |= (3u << DMA_SxCR_PL_Pos); // Very High priority
//If I set circular mode here, no DMA transaction occurs at all
//DMA2_Stream0->CR |= DMA_SxCR_CIRC; // Circular mode
DMA2_Stream0->CR |= 0b10 << DMA_SxCR_MSIZE_Pos;
DMA2_Stream0->CR |= 0b10 << DMA_SxCR_PSIZE_Pos;
//DMA1_Stream0->CR |= DMA_SxCR_HTIE | DMA_SxCR_TCIE;
__DSB();
DMA2_Stream0->CR |= DMA_SxCR_EN;
DMAMUX1_Channel8->CCR = 40;
SPI2->CR1 |= SPI_CR1_SPE;
SPI2->CR1 |= SPI_CR1_CSTART;
SPI2->TXDR = 0xFFFFFF; // jumpstart!
}
And brief idea of the main() code:
const uint32_t osc_sine_lut_raw[] = {... Sine LUT ... }
i2s2_init();
i2s2_start(&(osc_sine_lut_raw[0]), &(osc_sine_lut_raw[0]), 512);
while(DMA2_Stream0->NDTR != 0) asm("nop");
for(uint32_t i=0; i<0xBF; i++) asm("nop");
DMA2->LIFCR |= DMA_LIFCR_CTCIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0 | DMA_LIFCR_CFEIF0;
DMA2_Stream0->CR |= DMA_SxCR_CIRC;
DMA2_Stream0->M0AR = (uint32_t ) &(osc_sine_lut_raw[0]);
DMA2_Stream0->NDTR = 512;
__DSB();
DMA2_Stream0->CR |= DMA_SxCR_EN;
for(;;) { }
The DMA transaction then works, but the first one can't be circular for some reason.
- Why do I have to "jumpstart" the DMA?
- How to avoid essentially initializing the DMA twice in my code?