Skip to main content
Visitor II
January 11, 2024
Question

STM32H7 periodical TIM triggered SPI transfer w/o DMA?

  • January 11, 2024
  • 12 replies
  • 5714 views

On a STM32H745ZI-Q - Board I want to create periodical 4-byte SPI transfers  triggered by a timer. 

So far I got this working the following way:

TIM triggers DMA
DMA initiates the SPI transfer.

However, the SPI transfer timing (CS low) has a high jitter of 100 ns, which is not acceptable.

To reduce the jitter: Is there any known way to trigger the SPI transfer start directly by a timer on this specific MCU (from reading the RM, I don't think so)? 

The data to be transfered could be written to the SPI FIFO in advance by an ISR triggered with a proper delay. 

    This topic has been closed for replies.

    12 replies

    Explorer
    January 11, 2024

    Why not use SPI w/o timer, it has its own divider?

    MSZBAuthor
    Visitor II
    January 11, 2024

    Then the SPI must be able to generate burst transfers (send 32 bits at say 20 MHz and then wait until a period of 10 us has passed). And it would be necessary to trigger timers for the generation of dependent signals from the 100 kHz SPI transfer period.

    Super User
    January 11, 2024

    Do the clock/data also have a 100ns jitter? I wouldn't expect it to be different, but I also wouldn't expect a 100ns jitter on a DMA transfer.

    There's definitely ideas of generating the SCK signal through a timer and running the SPI in slave mode. It could be done such that timing is exact.

    MSZBAuthor
    Visitor II
    January 11, 2024

    Yes, clock/data have the same jitter. 
    The slave mode idea sounds interesting.  

    Technical Moderator
    January 11, 2024

    Hi @MSZB 

    Would you add more details about your software configuration resulting in jitter? Using STM32H745ZI-Q, you mean NUCLEO-H745ZI-Q? 

    To configure a timer to trigger the DMA transfer at the desired interval, you can use the timer's compare match to generate an interrupt at the desired interval. Also, if you can disable the SPI master temporarily during DMA config, you can set AFCNTR to prevent glitches on outputs.

    MSZBAuthor
    Visitor II
    January 12, 2024

    Hi F.Belaid,

    yes I mean NUCLEO-H745ZI-Q.

    In the current test configuration, it's as follows:

    TIM4 is the master timer triggering the start of TIM12, TIM1 and TIM 8. All Timers run at the same frequency (50 kHz for now). TIM1 and TIM8 are used for PWM generation (total 8 channels).

    TIM4:

    • Clock source internal
    • TRGO Parameters: MSM Enabled, TRGO = CNT_EN

    TIM12 is a TIM4 slave and provides the Trigger output for DMA

    • Slave Mode = Trigger mode, Trigger Source = ITR0
    • Clock source internal
    • TRGO Parameters: MSM Enabled, Trigger Event Selection: Compare Pulse (OC1)
    • Channel 1 = PWM Generation CH

    DMA Stream 1: DMA Request = SPI4_TX

    • DMA Request Synchronisation settings:
    • Enable yes, Synch. Signal = TIM12 TRGO
    • Event enable = False
    • Request number = 4

    Special SPI4 Settings

    • Mode: Full-Duplex Master
    • Hardware NSS Output Signal used
    • Baud Rate 15 MBit /s (Prescaler =6)

    After CubeMX-generated HAL-based initialization of the peripherals, the ARR registers of all timers are set up for 50 kHz and individual CCR values are set. Afterwards the SPI transfer is started by:

      HAL_SPI_Transmit_DMA(&hspi4, (unsigned char *)&data, 4);

    After starting the DMA transfer, there is (currently) no further software interaction with TIM, DMA or SPI.

    Here are the CubeMX-Konfigurations of the above modules from the .ioc-File:

    TIM4.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
    TIM4.Channel-PWM\ Generation2\ CH2=TIM_CHANNEL_2
    TIM4.Channel-PWM\ Generation3\ CH3=TIM_CHANNEL_3
    TIM4.Channel-PWM\ Generation4\ CH4=TIM_CHANNEL_4
    TIM4.IPParameters=Channel-PWM Generation1 CH1,Channel-PWM Generation2 CH2,Channel-PWM Generation3 CH3,Channel-PWM Generation4 CH4,TIM_MasterSlaveMode,TIM_MasterOutputTrigger
    TIM4.TIM_MasterOutputTrigger=TIM_TRGO_ENABLE
    TIM4.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_ENABLE

    TIM12.Channel-PWM\ Generation1\ CH1=TIM_CHANNEL_1
    TIM12.IPParameters=TIM_MasterOutputTrigger,TIM_MasterSlaveMode,Channel-PWM Generation1 CH1
    TIM12.TIM_MasterOutputTrigger=TIM_TRGO_OC1
    TIM12.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_ENABLE

    Dma.Request0=SPI4_RX
    Dma.Request1=SPI4_TX
    Dma.RequestsNb=2
    Dma.SPI4_RX.0.Direction=DMA_PERIPH_TO_MEMORY
    Dma.SPI4_RX.0.EventEnable=DISABLE
    Dma.SPI4_RX.0.FIFOMode=DMA_FIFOMODE_DISABLE
    Dma.SPI4_RX.0.Instance=DMA1_Stream0
    Dma.SPI4_RX.0.MemDataAlignment=DMA_MDATAALIGN_BYTE
    Dma.SPI4_RX.0.MemInc=DMA_MINC_ENABLE
    Dma.SPI4_RX.0.Mode=DMA_NORMAL
    Dma.SPI4_RX.0.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
    Dma.SPI4_RX.0.PeriphInc=DMA_PINC_DISABLE
    Dma.SPI4_RX.0.Polarity=HAL_DMAMUX_REQ_GEN_RISING
    Dma.SPI4_RX.0.Priority=DMA_PRIORITY_LOW
    Dma.SPI4_RX.0.RequestNumber=1
    Dma.SPI4_RX.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,SignalID,Polarity,RequestNumber,SyncSignalID,SyncPolarity,SyncEnable,EventEnable,SyncRequestNumber
    Dma.SPI4_RX.0.SignalID=NONE
    Dma.SPI4_RX.0.SyncEnable=DISABLE
    Dma.SPI4_RX.0.SyncPolarity=HAL_DMAMUX_SYNC_NO_EVENT
    Dma.SPI4_RX.0.SyncRequestNumber=1
    Dma.SPI4_RX.0.SyncSignalID=NONE
    Dma.SPI4_TX.1.Direction=DMA_MEMORY_TO_PERIPH
    Dma.SPI4_TX.1.EventEnable=DISABLE
    Dma.SPI4_TX.1.FIFOMode=DMA_FIFOMODE_DISABLE
    Dma.SPI4_TX.1.Instance=DMA1_Stream1
    Dma.SPI4_TX.1.MemDataAlignment=DMA_MDATAALIGN_BYTE
    Dma.SPI4_TX.1.MemInc=DMA_MINC_ENABLE
    Dma.SPI4_TX.1.Mode=DMA_CIRCULAR
    Dma.SPI4_TX.1.PeriphDataAlignment=DMA_PDATAALIGN_BYTE
    Dma.SPI4_TX.1.PeriphInc=DMA_PINC_DISABLE
    Dma.SPI4_TX.1.Polarity=HAL_DMAMUX_REQ_GEN_RISING
    Dma.SPI4_TX.1.Priority=DMA_PRIORITY_LOW
    Dma.SPI4_TX.1.RequestNumber=1
    Dma.SPI4_TX.1.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,SignalID,Polarity,RequestNumber,SyncSignalID,SyncPolarity,SyncEnable,EventEnable,SyncRequestNumber
    Dma.SPI4_TX.1.SignalID=NONE
    Dma.SPI4_TX.1.SyncEnable=ENABLE
    Dma.SPI4_TX.1.SyncPolarity=HAL_DMAMUX_SYNC_RISING
    Dma.SPI4_TX.1.SyncRequestNumber=4
    Dma.SPI4_TX.1.SyncSignalID=HAL_DMAMUX1_SYNC_TIM12_TRGO

     

    SPI4.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_8
    SPI4.CalculateBaudRate=15.0 MBits/s
    SPI4.DataSize=SPI_DATASIZE_8BIT
    SPI4.Direction=SPI_DIRECTION_2LINES
    SPI4.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,VirtualNSS,BaudRatePrescaler,DataSize,TIMode
    SPI4.Mode=SPI_MODE_MASTER
    SPI4.TIMode=SPI_TIMODE_DISABLE
    SPI4.VirtualNSS=VM_NSSHARD
    SPI4.VirtualType=VM_MASTER

     

    Graduate II
    January 12, 2024

    Between what do you need to eliminate jitter ? Only in CS period ? If yes, then you can simply generate CS signal by timer output, and use compare event (from the same timer) to generate DMA request to SPI to transfer data. 

    MSZBAuthor
    Visitor II
    January 15, 2024

    The jitter that needs to be eliminated occurs between the PWM output signals of the timers (synchronization master) and the SPI signals.

    The SPI signals (CS, SCK, MOSI) alone are jitter-free.

    Since the sample timing reference of the SPI connected external ADC is SCK (not CS!) it doesn't help to use timer based CS generation.

    Graduate II
    January 15, 2024

    OK. If i understood correctly you need to eliminate jitter between CS and SCK. Then you should generate SCK and CS by timer(s) and set SPI to slave mode (As TDK recommended a few posts above). There are multiple ways how to setup (or cascade) timers to generate your signal. Way how to setup timers depends if you need SCK to be "muted" between readings (when CS is deasserted), or SCK can be continuous (typical SPI is compatible with that).


    MSZBAuthor
    Visitor II
    January 15, 2024

    Thanks for all the suggestions so far. Before trying these - back to the original question:
    Is it possible to the trigger transfer start of the SPI peripheral directly by a timer TRGO, without having to generate the SPI timings (SCK and/or CS) by timers, and without using DMA, on the STM32H745ZI?

    Graduate II
    January 15, 2024

    As far as i know, without DMA no.

    Technical Moderator
    January 15, 2024

    Hello @MSZB 

    Have you tried different clock prescaler, source ? Which VOS used? Please check Table 117 in DS for SPI dynamic characteristics as reference.

    Graduate II
    January 15, 2024

    DMA jitter might be caused by too much traffic on the busses. I had that problem when the CPU never slept and constantly checked some variables / peripherals much too often. As soon as I put the CPU to sleep most of the time, DMAs ran much "smoother". Maybe take a look at that when using DMA.

    Graduate II
    January 15, 2024

    Just to make sure... what's your clock source?

    HSI is terrible concerning jitter, the clock on the Nucleo from ST-link is much better, but surely not as good as a dedicated decent crystal or oscillator.

    Super User
    January 15, 2024

    > HSI is terrible concerning jitter, the clock on the Nucleo from ST-link is much better,

    This Nucleo contains STLink V3, which by default outputs MCO generated from the same horrible-jittery HSI. It might be switchable to crystal-generated non-8-MHz, though.

    JW

    Graduate II
    January 16, 2024

    Oh my, I forgot that the ST-link clock might come from its HSI...

    @MSZB so this is really something you should check.
    Unless you are sure that your initial clock source is not any HSI from anywhere, you really should check this.
    And somewhere you can change the ST-link's MCO. Probably something ugly like 25 MHz / 3 = 8.333 MHz from its external crystal, but that will be much better than what comes from HSI.

    MSZBAuthor
    Visitor II
    January 16, 2024

    HSE is used, 8 MHz MCO from ST-LINK. It's jitter is roughly 1 ns.

    The jitter of the TIM12 output, which triggers the DMA, is ca. 40 ns (!). Maybe I'll have too look at the supply configuration in more detail. The board runs in Supply Config  3 (Internal SMPS and LDO cascaded).

    Thats the clock configuration:

    Screenshot 2024-01-16 154123.png

    Explorer
    January 16, 2024

    Jitter is likely be a frquency beating, since SPI has it's own clock prescaler (MBR). Simple solution is to make SPI in "free run" mode - no issue with NSS pin driving, H7 has advenced SPI hardware, 

    hspi2.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;

    Changing this parameter allows to make a "fine tunning" of the sampling rate. 

    Than configure timers to run synchronously with SPI, divider steps to be about +- MBR divider values. Or route SCLK line to input capture of the timer and generate PWM back. 

    Super User
    January 16, 2024

    @MasterT,

    > Jitter is likely be a frquency beating, since SPI has it's own clock prescaler (MBR).

    Do you know for fact that the baudrate clock generator is free-running?

    I don't doubt it, I'm genuinely curious; this never occured to me as a possibility, but of course the SPI may've been designed in this way.

    JW

    Explorer
    January 16, 2024

    Yes, sure. SPI clock divider (MBR) is just same thing as prescaler in timers. So you can't run two timers in sync if one has prescaler set = 0 and another to something else. It would be possible only if first timer has ClockDivision1 = ClockDivision2 x Prescaler2