Timer triggered SPI read with circular DMA

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

Timer triggered SPI read with circular DMA

Postby faisal » Sun Dec 31, 2017 1:37 am

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Sun Dec 31, 2017 8:35 am

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Wed Jan 03, 2018 4:24 pm

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Tue Jan 09, 2018 4:43 pm

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: 133
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 19 times
Been thanked: 15 times

Re: Timer triggered SPI read with circular DMA

Postby faisal » Thu Jan 11, 2018 1:16 am

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Thu Jan 11, 2018 7:07 am

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: 133
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 19 times
Been thanked: 15 times

Re: Timer triggered SPI read with circular DMA

Postby faisal » Fri Jan 12, 2018 12:53 am

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Fri Jan 12, 2018 8:46 am

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: 133
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 19 times
Been thanked: 15 times

Re: Timer triggered SPI read with circular DMA

Postby faisal » Fri Jan 12, 2018 4:08 pm

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: 10953
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 340 times
Been thanked: 281 times
Contact:

Re: Timer triggered SPI read with circular DMA

Postby Giovanni » Fri Jan 12, 2018 4:48 pm

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

Giovanni


Return to “ChibiOS/HAL”

Who is online

Users browsing this forum: No registered users and 1 guest