cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 triggering a SPI TX transmission via DMA with a synch event on EXTI0

JShro
Associate III

Hi All,

Looking at the STM32H7 Ref manual it seems the DMAMUX would allow for a DMA channel to be synchronized with an external event on the EXTI0 input.

I would like to use this capability to trigger an SPI transmission to an ADC (since this would allow me to avoid time consuming tasks in the ISR routines )

I figured I would be able to configure the SPI2 TX in DMA mode and enable the DMA request synchronization settings so I have the following configuration.

SPI2 TX is assigned to DMA1 Stream 0. and the DMA Request synchronization settings are setup for

Enable Synchronization

Synchronization signal EXTI0

SignalPolarity: Synchronize on falling edge

Request Number: 1

The SPI initialization is as follows

hdma_spi2_tx.Instance = DMA1_Stream0;
    hdma_spi2_tx.Init.Request = DMA_REQUEST_SPI2_TX;
    hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_spi2_tx.Init.MemInc = DMA_MINC_DISABLE;
    hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_spi2_tx.Init.Mode = DMA_CIRCULAR; //DMA_NORMAL;
    hdma_spi2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
    hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK)
    {
      Error_Handler();
    }
 
    pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
    pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_FALLING;
    pSyncConfig.SyncEnable = ENABLE;
    pSyncConfig.EventEnable = DISABLE;
    pSyncConfig.RequestNumber = 1;
    if (HAL_DMAEx_ConfigMuxSync(&hdma_spi2_tx, &pSyncConfig) != HAL_OK)
    {
      Error_Handler();
    }
 
    __HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx);

The EXTI input is configured as follows

GPIO_InitStruct.Pin = SPI2_EXTI_SYNC_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLDOWN; //GPIO_NOPULL;
HAL_GPIO_Init(SPI2_EXTI_SYNC_GPIO_Port, &GPIO_InitStruct);

The EXTI input is PA0 and it is driven via a PWM signal from the High resolution timer output pin connected to PA0.

I have verified that I am getting a pulse every 1 us on the EXTI input - however the SPI DMA TX is not being triggered.

Any thoughts on if this is an appropriate way to achieve the desired outcome or am I missing something?

Thanks

Jay

20 REPLIES 20
JShro
Associate III

Thanks for the confirmation - Yeah I ended up using LPTIM also. Still curious on why EXTI0 does not work in this scenario but I guess we will never know :sad_but_relieved_face:

> Still curious on why EXTI0 does not work in this scenario

Because, the input to DMAMUX is coming from the output of EXTI module, i.e. EXTI pending register.

>> I did not expect this to go away - as the DMAMUX synchro is edge-driven, there must be something which clears the pending exti0-interrupt signal, so that the next edge could be generated.

> Interestingly EXTi0 can be used in DMAMUX2/BDMA directly

That's because input to DMAMUX2 is not coming from EXTI module, ie. it's not the output of EXTI having been already latched, but directly from SYSCFG, i.e. it's the output of the EXTI input multiplexer, i.e. it's directly the pin assigned to given EXTI.

So the real question is, why did ST decide to wire it up in this particular manner, and also why don't they document it properly (as the bare minimum, the signal names in the DMAMUX chapter ought to match signal names in other relevant chapters).

JW

JShro
Associate III

Hi Jan,

Thanks for clarifying your comment - looks like I missed your explanation in the initial response but your clarification does make sense and certainly explains the behavior we are observing...

Agree on the documentation... the DMAMUX documentation is really sparse - hopefully some of this makes it back to the folks responsible for it...

DMast.1
Associate II

Hi all, I have the same problem to interface my stm32h745 with an external 16bit ADC that start samplig with a CNV signal and tell to MCU that conversion ended sending BUSY signal down. So I interesting to understand if JShro was able to synchronize SPI communication with and external interrupt EXTI and get a interrupt on DMA read end event.

Thanks in advance

DM

See the responses from @Community member​  That explains why EXTI will not work - I ended up using LPTIM input.

DMast.1
Associate II

So, let me explain if I understand correctly how you read out the conversion from ADC. You setup an LPTIM peripheral to generate the external CNV signal to start conversion (suppose 1MHz freq and 50% duty) and use the negative edge of this signal to synchronize the SPI DMA to get conversion from ADC setting at least 60MHz of SPI_SCK to respect the minimal timings? If this is, I suppose you use the SPI complete ISR to save data somewhere RAM memory and check the complete acquisition sequence (ex. 1000 samples)?

