AnsweredAssumed Answered

STM32F407: SPI as slave using DMA

Question asked by k.k.004 on May 27, 2016
Latest reply on May 28, 2016 by karpavicius.linas
Hi there,

I'm desperatly trying to get the STM32F407 work as a SPI slave with DMA TX/RX functionality. I literally read any thread about this topic Google found (~5 pages of results) but it's just not working and I just don't know why.
The STM32F4 should work as a SPI slave using the GPIOB Pins 13, 14, 15. This means I have to use SPI2 and DMA1 Stream 4 (TX) and DMA1 Stream 3 (RX). See my code:

First, I'm powering up all relevant parts.
void CTRLSPI_RCC_init()    
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_DMA1, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,  ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
}

Then I configure the pins named above as Alternate Function and couple them:
void CTRLSPI_GPIO_init()
{
    GPIO_InitTypeDef GPIO_InitStructure;
 
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource1, GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource2, GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI2);
 
    GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType     = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_DOWN;
    GPIO_InitStructure.GPIO_Speed    = GPIO_High_Speed;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

As a next step I set up the SPI as a slave and enable a interupt:
void CTRLSPI_SPI_init()
{
    SPI_I2S_DeInit(SPI2);
    SPI_InitTypeDef SPI_InitStructure;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 0;
     
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;
    SPI_Init(SPI2, &SPI_InitStructure);
    SPI_I2S_ITConfig(SPI2,SPI_I2S_IT_RXNE,ENABLE);
}

Then I configure and initialise the DMA Streams for receiving and transmitting over the SPI2 Data Register:
void CTRLSPI_DMA_init()
{
    DMA_InitTypeDef DMA_InitStructure;
 
    DMA_DeInit(DMA1_Stream4);
    DMA_DeInit(DMA1_Stream3);
 
    // Configure the Rx part
    DMA_InitStructure.DMA_Channel                 = DMA_Channel_0;                
    DMA_InitStructure.DMA_PeripheralBaseAddr     = (uint32_t)&(SPI2->DR);             
    DMA_InitStructure.DMA_Memory0BaseAddr       = (uint32_t)&CTRLSPI_RX_buffer[0];   
    DMA_InitStructure.DMA_DIR                     = DMA_DIR_PeripheralToMemory;        
    DMA_InitStructure.DMA_BufferSize             = CTRLSPI_RX_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_Normal;
    DMA_InitStructure.DMA_Priority                 = DMA_Priority_Medium;
    DMA_InitStructure.DMA_FIFOMode                 = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold         = DMA_FIFOThreshold_HalfFull;
    DMA_InitStructure.DMA_MemoryBurst             = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst         = DMA_PeripheralBurst_Single;
    DMA_Init(DMA1_Stream3, &DMA_InitStructure);
    DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Stream4, DISABLE);
 
 
    //DMA TX.. just modify the relevant parts...
    DMA_InitStructure.DMA_PeripheralBaseAddr     = (uint32_t)&(SPI2->DR);
    DMA_InitStructure.DMA_Memory0BaseAddr       = (uint32_t)&CTRLSPI_TX_buffer[0];
    DMA_InitStructure.DMA_DIR                     = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize             = CTRLSPI_TX_BUFFER_SIZE;
    DMA_Init(DMA1_Stream4, &DMA_InitStructure);
    DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);
    DMA_Cmd(DMA1_Stream4, DISABLE);
}

Here I the DMA interrupts I use:
extern "C" void DMA1_Stream4_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_Stream4, DMA_IT_TCIF4))
    {
        CTRLSPI_TX_statusled=!CTRLSPI_TX_statusled;
        if(CTRLSPI_TX_statusled){GPIOD->BSRRL=GPIO_Pin_14;}
        else{GPIOD->BSRRH=GPIO_Pin_14;}
        DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
    }
 
}
 
extern "C" void DMA1_Stream3_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3))
    {
        CTRLSPI_RX_statusled=!CTRLSPI_RX_statusled;
        if(CTRLSPI_RX_statusled){GPIOD->BSRRL=GPIO_Pin_15;}
        else{GPIOD->BSRRH=GPIO_Pin_15;}
        DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);
    }
}


As a last the I set up the Interrupts using NVIC:
void CTRLSPI_NVIC_init()
{
    NVIC_InitTypeDef NVIC_InitStructure;
 
    //DMA TX
    NVIC_InitStructure.NVIC_IRQChannel                     = DMA1_Stream4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority         = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                 = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    //DMA RX
    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);
}



Finally I put it all together and enable the stream:
SystemInit();
...
CTRLSPI_RCC_init();
CTRLSPI_GPIO_init()
CTRLSPI_SPI_init();
CTRLSPI_DMA_init();
CTRLSPI_NVIC_init();
 
DMA_Cmd(DMA1_Stream4, ENABLE);
DMA_Cmd(DMA1_Stream3, ENABLE);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Tx,ENABLE);
SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE);
 
DMA_ClearITPendingBit(DMA1_Stream4, DMA_IT_TCIF4);
DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3);
DMA_ClearFlag(DMA1_Stream3, DMA_FLAG_FEIF3);
SPI_Cmd(SPI2, ENABLE);
...

Then I connect some other SPI-port of the STM32F4 without DMA to the one I'm trying to configure with DMA. I already checked that the simple DMA is outputting data (used a logic analyzer), but they just won't be read in. Using the debugger I found out, that neither the DMA1_Stream4 nor the DMA1_Stream3 interrupt handler are ever called. What's wrong with my configuration?

Thank you very much for any advice! I appreciate your help :)

Outcomes