Sunteți pe pagina 1din 52

Let's Make ...

Featured Write an Instructable Login | Sign Up

Classes Contests Forums Answers Teachers

adv ertisement

Arduino Audio Output by amandaghassaei in arduino

Download h 10 Steps  Collection I Made it!  Favorite  Share 

adv ertisement

About This Instructable


PDF generated automatically by the HTML to PDF API of PDFmyURL
8 374,685 views License:

 531 favorites

amandaghassaei
uh-man-duh-guss-eye-dot-com

Follow 3526

Bio: I'm a grad student at the Center for


Bits and Atoms at MIT Media Lab. Before
that I worked at Instructables, writing code
for ... More »

M ore by amandaghassaei:

Generate sound or output analog voltages with an Arduino. This Instructable will
show you how to set up a really basic digital to analog converter so you can start
adv ertisement
generating analog waves of all shapes and sizes from a few digital pins on an
Arduino. (This article is a companion to another Instructable I've written about
sending audio into an Arduino, find that here)

Some ideas that come to mind:

sample based instrument- store samples on the Arduino or on an SD card and


trigger playback with buttons or other types of controls. Check out my Arduino
drum sampler for an idea of how to get started.
digital synthesizer- make saw, sine, triangle, pulse, or arbitrary waveshapes-
check out my waveform generator to get started
MIDI to control voltage module/ MIDI synthesizer- receive MIDI messages and
PDF generated automatically by the HTML to PDF API of PDFmyURL
translate them into a voltage so you can control an analog synthesizer with MIDI, or
use the MIDI data to output audio of a certain frequency
analog output- you may find yourself needing to generate analog voltages from
your Arduino at some point, maybe to communicate with an analog device
effects box/digital signal processing- in combination with a microphone/audio
input you can perform all kinds of digital signal manipulations and send the
processed audio out to speakers. Check out my vocal effects box for an example.
audio playback device- make your own ipod. With the addition of an SD shield
you could create your own Arduino mp3 player (check out the wave shield
documentation for an idea of how to get started with the code). The circuits and
code provided here are compatible with SD shields that communicate via SPI.

Feel free to use any of the info here to put together an amazing project for
the DIY Audio Contest! We're giving away an HDTV, some DSLR cameras,
and tons of other great stuff! The contest closes Nov 26.
PartsList:

(x9) 1/4 Watt 20kOhm Resistors Digikey 0KQBK-ND


(x7) 1/4 Watt 10kOhm Resistors Digiikey CF14JT10K0CT-ND
(x2) TS922IN Digikey 497-3049-5-ND I like these because they can be powered off
the Arduino's 5V supply (one 924 works too, but they don't seem to be available on
digikey at the moment)
(x1) 10kOhm potentiometer linear Digikey 987-1308-ND
(x1) 0.01uF capacitor Digikey 445-5252-ND
(x1) 220uF capacitor Digikey P5183-ND
(x1) 0.1uF capacitor Digikey 445-5303-ND
(x1) 1/4 Watt 3kOhm Resistor Digikey CF14JT3K00CT-ND
(x1) 1/4 Watt 10Ohm Resistor Digikey CF14JT10R0CT-ND
(x1) Arduino Uno Amazon

Additional Materials:

PDF generated automatically by the HTML to PDF API of PDFmyURL


(1x) usb cable Amazon
(1x) breadboard (this one comes with jumper wires) Amazon
(1x) jumper wires Amazon

Step 1: Digital to Analog Converter

PDF generated automatically by the HTML to PDF API of PDFmyURL


DAC stands for "digital to analog converter." Since the Arduino does not have
analog out capabilities, we need to use a DAC to convert digital data
(numbers/ints/bytes) to an analog waveform (oscillating voltage). A simple, easy to
program, and cheap way to do this is to use something called an R2R resistor
ladder. Essentially, it takes incoming digital bits (0V and 5V from Arduino), weights
them, and sums them to produce a voltage between 0 and 5 volts (see the
schematic in fig 2, taken from the Wikipedia resistor ladder page). You can think of
a resistor ladder as a multi-leveled voltage divider.
The resistor ladder I'll be demonstrating in this tutorial is an 8-bit DAC, this means it
can produce 256 (2^8) different voltage levels between 0 and 5v. I connected each
of digital pins 0-7 to each of the 8 junctions in my 8 bit DAC (shown in figs 1 and
3).
I like using these resistor ladder DACs because I always have the materials
around, they're cheap, and I think they're kind of fun, but they will not give you the
highest quality audio. You can buy a chip that works in the exact same was as an
R2R DAC (and will work with all the code in this instructable), but has internal,
highly-matched resistors for better audio quality, I like this one bc it runs off a
single 5V supply (you can even do stereo audio with it), but there are many more
available, look for "parallel input, 8 bit, dac ic".
PDF generated automatically by the HTML to PDF API of PDFmyURL
Alternatively, there are chips that take in serial data to perform digital to analog
conversion. These chips are generally higher fidelity (definitely better quality that
the resistor ladder DAC) and they only use two or three of the Arduino's output pins
(as opposed to 8). Downsides are they are a little more challenging to program,
more expensive, and will not work with the code in this Instructable, though I'm sure
there are some other tutorials available. After a quick search on digikey, these
looked good, for Arduino, try to find something that will run off a single 5V supply.
One more note - there seems to be kind of a misconception abut 8 bit audio- that it
always has to sound like the sounds effects from a Mario game- but 8bit audio with
this really basic DAC can actually replicate the sounds of people's voices and
instruments really well, I'm always amazed at the quality of sound that can come
from a bunch of resistors.

Step 2: Set Up DAC and Test

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
Show All 8 Items

I constructed my DAC on a breadboard (figs 1-3). The schematic is given in fig 8.


Below are a few pieces of sample code that generate the waveforms shown in figs
4-7. In the following pieces of code I send a value between 0 and 255 to "PORTD"
when I want to send data to the DAC, it looks like this:

PORTD = 125;//send data to DAC

This is called 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:
PDF generated automatically by the HTML to PDF API of PDFmyURL
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 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 I've described above. In the main loop()
function I've written:

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

PDF generated automatically by the HTML to PDF API of PDFmyURL


The output is shown on an oscilloscope in fig 4. The center horizontal line across
the oscilloscope represents 0V and each horizontal line represents a voltage
increase/decrease of 2V. The image notes on fig 4 show the output of each of the
lines of code above, click on the image to view the image notes.

//Analog out
//by Amanda Ghassaei
//https://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);
}
}
The code below outputs a ramp from 0 to 5V. In the loop() function, the variable
"a"void
is incremented
loop(){ from 0 to 255. Each time it is incremented, the value of "a" is
sentPORTD = 0;//send
to PORTD. This(0/255)*5
value is= held
0V out
forDAC
50us before a new value of "a" is sent. Once
delay(1);//wait 1ms
"a" reaches 255, it gets reset back to 0. The time for each cycle of this ramp (also
PORTD = 127;//send (127/255)*5 = 2.5V out DAC
called the period) takes:
delay(2);//wait 2ms
PORTD = 51;//send (51/255)*5 = 1V out DAC
period = (duration
delay(1);//wait 1ms of each step) * (number of steps)
PORTD
period = 255;//send
= 50us * 256 =(255/255)*5
12800us == 5V out DAC
0.0128s
delay(3);//wait 3ms
}
so the frequency is:
PDF generated automatically by the HTML to PDF API of PDFmyURL
frequency of ramp = 1/0.0128s = 78Hz

The output from the DAC on an oscilloscope can be seen in fig 5.

//Ramp out
//by Amanda Ghassaei
//https://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);
}
The
} code below outputs a sine wave centered around 2.5V, oscillating up to a max
of 5V and a min of 0V. In the loop() function, the variable "t" is incremented from 0
to void
100.loop(){
Each time it is incremented, the expression:
for (int a=0;a<256;a++){
127+127*sin(2*3.14*t/100)
PORTD = a;//send out ramp to digital pins 0-7
is sent to PORTD. This value is held for 50us before "t" is incremented again and a
delayMicroseconds(50);//wait 50us
new} value is sent out to PORTD. Once "t" reaches 100, it gets reset back to 0.
The} period of this sine wave should be:

period = (duration of each step) * (number of steps)


period = 50us * 100 = 5000us = 0.005s

so the frequency should be:


PDF generated automatically by the HTML to PDF API of PDFmyURL
frequency of ramp = 1/0.005s = 200Hz

//Sine out
//by Amanda Ghassaei
//https://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);
}
}
But this is not the case, the output from the DAC is shown in fig 6. As indicated in
thevoid
image notes, it does not have a frequency of 200hz, its frequency is more like
loop(){
45hz. This is because the line: "t"
for (int t=0;t<100;t++){//increment
PORTD = 127+127*sin(2*3.14*t/100);//send sine wave to DAC, centered around (127/255
)*5 = 2.5V
PORTD = 127+127*sin(2*3.14*t/100);
delayMicroseconds(50);//wait 50us
takes} a very long time to calculate. In general multiplication/division with decimal
numbers
} and the sin() function take the Arduino a lot of time to perform.

One solution is to calculate the values of sine ahead of time and store them in the
Arduino's memory. Then when the Arduino sketch is running all the Arduino will
have to do is recall these values from memory (a very easy and quick task for the
Arduino). I ran a simple Python script (below) to generate 100 values of
PDF generated automatically by the HTML to PDF API of PDFmyURL
127+127*sin(2*3.14*t/100):

import math
for x in range(0, 100):
print str(int(127+127*math.sin(2*math.pi*x*0.01)),)+str(","),

I stored these values in an array called "sine" in the Arduino sketch below. Then in
my loop, for each value of "t" I sent an element of sine[] to PORTD:

PORTD = sine[t];

The output from this DAC for this sketch is shown in fig 7. You can see that it
outputs a sine wave of 200hz, as expected.

//Sine out with stored array


//by Amanda Ghassaei
//https://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.
*
*/

byte sine[] = {127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 22
9, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238
, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119,
111, 103, 95, 87, 80, 72, 65, 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2,
3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};

