Looking to use SPI master + DMA + Interrupts

Status
Not open for further replies.
Teensy 3.6, So the deal is I'm using a DAC where the data format should like like this (below), This is just from polling the the spi bus the old fashioned way.

help.png

Meaning I need to do...

  1. Set CS low
  2. 3bytes DMA transaction
  3. Interrupt
  4. Set CS high
  5. GOTO step 1

Is there a "simple" way to do this with the SPI/DMA library? This is what I have so far... ( Please note spi_cb is being called by an interval timer at a set cadence, when I want this 12byte sequence above to happen.

Code:
#include "scan.h"
#include "SPI.h"
#include "ADC.h"

extern scan* pscan;        // Pointer to the scan object
#define SS 9               // Slave select pin

/* Timer ISR, this is tiggeer at a set frequency to update the DACs*/
void tim_isr(void){
  pscan->update(); 
}

/* This is a free running timer that triggers the ADC */
void pdb_isr(void) {
  PDB0_SC &=~PDB_SC_PDBIF; // clear interrupt
}

/* This is triggered when one SPI DMA transfer is done*/
void dma_isr(void){
  pscan->spi_cb();
}

scan::scan(scan** scanp, int interval)
:DACdataSize(3), interval(interval), points(250), LPerPoint(1/points), pointnum(0), spiPos(0)
{
	*scanp = this;
  float dt = float(interval) / 1e6;
  dac0 = new waveform(1 ,   dt);
  dac1 = new waveform(60,   dt);
  dac2 = new waveform(10,   dt);
  dac3 = new waveform(10,   dt);

	spiData = new uint8_t[DACdataSize]();

  pinMode(SS, OUTPUT);                                            // sets the digital pin 13 as output
  SPI.begin();                                                    //SS(9), MOSI(11), MISO(12), SCK(13)
  SPI0_SR = 0xFF0F0000;
  SPI0_RSER = 0x00;
  SPI0_RSER = SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS;              // Make sure SPI triggers a DMA transfer after each transmit
  // SPI.usingInterrupts(SPI0);
  // SPI.attachInterrupt((void*)&spi_isr);
  
  dmachannel.sourceBuffer(spiData, DACdataSize);                  // The data for which we wish to transmit and it's length
  dmachannel.destination((volatile uint8_t&)SPI0_PUSHR);          // Move data into the SPI FIFO register
  dmachannel.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI0_TX);       // Only transfer once the previous byte has been transmited 
  dmachannel.disableOnCompletion(); 
  dmachannel.interruptAtCompletion();
  dmachannel.attachInterrupt((void*)&dma_isr);
 
  /* ADC Config Below */
  pinMode(A8, INPUT);
  adc.setResolution(8);
  adc.setAveraging(1, ADC_0);                                     // set number of averages
  adc.setConversionSpeed(ADC_CONVERSION_SPEED::MED_SPEED, ADC_0); // LOW_SPEED adds +16 ADCK
  adc.setSamplingSpeed(ADC_SAMPLING_SPEED::HIGH_SPEED);

  adc.adc0->stopPDB();
  
  pinMode(2, OUTPUT);    // sets the digital pin 13 as output
  digitalWrite(SS, true);
}

scan::spi_cb()
{
  static uint16_t num(0);
  digitalWrite(SS, true);
  
  switch(spiPos)
  { 
    case 0:
      spiData[0] = 0b00011000;
      num = 0xffff * dac0->pos; 
      break;
    
    case 1:
      spiData[0] = 0b00011001;
      num = 0xffff * dac1->pos; 
      break;
    
    case 2:
      spiData[0] = 0b00011010;
      num = 0xffff * dac2->pos; 
      break;
    
    case 3:
      spiData[0] = 0b00011011;
      num = 0xffff * dac3->pos; 
      break;
   
    case 4:  // We done
      SPI.endTransaction(); 
      return;
  }  
  spiData[1] = num & 0xFF;         // Upper nibble 
  spiData[2] = num >> 8;           // Lower nibble
  
  spiPos++; 
  
  digitalWrite(SS, false); 
  
  dmachannel.enable(); // Begin transmit
}

It gets me something like this...

help2.png

So the dma is interrupting when it's transmission is finished, which isn't really what I want, I want an interrupt when the SPI is done three transmissions. If doing this with DMA isn't possible, it might even work if I just interrupt every SPI byte transmission, and then deal with the CS/timing every byte.
 
Status
Not open for further replies.
Back
Top