cancel
Showing results for 
Search instead for 
Did you mean: 

I2S DMA Problems

manuel
Associate II
Posted on May 28, 2013 at 10:31

Hi guys,

I'm in a bit of a crisis to get I2S working with DMA. I want to transfer Data from a TxBuffer array to the SPI3->DR Peripheral My setup is:
  • I2S3 Interface
  • DMA Dir Mem2Peripheral
  • DMA Interrupt on DMA_IT_TC
  • DMA in Double Buffered Mode
  • No Fifo

The Problem is, that the Interrupt is never called, so I guess the DMA isn't working and the DMACounter isn'T counting to zero (so no interrupt is called can be called)

Here is my Code: Defines:

#define Audio_DMA_I2S3_Stream DMA1_Stream5
#define Audio_DMA_I2S3ext_Stream DMA1_Stream2
#define Audio_DMA_I2S3_Channel DMA_Channel_0
#define Audio_DMA_I2S3ext_Channel DMA_Channel_2
#define Audio_DMA_I2S3_IRQ DMA1_Stream5_IRQn
#define Audio_DMA_I2S3ext_IRQ DMA1_Stream2_IRQn

Initialization:

GPIO_Configuration();
I2S_Configuration();
DMA_Configuration();

GPIO_Coinfiguration:

GPIO_InitTypeDef GPIO_InitStructure;
// Enable peripheral clocks
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE); // TODO: fix, enable alternate function clock
// Configure alternative functions for I2S3 pins
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI3); // I2S3_WS
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_SPI3); // I2S3_MCK
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3); // I2S3_CK
GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SPI3); // I2S3_ext_SD
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3); // I2S3_SD
//GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SPI3); /* I2S_CKIN - needed?? */
// Configure pins as alternate function
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // TODO: fix
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
GPIO_Init(GPIOC, &GPIO_InitStructure);

I2S_Configuration:

I2S_InitTypeDef I2S_InitStructure;
// Enable SPI3/I2S3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
// I2S3 peripheral configuration
SPI_I2S_DeInit(SPI3);
I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_8k;
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
I2S_Init(SPI3, &I2S_InitStructure);
I2S_FullDuplexConfig(I2S3ext, &I2S_InitStructure);
// Hook up I2S3 to DMA buffers
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
SPI_I2S_DMACmd(I2S3ext, SPI_I2S_DMAReq_Rx, ENABLE);

DMA_Configuration:

DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Enable DMA
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
// Common initialization
DMA_InitStructure.DMA_BufferSize = AUDIO_BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/* DMA configuration for input & output if I2S data. Configure each DMA channel to use double-buffered mode
* in circular mode. This allows the software codec read/write operation form the one memory while the DMA 
* is reading/writing to the other memory. */
// I2S3 - DMA configuration for sending data to the codec/speaker
DMA_Cmd(Audio_DMA_I2S3_Stream, DISABLE); 
DMA_DeInit(Audio_DMA_I2S3_Stream);
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_Channel = Audio_DMA_I2S3_Channel; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &SPI3->DR; // SPI data register for sending
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &audio_Tx_Buffer[AUDIO_BUFFER_0][0];
DMA_DoubleBufferModeConfig(Audio_DMA_I2S3_Stream, (uint32_t) &audio_Tx_Buffer[AUDIO_BUFFER_1][0], DMA_Memory_0);
DMA_DoubleBufferModeCmd(Audio_DMA_I2S3_Stream, ENABLE);
DMA_Init(Audio_DMA_I2S3_Stream, &DMA_InitStructure);
// I2S3ext - DMA configuration for reading data from the codec/microphone 
DMA_Cmd(Audio_DMA_I2S3ext_Stream, DISABLE); 
DMA_DeInit(Audio_DMA_I2S3ext_Stream); 
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Channel = Audio_DMA_I2S3ext_Channel; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &I2S3ext->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &audio_Rx_Buffer[AUDIO_BUFFER_0][0];
DMA_DoubleBufferModeConfig(Audio_DMA_I2S3ext_Stream, (uint32_t) &audio_Rx_Buffer[AUDIO_BUFFER_1][0], DMA_Memory_0);
DMA_DoubleBufferModeCmd(Audio_DMA_I2S3ext_Stream, ENABLE);
DMA_Init(Audio_DMA_I2S3ext_Stream, &DMA_InitStructure);
// NVIC: Configure the DMA interrupt priority
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3); 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// DMA interrupt for sending data to speaker
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannel = Audio_DMA_I2S3_IRQ;
NVIC_Init(&NVIC_InitStructure);
// DMA interrupt for reading data from microphone
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannel = Audio_DMA_I2S3ext_IRQ;
NVIC_Init(&NVIC_InitStructure);

