cancel
Showing results for 
Search instead for 
Did you mean: 

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

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

19 REPLIES 19
Bob S
Principal

> 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.

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??

> 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

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

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

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.

>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

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

ajmw_
Associate II

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

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

JW