Stepvoid3:setup(){
DAC Buffer
//set digital pins 0-7 as outputs
PDF generated automatically by the HTML to PDF API of PDFmyURL
PDF generated automatically by the HTML to PDF API of PDFmyURL
Now that we have a good signal coming out Arduino, we need to protect it. The
R2R DAC is very sensitive to any loads put on it, so trying to drive speakers
directly from the DAC will distort the signal heavily. Before doing anything with the
signal you need to set up some kind of buffer circuit. I set up one of the op amps
in the TS922 dual op amp package as a voltage follower to buffer my DAC from the
rest of my circuit (see schematic in fig 6, be sure to power the op amp with 5V and
ground).

Once this was set up I wired an LED and 220ohm resistor in series between the
output of the op amp and ground. The sketch below outputs a slow ramp out the
DAC so you can actually see the LED get brighter as the ramp increases in
voltage. The period of the ramp is:

period = (duration of each step) * (number of steps)


period = 5ms * 256 = 1280ms = 1.28s
so the LED takes 1.28 seconds to ramp up from off to full brightness.

PDF generated automatically by the HTML to PDF API of PDFmyURL


//Slow Ramp
//by Amanda Ghassaei
//https://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);
}
}
Step 4: Low Pass Filter
void loop(){
for (int a=0;a<256;a++){
PORTD = a;//send out ramp to digital pins 0-7
delay(5);//wait 5ms
}
}

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
The purpose of a low pass filter is to smooth out the output of the DAC in order to
reduce noise. By using a low pass filter on the signal, you can smooth out the
"steps" in your waveform while keeping the overall shape of the waveform intact
(see fig 4). I used a simple RC flow pass filter to achieve this: a resistor and a
capacitor in series to ground. Connect the resistor to the incoming signal and the
capacitor to ground, the signal coming from the junction between these two
components will be low pass filtered. I sent this filtered signal into another buffer
circuit (I wired an op amp in a voltage follower configuration) to protect the filtered
signal from any loads further down in the circuit. See the schematic in fig 5 for
more info.

You can calculate the values of the capacitor and resistor you need for a low pass
filter according to the following equation:

cutoff frequency = 1/ (2*pi*R*C)

Nyquist's Theroum states that for a signal with a sampling rate of x Hz, the highest
frequency that can be produced is x/2 Hz. You should set your cutoff frequency to
x/2Hz (or maybe slightly lower depending on what you like). So if you have a

PDF generated automatically by the HTML to PDF API of PDFmyURL


sampling rate of 40kHz (standard for most audio), then the maximum frequency
you can reproduce is 20kHz (the upper limit of the audible spectrum), and the
cutoff frequency of your low pass filter should be around 20kHz.

For a cutoff frequency of 20,000Hz and 1kOhm resistor:


20000=1/(2*3.14*1000*C)
C =~ 8nF

since 8nF capacitors are hard to come by I rounded up to 0.01uF. This gives a
cutoff frequency of about 16kHz. You can mess around with different values and
see what you like best, I tend to like heavier filtering because it removes more
unwanted noise.

Step 5: Signal Amplitude

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
Show All 7 Items

Next I added a potentiometer to control the amplitude of my signal. To do this I


wired the output from the 2nd voltage follower to one side of a 10k potentiometer.
The I wired the other side of the pot to ground. The signal coming out from the
middle of the pot has an adjustable amplitude (between 0 and 2.5V) depending on
where the pot is turned. See the schematic (fig 7) for more info. You can see the
output of the signal before the pot and after the pot (when turned to halfway point)
in fig 6.

Step 6: Amplifier

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
Many times when we talk about amplifiers we think about circuits which increase
the amplitude of a signal. In this case I'm talking about increasing the current of
the signal so that it can drive a load (like a speaker). In this stage of the circuit I
set up both op amps on one TS922 package as parallel voltage followers. What this
means is I sent the output from the amplitude pot to the non-inverting input of both
op amps. Then I wired both op amps as voltage followers and connected their
outputs to each other. Since each op amp can source 80mA of current, combined
they can source 160mA of current.

Step 7: DC Offset

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
Before sending a signal to speakers, you want to make sure it is oscillating around
0V (typical of audio signals). So far, the Arduino DAC output we've been dealing
with is oscillating around 2.5V. To fix this we can use a big capacitor. As
indicated in the schematic, I used a 220uF capacitor to DC offset my signal so that
it oscillates around 0V. The output of the DC offset signal (blue) and un-offset
signal (yellow) for two different amplitudes can be found in figs 2 and 3.

Step 8: Output

PDF generated automatically by the HTML to PDF API of PDFmyURL


PDF generated automatically by the HTML to PDF API of PDFmyURL
Finally, I wired up a 1/4" mono jack with two wires. I connected the ground lead to
the Arduino's ground and the signal lead to the negative lead of the 220uF
capacitor. The ground pin is usually the larger pin on the jack, test for continuity
with the threaded portion of the jack to make sure that you have located the ground
pin correctly (see fig 5). The signal pin will be continuous with the clip that extends
out from the jack (fig 5). See the schematic for more info.

Step 9: 40kHz Sampling Rate

PDF generated automatically by the HTML to PDF API of PDFmyURL


For those of you who are interested in producing audio at 40kHz sampling rate,
here is some code that uses timer interrupts to let you do that. Arduino timer
interrupts allow you to pause what you are doing in your main loop() function and
jump to a special function called an "interrupt routine." Once this routine is done
you come back to where you left off in the loop(). You set up and specify the
frequency of these interrupts in the setup() part of your code. You can learn the
specifics of setting up interrupts here, but if you are only interested in 40kHz
interrupts, then you can just copy parts of the code below.

