<> = DAC = == Parts == * 9 22 kOhm resistors 1% metal film (may be best to get the [[http://www.amazon.co.uk/Sunkee-Resistance-Metal-Resistor-600pcs/dp/B00ARTTPE6/ref=sr_1_14?s=diy&ie=UTF8&qid=1371048738&sr=1-14&keywords=resistors+10K+1%25|resistors in kit form]] but this is overkill unless a use is found for the other resistors.) * 7 10 kOhm resistors 1% metal film * Arduino Uno * wire jumpers ([[http://uk.farnell.com/twin-industries/tw-mp-10/kit-jumper-wire-mach-pin-10pcs/dp/2213351|Farnell seems too expensive.]] [[http://www.amazon.co.uk/Assorted-Multicolored-Flexible-Solderless-Breadboard/dp/B0087ZRVES/ref=sr_1_1?ie=UTF8&qid=1371048175&sr=8-1&keywords=jumper+wires|Amazon has sets for £2-4.]]) * 2 TS922IN IC, OP AMP, DUAL RRI/O, DIP8, 922 (Farnell # [[http://uk.farnell.com/stmicroelectronics/ts922in/ic-op-amp-dual-rri-o-dip8-922/dp/1094390?Ntt=TS922IN+IC|1094390]]) * 3 3310C-1-103L POTENTIOMETER, 10K 3310C-1-103L (Farnell # [[http://uk.farnell.com/bourns/3310c-1-103l/potentiometer-10k/dp/9353844?Ntt=3310C-1-103L+POTENTIOMETER|9353844]]) * 8 ohm speaker (Farnell: [[http://uk.farnell.com/visaton/2909/loudspeaker-mini-waterproof-8-ohm/dp/1675522|1675522]]) * Capacitors 0.01 microF, 0.1 microF and 220 microF (suggested kits [[http://www.amazon.co.uk/Velleman-CAP1-224-piece-Ceramic-Capacitor/dp/B001IRQOS6/ref=sr_1_cc_2?s=aps&ie=UTF8&qid=1371049874&sr=1-2-catcorr&keywords=capacitor+kits|ceramic capacitors]] and [[http://www.amazon.co.uk/Electrolytic-Capacitor-Selection-Pieces-Values/dp/B00AK86SMQ/ref=sr_1_cc_1?s=aps&ie=UTF8&qid=1371049874&sr=1-1-catcorr&keywords=capacitor+kits|electrolytic]]. Both will be needed) * Mounted breadboard ([[http://www.amazon.co.uk/Unbranded-BB-3T5D-01-Mounted-Breadboard-2420/dp/B00419MN0Q/ref=sr_1_10?s=electronics&ie=UTF8&qid=1371050062&sr=1-10&keywords=breadboards|Amazon]]. Farnell is too expensive.) * Small breadboard (optional- for pre-assembled op-amp parts) ([[http://www.amazon.co.uk/BB400-Solderless-Plug--BreadBoard-tie-points/dp/B0040Z1ERO/ref=sr_1_1?s=electronics&ie=UTF8&qid=1371050062&sr=1-1&keywords=breadboards|Amazon]]) * 2 Phototransistors (Farnell #1497882), alternatively, two photoresistors. == Circuit == {{attachment:arduino_8bit_dac_amp_circuit_1.jpg||width=600}} Taken from Amanda Aghassaei's page (check license!) The DAC is the first part only. Just the resistors. == DAC Theory == Useful information can be found here: * [[http://www.asdlib.org/onlineArticles/elabware/Scheeline_ADC/ADC_Home.html#Homebase|DAC description by Alexander Scheeline]] from the Department of Chemistry at the University of Illinois at Urbana-Champaign. Very good resource and worth reading. Explains how the ladder works as a complex voltabe divider. In particular, see the description of [[http://www.asdlib.org/onlineArticles/elabware/Scheeline_ADC/ADC_DAC_ladder.html|R-2R networks]]. * The description given in the above article doesn't directly apply to our circuit. Though you will see why it does work, if you'd like to dig more into our circuit have a look at [[http://www.electronics.dit.ie/staff/tscarff/dac_adc/DAC.htm|this article]] which uses [[http://en.wikipedia.org/wiki/Th%C3%A9venin%27s_theorem|Thevenin's Theorem]] to simplify a DAC similar to the one we use. * [[http://en.wikipedia.org/wiki/Resistor_ladder|Resistor Ladder from the Wikipedia]]. This is a shorter article. == Digital writes to PORTD == The following explanation of '''PORTD''' has been lifted verbatim from [[http://www.instructables.com/id/Arduino-Audio-Output/?ALLSTEPS|Amanda's article]]. We will soon been sending sequences of 8 bits (1 byte) to our DAC. From what we have already learnt about the [[http://arduino.cc/en/Tutorial/DigitalPins|digital pins of the Arduino]], we know that we can write out these 8 bits, 1 to each of the digital pins 0 to 7 using commands like {{{ digitalWrite(pin, HIGH); }}} where, to send the sequence 10110001, we'd need the lines [[attachment:sequence 10110100]] {{{ digitalWrite(0, HIGH); // 1 : remember we start from the rightmost bit digitalWrite(1, LOW); // 0 digitalWrite(2, LOW); // 0 digitalWrite(3, LOW); // 0 digitalWrite(4, HIGH); // 1 digitalWrite(5, HIGH); // 1 digitalWrite(6, LOW); // 0 digitalWrite(7, HIGH); // 1 }}} Of course, we could have saved many lines by looping over the bits in a byte: {{{ seq = (1,0,1,1,0,0,0,1); for (int i = 0, i < 8; i++) { digitalWrite(i,seq(i)); // seq(i) = 0 ==> LOW & seq(i) = 1 ==> HIGH } }}} But there is another, more convenient way of doing this: The Arduino allows us to write an entire byte to pins 0 to 7 via '''PORTD'''. In the following pieces of code we send a value between 0 and 255 to "PORTD" when we want to send data to the DAC, it looks like this: {{{ PORTD = 125;//send data to DAC }}} This is called [[http://www.arduino.cc/en/Reference/PortManipulation|addressing the port directly]]. On the Arduino, digital pins 0-7 are all on port d of the Atmel328 chip. The PORTD command lets us tells pins 0-7 to go HIGH or LOW in one line (instead of having to use digitalWrite() eight times). Not only is this easier to code, it's much faster for the Arduino to process and it causes the pins to all change simultaneously instead of one by one (you can only talk to one pin at a time with digitalWrite()). Since port d has eight pins on it (digital pins 0-7) we can send it one of 2^8 = 256 possible values (0-255) to control the pins. For example, if we wrote the following line: {{{ PORTD = 0; }}} it would set pins 0-7 LOW. With the DAC set up on pins 0-7 this will output 0V. if we sent the following: {{{ PORTD = 255; }}} it would set pins 0-7 HIGH. This will cause the DAC to output 5V. We can also send combinations of LOW and HIGH states to output a voltage between 0 and 5V from the DAC. For example: {{{ PORTD = 125; }}} 125 = 01111101 in binary. This sets pin 7 low (the msb is 0), pins 6-2 high (the next five bits are 1), pin 1 low (the next bit is 0), and pin 0 high (the lsb is 1). You can read more about [[http://www.arduino.cc/en/Reference/PortManipulation|how this works here]]. To calculate the voltage that this will output from the DAC, we use the following equation: voltage output from DAC = [ (value sent to PORTD) / 255 ] * 5V so for PORTD = 125: voltage output from DAC = ( 125 / 255 ) * 5V = 2.45V The code below sends out several voltages between 0 and 5V and holds each for a short time to demonstrate the concepts described above. In the main loop() function: {{{ PORTD = 0;//send (0/255)*5 = 0V out DAC delay(1);//wait 1ms PORTD = 127;//send (127/255)*5 = 2.5V out DAC delay(2);//wait 2ms PORTD = 51;//send (51/255)*5 = 1V out DAC delay(1);//wait 1ms PORTD = 255;//send (255/255)*5 = 5V out DAC delay(3);//wait 3ms }}} The output is should be visualised on an oscilloscope or sent to the Serial display using the technique used for the sine wave below. == DAC Practical == Creating the 8 bit DAC is rather simple. Probably best to use 1% tolerance resistances as the 5 or 10% varieties would introduce a lot of noise in the DAC. This experiment needs an oscilloscope, but failing this, we can use the serial output of the Arduino and a short code to visualise it. === Square waves === The first example is a simple square waveform generation code: {{{ //Analog out //by Amanda Ghassaei //http://www.instructables.com/id/Arduino-Audio-Output/ //Sept 2012 /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ void setup(){ //set digital pins 0-7 as outputs for (int i=0;i<8;i++){ pinMode(i,OUTPUT); } } void loop(){ PORTD = 0;//send (0/255)*5 = 0V out DAC delay(2000);//wait 1ms PORTD = 127;//send (127/255)*5 = 2.5V out DAC delay(2000);//wait 2ms PORTD = 51;//send (51/255)*5 = 1V out DAC delay(2000);//wait 1ms PORTD = 255;//send (255/255)*5 = 5V out DAC delay(2000);//wait 3ms } }}} I've increased the //delay//s to allow voltage measurement using a multimeter. If using the oscilloscope use the ms timings listed in the comments. === Ramp Wave === Next try getting a ramp waveform: {{{ //Ramp out //by Amanda Ghassaei //http://www.instructables.com/id/Arduino-Audio-Output/ //Sept 2012 /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ void setup(){ //set digital pins 0-7 as outputs for (int i=0;i<8;i++){ pinMode(i,OUTPUT); } pinMode(A0,INPUT); Serial.begin(9600); // Output to Serial while (! Serial); // Wait untilSerial is ready Serial.println("Waveform generation!"); } void loop(){ for (int a=0;a<256;a++){ PORTD = a;//send out ramp to digital pins 0-7 //delayMicroseconds(50);//wait 50us if using an oscilloscope delay(100);//wait 100ms if using a multimeter/serial port int DACout = analogRead(A0); if (Serial.available()) { Serial.println(DACout); } } } }}} === Sine wave and Serial display === Now for a sine wave. I've modified Amanda's code quite a lot for this one. Since I don't have an oscilloscope, we will use the serial display to visualise the wave. The display code has been based on the [[http://learn.parallax.com/node/265|BOE Bot serial display exercise]]. I modified the circuit to feed the output of the DAC (top pin of the resistor network) to pin '''A0''' on the Arduino. This allows the analogue output to be read and analysed/visualised. {{{ //Sine wave //based on code by Amanda Ghassaei //http://www.instructables.com/id/Arduino-Audio-Output/ //Sept 2012 // Modified by A.J.Misquitta to include Serial output, visualization of the wave // and period calculation. /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * */ void setup(){ //set digital pins 0-7 as outputs for (int i=0;i<8;i++){ pinMode(i,OUTPUT); } pinMode(A0,INPUT); Serial.begin(115200); // Output to Serial while (! Serial); // Wait untilSerial is ready Serial.println("Waveform generation!"); } void loop(){ boolean up = true; unsigned long time_up = 0; unsigned long time_down = 0; int crossover = 512; // timers activate at this value int samples = 100; // number of samples to use for (int t=0; t < samples;t++){//increment "t" //send sine wave to DAC, centered around (127/255)*5 = 2.5V PORTD = 127+127*sin(2*3.1415926*t/100); // The period of the wave can be controlled using the delay. // But other functions in the loop also effect the period! // The delay should be linked to the samples in a sensible way: delayMicroseconds(5000/samples); //delay(100);//wait 100ms if using a multimeter/serial port // Read the output of the DAC: int DACout = analogRead(A0); // Code to calculate period in ms if (up){ if (DACout > crossover){ time_up = millis(); up = false; } } else { if (DACout < crossover){ time_down = millis(); if (Serial.available()){ unsigned long period = 2 * (time_down - time_up); Serial.println(period); } up = true; } } // Print out the wave: if (Serial.available()) { for(int i = 0; i<((DACout / 8)); i++) { Serial.print(' '); } Serial.print('*'); Serial.println(DACout); } } } /* Use the following code for a simple (and faster) version of the sine wave code */ void loop_simple(){ for (int t=0;t<100;t++){//increment "t" PORTD = 127+127*sin(2*3.14*t/100);//send sine wave to DAC, centered around (127/255)*5 = 2.5V delayMicroseconds(50);//wait 50us } } }}} The above code not only lets us see the wave form, but it also calculates the period of the wave using a trigger technique: the timers start and stop when the wave crosses the 512 reading (2.5 V) on the ADC input. {{attachment:sine_wave_serial_display_1.png||width=500}} Sample output. The number //472// on the left/bottom is the initial guess at the period of the wave. The first value should be discarded. Observe that the period estimates quickly settle down to a near-constant value. The *s indicate the waveform and the adjacent numbers the ADC input values. The fact that these never get to 1024 is because the DAC output voltage never gets to 5V. By default the ADC converts the range $[0,5]$ Volts into an output range $[0,1024]$. It is a 10 bit ADC. Things to note: * Sampling can be increased by increasing //samples//. * But although the //delay// is reduced, the //Serial.print//s are increased and this causes the wave period to increase almost linearly with the samples. * Because of these delays, the waveform visualised on the Serial display will not have the period an oscilloscope would see. * How then do we calculate the period of the sine wave output by the DAC? * Notice any jumps in the sine wave? These can be caused by mis-matches in the R-2R resistances. See the Wikipedia article on [[http://en.wikipedia.org/wiki/R-2R#R-2R_resistor_ladder_network|R-2R circuits]] for a brief description of this phenomenon. {{{#!wiki note Note The serial display is a great way to probe the circuit. Particularly if you don't have an oscilloscope at hand. The jumper attached to analogue pin '''A0''' can be moved around to different parts of the circuit, allowing you to see what's going on in much the same way as you'd probe it using a multimeter, only the '''A0''' probe gives you voltages as a function of time. }}} = DAC Buffer = The R-2R DAC is very sensitive to loads. (I've still not understood why, but this may be related to the limitations of the Arduino.) Under load, the waveform distorts significantly. To protect the DAC output we use a buffer circuit built out of an Op-Amp. For a good introduction to Op-Amps (they are collections of transistors) have a look at the following: * [[http://en.wikipedia.org/wiki/Op-amp|Wikipedia article on Op-Amps]]. This is quite a lrge article which even includes an internal circuit diagram of an op-amp. Jump to the //Applications// section to read about feedback mechanisms and how we use them to control the output of an op-amp. * Or, shorter, and more to the point is the latter half of this [[http://www.asdlib.org/onlineArticles/elabware/Scheeline_ADC/ADC_DAC_ladder.html|R-2R networks]] page. == Parts == * (x9) 1/4 Watt 20kOhm Resistors * (x7) 1/4 Watt 10kOhm Resistors * (x2) TS922IN dual op-amp package. The only new parts are the TC922INs. These differ from the standard 081 or 741 op-amps in two ways: they contain two op-amps each and Vcc for these is between 2.7 and 15 Volts (as opposed to +-12 V needed for the 081/741). So we can run them off the Arduino's 5 V supply voltage. In addition we will use a standard red diode and 220 Ohm resistor to test the circuit. {{{#!wiki warning Warning The TDA7052 audio amp does not work without buffering. I've tried it. The DAC output gets badly clipped when it is connected to the TDA7052. This could be related to the load put on the DAC. Since the lower part of the sine wave is large un-distorted, I guess that the Arduino or DAC cannot sustain the high loads when the number of Arduino pins that are HIGH increases. }}} == Circuit == == Procedure & Code == Basically, these little devices allow us to sample the signal without loading it. We will use the op-amp in what is known as a [[http://en.wikipedia.org/wiki/Buffer_amplifier#Op-amp_implementation|voltage-follower]] setup.