Teensy LC DAC

Status
Not open for further replies.

Burnich

New member
Hi everybody,

I'm building an IoT object using a Teensy LC. I'm wondering if it can be possible using the DAC (pin 26), a DC blocking filter (1uF Capacitor) and a Mono Class D amplifier (https://www.adafruit.com/product/2130) and play a sound from a wav file ?

I'm actually able to read the wav file from the SD card and to control the DAC, but I'm not able to convert the wav file into a sound signal with the DAC...

Thanks
 
Last edited:
It is possible. You need to get the sample rate and the resolution from the wav file first, these values should be included in the header. Then set up an interval timer for the corresponding period which triggers a periodic interrupt, each time reading one value from the audio stream contained within the wav file, scaling it up or down by simple bit shifting depending on the wav's resolution, adding 2048 as an offset value since the Teensy DAC is 12bit unipolar, and writing it to the *(int16_t *)&(DAC0_DAT0L) address.
On the hardware side, I'd add at least a 2nd order RC low pass filter between the 1uF DC blocking capacitor and the class D amplifier to suppress aliasing ghost tones.

More info on the internal structure of wav files can be found here: https://en.wikipedia.org/wiki/WAV As long as the music is PCM encoded, the simple algorithm above will work. If other codings like uLaw come into play, it will be more complicated, but most wav files contain a PCM stream.
 
Last edited:
Here still some little helpers :
A library function which quickly converts a signed 16bit integer from 16 bit bipolar PCM to 12 bit unipolar for the Teensy's DAC. I'm just not sure if that works for the Teensy LC which has a weaker CPU core.
Code:
/* taken and adapted from Paul's dspinst.h audio library file */
static inline uint16_t convDAC12uni(int32_t val) __attribute__((always_inline, unused));
static inline uint16_t convDAC12uni(int32_t val) { //Specifically customized for the 12bit unipolar DAC of the Teensy 3.2
	int32_t res;
	asm volatile("ssat %0, #12, %1, asr #16" :"=r" (res): "r" (val));
	return (res + 0x800);
}

I use it with the following code to write out data to the DAC:
Code:
			*(int16_t *)&(DAC0_DAT0L) = convDAC12uni(y);
 
Eventually the Teensy Audio library will get limited support for Teensy LC. I've already put some work towards that goal, but so far none of the input or output objects have been ported.
 
I try to adapt this library from arduino zero (https://github.com/arduino-libraries/AudioZero) using timer1 but this isn't working well... I will try to look deeper in the audio library source to find the solution.

The timer1 in the AVR processors can not be compared with the whole bunch of different timers in the Kinetis processors. In practice, when you really follow my advice, you don't need to fiddle with the timers on low level. Apparently, you did not really read my post, because I suggested to use a high level interval timer object which does not require any knowledge about the timer internals. A short lookup would have brought you there: https://www.pjrc.com/teensy/td_timing_IntervalTimer.html

Never start from Arduino stuff. Arduino is a Fiat 500. Teensy 3.x are Land Rovers.
 
Thank you so much Paul :)

It basicaly do what I want. I will try to improve the library as soon as I can and share my update with you.

Theremingenieur my mistake was to read "InterNal" and not "InterVal"...
 
Here's a quick port of AudioZero to run on Teensy LC. It works, at least for playing 8 bit data (as it does on Arduino Zero). This library can't play 16 bit WAV.

https://github.com/PaulStoffregen/AudioZero

I also put a ready-to-use WAV file in the extras folder. :)

Hi Paul,

Thank you for doing this port. I'm trying to use it with the Prop Shield and a Teensy LC, and an SD adapter to play a wav.

It works perfectly, after I noticed your warning about 16-bit WAV files! Doh!

Ideally, though, I'd just use the built in SerialFlash on the Prop Shield. Unfortunately, your implementation of SerialFlashFile is not extended from File, and so the AudioZero library will not accept a SerialFlashFile passed to the AudioZero.play(File) method. It seems to me that the ideal approach would be to fix the SerialFlash library so that a SerialFlashFile is derived from File, but a short-term hack would be to change the parameter type required by the AudioZero library. I can do the latter with no problem, I'm sure, but the former is beyond my C programming skills, I'm afraid.
 
Hi Paul,

Thank you for doing this port. I'm trying to use it with the Prop Shield and a Teensy LC, and an SD adapter to play a wav.

It works perfectly, after I noticed your warning about 16-bit WAV files! Doh!

Ideally, though, I'd just use the built in SerialFlash on the Prop Shield. Unfortunately, your implementation of SerialFlashFile is not extended from File, and so the AudioZero library will not accept a SerialFlashFile passed to the AudioZero.play(File) method. It seems to me that the ideal approach would be to fix the SerialFlash library so that a SerialFlashFile is derived from File, but a short-term hack would be to change the parameter type required by the AudioZero library. I can do the latter with no problem, I'm sure, but the former is beyond my C programming skills, I'm afraid.

One additional problem I discovered with the AudioZero library, is that it does not free the buffer it allocates in ::begin(). There should be a complementary free in ::end(), something like:

Code:
void AudioZeroClass::end() {
    tcDisable();
    analogWrite(DAC_PIN, 0);
    if (__WavSamples)
        free(__WavSamples);
}
 
Status
Not open for further replies.
Back
Top