ISR: (It is never called)

extern ''C'' void DMA1_Stream5_IRQHandler(void) { 
uint32_t memory = DMA_GetCurrentMemoryTarget(Audio_DMA_I2S3_Stream);
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5);
}

To start the DMA transfer I call:

DMA_ITConfig(Audio_DMA_I2S3_Stream, DMA_IT_TC, ENABLE); // Enable/Disable DMA Interrupts
DMA_Cmd(Audio_DMA_I2S3_Stream, ENABLE); // Enable/Disable DMA
I2S_Cmd(SPI3, ENABLE); // Enable/Disable I2S

I really hope you guys can help me. I read the datasheet carefully and I thought I haven't made a mistake. Unfortunately anythere must be one. I hope someone could find it. Thanks a lot & Best Regards, Manuel #dma-i2s-spi-interrupts
13 REPLIES 13
fb
Associate III
Posted on November 30, 2013 at 19:04

You are right, in the datasheet (DocID022152 Revr 4), on page 62, there is I2S3ext_SD listed as AF5. In the old rev 2 of the datasheet which I had on my harddisk, it was listed as AF6. But looks like both settings are working.

I had another interesting problem with the DMA stream. First I used the same stream as the initial poster, stream 2 and it didn't work (on my stm32f4discovery board). The reference manual says in chapter 8.3.3:

  The 8 requests from the peripherals (TIM, ADC, SPI, I2C, etc.) are

  independently connected to each channel and their connection

  depends on the product implementation. Tabl e 20 and Table 21

  give examples of DMA request mappings.

No, these are not just examples. At least I can't find it in the specific datasheet for the CPU, and as far as I've tested it, it works only for the I2S3_EXT_RX setting listed in this table: channel3/stream0. With this fix now I can use I2S3 in full duplex mode, with DMA transfers.

Would be cool if there would be an easier to use library with just some easy calls to setup the right DMA transfer instead of reading hundreds of pages of ambigious datasheets and writing dozens of lines of code, with one wrong line or assumption and nothing works. It is such a nice microcontroller, but using the full power of it is nearly impossible for beginners and even for more experienced people like me it is difficult.

Posted on December 01, 2013 at 00:21

Unfortunately it isn't 1979 any more, SoC implementations are complex and have millions of transistors, most beginners lack the foundational background the older amongst us had with simpler and less complex systems.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on December 02, 2013 at 09:33

> Would be cool if there would be an easier to use library with just some easy calls to setup the

> right DMA transfer

Yes, but are you willing to pay the price of it? Either directly, or in the price of the chips?

JW

Posted on November 16, 2016 at 09:39

Hi

I had a problem similar to the one you described here.

And now when i have solved it I thought it would be good to add a comment here to help others having similar problems.

I have a STM32F746 mcu running Free RTOS that should send audio to a audio codec using I2S and DMA. But I had some problems getting the interrupts signaling the DMA transfer complete and DMA transfer half complete to work.

It turned out that while following the examples from STM I had set the priority for the interrupts to 0, which was not permitted by Free RTOS (who permits a priority in the range of 5 down to 15.

HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 5, 0);

I had also a NULL-pointer exception which was hard to find. Since I had not found a good example where DMA and I2S was used together I had missed to link the DMA handler and the I2S handler.

__HAL_LINKDMA (i2sHandle, hdmatx, DmaHandle);

And of course I had to implement the interrupt function to handle the DMA stream interrupt.

void

DMA1_Stream4_IRQHandler (void)

{

  HAL_DMA_IRQHandler (i2sHandle.hdmatx);

}