cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F415 DMA SPI transfer after stop mode

rkoehli
Visitor

I am using SPI2 and SPI3 with DMA1 to write and read to/from peripheral chips. This works fine most of the time. At the same time I am using the stop mode to save power. Now in some cases just after leaving the stop mode, the next DMA transfer does not work. The transfer is started but the complete-interrupt-routine is never called. And when checking the registers the half transfer bit is not set. A reset of the DMA(LL_DMA_DeInit(DMA1, LL_DMA_STREAM_ALL)) in this case seems to not resolve the problem. I have to reset the whole device to resolve it.

DMA and SPI transfers are setup before each transfer.

LL_SPI_InitTypeDef SPI_InitStruct; //Structure containing the settings for SPI2
LL_DMA_InitTypeDef DMA_InitStruct; //Structure containing the settings for DMA1
NVIC_InitTypeDef NVIC_InitStructure;
LL_DMA_DisableIT_TC(DMA1, LL_DMA_STREAM_2);
LL_DMA_DisableIT_TC(DMA1, LL_DMA_STREAM_5);
LL_SPI_Disable(SPI3);
 
LL_I2S_DeInit(SPI3);
LL_SPI_StructInit(&SPI_InitStruct);
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32;
SPI_InitStruct.CRCPoly = 0;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
LL_SPI_Init(SPI3, &SPI_InitStruct);
 

//--------------- DMA1 Channel 0, Stream 2: Rx--------------//
// Deinitialise
LL_DMA_DeInit(DMA1, LL_DMA_STREAM_2);
LL_DMA_StructInit(&DMA_InitStruct);default value.
DMA_InitStruct.Channel                = LL_DMA_CHANNEL_0;
DMA_InitStruct.NbData                 = 0;
DMA_InitStruct.Direction              = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
DMA_InitStruct.FIFOMode               = LL_DMA_FIFOMODE_DISABLE;
DMA_InitStruct.MemoryOrM2MDstAddress  = (uint32_t)bleUbtagRWCom.spi3_RWVal.dataIn;
DMA_InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
DMA_InitStruct.MemoryOrM2MDstIncMode  = LL_DMA_MEMORY_INCREMENT;
DMA_InitStruct.Mode                   = LL_DMA_MODE_NORMAL;
DMA_InitStruct.PeriphOrM2MSrcAddress  = (uint32_t)&SPI3->DR;
DMA_InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
DMA_InitStruct.PeriphOrM2MSrcIncMode  = LL_DMA_PERIPH_NOINCREMENT;
DMA_InitStruct.Priority               = LL_DMA_PRIORITY_HIGH;
LL_DMA_Init(DMA1, LL_DMA_STREAM_2, &DMA_InitStruct);

//--------------- DMA1 Channel 0, Stream 5: Tx------------------//
LL_DMA_DeInit(DMA1, LL_DMA_STREAM_5);
DMA_InitStruct.Channel                = LL_DMA_CHANNEL_0;
DMA_InitStruct.NbData                 = 0;
DMA_InitStruct.Direction              = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
DMA_InitStruct.FIFOMode               = LL_DMA_FIFOMODE_DISABLE;
DMA_InitStruct.MemoryOrM2MDstAddress  = (uint32_t)bleUbtagRWCom.spi3_RWVal.dataOut;
DMA_InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
DMA_InitStruct.MemoryOrM2MDstIncMode  = LL_DMA_MEMORY_INCREMENT;
DMA_InitStruct.Mode                   = LL_DMA_MODE_NORMAL;
DMA_InitStruct.PeriphOrM2MSrcAddress  = (uint32_t)&SPI3->DR;
DMA_InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
DMA_InitStruct.PeriphOrM2MSrcIncMode  = LL_DMA_PERIPH_NOINCREMENT;
DMA_InitStruct.Priority               = LL_DMA_PRIORITY_HIGH;
LL_DMA_Init(DMA1, LL_DMA_STREAM_5, &DMA_InitStruct);

// Enable DMA Interrupts
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_2);
LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_5);

/*---------- End configuration DMA and SPI ------------------------*/
//Inititialize NVIC Struct or DMA1 Channel 0 Stream 2
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = IRQPreemptionPriorityMedium;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = IRQSubPriorityMax;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

//Inititialize NVIC Struct or DMA1 Channel 0 Stream 5
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = IRQPreemptionPriorityMedium;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = IRQSubPriorityMax;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_2);
LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_5);

ENABLE_CHIP_SELECT;

LL_SPI_DisableDMAReq_RX(SPI3);
LL_SPI_DisableDMAReq_TX(SPI3);
        
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_2, bytesToRead);
LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_5, bytesToRead);
LL_DMA_ClearFlag_TC2(DMA1);
LL_DMA_ClearFlag_TC5(DMA1);
// Start transfer
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_2);
LL_SPI_EnableDMAReq_RX(SPI3);
LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_5);
LL_SPI_Enable(SPI3);
LL_SPI_EnableDMAReq_TX(SPI3);
// Wait for transfer to complete
OS_RESULT res = os_evt_wait_or (EVENT_SPI3, SPI_TRANSFER_TIMEOUT);
if(res == OS_R_TMO) {
    // A timeout occured. This means that the end of the transfer
    // could not be detected.
    LL_DMA_DeInit(DMA1, LL_DMA_STREAM_ALL);
}

DISABLE_CHIP_SELECT;

And of course there is the interrupt routine:

// BLE   SPI RX, CH0 Stream2
void DMA1_Stream2_IRQHandler(void){
    if (LL_DMA_IsActiveFlag_TC2(DMA1)) {
        LL_DMA_ClearFlag_TC2(DMA1);
        LL_I2S_DisableDMAReq_RX(SPI3);
        LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_2);
        isr_evt_set(EVENT_SPI3, callerIdSpi3); // signal task ready
    }
    if (LL_DMA_IsActiveFlag_TE2(DMA1)) {
        LL_DMA_ClearFlag_TE2(DMA1); // Clear transfer error flag
        _bleTransferError |= TRANSFER_ERROR_RX;
        // Handle DMA transfer error
    }
}

The stop mode is done as shown in the following simplified code segment. All running tasks are halted before entering the stop mode and the entering of the stop mode is done from the idle task.

// Enter stop mode
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// Init system clocks
SystemInit();
// Resume task scheduler and set time the system was in stop mode
os_resume(TdS);

Additionally I tried the following to approaches to find a possible workaround but which did not work:

  • Start the task executing the DMA SPI with a delay of up to 10ms after stop mode
  • Increase the priority to very high of the failing DMA stream.

Is there maybe any known issue with DMA, SPI and stop mode?

Or can the setup of stream 3 and 4 influence the operation of stream 2 and 5 of the same DMA?

1 REPLY 1

> All running tasks are halted before entering the stop mode

What does that mean exactly, in terms of the DMA? Can the DMA be running when stop is entered?

 

[...]the next DMA transfer does not work. The transfer is started[...]

What does that mean, exactly? When failure occurs, are there SPI clocks from the started transfer (observed using oscilloscope/LA), and if yes, how many of the total expected?

 

Read out and check/post GPIO and SPI registers at the moment of "failure". In debugger, in that state, try to write to SPI_DR manually and observe clocks.

JW