Thanks in advance

DM

DMast.1
Associate II

Hi to all, I've implement the synchronization with LPTIM2 to generate ad 1MHz (50% duty) to generate the start conversion signal for AD converte. I was able to synchronize the spi transmission start with the falling edge of conversion start signal but sometimes doesn't get out from MOSI all 3 byte but only two or one.

Did somebody have this issue?

P.S.: the external ADC that I'm using is the LTC2335ILX-16.

Thanks in advance for any suggestion to exit from this situation that cause me spent a lot of time.

DMast.1
Associate II

Hi in addition to previus message, here is the initialization code of SPI and DMA:

  SpiHandle.Instance               = SPI4;
  SpiHandle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;    // Recommended setting to avoid glitches
  SpiHandle.Init.Mode = SPI_MODE_MASTER;
 
  SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; //SPI_BAUDRATEPRESCALER_128,
  SpiHandle.Init.Direction = SPI_DIRECTION_2LINES;
 
  SpiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
  SpiHandle.Init.CLKPolarity = SPI_POLARITY_LOW;
 
  SpiHandle.Init.DataSize = SPI_DATASIZE_8BIT;
  SpiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
  SpiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
 
  SpiHandle.Init.NSS = SPI_NSS_HARD_OUTPUT;
  SpiHandle.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  SpiHandle.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
 
  SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  SpiHandle.Init.CRCPolynomial = 7;
  SpiHandle.Init.CRCLength = SPI_CRC_LENGTH_8BIT;
 
  SpiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
 
  if(HAL_SPI_Init(&SpiHandle) != HAL_OK)
  {
    // Initialization Error
    while(1){};
  }
  
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  SPI4_SCK_PORT_CLK_EN();          //PE2: SPI4_SCK
  SPI4_NSS_PORT_CLK_EN();          //PE4: SPI4_NSS
  SPI4_MISO_PORT_CLK_EN();         //PE5: SPI4_MISO
  SPI4_MOSI_PORT_CLK_EN();         //PE6: SPI4_MOSI
  SPI4_SCK_EN();                   //Peripheral clock
 
  /*##-2- Configure peripheral GPIO ##########################################*/
  /* SPI SCK GPIO pin configuration  */
  GPIO_InitStruct.Pin       = SPI4_SCK_PIN;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.Alternate = SPI4_SCK_AF;
  HAL_GPIO_Init(SPI4_SCK_GPIO_PORT, &GPIO_InitStruct);
 
  /* SPI NSS GPIO pin configuration  */
  GPIO_InitStruct.Pin       = SPI4_NSS_PIN;
  GPIO_InitStruct.Alternate = SPI4_NSS_AF;
  HAL_GPIO_Init(SPI4_NSS_GPIO_PORT, &GPIO_InitStruct);
 
  /* SPI MISO GPIO pin configuration  */
  GPIO_InitStruct.Pin = SPI4_MISO_PIN;
  GPIO_InitStruct.Alternate = SPI4_MISO_AF;
  HAL_GPIO_Init(SPI4_MISO_GPIO_PORT, &GPIO_InitStruct);
 
  /* SPI MOSI GPIO pin configuration  */
  GPIO_InitStruct.Pin = SPI4_MOSI_PIN;
  GPIO_InitStruct.Alternate = SPI4_MOSI_AF;
  HAL_GPIO_Init(SPI4_MOSI_GPIO_PORT, &GPIO_InitStruct);
 
  /*##-3- Configure the DMA ##################################################*/
  /* Enable DMA clock */
  DMA1_CLK_ENABLE();
 
  /* Configure the DMA handler for Transmission process */
  hdma_tx.Instance                 = SPIx_TX_DMA_STREAM;
  hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_ENABLE;//>>DMA_FIFOMODE_DISABLE;
  hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;//>>DMA_MBURST_SINGLE;//>>DMA_MBURST_INC4;
  hdma_tx.Init.PeriphBurst         = DMA_MBURST_INC4;//>>DMA_MBURST_SINGLE;//>>DMA_PBURST_INC4;
  hdma_tx.Init.Request             = SPIx_TX_DMA_REQUEST;
  hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
  hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
  hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
  hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
  hdma_tx.Init.Mode                = DMA_PFCTRL;//>>DMA_NORMAL;
  hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
 
  HAL_DMA_Init(&hdma_tx);
 
  /* Configure the DMAMUX with the Synchronization parameters */
  DmaSyncConfig.EventEnable   = ENABLE;                       /* Enable DMAMUX event generation each time  RequestNumber are passed from DMAMUX to the DMA */
  DmaSyncConfig.SyncPolarity  = HAL_DMAMUX_SYNC_FALLING;      /* Synchronization edge is Rising  */
  DmaSyncConfig.RequestNumber = 3;//3;//4;                        /* 4 requests are autorized after each edge of the sync signal */
  DmaSyncConfig.SyncSignalID  = HAL_DMAMUX1_SYNC_LPTIM2_OUT;  /* Sync signal is LPTIM1_OUT */
  DmaSyncConfig.SyncEnable    = ENABLE;                       /* Synchronization is enabled */
 
  HAL_DMAEx_ConfigMuxSync(&hdma_tx, &DmaSyncConfig);
 
  /* Associate the initialized DMA handle to the the SPI handle */
  __HAL_LINKDMA(hspi, hdmatx, hdma_tx);
 
  /* Configure the DMA handler for Transmission process */
  hdma_rx.Instance                 = SPIx_RX_DMA_STREAM;
 
  hdma_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
  hdma_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
  hdma_rx.Init.MemBurst            = DMA_MBURST_INC4;//>>DMA_MBURST_SINGLE;//>>DMA_MBURST_INC4;
  hdma_rx.Init.PeriphBurst         = DMA_MBURST_INC4;//>>DMA_MBURST_SINGLE;//>>DMA_PBURST_INC4;
  hdma_rx.Init.Request             = SPIx_RX_DMA_REQUEST;
  hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
  hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
  hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;
  hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
  hdma_rx.Init.Mode                = DMA_PFCTRL;//DMA_NORMAL;
  hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
 
  HAL_DMA_Init(&hdma_rx);
 
  /* Associate the initialized DMA handle to the the SPI handle */
  __HAL_LINKDMA(hspi, hdmarx, hdma_rx);
 
  /*##-4- Configure the NVIC for DMA #########################################*/
  /* NVIC configuration for DMA transfer complete interrupt (SPIx_TX) */
  HAL_NVIC_SetPriority(SPIx_DMA_TX_IRQn, 1, 1);
  HAL_NVIC_EnableIRQ(SPIx_DMA_TX_IRQn);
 
  /* NVIC configuration for DMA transfer complete interrupt (SPIx_RX) */
  HAL_NVIC_SetPriority(SPIx_DMA_RX_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(SPIx_DMA_RX_IRQn);
 
  /*##-5- Configure the NVIC for SPI #########################################*/
  /* NVIC configuration for SPI transfer complete interrupt (SPIx) */
  HAL_NVIC_SetPriority(SPIx_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(SPIx_IRQn);
 

And defined these interrupts definition

/**
  * @brief  This function handles SPIx interrupt request.
  * @param  None
  * @retval None
  */
void SPI4_IRQHandler(void)
{
  HAL_SPI_IRQHandler(&SpiHandle);
}
 
/**
  * @brief  This function handles DMA Rx interrupt request.
  * @param  None
  * @retval None
  */
void SPI4_DMA_RX_IRQHandler(void)
{
  HAL_DMA_IRQHandler(SpiHandle.hdmarx);
}
 
/**
  * @brief  This function handles DMA Tx interrupt request.
  * @param  None
  * @retval None
  */
void SPI4_DMA_TX_IRQHandler(void)
{
  HAL_DMA_IRQHandler(SpiHandle.hdmatx);
}

And use LPTIM2 to generate the ADC start conversion @1MHz and 50% duty and synchronize the SPI transmission start at LPTIM2 falling edge. At this moment I get only the first packet and there is no way to restart trasmission in LPTIM2 interrupt. I would litke that SPI data streaming with lpt synch continue till I stop the timer.

Any suggestion?

Thanks in advace to anybody who can help me.

DM

> I would like that SPI data streaming with lpt synch continue till I stop the timer.

Use circular DMA.

I don't use Cube and don't understand it. If you still need help with this, start a new thread, describing your problem and findings, perhaps with screenshots taken for the waveforms observed on LA. Read out and check/post of relevant timer, SPI, DMAMUX and DMA registers.

JW

thanks for sharing this

can you guys share your code and the configuration for the LPTIM1 ?

appreciate your help