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
manuel
Associate II
Posted on May 28, 2013 at 11:23

I've tried now to enable all Interrupts so see whether any ISR-Flag occured:

DMA_ITConfig(Audio_DMA_I2S3_Stream, DMA_IT_TC | DMA_IT_TE | DMA_IT_FE | DMA_IT_HT | DMA_IT_DME, ENABLE);

In ISR Method:

if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_HTIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_HTIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_FEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_FEIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_TEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TEIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_DMEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_DMEIF5);
}

Apparently I get DMA_FLAG_FEIF5 & DMA_FLAG_TEIF5. This are the FIFO overrun/underrun and the Transmission Error flags. I don't understand why these flags are set, especially the FIFO flag, cause I have the FIFO function disabled. I hope anyone has any suggestions? Thanks, Manuel
manuel
Associate II
Posted on May 28, 2013 at 14:07

So now I'm completely irritated.

I've disabled the DMA_IT_FE & DMA_IT_HT interrupts and only enabled the DMA_IT_TC.

DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5 | DMA_FLAG_HTIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_DMEIF5);
DMA_ITConfig(Audio_DMA_I2S3_Stream, DMA_IT_TC, ENABLE);
DMA_ITConfig(Audio_DMA_I2S3_Stream, DMA_IT_FE | DMA_IT_HT, DISABLE);
I2S_Cmd(SPI3, ENABLE);
DMA_Cmd(Audio_DMA_I2S3_Stream, ENABLE);

But I still get intrrupt flags in the ISR at FE and HT Why is that so?

if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TCIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_HTIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_HTIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_FEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_FEIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_TEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_TEIF5);
}
if(DMA_GetFlagStatus(Audio_DMA_I2S3_Stream, DMA_FLAG_DMEIF5) != RESET) {
DMA_ClearITPendingBit(Audio_DMA_I2S3_Stream, DMA_FLAG_DMEIF5);
}

DMA_FLAG_FEIF5 &

DMA_FLAG_HTIF5 are set sometimes.

Posted on May 28, 2013 at 19:14

Do you see data/clocks coming out of the respective pins?

JW
manuel
Associate II
Posted on May 31, 2013 at 06:56

Yes, in fact the transmission is working. But at the beginning of it I still get Fifo overrun/underrun and half transfer complete interrupts, although I have disabled the specific interrupt enable flags.

I just have to ignore the not wanted interrupts and the transmission is working properly.

But I'm curious why they are called in the first place.
manuel
Associate II
Posted on June 04, 2013 at 08:27

Hi guys,

I have some questions again, I hope anyone is willing to help me.

I still have the unwanted interrupts in my routine, although they are disabled, but I can live with that. I just ignore them in the routine.

My bigger problem now is, that I'm not able to receive the right data on my I2S3ext.

Is it always necessary to send out data if I only want to receive anything? Since the I2S3ext only works in slave mode, my processor although generates the clock, I have to start outputting audio too. I simple deactivate the DMA interrupt (TC) for sending and just active the DMA receiving interrupt. The DMA itself and the interface are both on, for sending and receiving. Is this correct or is there a more elegant solution, like just enabling the master clock for receiving?

I appreciate any help gratefully.

Thanks,

Manuel 

Posted on June 04, 2013 at 11:45

As you remarked yourself, the I2SxExt unit works always as a slave, thus it needs both the bit-clock and word-clock running. So if you want to keep the receiver running, you need to transmit dummy data from the respective I2Sx ''master''; the other way may be to swap the units and use the I2Sx as a Rx Master and the I2SxExt as Tx Slave when you can keep receiving while the transmitter is idle.

JW

Posted on June 04, 2013 at 11:52

Btw

> I still have the unwanted interrupts in my routine, although they are disabled

What makes you to assert this?

The status bits get set regardless of whether their respective ''enable'' is set or not; it's the interrupt which should not be invoked if the respective ''enable'' is not set. Thus, you should not infer that a given status bit caused the ISR invocation just by inspecting the status bits in the ISR.

JW

manuel
Associate II
Posted on June 04, 2013 at 14:30

Thanks for you answers JW.

So if I understand you right, I have to disable all the interrupts but the TC one and I just have to check for the TC flag status in the ISR ->Then I can be sure only TC interrupts are calling the ISR.

Back to the I2S3ext. I'm always reading unexpected data. I tested the whole think without DMA (just reading the I2S3ext->DR register each normal receive interrupt and there the data looks fine). I believe my initialization of the DMA is right, so it should be working. Has anyone of you used the I2S for receiving in DMA?

Best regards,

Manuel

Posted on June 04, 2013 at 19:54

GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SPI3); // I2S3_ext_SD

According to the datasheet, I2S3ext_SD on PC11 is AF5. JW