2024-10-01 01:01 AM
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:
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?
Solved! Go to Solution.
2025-02-11 10:54 PM
It's the other way round. The RTOS is manipulating clocks while my software starts a SPI/DMA transfer. I was also astonished about the RTOS doing this, especially in this late phase. But once I waited until the RTOS commands were done, the problem disappeared.