cancel
Showing results for 
Search instead for 
Did you mean: 

Is it possible to trigger a SPI transfer complete interrupt using circular DMA with 3 byte buffer on STM32F072

DSudo.2
Associate III

I'm working on a board that has a string of 6 SPI quad FET chips whose outputs I'm trying to PWM in software. The string of FET drivers have a latch signal to transfer the shifted data to the outputs. Because the DMA uses the SPI peripheral's FIFO, I need to generate the latch signal after every 3 bytes DMA'd to the SPI peripheral. It would seem that I need to set the SPI peripheral's FIFO size to 3 bytes and setup a SPI interrupt every time the SPI FIFO has emptied *and* the SPI data register is empty (i.e., after every 3rd byte has been completely shifted out). I don't see a way to set the SPI FIFO size. Since the FIFO size is 32-bits, I could send 4 bytes at a time (with one dummy byte) with the TXEIE (Transmit FIFO ready to be loaded) interrupt bit set, and when that interrupt occurred, if a TX complete interrupt was available I could enable that then to allow me to know when the entire 4-byte sequence was transmitted so that I could synchronously generate the latch signal to the SPI FET chips. Since that latter interrupt does not exist, I don't see any way that I can use DMA or simple SPI transfers *and* synchronously generate the latch signal for the SPI FET chips. Can anyone suggest a method to do this (i.e., DMA 3 bytes (in circular DMA mode, if possible) and after every 3rd byte was completely shifted out of the SPI peripheral then toggle an I/O pin twice to generate the latch signal, before starting the next 3 byte sequence)?

1 ACCEPTED SOLUTION

Accepted Solutions

> My task involves hardware that's already in production

Always a joy.

That's why I suggested to use a timer to generate the latch pulse, "manually" synchronized to the circular-DMA-SPI process, simply by starting it close to each other.

JW

View solution in original post

9 REPLIES 9
DSudo.2
Associate III

As luck would have it... the SPI select signal is used as GPIO to drive an LED on this board. Would it be possible to configure the SPI peripheral to control this pin automatically *and* configure the GPIO pin as an external interrupt such that when the select signal was negated that could trigger an interrupt that could then generate this latch signal and start the next 3 (or 4) byte transfer?

DSudo.2
Associate III

Is there any way to configure the SPI select signal to remain asserted while emptying the TX FIFO? That would be nice. Otherwise, I'll have to manage keeping track of which interrupt associated with each byte's negation of the SPI select signal so that I can know when to create the latch signal. Initial experimentation indicates that the NSS signal either frames each byte or no bytes. Bummer.

DSudo.2
Associate III
  // configure EXTI15 to generate an interrupt (EXTI4_15_IRQHandler) when PA15 has a rising edge
  EXTI->RTSR |= BIT(15);
  EXTI->FTSR &= ~BIT(15);
  EXTI->EMR |= BIT(15);
  EXTI->IMR |= BIT(15);
  SYSCFG->EXTICR[4] &= ~(0xF << 12);
  NVIC_EnableIRQ(EXTI4_15_IRQn);

I added this code to (hopefully) generate an interrupt with the SPI NSS signal was negated (which is on PA15). Unfortunately the ISR is never vectored to. I think this means that if PA15 is configured as an alternate function output, then it cannot generate an interrupt via the EXTI feature. Is my configuration incorrect, or is it that it won't work as I hoped since it's an alternate function output?

DSudo.2
Associate III

My bad... the interrupt on negation of SPI NSS is being vectored to. I had forgotten that I had tried setting the SPI NSS mode from PULSE_ENABLE to PULSE_DISABLE, hoping that it would change the behavior to keeping NSS asserted while the FIFO was being emptied. It did not. It disabled the NSS output. Sorry for the confusion. Now if I can just find out how to clear that EXTI interrupt...

DSudo.2
Associate III
	EXTI->PR |= BIT(15); // clear EXTI15 interrupt

Ok... that was easy. Getting close to the final solution. One last step is moving the call to DMA the next set of data from where it currently is to right after where the SPI FET latch signal is generated in the EXTI14/15 ISR to keep everything in sync.

I don't understand the description of what are you doing, but one way to tackle framing signal generation at the cost of one pin is to generate the it in a timer, clocked using one of the external-clock modes by the SPI's SCK.

You can try to sync a timer generating the framing purely in software, too.

JW

Thanks for the suggestion. My task involves hardware that's already in production, so I can't wire the SPI clock to a capture/compare input. I'm trying to use circular DMA with a circular table of 3-byte values, where the table index gets incremented after each 3-byte value gets DMA'd to a SPI peripheral. Additionally, between each time the SPI peripheral actually writes these values I need to toggle an I/O pin twice to generate a "latch" signal that causes the shifted data to be transferred from SPI quad FET chip shift registers to their outputs. It appears that there is no "native" way to do this using DMA and/or SPI interrupts, however, I found a workaround way to do it by (1) configuring the SPI peripheral to generate slave select pulses with each transmitted byte, (2) generating an EXTI interrupt on the negation edge of that signal (that signal currently is only connected to an LED, so it is "available" for this re-use purpose), (3) setting a count variable to 3 each time the SPI DMA transfer with interrupt call is made (because I just can't figure out how to make this work in circular DMA mode), and (4) decrementing this count value in the EXTI ISR and when it equals zero then generate the latch signal and start the next 3-byte SPI DMA transfer with interrupt call. Unfortunately, even though this EXTI interrupt is the highest priority interrupt in my system and interrupts are never disabled (globally or otherwise), sometimes one of these trailing edges of the SPI select signal don't generate, or should I say doesn't get serviced in time and then this process breaks. So, I'm adding a hardware timer as a watchdog of sorts to recover from this "occasionally delayed too long" servicing of the interrupt to resume the process from the next row in the data table. Since I'm doing this to generate variable duty cycle pulses to drive loads, an occasional hiccup of the duty cycle can be tolerated. For reference, I'm generating this SPI data at 3 bytes approximately every 20 microseconds. That does consume a lot of bandwidth, but that's the price I have to pay unfortunately.

> My task involves hardware that's already in production

Always a joy.

That's why I suggested to use a timer to generate the latch pulse, "manually" synchronized to the circular-DMA-SPI process, simply by starting it close to each other.

JW

Thanks for the suggestion. In hindsight, this was probably what I should've tried in the first place. :face_with_rolling_eyes: