Question
[STM32f4-Discovery] I2S RX Master with DMA
Posted on March 06, 2013 at 19:44
Hi
I have some questions for the following scenario. I want to configure the dma controller to transfer data from an digital i2s mems microphone to the memory. The I2S (SPI2) is the rx master and the memory should be a software ring buffer. The sample size is 24 bit that will be stored in a 32 bit integer variable. I've already implemented this configuration on a M3 device from texas instruments. Now I want to do the same on the stm32f4-discovery. On the TI device, everytime the i2s rx fifo is full, it will cause a dma request and the cpu jumps in the i2s interrupt handler. The dma is configured for ping pong modus with a primary and a secondary dma configuration structure (similar to the double buffer mode) and in the i2s handler, I check whether the transfers are done. If one transfer is completed, the dma stopps for this transfer configuration. Then, I configure a new transfer with the new destination pointer and enable a new transfer that will start if the other buffer is filled completely. The code looks like this:void
I2SIntHandler(
void
)
{
//UARTprintf(''I2S ISR\n'');
unsigned
long
ulStatus;
unsigned
long
ulMode;
// Interruptstatus abfragen
ulStatus = I2SIntStatus(I2S0_BASE , 1);
//Interruptquelle löschen, damit der Handler am Ende nicht erneut aufgerufen wird
I2SIntClear(I2S0_BASE, ulStatus);
// Status des uDMA I2S Kanals abfragen, primäre Kontrollstruktur
ulMode = uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT);
// Wenn der uDMA den Transfer abgeschlossen hat, stoppt er automatisch
if
(ulMode == UDMA_MODE_STOP)
{
oldIndex = (index-1) & (RING_BUFFER_SIZE-1);
/*
* Konfiguriert die Übertragungsparamter für den nächsten Transfer des I2SRx Kanals für den uDMA
* Primäre Kontrollstruktur (Ping Pong Modus, Buffer1), Datenquelle ist der I2SRx FIFO
*/
uDMAChannelTransferSet(UDMA_CHANNEL_I2S0RX | UDMA_PRI_SELECT,
UDMA_MODE_PINGPONG, (
void
*) I2S_SOURCE,
(
void
*)I2SPuffer[index], BUFFER_SIZE);
uDMAChannelEnable(UDMA_CHANNEL_I2S0RX);
index++;
index = index & (RING_BUFFER_SIZE-1);
bufferVoll = 1;
}
// Status des uDMA I2S Kanals abfragen, primäre Kontrollstruktur
ulMode = uDMAChannelModeGet(UDMA_CHANNEL_I2S0RX | UDMA_ALT_SELECT);
// Wenn der uDMA den Transfer abgeschlossen hat, stoppt er automatisch
if
(ulMode == UDMA_MODE_STOP)
{
oldIndex = (index-1) & (RING_BUFFER_SIZE-1);
/*
* Konfiguriert die Übertragungsparamter für den nächsten Transfer des I2SRx Kanals für den uDMA
* Primäre Kontrollstruktur (Ping Pong Modus, Buffer1), Datenquelle ist der I2SRx FIFO
*/
uDMAChannelTransferSet(UDMA_CHANNEL_I2S0RX | UDMA_ALT_SELECT,
UDMA_MODE_PINGPONG, (
void
*) I2S_SOURCE,
(
void
*)I2SPuffer[index], BUFFER_SIZE);
uDMAChannelEnable(UDMA_CHANNEL_I2S0RX);
index++;
index = index & (RING_BUFFER_SIZE-1);
bufferVoll = 1;
}
}
How does the same functionality work on the stm32?
The data sheet for
stm32f4
and
the
Standard
Peripherals
Library
documentation
could
not yet
answer
these questions
clearly enough
to me. Here are also my current (not complete) configuration for i2s and the dma controller. I'm new to stm32 so I would be glad for all answers.
// I2S Base Address für STM32f407vgt6
#define I2S_PeripheralBaseAdd ((unsigned long)0x40003800 + 0x0C)
#define BUFFERSIZE 48
#define RINGBUFFERSIZE 8
volatile
signed
long
I2SPuffer[RINGBUFFERSIZE][BUFFERSIZE];
void
DMAInit()
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// DMA 1 mit Takt versorgen
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
/*
* DMA konfigurieren
* SPI2_RX (=I2S_RX): DMA1 Kanal 0, Stream 3
* Double Buffer Mode (Circular Mode muss enable sein)
*/
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_PeripheralBaseAdd;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_BufferSize = 48;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC8;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_Memory0BaseAddr = (
signed
long
) &I2SPuffer[0];
DMA_Init(DMA1_Stream3,&DMA_InitStructure);
DMA_DoubleBufferModeConfig(DMA1_Stream3, (
signed
long
) &I2SPuffer[1], DMA_Memory_0);
// Double buffer mode aktivieren
DMA_DoubleBufferModeCmd(DMA1_Stream3, ENABLE);
// Transfer complete interrupt aktivieren
DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);
/* Configure and enable DMA interrupt */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void
I2SInit()
{
// I2S und GPIO TypDefs
GPIO_InitTypeDef I2S_GPIOInitStructure;
I2S_InitTypeDef I2S_InitStructure;
// Port B mit Takt versorgen
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
// SPI2 mit Takt versorgen
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
// Hochgenaue PLL für I2S aktivieren
RCC_PLLI2SCmd(ENABLE);
// GPIO Pins für I2S konfigurieren
I2S_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF;
I2S_GPIOInitStructure.GPIO_OType = GPIO_OType_PP;
I2S_GPIOInitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
I2S_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// PB9 konfigurieren, aktivieren und mit I2S_2_WS verknüpfen
I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2);
// PB10 konfigurieren, aktivieren und mit I2S_2_SCK verknüpfen
I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);
// PB15 konfigurieren, aktivieren und mit I2S_2_WS verknüpfen
I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
// I2S Schnittstelle konfigurieren
I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_8k;
I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_24b;
I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx;
I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
// I2S Schnittstelle initialisieren
I2S_Init(SPI2, &I2S_InitStructure);
// I2S2 DMA Interface aktivieren
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
// I2S Schnittstelle aktivieren
I2S_Cmd(SPI2, ENABLE);
}
#i2s-rx-master-with-dma #dma-i2s-spi-interrupts