Skip to main content
Explorer
October 15, 2018
Solved

STMF4 External interrupt → DMA 20-bit SPI xfer → completion interrupt to ISR

  • October 15, 2018
  • 17 replies
  • 5421 views

Background, using STM32F412CG:

- we have an external 20-bit ADC triggered by an external conversion strobe

- the ADC produces a signal when conversion is complete

- external ADC-complete signal must trigger DMA to read the ADC results (20-bit over SPI)

- DMA-completion of SPI xfer must trigger a transfer-complete interrupt (enter ISR after data received&ready)

I’ve done this with several other vendors’ parts but I’m a newbie to the STM family, so a few questions:

  1. I read application note AN4031 on DMA controller use, but I don’t see from this note how DMA can be triggered from an external pin? Any recommended example?
  2. How does the 16-bit SPI do a 20-bit transfer using DMA, and trigger an interrupt on completion? (SPI transfers >16-bit are very common so this must be possible)? Recommended examples?

Thanks in advance for any pointers,

Best Regards, Dave

    This topic has been closed for replies.
    Best answer by Dave Nadler

    OK, I got this to work as follows (thanks again for the pointers @Community member​ @Bob S​ !):

    1. DMA for SPI TX (DMA1 stream 4) setup for 3 one-byte transfers for the outbound SPI data, triggered by SPI’s TX data empty, but not yet enabled.
    2. DMA for SPI RX (DMA1 stream 3) setup for 3 one-byte transfers, triggered by SPI RX data available, with the DMA transfer-complete interrupt enabled, and DMA enabled.
    3. DMA for TIMx_CC3 (DMA2 stream 6) set to transfer a 32-bit word to the DMA stream controlling SPI TX (so that TIMx_CC3 will effectively enable SPI transmit of 3 bytes), and DMA enabled.
    4. The externally-generated ADC conversion strobe CNV is connected to and triggers:
    • external ADC’s conversion strobe input
      • TIMx_ETR input, timer in slave trigger mode (on ETR) and One-Time-Pulse mode.
    1. TIMx_CC2 delays for ADC 3uSec conversion time, then lowers CH2 output (connected to SPI CS). At end of overall timer period (ARR = 5uSec), CH2 output reverts to high.
    2. TIMx_CC3 delays additional CS-time, then CC match triggers DMA request for memory→peripheral (DMA2 stream 6). This starts SPI transmission, which in turn starts SPI reception.
    3. After DMA for SPI RX (DMA1 stream 3) completes 3rd transfer, DMA-complete interrupt triggers ISR.
    4. ISR:
    • Process received 3 bytes of SPI data.
      • Reset all 3 DMA streams for next cycle. For each DMA stream:
    1. disable stream
      1. clear all DMA completion flags
      2. reset NTDR
      3. enable stream except SPI TX (SPI TX will be enabled by timer next cycle).

    Here's the picture:

    0690X000006CHn0QAG.png

    PS: As you guys hinted, the HAL/LL stuff was more in the way then helpful, so I ended up doing the peripheral initialization the old-fashioned way (const structure to initialize entire peripheral register set, then enable when ready). Also CubeMX-generated code failed to initialize all the required registers.

    17 replies

    Super User
    October 15, 2018

    > how DMA can be triggered from an external pin

    It can't be. Timers can be triggered from external pin, and then in turn trigger DMA transfer(s).

    > SPI transfers >16-bit are very common so this must be possible

    It is not. The 'F4xx SPI is capable only of 8- or 16-bit transfers. Maybe your ADC can tolerate 24-bit (= 3x8) transfers, or if you don't need individual transfers but continuous ADC conversions, you can group 2 conversions into 5x8-bit transfers.

    JW

    Graduate
    July 20, 2024

    >It can't be. Timers can be triggered from external pin, and then in turn trigger DMA transfer(s).

    Why not? There is an option given in CubeIDE to synchronize by EXTI, so what is that for? Why should I trigger DMA by a timer instead of directly by EXTI1?

    I have gone through the DMAMUX and DMA sections of the reference manual RM0432. It states:

    "Each DMAMUX request line multiplexer channel x can be individually synchronized by setting the synchronization enable (SE) bit in the DMAMUX_CxCR register. If a synchronization event occurs while there is no pending selected input DMA request line, it is discarded. The following asserted input request line is not connected to the DMAMUX multiplexer channel output until a synchronization event occurs again."

    This means that DMA would wait for the EXTI every time to start the DMA transfer, right?

    I am using the STM32L4P5-DK and ADS131M08 ADC. I need to get 8 channel samples (27 bytes with status response). I am getting DRDY every 125 µs (8000 sps). So, DRDY signal is taken as EXTI1 and the request number is 27. Won't the below configuration work?

    ajmw__0-1721468351481.png

    Regards , 

    AJ

    Explorer
    October 15, 2018

    Thanks Jan, but Wow, that is a pretty kludgy business.

    Can the timer-completion trigger the DMA SPI transfer directly?

    Again, I need to avoid any CPU/ISR involvement until the data is in memory ready to process.

    On the 20-bit SPI xfer, three 8-bit transfers would be fine but CS must be brought low only once.

    Surely this is possible, there are a huge number of parts that need CS low for more than 16 bits (DAC, ADC, etc).

    But how is it done with STMF4? Any examples?

    Thanks again,

    Best Regards, Dave

    Super User
    October 15, 2018

    Dave,

    > Wow, that is a pretty kludgy business.

    Take it or leave it...

    > On the 20-bit SPI xfer, three 8-bit transfers would be fine but CS must be brought low only once.

    There is no CS driven from the SPI anyway...

    > Can the timer-completion trigger the DMA SPI transfer directly?

    What do you mean by timer *completion*? An external signal can be effectively "routed through" the timer to trigger DMA - it would cause Capture in the timer, and that Capture can be one of the DMA triggers. But you need 3 transfers and this would be only one of them. So you want to start the timer and make it produce the 3 transfers. That could be performed by some of those "better" timers which have Repetition Counter; other option is to use the timers linking feature.

    And, as there's no usable CS output by SPI in STM32, you might want also this to be generated by the timer.

    If you don't want to Tx on the SPI, there's also an option to spend one pin and generate the clocks entirely by timers, and use SPI Rx as slave.

    Post more details, if you want to discuss this further. Waveforms, datasheet links, description of the requirements.

    I'd say it might be perhaps easier to accomplish this task using SAI on a STM32 equipped with it - 'F446, 'L4xx, but as it's not a standard audio which is what these modules are primarily for, maybe it would need to investigate a bit more in depth.

    JW

    Super User
    October 15, 2018

    [forum software was so kind to double my above post]

    Super User
    October 15, 2018

    > I need to avoid any CPU/ISR involvement until the data is in memory ready to process.

    ANY? wow. But OK.... have the timer's output compare assert the A/D's chip select line (in hardware) *and* trigger the DMA to transfer 3 bytes from the A/D (i.e. DMA count=3, datasize=8 bits). The DMA can then issue a "transfer complete" interrupt (which I presume is OK because data is now in memory waiting for you to deal with it). The ISR de-asserts the A/D's chip select and does something with the data.

    This presumes a couple of things:

    (1) That you have the flexibility to connect whatever pin the timer's output compare can toggle to your A/D chip select

    (2) that the time from timer compare asserting CS to the SPI port's first clock pulse meets your A/D's "CS asserted" to "start of data transfer" spec. Experiment and see what the timing is from the STM32.

    Explorer
    October 15, 2018

    An 100MHz CPU isn't very useful while it's sitting inside ISR in a loop waiting for SPI or bit-banging CS lines!

    I've been able to do this all via DMA using other vendors' parts; perhaps we should reconsider using STM32?

    Here are a couple parts considered for the current project:

    http://www.ti.com/lit/ds/symlink/ads8904b.pdf

    https://www.maximintegrated.com/en/ds/MAX5717-MAX5719.pdf

    Note in particular the CS requirements in the either datasheet above.

    There many other 20-bit SPI parts with similar requirements (CS must be held low for 20-bit duration).

    CS is controlled by SPI subsystem in many vendors' parts including long xfers...

    I think I understand STM SPI slave requires CS toggle to latch data at least once every 16 bits, so slave mode won't work, right?

    Thanks Jan and Bob for the timer suggestion; I'll have a look.

    Have you folks been able to use the HAL, or LL functions for this kind of thing?

    Or should one plan on writing the required drivers?

    Thanks again for the help and pointers,

    Best Regards, Dave

    Explorer
    October 15, 2018

    PS: Here's an example of what we did with a different vendor's DMA: Send excitation waveform via onboard DAC, initiate ADC once per excitation cycle, read ADC, accumulate 120 samples in a buffer, then invoke ISR. No CPU involvement until a pile of samples ready. Used 4 DMA channels and DMA chaining. Yes, ADC transfer was >16-bits (this MCU's SPI module supports >16 bit SPI with multiple transfers while holding CS):

    0690X000006CBrfQAG.png

    PPS: DMA use for a different product (and different MCU) I did last year where CPU activity was deferred until results loaded; didn't have long SPI xfers though: https://www.silabs.com/community/mcu/32-bit/forum.topic.html/giant_gecko_control-Dd4u

    Super User
    October 16, 2018

    Ah, so this is a planning stage of things.

    There are many "innovative" ways to design a serially connected device, and there's no single common standard upon which you can build, so it's always about a thorough reading of the datasheets and a bit of proof-of-concept experimenting.

    So here are the facts: STM32 SPI does not manage CS in any reasonable way; many of sequencing requirements can be accomplished by using timers but one has to know exactly what he does.

    Cube/HAL is intended for the "usual" cases, mostly those which can be clicked out of CubeMX, so once you want anything beyond "usual" it's inevitably more a nuissance than help - but that's the case with any Hardware Abstraction and "Library" and "Drivers". Cube/LL is just a way to rename registers, so IMO superfluous. If you call accessing a handful registers "driver", then yes, you are better off writing your own "driver".

    But I don't understand one thing: you seem to have expertize in the geckos, and tout their DMA and SPI here, so why would you want to use STM32s at the first place?

    JW

    Explorer
    October 16, 2018

    I'm hardly touting the Gecko (or the other much more capable MCU illustrated above), rather pointing out that many MCUs provide DMA usable for these kinds of applications, and that this is rather routine. Frankly I'm a bit horrified that ST products don't seem to properly support SPI (especially via DMA); normal SPI support will obviously include managing CS and >16 bit transfers. Also DMA chaining and direct initiation of DMA from an external signal seem to be missing, again rather basic features.

    My customer has requested we take a look at the ST parts as they have a good range of packaging, speed, general features, availability, price, etc. But if they don't provide facilities we need we'll have to look elsewhere.

    Thanks though for your help here, really, much appreciated!

    Best Regards, Dave

    Super User
    October 16, 2018

    > An 100MHz CPU isn't very useful while it's sitting inside ISR in a loop waiting

    > for SPI or bit-banging CS lines!

    1 instruction to assert the chip select line (OK, maybe 2 or 3 assembler instructions). A couple more instructions to manually start the DMA/SPI transfer. No "sitting inside the ISR" waiting for anything. The DMA transfer complete interrupt can count the number of transfers, set up the next DMA xfer (if required) set the "I got a bunch of data" flag. Again, no "sitting" and not a lot of instructions.

    Or use 2 timers. 1 as described previously to handle the A/D sample ready->SPI CS->Start DMA, and the 2nd to count the number of DMA transfers. This 2nd timer uses the "sample ready" input as a clock and set the compare event for the number of data samples you want. Yes, this takes careful planning and many times through the user manuals/data sheets up front to get the signals to pins that can be used for these functions. But it is possible.