cancel
Showing results for 
Search instead for 
Did you mean: 

In STM32G474RE, How to properly re-configure SPI peripheral during run-time (using DMA for both Rx/Tx channels) ? I need to increase data to be received & transmitted with SPI after some time of running. Thanks, Tal.T

TTaie.1
Associate II

Here's my code for re-configuring the SPI1 peripheral, but after that the data being corrupted (even if I re-configure the same size of the previous configuration):

void AppSpiReconfig(void * ulRxBuffAddr, void * ulTxBuffAddr, unsigned long ulSize)

{

LL_SPI_InitTypeDef SPI_InitStruct = {0};

/* Disable DMA channels to reset their configurations */

LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);

LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);

while((LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY) && (LL_SPI_IsActiveFlag_BSY(SPI1) != 0))

{

};

LL_SPI_Disable(SPI1);

do {

LL_SPI_ReceiveData16(SPI1);

} while(LL_SPI_GetRxFIFOLevel(SPI1) != LL_SPI_RX_FIFO_EMPTY);

LL_SPI_DisableDMAReq_TX(SPI1);

LL_SPI_DisableDMAReq_RX(SPI1);

SET_BIT(RCC->APB2RSTR, RCC_APB2RSTR_SPI1RST);

CLEAR_BIT(RCC->APB2RSTR, RCC_APB2RSTR_SPI1RST);

while((LL_SPI_IsActiveFlag_BSY(SPI1) != 0))

{

};

/* SPI1 parameter configuration*/

SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;

SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;

SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_16BIT;

SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;

SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;

SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;

SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV4;

SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;

SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;

SPI_InitStruct.CRCPoly = 7;

LL_SPI_Init(SPI1, &SPI_InitStruct);

LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);

LL_SPI_EnableNSSPulseMgt(SPI1);

/* ------------------------------------------------------------------------------- */

/* Configure the DMA1_Channel2 functional parameters */

/* Prepare DMA data and length - RX */

LL_DMA_ConfigAddresses(DMA1,

LL_DMA_CHANNEL_1,

LL_SPI_DMA_GetRegAddr(SPI1), (unsigned long)ulRxBuffAddr,

LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));

LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ulSize);

/* Enable DMA SPI RX request */

LL_SPI_EnableDMAReq_RX(SPI1);

/* Prepare DMA data and length - TX */

LL_DMA_ConfigAddresses( DMA1,

LL_DMA_CHANNEL_2,

(unsigned long)ulTxBuffAddr, LL_SPI_DMA_GetRegAddr(SPI1),

LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));

LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, ulSize);

/* Enable DMA SPI TX request */

LL_SPI_EnableDMAReq_TX(SPI1);

/* Enable DMA SPI TX/RX channels */

SpiDmaActivate();

/* Enabel SPI peripheral requests */

LL_SPI_Enable(SPI1);

}

7 REPLIES 7
gbm
Lead II

You cannot continue to receive data after you disable SPI - these two actions should be reversed (if they are needed at all). Actually you don't need to do anything with SPI. It's enough to stop TX DMA, then complete and stop RX DMA, then program new RX DMA, then new TX DMA settings.

Hi, thanks for your reply !

So you mean something like this :

  • disable DMA TX channel and wait for FTLVL & BSY to be cleared.
  • disable DMA RX channel and read all remain data in buffer until its empty
  • reconfigure the source/dest buffer and the length of each DMA channel
  • Enable the DMA channels

BTW, which API is preferred to stop DMA,

LL_DMA_DisableChannel or

LL_SPI_DisableDMAReq ?

Thanks,

Tal.T

The name LL_DMA_***() says that it configures the DMA peripheral. The other one configures the SPI peripheral - disables the DMA request generation.

S.Ma
Principal

I usually rely on dma rx completed transaction interrupt in master mode before preparing the next block size. Now some newer STM32 have an automated blocks linked list support to minimize the pause time between blocks. Remains NSS hw handling to make SPI HW bandwidth efficient.

TTaie.1
Associate II

Ok, thanks for the replies, I've changed my code according to what people said here to this:

void AppSpiReconfig(void * ulRxBuffAddr, void * ulTxBuffAddr, unsigned long ulSize)

{

/* Disable DMA channels to reset their configurations */

while(!(LL_DMA_IsActiveFlag_TC2(DMA1))) {};

LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);

while((LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY) && (LL_SPI_IsActiveFlag_BSY(SPI1) != 0))

{

};

while(!(LL_DMA_IsActiveFlag_TC1(DMA1))) {};

LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);

do {

LL_SPI_ReceiveData16(SPI1);

} while(LL_SPI_GetRxFIFOLevel(SPI1) != LL_SPI_RX_FIFO_EMPTY);

/* ------------------------------------------------------------------------------- */

/* Configure the DMA1_Channel2 functional parameters */

/* Prepare DMA data and length - RX */

LL_DMA_ConfigAddresses(DMA1,

LL_DMA_CHANNEL_1,

LL_SPI_DMA_GetRegAddr(SPI1), (unsigned long)ulRxBuffAddr,

LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1));

LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, ulSize);

LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);

/* Prepare DMA data and length - TX */

LL_DMA_ConfigAddresses( DMA1,

LL_DMA_CHANNEL_2,

(unsigned long)ulTxBuffAddr, LL_SPI_DMA_GetRegAddr(SPI1),

LL_DMA_GetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2));

LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, ulSize);

LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);

}

Still the data keep arrive corrupted to the slave...

Any advice ?

S.Ma
Principal

When using DMA, avoid manual data push without it. Reprogram the DMA for your 2 bytes, even if less efficient.

I would not use fifo flags. Get the dma receive block complete info to prepare the next block. Damn.

Unless I couldn't dig it, Refman examples from Cube are like making a Frankeinstein prototype from molecules instead of stitching organs....

Pavel A.
Evangelist III

> Refman examples from Cube are like making a Frankeinstein prototype from molecules instead of stitching organs.

ChatGPT, yeah ; )