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()

    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) {

    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;


    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_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_Stream0->CR |= DMA_SxCR_CIRC;
    DMA2_Stream0->M0AR = (uint32_t ) &(osc_sine_lut_raw[0]);
    DMA2_Stream0->NDTR = 512;
    DMA2_Stream0->CR |= DMA_SxCR_EN;

    for(;;) {   }

The DMA transaction then works, but the first one can't be circular for some reason.

  1. Why do I have to "jumpstart" the DMA?
  2. How to avoid essentially initializing the DMA twice in my code?
  • \$\begingroup\$ Well you are enabling the I2S which requests DMA before DMA is configured. Can that be the reason? \$\endgroup\$
    – Justme
    Commented Feb 6 at 15:25
  • \$\begingroup\$ @Justme Moved the enabling, you are right. Still, no luck. I've also noticed an error, that I was setting the circular mode for DMA1, not DMA2. Now circular mode works, but in the SECOND transaction. The first one still has to be "jumpstarted" and can't be circular for some reason. \$\endgroup\$
    – sx107
    Commented Feb 6 at 15:33
  • \$\begingroup\$ Try to move setting SPI_CFG1.TXDMAEN after all DMA and DMAMUX setup, just before SPI_CR1.SPE is set (or maybe even after that). Does that solve 1. or even 2.? \$\endgroup\$
    – wek
    Commented Feb 6 at 18:29
  • \$\begingroup\$ @wek Tried that, did not help. The problem was the D-Cache. Never encountered that before, this is my first time programming H7 series. \$\endgroup\$
    – sx107
    Commented Feb 6 at 18:33

1 Answer 1


The problem was the __DSB(); instruction and the enabled D-Cache. After disabling the D-cache and removing the __DSB() everything works fine, but only at -O0 optimization. Guess I'll stick with -O0 optimization for the DMA initialization for now using pragmas.

  • \$\begingroup\$ The dcache isn’t the problem, maybe the problem is that you may not understand it. This is like disabling all but first gear in your car because the wheels vibrate at speed. \$\endgroup\$ Commented Feb 6 at 20:17
  • \$\begingroup\$ @ErikFriesen I've found this page: community.st.com/t5/stm32-mcus/… Is it wrong? I've put my DMA buffers in the D2 memory and working on disabling the D-Cache for that particular region. \$\endgroup\$
    – sx107
    Commented Feb 7 at 6:19
  • \$\begingroup\$ No, it’s not wrong. I’m just saying you typically don’t want to disable global cache, neither do you want to use -O0 as it likely just masks marginal code. \$\endgroup\$ Commented Feb 7 at 12:13
  • \$\begingroup\$ In your problem here, remember that you are probably pretty close to success, either the order needs tweaking, or you are missing a setting. You can sometimes follow the HAL libs to see how they do it, or sometimes I’ve used the debugger to compare settings in the dma and other peripheral, hal vs my program. \$\endgroup\$ Commented Feb 7 at 12:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.