cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F427 I2S with DMA - noise - not audio

Joe.H
Associate III

I don't use libraries.

I got I2S working by polling. Below was my test. I had an array of 16 bit signed words for a tone of 1khz. The I2S pins from the MCU feed a an external DAC and produces a very nice tone.

 
    RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;	// turn SPI3 clock on
 
	Config_STM_Pin(GPIOB, 3, 6, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_Bclk  B3
	Config_STM_Pin(GPIOA, 15, 6, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_LRClk A15
	Config_STM_Pin(GPIOD, 6, 5, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_Data D6
 
	AudioMutePIN(1);	// mute off
 
// set up I2SPLL
 
	RCC->PLLI2SCFGR = (271 << RCC_PLLI2SCFGR_PLLI2SN_Pos) | (2 << RCC_PLLI2SCFGR_PLLI2SR_Pos);	// this gives correct I2S_clock
 
	// enable I2S clock
 
	RCC->CR |= RCC_CR_PLLI2SON;
 
	// wait until stable
 
	while((RCC->CR & RCC_CR_PLLI2SRDY) == 0)
	{}
 
 
	// set up SPI4
 
	SPI3->I2SCFGR = SPI_I2SCFGR_I2SMOD | (2 << SPI_I2SCFGR_I2SCFG_Pos);
 
	SPI3->I2SPR = 12;	// odd = 0,  idiv = 6
 
	SPI3->I2SCFGR |= SPI_I2SCFGR_I2SE;
 
	while(1)
	{
		if(SPI3->SR & SPI_SR_TXE)
		{
			SPI3->DR = AA[I];
 
			I++;
 
			if(I >= Num_Audio_Samples)
				I = 0;
		}
	}

Next step was to use DMA instead of polling.

I need to use two buffers but I can't use double buffer DMA because I have additional processing that needs to be done at the conclusion of each buffer. I removed the while loop and replaced it with 16 bit SPI DMA code that I use in a SPI process.

void Init_I2S(void)
{
	dword V;
 
    RCC->APB1ENR |= RCC_APB1ENR_SPI3EN;	// turn SPI3 clock on
 
	Config_STM_Pin(GPIOB, 3, 6, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_Bclk  B3
	Config_STM_Pin(GPIOA, 15, 6, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_LRClk A15
	Config_STM_Pin(GPIOD, 6, 5, H_GPIO_Alt_Function, H_GPIO_Output_PushPull, SlewRate_Medium, No_Pull_Up_or_Down);	// I2S_Data D6
 
	AudioMutePIN(1);	
 
// set up I2SPLL
 
	RCC->PLLI2SCFGR = (271 << RCC_PLLI2SCFGR_PLLI2SN_Pos) | (2 << RCC_PLLI2SCFGR_PLLI2SR_Pos);	// this gives correct I2S_clock
 
	// enable I2S clock
 
	RCC->CR |= RCC_CR_PLLI2SON;
 
	// wait until stable
 
	while((RCC->CR & RCC_CR_PLLI2SRDY) == 0)
	{}
 
	// set up SPI3
 
	SPI3->I2SCFGR = SPI_I2SCFGR_I2SMOD | (2 << SPI_I2SCFGR_I2SCFG_Pos);
 
	SPI3->I2SPR = 12;	// odd = 0,  idiv = 6
 
	SPI3->I2SCFGR |= SPI_I2SCFGR_I2SE;	// enable I2S
 
 
	SPI3->CR2 |= SPI_CR2_TXDMAEN;	// allow spi to tell dma
 
 
 
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;	// turn clock on for DMA1
 
	// stream 5 channel 0 is SPI3_Xmit
 
    DMA1_Stream5->CR = (0 << DMA_SxCR_CHSEL_Pos) | (3 << DMA_SxCR_PL_Pos) |  (1 << DMA_SxCR_DIR_Pos) | DMA_SxCR_MINC | (1 << DMA_SxCR_MSIZE_Pos) | DMA_SxCR_TCIE;
		// TX dma stream 5 - channel 0, single transfer, 16 bits,  Priority very hi, peripheral fixed, direction memory to spi, dma controls flow, transfer complete irq Active, 
 
	DMA1_Stream5->PAR = (dword)&SPI3->DR;
 
    NVIC_SetPriority(DMA1_Stream5_IRQn, AudioDMA_Priority);
    NVIC_EnableIRQ(DMA1_Stream5_IRQn);
 
      Audio_A_Playing = 1;
      DMA1_Stream5->M0AR = (dword)Audio_A; 
      DMA1_Stream5->NDTR = BytesPerAudioBlock;
 
      DMA1_Stream5->CR |= DMA_SxCR_EN;    // starts DMA I2S process
 
 
} /* end init() */
 
 
 
 
void DMA1_Stream5_IRQHandler(void)
{
 
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
 
	DMA1->HIFCR = DMA_HIFCR_CFEIF5 | DMA_HIFCR_CDMEIF5 | DMA_HIFCR_CTEIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTCIF5;
 
		if(Audio_A_Playing)                         // finished playing buffer A
		{
			Audio_A_Playing = 0;
			DMA1_Stream5->M0AR = (dword)Audio_B;
		}
		else
		{
			Audio_A_Playing = 1;
			DMA1_Stream5->M0AR = (dword)Audio_A; 
		} 
 
        DMA1_Stream5->NDTR = BytesPerAudioBlock;
	DMA1_Stream5->CR |= DMA_SxCR_EN;
 
   
  // DMA finished playback an ready for a new buffer
	xTaskNotifyFromISR(Audio_TaskHandle, Audio_I2S_IRQ, eSetBits, &xHigherPriorityTaskWoken );	// Audio_DMA_IRQ_Fired
	
 
   portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
 
}
 
 
 

The Audio_TaskHandle task that is released via the IRQ fills the empty buffer and does other work.

I've stared at this for two days and can't figure out why noise - not audio. The audio source is PCM data coming off an SD card. I've checked to make sure the audio data is good.

Can anyone see anything I've done incorrectly?

Thanks.

1 ACCEPTED SOLUTION

Accepted Solutions
Piranha
Chief II

You haven't set PSIZE in DMA stream CR register. By default it is 8-bit, but for SPI/I2S DR register it should be 16-bit.

View solution in original post

3 REPLIES 3
Piranha
Chief II

You haven't set PSIZE in DMA stream CR register. By default it is 8-bit, but for SPI/I2S DR register it should be 16-bit.

That was it. My mind thought it was tied directly to msize and I was overlooking that setting altogether.

I knew it had to be something simple.

Thanks for taking your time to look at the code.

> My mind thought it was tied directly to msize

It's the other way round, i.e. PSIZE determines both the peripheral- and memory-side transfer, unless FIFO is switched on.

In audio, whatever you mess up, it almost invariably ends with ugly noise. Wrong endian, noise. Swapped halfwords (which is the case in STM32's 16-bit I2S/SPI if you want 24/32-bit transfers) - noise. Messed up signed arithmetics (24-bit signed on arm/C is lots of fun) - noise.... 😉

JW