To set up the interrupt you need to copy the following lines into your setup()
PDF generated automatically by the HTML to PDF API of PDFmyURL
function:

cli();//disable interrupts
//set timer0 interrupt at 40kHz
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
// set compare match register for 40khz increments
OCR0A = 49;// = (16*10^6) / (40000*8) - 1 (must be <256)
// turn on CTC mode
TCCR0A |= (1 << WGM01);
// Set CS11 bit for 8 prescaler
TCCR0B |= (1 << CS11);
// enable timer compare interrupt
TIMSK0 |= (1 << OCIE0A);
sei();//enable interrupts

the contents of the interrupt routine are encapsulated in the following function:

ISR(TIMER0_COMPA_vect){ //40kHz interrupt routine


}

You want to keep the interrupt routine as short as possible, only the necessities.
You can do all of your other tasks (checking on buttons, turning on leds, etc) in the
loop(). Also keep in mind that setting up interrupts may affect other Arduino
functions such as analogWrite and delay.

In the code below, I use the interrupt function to send a new value of sine[] to
PORTD at a rate of 40kHz and increment the variable "t." Figs 1 and 2 show the
(unfiltered) output of the code on an oscilloscope. We can calculate the expected

PDF generated automatically by the HTML to PDF API of PDFmyURL


frequency as follows:

frequency = (sampling frequency) / (steps per cycle)


frequency = 40,000 / 100 = 400hz

at a sampling frequency of 40kHz we expect the duration of each step to be:

duration of each sample step = 1/(sampling frequency)


duration of each sample step = 1/40,000 = 25us

//Sine out w/ 40kHz sampling rate


//by Amanda Ghassaei
//https://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.
*
*/

