Simple sine wave generator with two DMA channels on T3.5/3.6

StefanM

Member
Hi everybody,

I hope this is the right place - I put together a simple sine wave generator sketch for Teensy 3.5 that uses two DMA channels to output a sine wave through DAC0 and DAC1. Simply attach your output electrodes or measurement device or whatever to A21 (DAC0) and A22 (DAC1) and adjust the amplitude and the frequency of the sine wave in the settings section of the sketch.
Why is this useful if there are much more sophisticated solutions like the Audio library? Because it's small and versatile and it's not locked to 44.1 kHz, so it can be used for higher frequency output, if I'm not mistaken.

The code is partially frankensteined together from different sources e.g. this thread: https://forum.pjrc.com/threads/48457-Using-2-DMA-Channels-for-2-DACs-on-Teensy-3-6
Also, I have posted a somewhat related sketch that makes the teensy generate pulses but is not as versatile as this one, before - it's here: https://forum.pjrc.com/threads/56567-2-DMA-channels-with-2-DACs-and-variable-output-for-electric-fish-bait

I hope this might be interesting for some of you - especially beginners like me that are starting to fiddle with analog I/O. Feedback is also always welcome :)

Cheers

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)


/*SETTINGS--------------------------------------------------------*/
const uint32_t freq = 1000;           //Frequency of sine wave in Hz
const int res = 100;                  //Resolution of sine wave (values per cycle)
const uint32_t pdb_freq = freq*res;   //Output samplerate
static float amp = 1.5;               //Output amplitude in Volt - max: 6.6V

static volatile uint16_t pos_phase[res];        // lookup table for positive phase of sine wave
static volatile uint16_t neg_phase[res-1];      // lookup table for negative phase of sine wave
/*----------------------------------------------------------------*/

/*PDB-------------------------------------------------------------*/
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;
}
/*----------------------------------------------------------------*/

/*DMA-------------------------------------------------------------*/
DMAChannel dma1(false);
DMAChannel dma2(false);

// Setup DMA for DAC transfer
void setup_dma1() {
  dma1.disable();
  dma1.sourceBuffer(pos_phase, sizeof(pos_phase));
  dma1.transferSize(2);
  dma1.destination(*(volatile uint16_t *)&(DAC0_DAT0L));
  dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);
}

void setup_dma2() {
  dma2.disable();
  dma2.sourceBuffer(neg_phase, sizeof(neg_phase));
  dma2.transferSize(2);
  dma2.destination(*(volatile uint16_t *)&(DAC1_DAT0L));
  dma2.triggerAtTransfersOf(dma1);  
  dma2.disableOnCompletion();
}



void 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);
  }
  /*----------------------------------------------------------------*/

  /*DMA-------------------------------------------------------------*/
  // fill lookup tables with rounded sine values
  for(int i = 0; i < res/2; i++){
    pos_phase[i] = round(sin((PI/(res/2))*i)*(amp*4095/6.6));
    pos_phase[res/2+i] = 0;
    neg_phase[i] = 0;
    neg_phase[res/2+i-1] = round(sin((PI/(res/2))*i)*(amp*4095/6.6));
  }

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

  setup_dma1();
  setup_dma2();

  SIM_SCGC6 |= SIM_SCGC6_PDB; // enable PDB clock
  setup_pdb();
  
  dma1.enable();
  /*----------------------------------------------------------------*/

  /*Serial output---------------------------------------------------*/
  Serial.begin(9600);
  Serial.println("Sine wave playback sketch");
  Serial.print("Frequency (Hz): ");
  Serial.println(freq);
  Serial.print("Amplitude (V): ");
  Serial.println(amp);
  /*----------------------------------------------------------------*/
  // Turn on LED to signal start of output
  pinMode(13, OUTPUT); 
  digitalWrite(13, HIGH);
}

void loop() {
  // whatever else you want to do
}
 

Attachments

  • sine_generator.ino
    3.4 KB · Views: 37
Back
Top