Using 2 DMA Channels for 2 DACs on Teensy 3.6

Status
Not open for further replies.

SphCow

Member
Here I'm trying to generate two sin waveforms of two different frequencies on the two DACs available in teensy 3.6. I'm using two DMA Channels to transfer data from two look up tables (LUT) to the DACs. Both the DMA transfers are triggered by a PDB running at 200KHz.

To my surprise, the frequency the the generated sine waveform on DAC1 is doubled! It seems that that the DMA associated with it misses one data from LUT out of each two data. I suspect there is a collision between the two DMA channels or something.

As the size of the two LUTs are different so I think I can't use one DMA channel to transfer data from the LUT to DAC as Paul did in out_dacs in the audio library. Please suggest me some workaround.

Here is the source code:

Code:
#define ARM_MATH_CM4

#include <DMAChannel.h>

#define PDB_CONFIG     (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_PDBIE | PDB_SC_CONT | PDB_SC_DMAEN)

// DMA for ADC
DMAChannel dma1(false);
DMAChannel dma2(false);

// Set fs - the sampling frequency at 200KHz
const uint32_t pdb_freq = 200000; 

// Setup PDB at pdb_freq
void setup_pdb() {
  uint32_t mod = (F_BUS / pdb_freq);
    
  PDB0_MOD = (uint16_t)(mod-1);  
  PDB0_IDLY = 0;
  PDB0_SC = PDB_CONFIG | PDB_SC_LDOK;
  PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG;
  PDB0_CH0C1 = 0x0101;
}

uint lut_size1;
uint lut_size2;
static uint16_t lut1[256];
static uint16_t lut2[256];

// Setup DMA for DAC transfer - ref sin 
void setup_dma1() {
  dma1.disable();
  dma1.sourceBuffer(lut1, lut_size1*sizeof(uint16_t));
  dma1.transferSize(2);
  dma1.destination(*(volatile uint16_t *)&(DAC0_DAT0L));
  dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);
  
  //dma_dac.transferCount(lut_size/3);
  //dma_dac_ref.interruptAtCompletion();
  //dma_dac.interruptAtHalf();

  dma1.enable();
  //dma_dac_ref1.attachInterrupt(dma_dac_ref_isr);
}

void setup_dma2() {
  dma2.disable();
  dma2.sourceBuffer(lut2, lut_size2*sizeof(uint16_t));
  dma2.transferSize(2);
  dma2.destination(*(volatile uint16_t *)&(DAC1_DAT0L));
  dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);
  
  //dma_dac.transferCount(lut_size/3);
  //dma_dac_ref.interruptAtCompletion();
  //dma_dac.interruptAtHalf();

  dma2.enable();
  //dma_dac_ref1.attachInterrupt(dma_dac_ref_isr);
}

void setup_lut(uint target_freq1, uint target_freq2) {
  //lut1
  lut_size1 = uint(pdb_freq/target_freq1);
  Serial.printf("pdb_freq = %d\ntarget_freq1 = %d\nlut_size1 = %d\n", pdb_freq,target_freq1,lut_size1);

  for(uint i=0; i<lut_size1; i++) {
    lut1[i] = 2047 + 2000*sin(2.0*M_PI*i/lut_size1);
  }
  Serial.printf("True freq1 = %f\n", pdb_freq/float(lut_size1));

  // lut2
  lut_size2 = uint(pdb_freq/target_freq2);
  Serial.printf("pdb_freq = %d\ntarget_freq2 = %d\nlut_size2 = %d\n", pdb_freq,target_freq2,lut_size2);

  for(uint i=0; i<lut_size2; i++) {
    lut2[i] = 2047 + 2000*sin(2.0*M_PI*i/lut_size2);
  }
  Serial.printf("True freq2 = %f\n", pdb_freq/float(lut_size2));



}

void setup() {
  // setup DAC 
  SIM_SCGC2 |= SIM_SCGC2_DAC0 | SIM_SCGC2_DAC1;
  DAC0_C0 = DAC_C0_DACEN; // 1.2V VDDA is DACREF_2
  DAC0_C0 |= DAC_C0_DACRFS; // 3.3V
  
  DAC1_C0 = DAC_C0_DACEN; // 1.2V VDDA is DACREF_2
  DAC1_C0 |= DAC_C0_DACRFS; // 3.3V

  // slowly ramp up to DC voltage, approx 1/4 second
  for (int16_t i=0; i<=2048; i+=8) {
    *(int16_t *)&(DAC0_DAT0L) = i;
    *(int16_t *)&(DAC1_DAT0L) = i;
    delay(1);
  }

  // allocate the dma channels
  dma1.begin(true);
  dma2.begin(true);



  setup_lut(1000,2000);

  setup_dma1();
  setup_dma2();

  SIM_SCGC6 |= SIM_SCGC6_PDB; // enable PDB clock
  setup_pdb();


}

void loop() {

}
 
Here I'm trying to generate two sin waveforms of two different frequencies on the two DACs available in teensy 3.6. I'm using two DMA Channels to transfer data from two look up tables (LUT) to the DACs. Both the DMA transfers are triggered by a PDB running at 200KHz.

To my surprise, the frequency the the generated sine waveform on DAC1 is doubled! It seems that that the DMA associated with it misses one data from LUT out of each two data. I suspect there is a collision between the two DMA channels or something.

As the size of the two LUTs are different so I think I can't use one DMA channel to transfer data from the LUT to DAC as Paul did in out_dacs in the audio library. Please suggest me some workaround.

You are right,
you cannot have 1 source (DMAMUX_SOURCE_PDB), triggering 2 events (dma1,dma2), but could could chain dma. I have not done this, but I recall, that you could setup dma1 trigger dma2. A read of the dma/dmamux in the MK66 documentation may help.
 
You are right,
you cannot have 1 source (DMAMUX_SOURCE_PDB), triggering 2 events (dma1,dma2), but could could chain dma. I have not done this, but I recall, that you could setup dma1 trigger dma2. A read of the dma/dmamux in the MK66 documentation may help.

Ah! I missed the fact that one source can't trigger multiple events. I changed

Code:
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);

to

Code:
dma2.triggerAtCompletionOf(dma1);
dma2.triggerAtTransfersOf(dma1);

and the issue was fixed. Thanks a lot WMXZ for pointing this out. It was easy enough though :)
 
Ah! I missed the fact that one source can't trigger multiple events. I changed

Code:
dma2.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);

to

Code:
dma2.triggerAtCompletionOf(dma1);
dma2.triggerAtTransfersOf(dma1);

and the issue was fixed. Thanks a lot WMXZ for pointing this out. It was easy enough though :)

you are welcome.
and I learned how to do it.
 
Status
Not open for further replies.
Back
Top