byte sine[] = {127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 22
9, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238
, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119,
111, 103, 95, 87, 80, 72, 65, 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2,
3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};
int t = 0;//time
Step 10: Extra Tips
void setup(){
//set digital pins 0-7 as outputs
for (int i=0;i<8;i++){
pinMode(i,OUTPUT);
}
PDF generated automatically by the HTML to PDF API of PDFmyURL
This DAC uses quite a bit of the Arduino's available digital pins, including some that
are normally used for serial communications and PWM, so here are a few tips that
will help you deal with pin conflicts.

If you want to do serial communication: Software Serial is an Arduino library


that allows you to turn any of the Arduino's pins into serial pins. Usually when you
PDF generated automatically by the HTML to PDF API of PDFmyURL
are doing an Arduino project that requires serial communication, you avoid using
digital pins 0 and 1 because they need to be free to send serial data. I like to use
them for the 8 bit DAC because pins 0-7 are all part of PORTD on the Arduino's
Atmel328 chip, this allows me to address all of them in a single line of code.
PORTB only has 6 pins (digital pins 8-13) and PORTC only has 6 pins (analog pins
0-5), so you cannot construct an 8 bit DAC with these ports alone.

If you need to use the PWM pins, or otherwise need to use different pins as
the DAC: If you must use the PWM pins you can use bit manipulation to free up
pins 3, 5, and 6 and replace them with pins 8, 12, and 13. Say you want to send
the number 36 to PORTD. You can use the following lines:

//define variables:
boolean bit3state;
boolean bit5state;
boolean bit6state;

//in your main loop():

bit3state = (36 & B00001000)>>3;//get the third bit of 36


bit5state = (36 & B00100000)>>5;//get the fifth bit of 36
bit6state = (36 & B01000000)>>6;//get the sixth bit of 36

//send data to portd w/o disrupting pins 3, 5, and 6


PORTD |= (36&B10010111);//set high pins high using the number 36 with
zeros replacing bits 3, 5, and 6
PORTD &= (36|B01101000);//set low pins low using the number 36 with ones
replacing bits 3, 5, and 6
//send data to portb w/o disrupting pins 9, 10, and 11
PORTB |= 0 | (bit3state) | (bit5state<<4) | (bit6state<<5);//set high pins

PDF generated automatically by the HTML to PDF API of PDFmyURL


PORTB &= 255 & ~(1-bit3state) & ~((1-bit5state)<<4) & ~((1-bit6state)
<<5);//set low pins

be sure to keep these PORTD and PORTB lines right next to each other in your
code, you want the pins on port d and port b to switch at as close to the same time
as possible.

Here is the code from the previous step, edited so that it does not use any PWM
pins. As you see in fig 1, the unfiltered output from the DAC has many
discontinuities caused by the lag between sending data to port d and port b, as well
as splitting up the commands for setting pins high and low. You can get rid of most
of these discontinuities with the low pass filter (fig 2). If you wanted to use this
technique you might consider increasing the cutoff frequency of your low pass
filter. If you wanted to make this really good, you could send your 5 most
significant bits to port d and your 3 least significant bits to port b. This would
decrease the amplitude of some of the discontinuities, reducing the magnitude of
the noise. I'll let you figure that one out on your own.

PDF generated automatically by the HTML to PDF API of PDFmyURL


//Sine out, 40kHz sampling rate, w/o using PWM pins
//by Amanda Ghassaei
//https://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.
*
*/

byte sine[] = {127, 134, 142, 150, 158, 166, 173, 181, 188, 195, 201, 207, 213, 219, 224, 22
9, 234, 238, 241, 245, 247, 250, 251, 252, 253, 254, 253, 252, 251, 250, 247, 245, 241, 238
, 234, 229, 224, 219, 213, 207, 201, 195, 188, 181, 173, 166, 158, 150, 142, 134, 127, 119,
111, 103, 95, 87, 80, 72, 65, 58, 52, 46, 40, 34, 29, 24, 19, 15, 12, 8, 6, 3, 2, 1, 0, 0, 0, 1, 2,
3, 6, 8, 12, 15, 19, 24, 29, 34, 40, 46, 52, 58, 65, 72, 80, 87, 95, 103, 111, 119,};
Ifyou
int t =run out of digital pins and need more: Remember you can always use
0;//time
your analog pins as Digital I/O. Try out the following functions, they work just like
you are dealing
boolean with a regular digital pin.
bit3state;
boolean bit5state;
boolean bit6state;
digitalWrite(A0,HIGH);//set pin A0 high
digitalWrite(A0,LOW);//set
void setup(){ pin A0 low
digitalRead(A0);//read digital
//set digital pins 0-13 as data from pin A0
outputs
for (int i=0;i<14;i++){
pinMode(i,OUTPUT);
Otherwise, try using a multiplexer. If you need more digital outputs, the 74HC595
}
allows you to turn three of the Arduino's digital pins into 8 outputs. You can even
daisy chain multiple
cli();//disable 595's together to create many more outputs pins. You could
interrupts
set //set
up yourtimer0whole DAC
interrupt on one of these chips if you wanted (though it would take a
at 40kHz
fewTCCR0A
lines of =code to entire
0;// set address it andregister
TCCR0A might to
slow
0 you down too much for higher
TCCR0B
sampling = 0;// same
rates). for TCCR0B
The Arduino website is a good place to start learning about how to
TCNT0 = 0;//initialize counter value to 0
use the 595.
// set compare match register for 40khz increments
PDF generated automatically by the HTML to PDF API of PDFmyURL
If you need more digital inputs, the 74HC165 or CD4021B let you turn three of the
Arduino's digital pins into 8 inputs. Again, the Arduino website is a good place to
start learning how to use these chips.

If you want to use the info in this Instructable with the Mega or other
boards: In this Instructable I talked exclusively about the Arduino Uno with
Atmel328. The same code will run fine on any board with an Atmel328 or Atmel168
chip on it. You can also use the same ideas with a Mega. You should try to attach
your DAC to any port that has 8 available pins, that way you can address your
DAC with one line of code ("PORTD =" ) On the Uno, the only port that has 8
available pins is port d. This picture indicates that the Mega has several ports with
8 pins: ports a, b, c, and l are the obvious choices. If you don't care about wasting
analog pins you could also use ports f or k.

adv ertisement

Comments

PDF generated automatically by the HTML to PDF API of PDFmyURL


We have a be nice comment policy.
Please be positive and constructive. w I Made it!  Add Images Post Comment

KnightT3 2017-01-21 Reply

Would you please hint to how this project or your waveform generator project (that I
got from Jameco) can output a suitable Dc/Ac voltage(considering the pot max
amplitude ) to be hooked up to smartphone Microphone jack , without damaging
the internal circuit of the smartphone.
Do I need to do a change to the software ,hardware or the power source?
Thanks.

techiestudent 2017-01-04 Reply

I am doing a blind stick with ultrasonic sensor and voice guide where it tells if you
should go left or right. Is it possible if i use this audio output?

amandaghassaei . techiestudent 2017-01-06 Reply

use a text to speech chip, it will be much easier:


https://www.instructables.com/id/Twitter-Enabled-Text-to-Speech/

PDF generated automatically by the HTML to PDF API of PDFmyURL


adrianio 2016-11-27 Reply

Hello! Nice tutorial. I want to make a low-pass filter like you for Arduino toneAC().
With toneAC, we're sending out of phase signals on two pins. How will I connect
the resistor and capacitor? I need two resistors and two capacitors connected
between every pin and ground? Or is sufficient one resistor and one capacitor
between one pin and ground?

DebojitK 2016-07-24 Reply

I did a test on the timings of direct port write. I used the following pins: PB1,PB0,
PD7,PD6,PD5,PD4,PD3,PD2 ()leaving PD1 and PD0 for rx/tx).
PORTD = (PORTD & B00000011)|((input<<2)&B11111100);
PORTB = (PORTB & B11111100)|((input>>6)&B00000011);
These two lines set the input on the aforementioned pins with direct bit-banging
write method described. The result is astonishing- It only takes ~1.6 usec to
execute these two lines. So for interrupt service routine you get ample time to do
other processing.
Here is the code:
#include "Arduino.h"
//The setup function is called once at startup of the sketch
uint8_t input=100;
String inputString="";
void setup()
{
// Add your initialization code here
Serial.begin(115200);
}

// The loop function is called in an endless loop


void loop()
PDF generated automatically by the HTML to PDF API of PDFmyURL
{
//Add your repeated code here
testSerialEvent();
long sTime=millis();
for(long i=0;i<100000;i++){
PORTD = (PORTD & B00000011)|((input<<2)&B11111100);
PORTB = (PORTB & B11111100)|((input>>6)&B00000011);
}
long eTime=millis()-sTime;
Serial.println(eTime);
Serial.print("[");
for(int i=1;i>=0;i--){
Serial.print(((PORTB&(1<<i))>>i));Serial.print(" ");
}
for(int i=7;i>=2;i--){
Serial.print(((PORTD&(1<<i))>>i));Serial.print(" ");
}
Serial.println("]");
delay(1000);
//output-every loop takes 170 msec that mean one iteration takes ~1.7 usec. Its too
good.

void testSerialEvent(){
while(Serial.available()){
char c=(char)Serial.read();
inputString += c;
if (c == '\n') {
input=inputString.toInt();
inputString="";
Serial.print("Input->");Serial.println(input);
break;
PDF generated automatically by the HTML to PDF API of PDFmyURL
}
}
}

chrisdefrancisci 2016-07-01 Reply

Hello, I think this tutorial is wonderful, thank you so much for posting it, however I've
run into a problem now. When I hook the Arduino up to an oscilloscope, it shows a
perfect sine wave. However, when I plug my Arduino into my audio interface, so I
can record the output on my computer, the signal becomes truncated and only has
a peak to peak voltage of 80 mV. Could you please help me understand why this
is happening, and what I can do to fix it?
Thank you.

AndreaS76 2016-04-10 Reply

Hi Amanda,
thankyou very much, all this is great.
I would use this project to generate a wave and split the signal from one output to
multiple guitar amps through something like a plugboard(I believe it will be
connected in paralel), do i need to change something in the scheme to send a
good signal to all the speakers?
Thank you :)

gabor64 made it! 2016-02-06 Reply

Hi Amanda,
thx for the brilliant project. I sent the 6 most significant bits to PORTD and 2 to
PDF generated automatically by the HTML to PDF API of PDFmyURL
PORTB. I used the following commands to decrease the discontinuities as much
as possible and also speed up the output by using just one instruction per port:
PORTD = x[i] & B11111100 | PORTD & (x[i] | B00000011);
PORTB = x[i] & B00000011 | PORTB & (x[i] | B11111100);
Now I go on to the analog part.
Thx, rgds,
Gabor

PaulC175 2016-01-26 Reply

thank you for this excellent project

LucianM 4 2016-01-08 Reply

Hi amandaghassaei! Nice work!


I trying to do this project but i have one big problem. I can't hind TS922IN/24 in my
country! and i don't have enough time to buy from other one. Isn't an alternative for
TS922IN/24?

henroljeo.gutierrez 2015-09-25 Reply

PDF generated automatically by the HTML to PDF API of PDFmyURL


Hello Sir, I want to output an analog Signal using this tutorial, but my problem is I
want my input to be digital, what I mean is, someone is going to send me a bunch
of bits and then I want to output them into analog, My problem is I dont know how to
read that incoming bits in my arduino. Do you have any codes for reading a bits in
the pin of arduino. ?

syikas93 . henroljeo.gutierrez 2015-11-25 Reply

did u hve get the ans, if yes...can u share it =)

