cancel
Showing results for 
Search instead for 
Did you mean: 

How to setup I2S with DMA bare Metal

CLeo.1
Senior II

Hi guys,

I am having the hardest of times trying to get this to work. The only thing I got working is seeing the MCLK which shows a more of a sinewave then a PWM (is that right even)?

I tried everything, to copying the HAL code as well. When using HAL I can see my FW CLCK and SCLK, but I cant find how the HAL code is getting it to work.

Also tried using the example in the reference menu and nothing

What I have done so far procedurally is:

  • Setup the SPI1/I2S Clock
  • Enable the GPIOA, GPIOC, DMA1, and SPI1 Clocks
  • Let SP1 use the PLL2 Clock
  • Setup GIPO A and C for alternative functions for A5F (PA4, PA5, PA6, PA7, PC4)
  • Setup DMAMUX1
  • Setup DMA1 Stream0/1 for Tx and Rx
  • Setup SPI

void init_I2S(){
 
	//Setting Clock for 98.304MHz
	//N = 122
	//P = 8
	//M = 10
	//FRACT = 7209
 
	// RCC_PLL2DIVR
	// MASKING:
	RCC -> PLL2DIVR &= ~RCC_PLL2DIVR_P2;
	RCC -> PLL2DIVR &= ~RCC_PLL2DIVR_N2;
	// WRITING:
	RCC -> PLL2DIVR |= RCC_PLL2DIVR_P2_DIV8;
	RCC -> PLL2DIVR |= RCC_PLL2DIVR_N2_MULT122;
 
	// RCC_PLLCKSELR
	// MASKING:
	RCC -> PLLCKSELR &= ~RCC_PLLCKSELR_DIVM2;
	// WRITING:
	RCC -> PLLCKSELR |= RCC_PLLCKSELR_DIVM2_DIV10;
 
	// RCC_PLL2FRACR
	// MASKING:
	RCC -> PLL2FRACR &= ~RCC_PLL2FRACR_FRACN2;
	// WRITING:
	RCC -> PLL2FRACR |= RCC_PLL2FRACR_FRACN_7209;
 
	// RCC_PLLCFGR
	// MASKING:
	RCC -> PLLCFGR &= ~RCC_PLLCFGR_DIVP2EN;
	RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2RGE;
	RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2VCOSEL;
	RCC -> PLLCFGR &= ~RCC_PLLCFGR_PLL2FRACEN;
	// WRITING:
	RCC -> PLLCFGR |= RCC_PLLCFGR_DIVP2EN;
	RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2RGE_4_8;
	RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2VCOSEL_192_836;
	RCC -> PLLCFGR |= RCC_PLLCFGR_PLL2FRACEN;
 
	// RCC_CR
	// MASKING:
	RCC -> CR &= ~RCC_CR_PLL2ON;
	// WRITING:
	RCC -> CR |= RCC_CR_PLL2ON;
	// WAITING:
	while (((RCC -> CR) & (RCC_CR_PLL2RDY)) == 0){};
 
	// ENALBING CLOCKS
 
	// RCC_AHB4ENR
	// MASKING:
	RCC -> AHB4ENR &= ~ RCC_AHB4ENR_GPIOAEN;
	RCC -> AHB4ENR &= ~ RCC_AHB4ENR_GPIOCEN;
	// WRITING:
	RCC -> AHB4ENR |= RCC_AHB4ENR_GPIOAEN;
    RCC -> AHB4ENR |= RCC_AHB4ENR_GPIOCEN;
 
    // RCC_APB2ENR
    // MASKING:
    RCC -> APB2ENR &= ~RCC_APB2ENR_SPI1EN;
    // WRITING:
    RCC -> APB2ENR |= RCC_APB2ENR_SPI1EN;
 
    // CHANGING CLOCKS OF PERIPHERALS
    // RCC_D2CCIP1R
    // MASKING;
    RCC -> D2CCIP1R &= ~RCC_D2CCIP1R_SPI123SEL;
    // WRITING:
    RCC -> D2CCIP1R |=  RCC_D2CCIP1R_SPI123SEL_PLL2_P_CK;
 
    // CHANGING GPIO PINS TO ALETERNATIVE
    // GPIOx_MODER
    // MASKING
    GPIOA -> MODER &= ~GPIO_MODER_MODE4;
    GPIOA -> MODER &= ~GPIO_MODER_MODE5;
    GPIOA -> MODER &= ~GPIO_MODER_MODE6;
    GPIOA -> MODER &= ~GPIO_MODER_MODE7;
    GPIOC -> MODER &= ~GPIO_MODER_MODE4;
    // WRITING:
    GPIOA -> MODER |= GPIO_MODER_MODE4_ALT;
    GPIOA -> MODER |= GPIO_MODER_MODE5_ALT;
    GPIOA -> MODER |= GPIO_MODER_MODE6_ALT;
    GPIOA -> MODER |= GPIO_MODER_MODE7_ALT;
    GPIOC -> MODER |= GPIO_MODER_MODE4_ALT;
 
    //SETTING ALT FUNCTIONS TO PINS
    // GPIOx_AFRL
    // MASKING:
    GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL4;
    GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL5;
    GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL6;
    GPIOA -> AFR[0] &= ~GPIO_AFRL_AFSEL7;
    GPIOC -> AFR[0] &= ~GPIO_AFRL_AFSEL4;
    // WRITING;
    GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL4_AF5;
    GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL5_AF5;
    GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL6_AF5;
    GPIOA -> AFR[0] |= GPIO_AFRL_AFSEL7_AF5;
    GPIOC -> AFR[0] |= GPIO_AFRL_AFSEL4_AF5;
 
    // ENABLING DMA1
    // RCC_AHB1ENR
    // MASKING:
    RCC -> AHB1ENR &= ~RCC_AHB1ENR_DMA1EN;
    // WRITING:
    RCC -> AHB1ENR |= RCC_AHB1ENR_DMA1EN;
 
    // SETTING UP DMA FOR I2S
    // DMAMUX1_Channel0_CCR
    // DMAMUX1_Channel1_CCR
    // NOTE: Using DMAMUX1 -> DMA1_Channel 0 & 1
    // 0x25 = Rx | 0x26 = Tx
    // MASKING:
    DMAMUX1_Channel0 -> CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
    DMAMUX1_Channel1 -> CCR &= ~DMAMUX_CxCR_DMAREQ_ID;
    // WRITING:
    DMAMUX1_Channel0 -> CCR |= 0x25; //Rx
    DMAMUX1_Channel1 -> CCR |= 0x26; //Tx
 
    // DMA1_Stream0_CR
    // DMA1_Stream1_CR
    // MASKING:
    DMA1_Stream0 -> CR &= ~DMA_SxCR_CT;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_PL;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_MSIZE;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_PSIZE;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_MINC;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_CIRC;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_DIR;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_PFCTRL;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_TCIE;
    DMA1_Stream0 -> CR &= ~DMA_SxCR_HTIE;
 
    DMA1_Stream1 -> CR &= ~DMA_SxCR_CT;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_PL;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_MSIZE;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_PSIZE;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_MINC;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_CIRC;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_DIR;
    DMA1_Stream1 -> CR &= ~DMA_SxCR_PFCTRL;
    // WRITING:
    DMA1_Stream0 -> CR |= DMA_SxCR_CT_MEM0;
    DMA1_Stream0 -> CR |= DMA_SxCR_PL_Very_High;
    DMA1_Stream0 -> CR |= DMA_SxCR_MSIZE_32BIT;
    DMA1_Stream0 -> CR |= DMA_SxCR_PSIZE_32BIT;
    DMA1_Stream0 -> CR |= DMA_SxCR_MINC;
    DMA1_Stream0 -> CR |= DMA_SxCR_CIRC;
    DMA1_Stream0 -> CR |= DMA_SxCR_DIR_P_TO_M;
    DMA1_Stream0 -> CR |= DMA_SxCR_PFCTRL_DMAFLOW;
    DMA1_Stream0 -> CR |= DMA_SxCR_TCIE;
    DMA1_Stream0 -> CR |= DMA_SxCR_HTIE;
 
    DMA1_Stream1 -> CR |= DMA_SxCR_CT_MEM0;
    DMA1_Stream1 -> CR |= DMA_SxCR_PL_Very_High;
    DMA1_Stream1 -> CR |= DMA_SxCR_MSIZE_32BIT;
    DMA1_Stream1 -> CR |= DMA_SxCR_PSIZE_32BIT;
    DMA1_Stream1 -> CR |= DMA_SxCR_MINC;
    DMA1_Stream1 -> CR |= DMA_SxCR_CIRC;
    DMA1_Stream1 -> CR |= DMA_SxCR_DIR_M_TO_P;
    DMA1_Stream1 -> CR |= DMA_SxCR_PFCTRL_DMAFLOW;
 
    // DMA_SxNDTR
    // WRITING:
    DMA1_Stream0 -> NDTR = 0x08;
    DMA1_Stream1 -> NDTR = 0x08;
 
    // DMA_SxPAR
    // WRITING:
    DMA1_Stream0 -> PAR = (uint32_t)&SPI1->TXDR;
    DMA1_Stream1 -> PAR = (uint32_t)&SPI1->RXDR;
 
    // DMA_SxM0AR
    // WRITING:
    DMA1_Stream0 -> M0AR = (uint32_t)RxBuff;
    DMA1_Stream1 -> M0AR = (uint32_t)TxBuff;
 
    // DMA_SxCR
    // WRITING:
    DMA1_Stream0 ->CR |= DMA_SxCR_EN;
    DMA1_Stream1 ->CR |= DMA_SxCR_EN;
 
    // Setting up the SPI/I2S Peripheral
    // MASKING:
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_MCKOE;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_ODD;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_I2SDIV;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_DATFMT;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_WSINV;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_DATLEN;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_CHLEN;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_CKPOL;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_I2SSTD;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_I2SCFG;
    SPI1 -> I2SCFGR &= ~SPI_I2SCFGR_I2SMOD;
    SPI1 -> CFG1    &= ~SPI_CFG1_RXDMAEN;
    SPI1 -> CFG1    &= ~SPI_CFG1_TXDMAEN;
    SPI1 -> CFG1    &= ~SPI_CFG1_FTHLV;
    // WRITING:
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_MCKOE;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_ODD_MULT2;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_I2SDIV_2;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_DATFMT_RAlign;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_WSINV_I2S;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_DATALEN_24BIT;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_CKPOL_FALL_RISE;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_I2SSTD_I2STAND;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_I2SCFG_MASTER_TRANSMIT;
    SPI1 -> I2SCFGR |= SPI_I2SCFGR_I2SMOD_I2S_PCM_MODE;
    SPI1 -> CFG1 |= SPI_CFG1_RXDMAEN;
    SPI1 -> CFG1 |= SPI_CFG1_TXDMAEN;
    SPI1 -> CFG1 |= SPI_CFG1_FTHLV_2_Data;
    SPI1 -> SR = 0x00;
    SPI1 ->  CR1 |= SPI_CR1_SPE;
    SPI1 ->  CR1 |= SPI_CR1_CSTART;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
}

5 REPLIES 5

Try polled implementation first.

Read our and check the relevant registers content, including status registers.

For DMA, make sure the used memories are fit for DMA. The H7 is overly complex.

JW

CLeo.1
Senior II

So I got the FWS and MCLK and SCLK outputting, it looks like now the RXDR doest want to fill up and theres no DMA request nor transfers.

> it looks like now the RXDR doest want to fill up

> SPI1 -> I2SCFGR |= SPI_I2SCFGR_I2SCFG_MASTER_TRANSMIT;

That sounds much like you've set it to transmit-only, i.e. no receive.

JW

CLeo.1
Senior II

Oh? Thats really interesting, so I guess what I am looking for is a full duplex then (both receive and transmit). Will confirm soon

Thank you that did it. RxBuff is now filling up. So weird HAL only allows Receive or Transmit but not duplex