Hello everybody,
I'm using both DACs of the teensy 3.5 to emit short biphasic pulses. These pulses are recorded signals from weakly electric fish and are pretty fast (ca. 0.5ms duration). I want to build a little battery-driven playback device that emits these signals to lure weakly electric fish into minnow traps in the swamp
To extend the output voltage range, I split the positive and negative phase of the pulse, stored them in arrays and feed them to the DACs (currently at 100 kHz) for 5 seconds and then put the teensy to deepSleep for 5 seconds to save battery life. Also, I use the teensy 3.5 at CPU speed of 24 mHz to save energy.
I would like to make the playback more variable, e.g. vary the amplitude and intervals between pulses. I'm still learning a lot about programming and the code that I use is honestly mostly copy-pasted from this post: https://forum.pjrc.com/threads/48457-Using-2-DMA-Channels-for-2-DACs-on-Teensy-3-6
I would like to ask for advice, how to best achieve a variable output at min. 100kHz. I thought that I probably needed a dynamic buffer in which I store the variable pulses (e.g. pulseArray * someFactor) and append a pause (Array with zeroes) that will be fed to the DACs but I don't know yet how to best do this.
Here is the code that I use:
I'm using both DACs of the teensy 3.5 to emit short biphasic pulses. These pulses are recorded signals from weakly electric fish and are pretty fast (ca. 0.5ms duration). I want to build a little battery-driven playback device that emits these signals to lure weakly electric fish into minnow traps in the swamp
To extend the output voltage range, I split the positive and negative phase of the pulse, stored them in arrays and feed them to the DACs (currently at 100 kHz) for 5 seconds and then put the teensy to deepSleep for 5 seconds to save battery life. Also, I use the teensy 3.5 at CPU speed of 24 mHz to save energy.
I would like to make the playback more variable, e.g. vary the amplitude and intervals between pulses. I'm still learning a lot about programming and the code that I use is honestly mostly copy-pasted from this post: https://forum.pjrc.com/threads/48457-Using-2-DMA-Channels-for-2-DACs-on-Teensy-3-6
I would like to ask for advice, how to best achieve a variable output at min. 100kHz. I thought that I probably needed a dynamic buffer in which I store the variable pulses (e.g. pulseArray * someFactor) and append a pause (Array with zeroes) that will be fed to the DACs but I don't know yet how to best do this.
Here is the code that I use:
Code:
#include <Snooze.h>
#include <SnoozeBlock.h>
#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)
//configure snooze driver and install it
SnoozeTimer timer;
SnoozeBlock config(timer);
static volatile uint16_t posphase[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 4, 4, 6, 6, 10, 10, 10, 12, 16, 16, 18, 18, 22, 26, 28, 28, 32, 38, 42, 46, 50, 58, 64, 74, 78, 88, 98, 110, 120, 134, 148,
168, 186, 208, 232, 262, 296, 332, 372, 416, 468, 522, 582, 648, 720, 798, 880, 966, 1058, 1158, 1260, 1368, 1498, 1666, 1890, 2180, 2516, 2832, 3048, 3114,
3026, 2816, 2490, 2052, 1470,
};
static volatile uint16_t negphase[] = {
270, 1292, 2250, 3032, 3582, 3918, 4078, 4095, 3984, 3752, 3412, 2984, 2508, 2024, 1570, 1182, 872, 640, 466, 338, 244, 180, 134, 100, 72, 52, 38, 30, 18, 10,
6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
// DMA for ADC
DMAChannel dma1(false);
DMAChannel dma2(false);
// Set fs - the sampling frequency at 100kHz
const uint32_t pdb_freq = 100000;
// 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;
}
// Setup DMA for DAC transfer - ref sin
void setup_dma1() {
dma1.disable();
dma1.sourceBuffer(posphase, sizeof(posphase));
dma1.destination(*(volatile uint16_t *)&(DAC0_DAT0L));
dma1.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);
}
void setup_dma2() {
dma2.disable();
dma2.sourceBuffer(negphase, sizeof(negphase));
dma2.destination(*(volatile uint16_t *)&(DAC1_DAT0L));
dma2.triggerAtCompletionOf(dma1);
dma2.triggerAtTransfersOf(dma1);
}
void setup() {
timer.setTimer(5000); // sleep duration between signal playback
// 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_dma1();
setup_dma2();
SIM_SCGC6 |= SIM_SCGC6_PDB; // enable PDB clock
setup_pdb();
}
void loop() {
int who = Snooze.deepSleep( config ); // return module that woke processor
for (int16_t i=0; i<=2048; i+=8) {
*(int16_t *)&(DAC0_DAT0L) = i;
*(int16_t *)&(DAC1_DAT0L) = i;
delay(1);
}
dma1.enable();
dma2.enable();
delay(5000);
dma1.disable();
dma2.disable();
}