cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F415 DMA SPI transfer after stop mode

rkoehli
Associate

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?

4 REPLIES 4

> 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

The error is not so easy to reproduce. I tested on a device overnight but had no luck. So I will provide some of the information later on. But for now here some answers:

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

The DMA is not running in stop mode, which I think isn't even possible. The DMA/SPI transfer is started in normal mode. But the observed problems always occured just after leaving the stop mode, which is why I was asking if there is any known issue or link between them. 

I was not able to observe the signals with the oscilloscope. Again because it happens rarely.

And I can't use the debugger since the debugger is disconnected when the processor enters the stop mode.

I will try to record the register contents of SPI and DMA and come back with this information soon.


@rkoehli wrote:

And I can't use the debugger since the debugger is disconnected when the processor enters the stop mode.


You should be able to configure the debugger for 'Debug in low power modes'

(This may or may not help, as it does affect the behaviour in the low-power modes)

 


@rkoehli wrote:

I was not able to observe the signals with the oscilloscope. Again because it happens rarely.


Could you generate a trigger signal on a GPIO when the problem occurs, and use that to trigger the scope to capture up to that point?

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

> The DMA is not running in stop mode, which I think isn't even possible.

My question is, is DMA running *when* stop is entered (not *during stop*).

Yes, this, together with capturing the events when DMA refuses to run, call for advanced observation-fu. I don't have specific guideline, generally these things involve toggling various GPIO outputs and triggering oscilloscopes/LAs based on their combinations.

JW