ferdinandkeil
Member
It took me some time to figure out how to control the DAC output through DMA. I've started with Paul's Audio library (AudioOutputAnalog to be exact) and then tried to replicate that functionality with the high level functions provided by DMAChannel. In the end I got it working and you can find the source-code at the end of this message. During my experiments I found that these are the crucial steps to make it work:
The attached code generates a 1 kHz sine-wave using a sample-rate of 128 kHz.
File pdb.h
File dac_test.ino
- the buffer used as source has to be declared as static volatile uint16_t, but not DMAMEM,
- if you want the buffer to wrap over at the end and start at the beginning, use DMAChannel.xxxxBuffer(...),
- and when using &DAC0_DAT0L as destination it has to be cast to *(volatile uint16_t *).
The attached code generates a 1 kHz sine-wave using a sample-rate of 128 kHz.
File pdb.h
Code:
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
* Modified 2015, Ferdinand Keil, ferdinandkeil@gmail.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef pdb_h_
#define pdb_h_
#include "kinetis.h"
// Multiple input & output objects use the Programmable Delay Block
// to set their sample rate. They must all configure the same
// period to avoid chaos.
#define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN)
#if F_BUS == 60000000
#define PDB_PERIOD (468-1)
#elif F_BUS == 56000000
#define PDB_PERIOD (437-1)
#elif F_BUS == 48000000
#define PDB_PERIOD (375-1)
#elif F_BUS == 36000000
#define PDB_PERIOD (281-1)
#elif F_BUS == 24000000
#define PDB_PERIOD (187-1)
#elif F_BUS == 16000000
#define PDB_PERIOD (125-1)
#else
#error "Unsupported F_BUS speed"
#endif
#endif
File dac_test.ino
Code:
#include <DMAChannel.h>
#include "pdb.h"
DMAChannel dma(false);
static volatile uint16_t sinetable[] = {
2047, 2147, 2248, 2348, 2447, 2545, 2642, 2737,
2831, 2923, 3012, 3100, 3185, 3267, 3346, 3422,
3495, 3564, 3630, 3692, 3750, 3804, 3853, 3898,
3939, 3975, 4007, 4034, 4056, 4073, 4085, 4093,
4095, 4093, 4085, 4073, 4056, 4034, 4007, 3975,
3939, 3898, 3853, 3804, 3750, 3692, 3630, 3564,
3495, 3422, 3346, 3267, 3185, 3100, 3012, 2923,
2831, 2737, 2642, 2545, 2447, 2348, 2248, 2147,
2047, 1948, 1847, 1747, 1648, 1550, 1453, 1358,
1264, 1172, 1083, 995, 910, 828, 749, 673,
600, 531, 465, 403, 345, 291, 242, 197,
156, 120, 88, 61, 39, 22, 10, 2,
0, 2, 10, 22, 39, 61, 88, 120,
156, 197, 242, 291, 345, 403, 465, 531,
600, 673, 749, 828, 910, 995, 1083, 1172,
1264, 1358, 1453, 1550, 1648, 1747, 1847, 1948,
};
void setup() {
dma.begin(true); // allocate the DMA channel first
SIM_SCGC2 |= SIM_SCGC2_DAC0; // enable DAC clock
DAC0_C0 = DAC_C0_DACEN | DAC_C0_DACRFS; // enable the DAC module, 3.3V reference
// slowly ramp up to DC voltage, approx 1/4 second
for (int16_t i=0; i<2048; i+=8) {
*(int16_t *)&(DAC0_DAT0L) = i;
delay(1);
}
// set the programmable delay block to trigger DMA requests
SIM_SCGC6 |= SIM_SCGC6_PDB; // enable PDB clock
PDB0_IDLY = 0; // interrupt delay register
PDB0_MOD = PDB_PERIOD; // modulus register, sets period
PDB0_SC = PDB_CONFIG | PDB_SC_LDOK; // load registers from buffers
PDB0_SC = PDB_CONFIG | PDB_SC_SWTRIG; // reset and restart
PDB0_CH0C1 = 0x0101; // channel n control register?
dma.sourceBuffer(sinetable, sizeof(sinetable));
dma.destination(*(volatile uint16_t *)&(DAC0_DAT0L));
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_PDB);
dma.enable();
}
void loop() {
// do nothing here
}