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 II

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

You don't want to synchronize, but to generate an event. Read the DMAMUX chapter in RM, DMAMUX request generator subchapter.

JW

PS. The signals naming is a hopeless mess, extit0 in DMAMUX chapter, exti_exti0_it in the interconnections chapter, exti_exti0_wkup in NVIC chapter, probably all denoting the same signal, neither of which is mentioned in the EXTI chapter... @Vincent Onde​ 

JShro
Associate II

Hi Jan,

Thanks for the input - but I think I am even more confused now... Please bear with me for a moment as I try to clarify...

I read the DMAMUX chapter but it is a bit confusing for me so just to clarify my understanding here's is how I interpreted it - please correct my misunderstanding...

The block diagram - copied below

0690X00000BvV5FQAV.png

Seems to indicate that we can have a DMA_REQUEST from peripheral not processed until a Synchronization input is present.

In my case I was hoping to achieve the following...

I was assigning the DMA1_STREAM0 channel to SPI2_TX. (I request a DMA TX operation by invoking the following in the code)

  /* Enable the Tx DMA Stream/Channel  */
  if (HAL_OK != HAL_DMA_Start_IT(hspi2.hdmatx, (uint32_t)aTxBufferNormal, (uint32_t)&(hspi2.Instance->TXDR), 1))
  {
    /* Update SPI error code */
    SET_BIT(hspi2.ErrorCode, HAL_SPI_ERROR_DMA);
    errorcode = HAL_ERROR;
    hspi2.State = HAL_SPI_STATE_READY;
    return errorcode;
  }
 
 
  // MODIFY_REG(hspi2.Instance->CR2, SPI_CR2_TSIZE, 1);
  
 
  /* Enable Tx DMA Request */
  SET_BIT(hspi2.Instance->CFG1, SPI_CFG1_TXDMAEN);
 
  /* Enable the SPI Error Interrupt Bit */
  __HAL_SPI_ENABLE_IT(&hspi2, (SPI_IT_OVR | SPI_IT_UDR | SPI_IT_FRE | SPI_IT_MODF));
 
  /* Enable SPI peripheral */
  __HAL_SPI_ENABLE(&hspi2);
 
 
    /* Master transfer start */
    SET_BIT(hspi2.Instance->CR1, SPI_CR1_CSTART);

The intent was that until the exti input (on PA0 ) goes from high to low - falling edge trigger - the DMA transaction that was setup would not be executed since the synch input condition has not been met.

I was assuming that by setting up the DMAMUX in the following fashion

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();
    }

I was basically telling the DMA subsystem to not start the SPI2_TX DMA transaction until the exti input goes from high to low.. - is that not the case?

What is interesting is that with the above setup if I manually toggle bit PR1 in register CPUPR1 the transmission happens (in fact I can toggle any bit but PR0) in the CPUPR1 register and the SPI transmission is initiated... - Obviously the intent was to have the PR0 flag automatically trigger the TX but that does not occur...

I think what you are suggesting is setting up the DMAMUX request generator and using the EXTI input as a trigger to write to the SPI TXDR register directly? - That could work but I am a bit confused as to why the above setup should not work especially since the block diagram seems to indicate that this scheme is feasible - but again as I mentioned initially I am still confused on exactly how the DMAMUX works and the empirical results I am getting are confusing me even more...

Really appreciate any help in clearing up my confusion.

Thanks!

Jay

I misunderstood your - you indeed appear to need the synchronization feature of DMAMUX.

I don't use the 'H7 nor DMAMUX, but I doubt there are many users here who do (other than using the straightforward request mapping).

I also don't use Cube. I believe in this case you should also stop talking in terms of Cube/HAL and start discussing directly registers content.

> What is interesting is that with the above setup if I manually toggle bit PR1 in register CPUPR1

How? You can only clear bits in CPUPR1, its bits are rc1.

But, as you write the register (and register bits can't be written alone, hardware always writes at least a byte) there may be some mechanism in hardware which generates an edge on the output signal which then goes into DMAMUX. This is not well described in the manual (as I wrote above, it's not clear at all what the signal which goes into DMAMUX really is).

But talking about EXTI_CPUPR1, you should be able to *see* that the selected edge occured, that should set this register's bit 0. You should be also able to set it indirectly by setting the respective bit in EXTI_SWIER1.

As an experiment, you can also enable the respective EXTI0 interrupt and write a simple ISR for it to check it is working - e.g. toggle a pin observed by LA.

That in turn should generate the required edge in the synchronization input of DMAMUX. Check the respective DMAMUX1_CxCR register, if the proper SYNC_ID and proper DMAREQ_ID (i.e. the number corresponding to SPI2_TX) is set in it.

JW

>How? You can only clear bits in CPUPR1, its bits are rc1.

I was just toggling it in the system view window under uvision but that made me think some more about what you said below ...

>As an experiment, you can also enable the respective EXTI0 interrupt and write a simple ISR for it to check it is working - e.g. toggle a pin observed by LA.

Yes I did that and lo and behold the SPI started working. :o . It seems the internal routing must require the int to be serviced for the DMAMUX to be notified - makes no sense to me still... Since this means I am wasting time in the ISR (even if it is simply to service the ISR) basically I was hoping to start the SPI transmission immediately after the EXTI_IT edge detection but as it stands the ISR takes up 230 ns so the SPI transmission is delayed by that much time which will not allow me to meet the timing spec for the A/D converter that I am talking to

So maybe I need to look for a better synch signal - however I am assuming any synch input will behave in the same way - can any one from ST confirm?

Thanks

Jay

Interesting.

Can you please try the other (rising edge) polarity set for the synchronization input in DMAMUX?

JW

JShro
Associate II

Just checked that also - it seems the polarity does not matter both rising & falling edge require the ISR to be enabled - would love to understand the reason behind this as it seems very inefficient to have to do it this way. Hope someone from ST can shed some light

Thanks

berendi
Principal

If it doesn't work out with EXTI, then see if you can route the external signal to a timer channel.

Then you can set up the timer channel in capture mode, and issue a DMA request on capture to write the SPI TX register.

> both rising & falling edge require the ISR to be enabled -

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. I expected to see the interrupt-caused latency to go away with the opposite polarity.

To avoid the interrupt yet still clear the pending interrupt, you could perhaps use another DMAMUX channel to generate a DMA from the same exti0-interrupt, which would write the appropriate constant into EXTI_CPUPR1 clearing the pending interrupt.

Or, use the workaround outlined by berendi above.

JW

NPato
Associate II

Hi,

I had a very similar problem and ended up routing the external signal in via LPTIM1 (rather than EXTI0) and using that as the synchronisation event for the DMA.

Interestingly EXTi0 can be used in DMAMUX2/BDMA directly as I've also tried this when using SPI6.

Cheers

Nigel