AnsweredAssumed Answered

[STM32f4-Discovery] I2S RX Master with DMA

Question asked by AM85 on Mar 6, 2013
Latest reply on Aug 26, 2014 by Clive One

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.

001.// I2S Base Address für STM32f407vgt6
002.#define I2S_PeripheralBaseAdd ((unsigned long)0x40003800 + 0x0C)
003. 
004.#define BUFFERSIZE 48
005.#define RINGBUFFERSIZE 8
006. 
007.volatile signed long I2SPuffer[RINGBUFFERSIZE][BUFFERSIZE];
008. 
009.void DMAInit()
010.{
011.    DMA_InitTypeDef DMA_InitStructure;
012.    NVIC_InitTypeDef NVIC_InitStructure;
013. 
014. 
015.    // DMA 1 mit Takt versorgen
016.    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
017. 
018. 
019.    /*
020.     * DMA konfigurieren
021.     * SPI2_RX (=I2S_RX): DMA1 Kanal 0, Stream 3
022.     * Double Buffer Mode (Circular Mode muss enable sein)
023.     */
024.    DMA_InitStructure.DMA_Channel = DMA_Channel_0;
025.    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
026.    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
027.    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Word;
028.    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
029.    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
030.    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
031.    DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_PeripheralBaseAdd;
032.    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
033.    DMA_InitStructure.DMA_BufferSize = 48;
034.    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC8;
035.    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
036.    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
037.    DMA_InitStructure.DMA_Memory0BaseAddr = (signed long) &I2SPuffer[0];
038. 
039.    DMA_Init(DMA1_Stream3,&DMA_InitStructure);
040. 
041. 
042.    DMA_DoubleBufferModeConfig(DMA1_Stream3, (signed long) &I2SPuffer[1], DMA_Memory_0);
043. 
044.    // Double buffer mode aktivieren
045.    DMA_DoubleBufferModeCmd(DMA1_Stream3, ENABLE);
046. 
047.    // Transfer complete interrupt aktivieren
048.    DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);
049. 
050. 
051.    /* Configure and enable DMA interrupt */
052.    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;
053.    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
054.    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
055.    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
056.    NVIC_Init(&NVIC_InitStructure);
057.}
058. 
059.void I2SInit()
060.{
061.    // I2S und GPIO TypDefs
062.    GPIO_InitTypeDef I2S_GPIOInitStructure;
063.    I2S_InitTypeDef I2S_InitStructure;
064. 
065.    // Port B mit Takt versorgen
066.    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
067. 
068.    // SPI2 mit Takt versorgen
069.    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
070. 
071.    // Hochgenaue PLL für I2S aktivieren
072.    RCC_PLLI2SCmd(ENABLE);
073. 
074.    // GPIO Pins für I2S konfigurieren
075.    I2S_GPIOInitStructure.GPIO_Mode = GPIO_Mode_AF;
076.    I2S_GPIOInitStructure.GPIO_OType = GPIO_OType_PP;
077.    I2S_GPIOInitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
078.    I2S_GPIOInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
079. 
080.    // PB9 konfigurieren, aktivieren und mit I2S_2_WS verknüpfen
081.    I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_9;
082.    GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
083.    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2);
084. 
085.    // PB10 konfigurieren, aktivieren und mit I2S_2_SCK verknüpfen
086.    I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_10;
087.    GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
088.    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);
089. 
090.    // PB15 konfigurieren, aktivieren und mit I2S_2_WS verknüpfen
091.    I2S_GPIOInitStructure.GPIO_Pin = GPIO_Pin_15;
092.    GPIO_Init(GPIOB,&I2S_GPIOInitStructure);
093.    GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
094. 
095. 
096.    // I2S Schnittstelle konfigurieren
097.    I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_8k;
098.    I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_24b;
099.    I2S_InitStructure.I2S_Mode = I2S_Mode_MasterRx;
100.    I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
101.    I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
102.    I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
103. 
104.    // I2S Schnittstelle initialisieren
105.    I2S_Init(SPI2, &I2S_InitStructure);
106. 
107.    // I2S2 DMA Interface aktivieren
108.    SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
109. 
110.    // I2S Schnittstelle aktivieren
111.    I2S_Cmd(SPI2, ENABLE);
112.}

Outcomes