As this is not strictly Arduino-related (though I used one in my tests), I post this here and not in the exhibition section.

Inspired (once again) by a thread in this (the old) forum, "The 2ct DAC" (

http://arduino.cc/forum/index.php/topic,8715.0.html), I wanted to see if a trinary DAC was possible by utilizing the high-Z state of a digital output pin, thus getting 3 states total.

The solution (as far as I know it) is extremely simple, yet maybe not that usable if many bits are desired, nor high speed. My test code for the Arduino is also very simple, no direct port access nor interrupts, it was just for a proof-of-concept three-state DAC. It is also extremely slow.

In short, a binary weighted resistor DAC (not R-2R) looks like this:

Where each resistor doubles in value.

A corresponding (except 4 bit instead of 3) trinary ladder would look like this (at least I'm pretty sure it will):

Where each resistor triples in value compared to the one before it.

But a digital output is not a trinary one. What I found was that by using a resistor divider on the digital outputs, as well as a trinary weighted resistor the output is fairly linear. With more bits comes the usual culprits like the required low resistor tolerances, especially for the MSB resistors.

I only tested with 5% resistors, up to 4 bits. And even that showed some irregularities (for 3^4 = 81 steps). This is also due to the fact I wouldn't load the digital outputs too much, as a lower resistor divider on the outputs also improves this. I mostly used two 180 ohm resistors, which translates to about 28 mA on each output that are either 0 or 2 (as I call the trinary equivalent to a binary 1 - Full Vcc). In addition to the trinary weighted resistors, but these were set to be 3.33 k ohm, 10 k ohm, 30 k ohm and finally 90 k ohm.

A 3-bit test, with a to-scale overlay from a processing program I made:

A 4-bit test. Not entirely even as can be seen (+ noisy):

And a 4-bit sine wave test, also with a simple RC filter on channel 2:

More details in my blog for the specially interested:

http://raronoff.wordpress.com/2011/02/02/the-three-state-trinary-ternary-resistor-ladder-dac/The Arduino test code (No schematics as of now, also this is not cleaned up):

`/* 4-bit tristate resistor DAC test`

2 buttons, to select mode and function:

- 2 modes: binary or trinary (requires different resistor circuits)

- 2 functions: ramp-up and sine wave

To test a 2,3 and 4-bit tristate DAC

NOTE: Proof-of-concept kind of thing

Also, this is not optimized code at all!

(C) 2011 raron

2011.01.07-08

2011.01.27

*/

const int outputs = 4; // nr of tristate outputs for the resistor DAC

int base = 3;

const int binStates = pow(2,outputs);

const int triStates = pow(3,outputs);

int waitTime = 1; // in millis

const int buttonPin2 = 2; // A button to select generator mode

const int buttonPin1 = 3; // DAC mode (binary / trinary)

//const int potPin = 0; // frequency adjust

const int ledPin1 = 13; // sine wave indicator

const int ledPin2 = 10; // tristate DAC indicator

int genMode = 0;

int genModes = 1; // nr. of signal generator modes-1

int button1 = 0;

int oldButton1 = button1;

int button2 = 0;

int oldButton2 = button2;

char outputString[outputs];

// non-PWM outputs for the slightly faster digitalWrite

int outputPin[outputs] = { 4, 7, 8, 12 }; // MSB to LSB

int countVal = 0;

int outValue = 0;

float value;

unsigned long time;

void setup()

{

pinMode(buttonPin1, INPUT);

digitalWrite (buttonPin1, HIGH); // pullup on the button input

pinMode(buttonPin2, INPUT);

digitalWrite (buttonPin2, HIGH); // pullup on the button input

pinMode(ledPin1, OUTPUT);

pinMode(ledPin2, OUTPUT);

for (int i=0; i<outputs; i++)

{

pinMode(outputPin[i],INPUT); // starts (output) pins as inputs (high-Z state)

digitalWrite(outputPin[i],LOW); // No internal pullups

}

}

void loop()

{

button1 = !digitalRead(buttonPin1);

button2 = !digitalRead(buttonPin2);

//waitTime = analogRead(potPin)/4;

//time = micros();

//time = millis();

// crude, non-debounced buttons

if (button1 && oldButton1 != button1) genMode++;

if (button2 && oldButton2 != button2) base++;

if (genMode > genModes) genMode = 0;

if (base > 3)

{

base = 2;

for (int i=0; i<outputs; i++) pinMode(outputPin[i],OUTPUT);

}

if (base == 2) digitalWrite(ledPin2,LOW);

if (base == 3) digitalWrite(ledPin2,HIGH);

countVal++;

// ramp up

if (genMode == 0)

{

digitalWrite(ledPin1,LOW);

// Binary resistor DAC

if (base == 2)

{

//outValue = (time*binStates/period)%binStates;

if (countVal >= binStates) countVal = 0;

writeBinaryDAC(countVal);

}

// Tristate (ternary / trinary resistor DAC)

if (base == 3)

{

if (countVal >= triStates) countVal = 0;

// convert output to string

value2baseM(countVal, outputs, base);

writeTristateDAC();

}

}

// sine wave

if (genMode == 1)

{

digitalWrite(ledPin1,HIGH);

// Binary resistor DAC

if (base == 2)

{

if (countVal >= binStates) countVal = 0;

outValue = (int)(binStates/2.0 * (1.0 + sin( 2 * PI * ((double)countVal/binStates))));

if (outValue < 0) outValue = 0;

if (outValue >= binStates) outValue = binStates-1;

writeBinaryDAC(outValue);

}

// Tristate (ternary / trinary resistor DAC)

if (base == 3)

{

if (countVal >= triStates) countVal = 0;

outValue = (int)(triStates/2.0 * (1.0 + sin( 2 * PI * ((double)countVal/triStates))));

if (outValue < 0) outValue = 0;

if (outValue >= triStates) outValue = triStates-1;

value2baseM(outValue, outputs, base);

writeTristateDAC();

}

}

oldButton1 = button1;

oldButton2 = button2;

// delay(waitTime);

}

// Convert and make a string with the value in baseY

void value2baseM(int value, int digits, int baseM)

{

int temp;

int remainder;

int digit;

for (int i=digits-1; i>=0; i--)

{

temp = value / baseM;

remainder = value % baseM;

value = temp;

// Make the ASCII code

digit = remainder + '0';

outputString[i] = (char)digit; // build string

}

//outputString[digits]='\0';

}

// Tristate (ternary / trinary resistor DAC)

// Note: Very ineffecient (slow!)

void writeTristateDAC()

{

for (int i=0; i<outputs; i++)

{

switch(outputString[i])

{

case '0':

pinMode(outputPin[i],OUTPUT);

digitalWrite(outputPin[i], LOW);

break;

case '1':

pinMode(outputPin[i],INPUT);

digitalWrite(outputPin[i],LOW); // no internal pullup!

break;

case '2':

pinMode(outputPin[i],OUTPUT);

digitalWrite(outputPin[i],HIGH);

}

}

}

// Binary resistor DAC

void writeBinaryDAC(int value)

{

int temp;

for (int i=0; i<outputs; i++)

{

temp = (value >> (outputs-i-1)) & 0x0001;

digitalWrite(outputPin[i],temp);

}

}