Reading ADS868 values with hardware CS pin and DMA

Status
Not open for further replies.
Hello all,

As per the title, I am trying to read samples from the AD8688 SPI ADC, on a Teensy 4.1 using the Arduino environment on PlatformIO. I also want to set it up so that the Teensy will automatically handle the communication using the PCS0 pin connected to the SPI module.

I connected MOSI, MISO and CLK to pins 11, 12, and 13 respectively. Then I connected the CS pin to pin 10, which should be possible to use as a dedicated hardware CS pin.

In the code, I use the SPI library to set the bus up and add some register accesses to configure the SPI module (LPSPI4, which is aliased as SPI in the Arduino libraries) to use pin 10 as internal PCS0. Writing 0 into LPSPI4.TDR, in a loop that executes 8 times, will cause the TX FIFO to fill up with 8x zeros. These will then be automatically shifted out by the module, which will also handle setting the CS pin to the appropriate level. This eliminates the need for digitalWrite(CS_PIN, LOW) to start the transfer and digitalWrite(CS_PIN, HIGH) once the transfer is done. This works ok so far, meaning I get readouts that change as I apply different voltages to the inputs, but I only seem to get 8 significant bits of data, instead of 16.

But actually the question I would like to as here is about DMA. I am trying to substitute the loop writing 0's to LPSPI4.TDR with a DMA, which I will trigger manually. Find the code below. The issue seems to be that the DMA operation never completes. Does anyone notice if I'm missing something in my code? By playing around with some parameters I noticed that the DMA only completes when I set the transferCount(1). It doesn't with any other value of transfer count.

Code that sets up the SPI bus as well as sending some chip-specific instructions
Code:
void adc_setup() {
  SPI.begin(); /* Configures LPSPI4 data and clock pins @4MHz by default */
  SPI.beginTransaction(SPISettings(17000000, MSBFIRST, SPI_MODE0)); /* Setting clock, byte order and mode. Actual freq 16MHz. Also setting master mode and delayed sampling */
  SPI.endTransaction();
  /* Now setting options not handled by SPI.begin() */
  IOMUXC_SW_PAD_CTL_PAD_GPIO_B0_00 |= IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(2); /* Setting pin 10 as output, maximum drive strength, rather fast */
  IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_00 = 3 | 0x10; /* Set pin 10 in ALT3 mode, that is LPSPI4 PCS0, and enable sw loopback */
  IOMUXC_LPSPI4_PCS0_SELECT_INPUT = 0; /* Set LPSPI4 daisy chain register to connect PCS0 to pin 10 */

  /* Setting SPI transfer parameters */
  LPSPI4_TCR = 0x17; /* Setting SPI frame size to 24, as per ADC datasheet */

  /* Now talking to the ADC */
  LPSPI4_TDR = 0xA00000; /* Set auto-reset mode */
  for (uint8_t i = 0x05; i <= 0x0C; i++) {
    LPSPI4_TDR = (i << 9) | (1 << 8) | 1; /* Select +-5V range */
  }
  LPSPI4_CR |= (LPSPI_CR_RRF | LPSPI_CR_RTF); /* Reset FIFOs */
}

Code that sets up DMA (or so I think it should)
Code:
void dma_setup() {
  spiGetFrame.source(zero);
  spiGetFrame.destination(LPSPI4_TDR);
  spiGetFrame.transferCount(8);
  spiGetFrame.enable();
}

Setup and loop code
Code:
#include <Arduino.h>
#include <SPI.h>
#include <DMAChannel.h>

uint32_t readings[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint32_t zero = 0;
DMAChannel spiGetFrame;

void setup() {
  Serial.begin(115200);
  adc_setup();
  dma_setup();
  delay(100);
}

void loop() {
  // for (int i=0;i<8;i++)
  //   LPSPI4_TDR = 0; /* NOP */
  spiGetFrame.triggerManual();
  while ((LPSPI4_FSR >> 16) < 8) { /* This while never ends */
    Serial.printf("RX FIFO count: %d. TX FIFO count: %d\n\r", LPSPI4_FSR >> 16, LPSPI4_FSR & 0xff);
    delay(100);
   } /* Waiting for something to appear on the RX FIFO*/
  for (int i=0;i<8;i++) {
    readings[i] = LPSPI4_RDR; /* Retrieve data */
    Serial.printf("CH%d = 0x%x | ", i, readings[i]);
  }
  Serial.println(" ");
  delay(1000);
}

Thank you all in advance for your help
 
So I did some more experimenting and I got it to work by changing the type of transfer from 32 bit transfer to 8 bit. The new code now does one "major iteration" with 8x single byte transfers, instead of 8 'major iterations" with 1x 4-byte transfers.

Setup DMA:
Code:
void dma_setup() {
  spiGetFrame.source(zero); /* This is now a uint8_t variable */
  spiGetFrame.destination((volatile uint8_t &)LPSPI4_TDR); /* Forces DMAChannel to set up the transfer to be single byte */
  spiGetFrame.transferCount(1);
  spiGetFrame.TCD->NBYTES = 8;
  spiGetFrame.enable();
}

The rest is the same. I still have no idea why the first version didn't work.
 
I also found out why I was getting only 8 bits as the answer from the module and not 16: I had set the frame size of the SPI module to 24, but I should have set it to 32.
 
Finally, I also found out that in order to have the first solution work properly, I have to call spiGetFrame.triggerAtTransfersOf(spiGetFrame). So the dma setup function looks like this now

Code:
void dma_setup() {
  spiGetFrame.source(zero); /* This is now a uint8_t variable */
  spiGetFrame.destination((volatile uint8_t &)LPSPI4_TDR); /* Forces DMAChannel to set up the transfer to be single byte */
  spiGetFrame.triggerAtTransfersOf(spiGetFrame);
  spiGetFrame.enable();
}
 
Status
Not open for further replies.
Back
Top