Timer triggered SPI read with circular DMA

Discussions and support about ChibiOS/HAL, the MCU Hardware Abstraction Layer.
faisal
Posts: 382
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 45 times
Been thanked: 62 times

Timer triggered SPI read with circular DMA

Post 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?
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post 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
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post 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
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post 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
faisal
Posts: 382
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 45 times
Been thanked: 62 times

Re: Timer triggered SPI read with circular DMA

Post 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.
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post 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
faisal
Posts: 382
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 45 times
Been thanked: 62 times

Re: Timer triggered SPI read with circular DMA

Post 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.
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post 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
faisal
Posts: 382
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 45 times
Been thanked: 62 times

Re: Timer triggered SPI read with circular DMA

Post 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?
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Timer triggered SPI read with circular DMA

Post by Giovanni »

Consistency is always a good thing, reworking things not necessarily as good, especially if one is trying to close a release.

Giovanni
Post Reply