Juan SebastianO1 2015-09-12 Reply

NeeravP 2015-04-08 Reply

hey can i use 8 analog out put from Arduino UNO .

PaulS20 . NeeravP 2015-06-02 Reply

Interesting question, but I see three problems with it:


1) The Uno only has 6 analog output pins; 3, 5, 6, 9, 10, and 11.
2) The are not true analog, but instead use square waves for "Pulse Width
Modulations", As a result, they'd be playing tones of approximately 490 Hz
and 980 Hz into your sound.
3) You need to use eight pins that are all part of an eight-bit port so that you
can enter one number, such as -- PORTD = 125; -- setting all eight pins at
once. Otherwise, you'd have to set one pin at a time with digitalWrite() (or
analogWrite) which would take too much time to keep up with your sound
PDF generated automatically by the HTML to PDF API of PDFmyURL
waves.

KaushikK1 2015-01-18 Reply

Hey..
First off, thank you for the great tutorials.
I'm trying to incorporate your instructable into my "Arduino short range walkie
talkie" project.
I'm using a mems mic (https://www.sparkfun.com/products/9868) to record my
voice. Can you help me out with how to reduce lag during tranmission?
I've tried decreasing delay during input but that rally doesnt seem to do the job.

m_osik 2014-09-07 Reply

HI all,
at first i would like to THNX for cool tutorial.
I tried to use as output converter MCP4921 (with AH_MCP4921.h) but i am not
able to get any "audible" sounds. Have anybody tried this D/A converter with this
solution ?
Best regards
Jan

zacaj 2014-05-16 Reply

Hi, thanks for this great tutorial!


I've got limited inputs, so I was looking at using a serial-in DAC chip instead of
using a ladder, do you think that this http://www.digikey.com/product-
PDF generated automatically by the HTML to PDF API of PDFmyURL
detail/en/MCP4901-E... (MCP4901-E/P) would work? Could I just drop it in to your
schematic as long as I handle the programming correctly?
Also, I hope to power some computer speakers (with their own amp/volume
control) using this circuit. Since they'd have their own volume, I assume I don't need
to install a potentiometer? Would the same buffer work, or would I need something
bigger to support louder/larger speakers?
Thanks

