Problem with Teensy3 resistor DAC

JarkkoL

Well-known member
I ported my 8-bit resistor DAC mod player from Arduino Uno to Teensy3 and the quality of the output is pretty poor in comparison. I suspect this has something to do with how voltage levels vary when different pins are enabled, and that the current flows into other pins when they are set to low (sorry for my newbie explanation). In my test where I outputted 10KHz square wave to pin-0 of the DAC (all the other 7 pins were set to low) and had only the pin-0 connected to DAC, I heard notable decrease in amplitude when I connected pin-1 (which was low) to Teensy. This amplitude drop doesn't happen on Arduino Uno. Below is a video of the quality I get with Arduino, but it's very noisy and barely recognizable on Teensy :(


The source code is in attachment with the piece of music in it as played in the video. Teensy specific code is in pmf_player_teensy.cpp. I originally used PORTD to output the data on Teensy, but changed it to directly use GPIOD_PSOR & GPIOD_PCOR registers instead for faster pin state update, though I'm not 100% sure I did it right. However this didn't improve the quality. Here's the picture of the DAC and how it's connected to Teensy:

teensy_player.jpg

Just to clarify if it's not clear from the image, there's 2 resistors parallel in the DAC for each pin (MSB on leftLSB on right) and resistances for each pin are power-of-two (10467Ohm, 5194Ohm, 2597Ohm, 1304Ohm, 650Ohm, 326Ohm, 163Ohm, 82Ohm for bits 0-7 respectively). The DAC pins 0-7 are connected to Teensy3 pins 2, 14, 7, 8, 6, 20, 21, 5 respectively, which are controlled by GPIOD_PSOR/PCOR bits 0-7 as far as I know.

I tried to add diodes to each pin to avoid current in-flow into pins, but that didn't help. It could be because the diodes I used had quite high resistance and probably a lot of variance.

So, any suggestions how to fix this? I'm not 100% sure if this is software or hardware issue, but I suspect hardware is the culprit.

Ps. Paul, you might be interested to check out the audio mixing routine and envelope handling for your audio shield project. I'll write the mixer and interrupt routines in asm later for Teensy, but I should get the playback fixed first so I can test that my code works.

Thanks, Jarkko
 

Attachments

  • pmf_player_v0.3.zip
    34.2 KB · Views: 315
It is typical for resistor network DAC's to have outputs that are low acting as sinks while high outputs are sources.
They are also quite sensitive to the load resistance which must be matched to the DAC resistors.
A complete schematic would be helpful.
 
These are 5% (gold band) resistors? You can only make a 4bit DAC with those. With 1% you can get 6 bits and with 0.1%, 9bits (although only 3 or 4 of them need to be 0.1%, the rest can be 1%).

Code:
1     1/2           50%
2     1/4           25%
3     1/8           12.5%
4     1/16          6.25%
5     1/32          3.13%
6     1/64          1.56% 
7     1/128         0.78%
8     1/256         0.39%
9     1/512         0.20%
10   1/1024        0.097%
11   1/2048        0.048%
12   1/4096        0.024%

For 10 bits or better (and likely for 8bits) its cheaper, easier and better to use an actual DAC - where if there is a resistor ladder, they will all be on the same thermal substrate and be laser-trimmed to the required accuracy.

This is orthogonal to whether you have enough current source and sink capability to drive the resistors (which is likel to be the reason for the Arduino Uno vs. Teensy 3.0 difference you are seeing).
 
The diodes will make the system worse.

The simplest thing is to use an R-2R ladder (e.g. see Wikipedia). Basically each pin of the T3 is connected to a (say) 20k, and the end of of the 20k's are joined with 10k's. At the end of the ladder there's another 20k to ground.

This type of circuit will have minimal errors when you include the effects of drive strength of the Teensy MCU. Basically the T3's output pins have a drive capability that looks like a switch (to ground or supply) in series with a resistance of 10-20 ohms (and highly variable). In your circuit above, the 82 ohms will be significantly affected by this drive strength.

It is important that the 20k's are precisely 2.000 times the 10k's. Easiest is to buy 10k's, and use 2 in series for 20k.
 
Cool, was thinking of R2R but wasn't sure if it would make any difference. I'll definitely try it out. Thanks.
 
JarkkoL: Very nice work!

I got audio output from the internal DAC on the Teensy 3.1 by changing pmf_player_teensy.cpp to use analogWrite(A14, smp) instead of PORTD:

So in playback_interrupt()
-- analogWrite(A14, smp); instead of GPIOD_PSOR=smp && GPIOD_PCOR=~smp;

And in start_playback()
-- analogWriteResolution(8); instead of GPIO_* stuff.

Cheers
 
Nice, I tried to use the DAC in Teensy 3.1 but the sound quality wasn't very good for some reason. Maybe it's the missing interrupt disable/enable calls in get_mixer_buffer() because when I increase pmfplayer_audio_buffer_size it gets better. Would be nice to get it working properly.

This project is in Google Code btw: https://code.google.com/p/arduino-music-player with an exe for converting music files to the proper format for the player.
 
Last edited:
Ok, I was able to fix the Teensy 3.1 port! Now it's using the built-in 12-bit DAC. I submitted the fix to Google Code if you want to try it out.

 
Hi Jarkko - great work on this player!

I know it's probably been a very (very) long time since you last looked at your MOD player code for Teensy, but I was wondering if it was possible to play the MOD (or other format) files directly without converting them to your unified PMF format first? Now, with the likes of Teensy 3.6 (which I am using) which has 1MB of flash, it seems that size is less of a limitation and being able to use the original file as-is has some benefits.
 
Back
Top