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

    Explorer
    October 16, 2018

    I read that STMF412 Serial peripheral interface/ inter-IC sound (SPI/I2S) does seem to manage CS (though mysteriously called NSS). In one place it says frames can be 24-bit but then in the SPI sub-section it again says only 8-bit or 16-bit frames. Will this part really do a longer frame??

    Super User
    October 16, 2018

    > though mysteriously called NSS

    That's because that's what it is: a Slave Select (and N stands for Negated/Not). This is the standard terminology for SPI as it came from Motorola back then.

    In the older STM32s SPIs, the NSS in master mode simply drops active when SPI is used for the first time and stays there. In newer STM32s (i.e. not 'F4) there is a "framing" mode, where NSS pulses between frames; frames in these newer STM32s may be from 4 (IIRC) to 16 bits long. There's also the TI mode, where NSS pulses during one clock cycle at the beginning of the frame.

    As I've told you, there's no pre-chewed CS of your liking.

    > In one place it says frames can be 24-bit but then in the SPI sub-section it again says only 8-bit or 16-bit frames.

    The module can work as I2S and then it supports 24-bit data (for one channel), but that still transmitted with 32 clocks per channel and internally transported from/to a 16-bit register... confusing I know but you don't want to use I2S anyway.

    I'd recommend to get some real hardware (e.g. a cheap Nucleo or a Disco board) and experiment.

    JW

    Explorer
    October 18, 2018

    Jan and Bob, studied the manual a bit, seems like its possible but a huge pile of hooey.

    Will this work?

    Potential sequence for 24-bit transfer (note CONVST is externally-generated ‘conversion start’ signal for ADC):

    1. CONVST rising edge starts ADC
    2. CONVST brought low after conversion time; doubles as CS input to ADC (modify generated signal timing to make this work).
    3. trigger TMx via TMx_ETR external trigger pin when CONVST goes low (equivalent to ADC complete)
    4. TMx_CC1 matches (immediately), starts DMA stream A
    5. DMA stream A writes control word for DMA stream B to enable stream B
    6. DMA stream B is triggered each time SPI-TXE is set to 1, transmits 3 SPI bytes (send dummy bytes to cause 3 SPI transfers; manual implies this might not be required but explanation is very poor)
    7. DMA stream C is triggered each time SPI-RXNE is set to 1; the DMA then reads the SPIx_DR register. DMA transfer count of 3 to get 3x 8-bit
    8. DMA stream C completes 3 transfers and generates DMA xfer complete interrupt
    9. ISR processes the data and resets DMA streams B&C for next round

    Would this work? Is all this really required???

    Thanks!

    Best Regards, Dave

    Super User
    October 19, 2018

    Yes, this is one very viable way to do things. This assumes CONVST is generated externally and is good as the SPI framing/chipselect, thus no responsibility of the STM32 in this regard.

    Timer does not need the TIMx_ETR signal for triggering, only a few of timers have that; any TIMx_CHy would do, provided there is a DMA from the respective capture (should be for all except the "basic" TIM6 and TIM7). If more action is desired (timer start as outlined below), TIMx_CH1 and TIMx_CH2 are needed.

    What I've suggested is to use the trigger to start a timer, and use that timer to generate the DMA requests to transfer to SPI_DR for Tx. Timers can be chained and another chained trigger can also generate the chipselect. Assuming you want to generate CONVST in STM32 too, and maybe even signals/data for the DAC you've mentioned initially, I'd perhaps go for a continuously running timer-based machine. The SPI Tx could also be omitted at the cost of one pin, generating the SPI clocks also from timers and bringing them back to SPI set as a slave. But this depends on knowing the complete specs.

    You can also use the RXONLY mode of SPI you referred to, where SPI generates the clocks automagically without need for Tx; in that case the first DMA would write to control register of SPI to start it, except that you'd then also need to invent a mechanism to stop the SPI somehow, and that may be very far from trivial.

    Please stop making those disgusted remarks - we've told you from start how this is, please take it or leave it.

    JW

    Dave NadlerAuthorAnswer
    Explorer
    October 30, 2018

    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.

    Super User
    July 20, 2024

    The original thread is for 'F412, which does not have DMAMUX.

    In newer STM32 such as the 'L4+, the picture is of course very different - not just DMA can be triggered directly from some pins (through "EXTI"), but also the v2 SPI does have a continuous selection of transfer width rather than just 8 and 16 bits in 'F4.

    JW

    Graduate
    July 20, 2024

    Hi Jan,

    I resolved the issue with SPI2 being triggered by EXTI1. The problem was with the code generated by CubeMX, which did not set the number of request bits correctly. I manually set the bits using

     

    *dmamux_c8cr_addr |= 0x1a << 19;

     

    Since SPI2 is a full duplex master, how should I simultaneously enable SPI transmission to send dummy data to generate the clock and receive the ADC samples?

     

    Regards,  

    AJ

    Super User
    July 20, 2024

    You should externally trigger the DMA which performs the transmission; DMA which picks the received data should be triggered by SPI itself.

    JW