amandaghassaei . zacaj 2014-05-18 Reply

Yeah that chip looks good, but (as you mentioned) the code will have to
change a bit. If your speakers have their own amp, you can probably get
away with connecting the output of the buffer to the input of your speaker's
amp. It's possible you may need a little preamp in there, in that case you can
use the amp in my schematic w a resistor in place of the potentiometer.

zacaj . amandaghassaei 2014-06-21 Reply

It also seems I'm going to need to run off 3V instead of 5V. I know I'll need a
different DAC, but other than that: I assume I'll need a different DC offset
capacitor? Are there any other components I'll need to change?

somethingsmart 2014-06-01 Reply

Hey, thanks for the tutorial!


The input tutorial works great. For this one, though, TS922IN Digikey 497-3049-5-
ND is obsolete. I called Digikey and http://www.digikey.com/product-
detail/en/TS922IDT/... is the closest, but it's a surface mount chip. We are just
gonna solder some wires and give it a go.
PDF generated automatically by the HTML to PDF API of PDFmyURL
lynettequek 2014-05-14 Reply

Could the bit manipulation be used to free up Digital pins 0 and 1 for serial
communication? I have only one serial communication used so I can't use software
serial for that. I have this code so far:
// bit manipulation, sending number 36 to 0 and 1. PORTB: digitalPin 8-13
//define variables:
boolean bit0state;
boolean bit1state;
//in your main loop():
bit0state = (36 & B00000001)>>0;//get the zero bit of 36
bit1state = (36 & B00000010)>>1;//get the first bit of 36

//send data to portd w/o disrupting pins 0 and 1


PORTD |= (36&B11111100);//set high pins high using the number 36 with zeros
replacing bits 0 and 1
PORTD &= (36|B00000011);//set low pins low using the number 36 with ones
replacing bits 0 and 1

//send data to portb w/o disrupting pins 9, 10, and 11, affect 8, 12 and 13 ???
PORTB |= 0 | (bit3state) | (bit5state<<4) | (bit6state<<5);//set high pins
PORTB &= 255 & ~(1-bit3state) & ~((1-bit5state)<<4) & ~((1-bit6state)<<5);//set
low pins
i'm stuck at the last part where i send data to portb, which i only want to affect pins
11 and 12, in replacement of pins 0 and 1. Am I on the right track to do this?
Thanks so much!

amandaghassaei . lynettequek 2014-05-14 Reply

PDF generated automatically by the HTML to PDF API of PDFmyURL


is the number 36 just an example? you can send out numbers less than 64
using only six bits, so you wouldn't even have to worry about pins 8, 12, and
13.
//send data to portd w/o disrupting pins 0 and 1
PORTD |= ((36<<2)&B11111100;);
PORTD &= ((36<<2)|B00000011);

lynettequek . amandaghassaei 2014-05-14 Reply

thanks for your reply! yup 36 is just an example. following what you did for the
instructable :) what if I want to "shift" pins 0 and 1 back to pins 11 and 12? do
I have to include this part?
//send data to portb w/o disrupting pins 9, 10, and 11, affect 8, 12 and 13
PORTB |= 0 | (bit3state) | (bit5state<<4) | (bit6state<<5);//set high pins
PORTB &= 255 & ~(1-bit3state) & ~((1-bit5state)<<4) & ~((1-bit6state)
<<5);//set low pins

amandaghassaei . lynettequek 2014-05-15 Reply

PORTB |= 0 | (bit0state<<4) | (bit1state<<5);//set high pins


PORTB &= 255 & ~((1-bit0state)<<4) & ~((1-bit1state)<<5);//set low pins
I'm pretty sure that will work for pins 12 and 13

lynettequek . amandaghassaei 2014-05-15 Reply

thanks so much! will try it out!

PDF generated automatically by the HTML to PDF API of PDFmyURL


AbdulW87 2014-05-06 Reply

Thanks for this instructable, very helpful. Can i use an AD712KNZ since they dont
make the 922 anymore.

amandaghassaei . AbdulW87 2014-05-07 Reply

yes, you might also check out the lm386 bc it doesn't require a dual power
supply.

hopkinskong 2014-05-03 Reply

Hello,
Thanks for your instructable! I am now managed to use this trick on one of my
8051 microcontroller!

amandaghassaei . hopkinskong 2014-05-04 Reply

nice!

starock 2014-04-26 Reply

Tnank you so much~! \(≧▽≦)/~


1Question Plz:
How can I generate a sound like Mario-Gmae? I Love 8Bit music. Make a 8Bit
synthesizer is my dream. T_T

PDF generated automatically by the HTML to PDF API of PDFmyURL


amandaghassaei . starock 2014-04-28 Reply

http://arduino.cc/en/Reference/tone

Reddyco 2014-03-12 Reply

Hey Amanda! Great job on this instructable, I'm using it to embase my work on a
eletronic drum sound generator with Arduino (hopefully, one day I'll post here how
to do it).
I'm writing to ask abuot that 0.01 uF and 10ohm resistor in parallel with the
speaker and the DC offset capacitor. What are they used for?
Thanks in advance!

amandaghassaei . Reddyco 2014-03-22 Reply

thanks! they're just threre to reduce noise, not a big deal if you don't have
them

