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?
2024-10-01 01:29 AM - edited 2024-10-01 01:32 AM
> 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
2024-10-02 02:17 AM
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.
2024-10-02 02:26 AM
@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?
2024-10-02 08:35 AM
>> 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
2024-12-10 05:13 AM
Ok, it took me a while to advance on this problem. I redesigned my project so that all access to DMA1 are made from a timer interrupt and therefore never at the same time. The problem still occurs.
I recorded the signals when the DMA access is interrupted (1: Test-Signal 2: MOSI 3: SCK 4: Chip Select). The first picture shows the complete SPI access. The second shows a close up of the moment the SPI access stops. One can see that just at that moment the clock signal frequency changes.
I then tried to reset one by one the DMA, the SPI and then the Clock through the RCC AHB1 peripheral reset, resp. RCC APB1 peripheral reset. But I could not get the SPI running again. I again had to reset the complete board.
One note on the system clocks. After leaving the stop mode the software has to reset and setup the clock registers. This is library code from system_stm32f4xx.c, function SystemInit(). This happens before the first SPI access is started.
2024-12-10 05:21 AM
I also wrote out the registers after the error occurs.
10.12. 12:50:51.501 || i || DMA1:LISR=04100000,HISR=00000410,S3CR=00020411,S4CR=00020451,S3NDTR=008C,S4NDTR=008A || 0008 || hw_spi.c 1145
10.12. 12:50:51.513 || i || SPI2:CR1=0357,CR2=03,SR=0043 || 0008 || hw_spi.c 1157
10.12. 12:50:51.524 || i || DMA1:LISR=04100000,HISR=00000410,S2CR=00020410,S5CR=00020450,S2NDTR=0000,S5NDTR=0000 || 0008 || hw_spi.c 1145
10.12. 12:50:51.530 || i || SPI3:CR1=0367,CR2=00,SR=0002 || 0009 || hw_spi.c 1157
10.12. 12:50:51.634 || i || RCC CSR:00000003,CFGR:0000100A,PLLCFGR:08433008,CIR:00000000,CSR:00000003 || 0008 || hw_spi.c 1167
10.12. 12:50:51.645 || i || RCC AHB1RSTR:00000000,APB1RSTR:00000000,AHB1ENR:0070101F,APB1ENR:160AC02B || 2000008 || hw_spi.c 1173
The SPI status register reports an "Overrun flag", which indicates that the data have been read from the SPI data lines but the register has not yet been read. The reading of the register would be the DMAs task. Does anyone have a clue how this could happen?
2024-12-10 05:27 AM
The DMA is not running in stop mode. It is started approx 1msec after leaving stop mode.