Page 1 of 2

Timer triggered SPI read with circular DMA

Posted: Sun Dec 31, 2017 1:37 am
by faisal
Sampling an external ADC using is a common usage of the SPI bus. At high sample rates, you need to use a hardware timer to trigger a DMA write to SPI TX register, and setup a DMA request on SPI RX in circular mode with half transfer and transfer complete interrupts enabled. That is really the only feasible way to sample the ADC at high sample rates (the same thing applies to the internal ADC).

I'm currently working on writing a driver for an external SPI ADC (AD7980), and would like to reuse the hal_adc, hal_spi, and hal_pwm to make it portable. See below:

Code: Select all

                      +---------------+                                                                                           
                      |   hal_adc.c   |                                                                                           
                      +---------------+                                                                                           
                              |                                                                                                   
                      +-------|-------+                                                                                           
                      |   ad7980.c    |                                                                                           
                      +-------|-------+                                                                                           
                              |                                                                                                   
        |---------------------|------------------|                                                                               
+-------|-------+     +-------|-------+  +-------|-------+                                                                       
|   hal_pwm.c   |     |   hal_spi.c   |  |  stm32_dma.c  |                                                                       
+---------------+     +---------------+  +-------+-------+


There are a couple of issues I've discovered so far, and probably more. I think the HAL - or at least the STM32 implementation should be updated to at the very least allow this sort of usage.

The spi driver doesn't expose the half transfer interrupt. It also disables the interrupts after the dma transfer is complete, thus not supporting 'continuous' mode. A couple of other changes probably need to happen as well to have the rights hooks to be able to use the SPI driver as part of a more complex composite device driver.

Have anyone of you already done this? Any idea for how to do this cleanly?

Re: Timer triggered SPI read with circular DMA

Posted: Sun Dec 31, 2017 8:35 am
by Giovanni
Hi,

You are correct, the SPI driver model does not support this use case. I think that the cleanest approach would be to implement an ADC driver using the SPI peripheral, the exposed API would be ADC not SPI.

I will look into adding a continuous mode to the SPI driver but don't expect it by tomorrow.

Giovanni

Re: Timer triggered SPI read with circular DMA

Posted: Wed Jan 03, 2018 4:24 pm
by Giovanni
Hi,

I have not forgot this, the change will require a small change to API, functions will return msg_t instead of void, and a new field in the configuration structure for selecting linear or circular mode. A new spiStopTransfer() function is also required.

I am considering this for next major release (18.x).

Giovanni

Re: Timer triggered SPI read with circular DMA

Posted: Tue Jan 09, 2018 4:43 pm
by Giovanni
Hi,

I added circular (continuous) support to the SPIv3 driver (STM32H7), next I will do the same to SPIv1 and SPIv2.

I have not yet clear how time triggering could be done, right now it is SPI triggering the DMA. On H7 it would be possible to change the DMAMUX-peripheral association after calling spiStart() but the same is not possible on older devices.

At very least now the SPI driver is able to generate patterns on MOSI or sample signals on MISO, lots of possible applications.

Giovanni

Re: Timer triggered SPI read with circular DMA

Posted: Thu Jan 11, 2018 1:16 am
by faisal
Giovanni wrote:Hi,

I added circular (continuous) support to the SPIv3 driver (STM32H7), next I will do the same to SPIv1 and SPIv2.

I have not yet clear how time triggering could be done, right now it is SPI triggering the DMA. On H7 it would be possible to change the DMAMUX-peripheral association after calling spiStart() but the same is not possible on older devices.

At very least now the SPI driver is able to generate patterns on MOSI or sample signals on MISO, lots of possible applications.

Giovanni


The way I've seen it done is to require the user code to setup a PWM timer which does a DMA transfer to the SPI Data register. In that case, when spi_lld_receive() is called - in the time-triggered continuous case the dmatx stream should *not* be enabled - as the user will control the writes to the SPI Data register (thru PWM+DMA, or PWM+INT).

So, for continuous mode, there seems to be two cases: Externally triggered (where timing needs to be controlled), and As Fast As Possible (i.e. spi_lld_receive() *should* enable the dmatx stream). The As Fast As Possible mode would be useful for reading memory or something of the such.

Re: Timer triggered SPI read with circular DMA

Posted: Thu Jan 11, 2018 7:07 am
by Giovanni
Hi,

The stream will be enabled by the spiXXX() transfer call, what you can do is to change (or disable) the TX DMA trigger by changing the dmatx field of the SPIDriver structure, you can make the DMA be triggered by a timer instead of SPI or you may write the SPI DR register in the PWM callback.

I updated also SPIv1 and SPIv2.

Giovanni

Re: Timer triggered SPI read with circular DMA

Posted: Fri Jan 12, 2018 12:53 am
by faisal
Thanks for addressing this need!

Looking at your changes, I have the following comment:

The user will not know which buffer to read from upon half transfer, and transfer complete.
Why not make the driver model similar to HAL_ADC for example, which has good support for circular mode? When you start the circular acquisition with adcStartConversion it stores the pointer to the user buffer inside the driver structure (*samples), so that on the callback it provides a pointer to the relevant section of the buffer. The adc callback looks like this: typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n);

Would be nice if they presented a similar API to the user, as functionally they are doing the same thing...

Here are some of the differences I see apart from the above:

Error handling: ADC driver has an error callback which provides an error type, instead of a hook.
DMA interrupt handler design and naming:
The interrupt handler is called "adc_lld_serve_dma_interrupt" in hal_adc, and "spi_lld_serve_rx_interrupt" in hal_spi. The interrupt handlers have a different design to, with the hal_spi interrupt handler checking for circular mode and calling the _half1 or _half2 portable macros, while the hal_adc handler does that checking in the portable macros.
hal_adc defines the macros "_adc_isr_half_code", and "_adc_isr_full_code", and hal_spi has _spi_isr_code, _spi_isr_code_half1, _spi_isr_code_half2

I think the two interrupt handlers and associated portable macros could be nearly identical.

Re: Timer triggered SPI read with circular DMA

Posted: Fri Jan 12, 2018 8:46 am
by Giovanni
Hi,

if spip->state is SPI_ACTIVE then the callback is for the first half of the buffer, else it is SPI_COMPLETE.

Giovanni

Re: Timer triggered SPI read with circular DMA

Posted: Fri Jan 12, 2018 4:08 pm
by faisal
Giovanni wrote:Hi,

if spip->state is SPI_ACTIVE then the callback is for the first half of the buffer, else it is SPI_COMPLETE.

Giovanni


Any comments on the idea of massaging hal adc and spi (and other drivers potentially) to be more consistent in their design to support continuous mode API?

Re: Timer triggered SPI read with circular DMA

Posted: Fri Jan 12, 2018 4:48 pm
by Giovanni
Consistency is always a good thing, reworking things not necessarily as good, especially if one is trying to close a release.

Giovanni