flowirin 2014-03-17 Reply

what's the upper limit on the sampling frequency for the arudino? can i get it up to
80kHz?

amandaghassaei . flowirin 2014-03-22 Reply

definitely, you could get up to a few hundred kHz with no problem.


One concern is that the resistor ladder dac that I show in this ible might not
respond fast enough as you increase the sampling rate - this might end up

PDF generated automatically by the HTML to PDF API of PDFmyURL


applying a low pass filter on your output. I updated step 1 with a little more
info about alternative DACs, you might check out the R2R DAC IC I
demonstrated in this Instructable (you can wire it up to only use one channel if
you need), it has much better quality control then just throwing a bunch of
resistors together on a breadboard and I think it will give you better results. It
says the settling time for that DAC is 100ns, which should work fine for
80kHz sampling rate.
you will also have to change the frequency of the interrupt. For 40kZ I used
this line:
OCR0A = 49;// = (16*10^6) / (40000*8) - 1
try this instead:
OCR0A = 24;// = (16*10^6) / (40000*8) - 1

ibirnam 2013-10-06 Reply

Just wanted to confirm this: since the TS922IN is now obsolete, would this be a
sufficient replacement? Thank you!

amandaghassaei . ibirnam 2013-10-08 Reply

that's going to be really hard to work w bc it's surface mount. Just get the
lm386 chip and a couple of resistors and capacitors and wire it up like this:
http://www.hobby-hour.com/electronics/lm386-20.gif
it may need a 9v supply instead of 5v, I can't remember.

heymarky . amandaghassaei 2014-03-04 Reply

I'm working my way through this trying to substitute the lm386 at Step 3. The

PDF generated automatically by the HTML to PDF API of PDFmyURL


gif you linked looks like the replacement for the amplifier. I can see the part
that replaces the low pass filter (thanks to your excellent explanation of what
that is) but I don't see anything that I recognize as being the DAC buffer. Is
the buffer unnecessary with the lm386, or is it there and I don't recognize it?
Thanks!
PS I'm a software engineer, so use small words. :-)

amandaghassaei . heymarky 2014-03-05 Reply

actually, a resistor and capacitor only act as a low pass filter when the output
signal is connected to the junction between them, here is a pic. You can see
that switching the order of the components will turn it into a high pass filter.
The lm386 circuit is not wired up the same way, so it won't act as a low pass
filter. So here's what I would do:
arduino - dac - lm386 - low pass filter - output
you could also use a tl1072 or tl082 to replace both ts922's, but these
require a +/- 9v supply, which is annoying.

ibirnam . amandaghassaei 2013-10-08 Reply

Awesome, thanks for the help!

waqarahmed236 . ibirnam 2014-01-16 Reply

Ibirnam Is LM386 working instead of using TS922IN ?

joshuaphua1 2014-01-31 Reply

PDF generated automatically by the HTML to PDF API of PDFmyURL


near the end of the circuit, what's the 0.1uF capacitor and 10Ohm resistor for?
another kind of filter?

amandaghassaei . joshuaphua1 2014-02-13 Reply

not a filter, just helps make the DC offset more stable.

joshuaphua1 2014-01-09 Reply

Can you explain the DC offset more? I understand how a +2.5VDC offset works
but am confused about this one. Thanks!

amandaghassaei . joshuaphua1 2014-01-13 Reply

I always think of it like this: the signal going into one side of the capacitor
causes an alternating excess of positive or negative charge on one side of
the cap. The other side of the cap reacts by accumulating opposite charge -
this causes an alternating voltage on the opposite side of the cap. Since no
current (or a negligible amount) actually gets passed across the cap, the DC
voltage on one side does not transfer over to the other side, so the
alternating voltage is centered around 0.

joshuaphua1 . amandaghassaei 2014-01-31 Reply

thanks!!

punk1290 2013-12-08 Reply

PDF generated automatically by the HTML to PDF API of PDFmyURL


I had to replace the TS922 with an LM386. I am trying to get step 3 working.
Unfortunately I don't have an oscillator to verify I did things right in step 2. My issue
is that when I connect my Adruino to my breadboard with this circuit the power
LED dims and my computer no longer sees the Arduino. I tried unhooking the
breadboard and then loading the program to my Arduino. That worked fine. When I
connected everything back up, the LED came on immediately without ramping up.
Any thoughts or ideas for me?

amandaghassaei . punk1290 2014-01-13 Reply

sounds like you're shorting out one of the arduino's power pins (the strip of
pins near the analog inputs). Double check those.

Ploopy 2013-11-23 Reply

How did you write your code in those squares that you scroll?

I More Comments

PDF generated automatically by the HTML to PDF API of PDFmyURL


Newsletter About Us Find Us Resources
Let your inbox help you discover our best Who We Are Facebook For Teachers
projects, classes, and contests. Instructables Advertise Youtube Residency Program
will help you learn how to make anything! Contact Gift Premium Account
Twitter
enter email Jobs Forums
I'm in! Pinterest
Help Answers
Google+
Sitemap

© 2017 Autodesk, Inc. Terms of Service | Privacy Statement | Legal Notices & Trademarks | Mobile Site

PDF generated automatically by the HTML to PDF API of PDFmyURL

S-ar putea să vă placă și