In this instructable I'll show you how to use the TLC7528 with the Arduino to output stereo audio. Stereo audio means 2 independent channels of audio. Stereo audio is especially fun when sent to headphones because you can achieve some interesting auditory effects since each ear is hearing its own independent channel of sound, some ideas include:
"3D audio" spatial effects- by adjusting the filtering, amplitude, and phase of two channels of audio you can simulate the experience of sound directionality, making a sound source seem to originate from a precise location in the space around you, here's a great example
binaural beats- by sending two sine waves of similar -but unequal- frequencies to headphones (one to each ear), you will hear a pulsating beatnote that is thought to induce relaxation and other meditative effects. Here's an example.
panning- change the relative amplitude of a sound source in each channel of the stereo mix. This effect is simple, but can be really cool sounding, a great example is in the bridge of Led Zeppelin's Whole Lotta Love (listen to it with headphones!)
Parts List:
(x1) TLC7528 Digikey 296-1871-5-ND
(x1) Arduino Uno Sparkfun DEV-11021
Other Materials:
22 gauge jumper wire
oscillosope
Remove these ads by
Signing UpStep 1: 8 bit DACs and Serial vs Parallel
output voltage from 8 bit DAC = (supply voltage) * (digital input data) / 255
In this Instructable, I'll be powering the DAC from the Arduino's built in 5V supply, so the equation above can be simplified to:
output voltage from 8 bit DAC = 5V * (digital input data) / 255
From this equation, we can see that the TLC7528 would output 5V if it receives a value of 255, 0V if it receives a value of 0, 2.5V if it receives a value of 127, and so on. You may be wondering where the 255 came from, this is a result of the TLC7528 being an 8 bit DAC. 8 bit means that the binary numbers we can send to the DAC must have no more than 8 digits in them. In binary, numbers that are represented with 8 digits (or less) range in value from 0 to 255 (as opposed to the regular decimal numeral system where and 8 digit numbers range from 0 to 99999999). So there are 256 possible values (0-255) that the 8 bit DAC can receive. This can be calculated quickly from the equation below:
2^8 = 256 possible values
If we were using a 10 bit DAC, then it could receive 2^10 = 1024 different values, ranging from 0-1023. This means a 10 bit DAC has a higher resolution than an 8 bit DAC. Despite this, I've found that 8 bit DAC's are generally much more useful than 10 bit DACs because data is easier to store and output from the Arduino in 8 bit form than in 10 bit form. For example, the data type byte in the Arduino language is for storing 8 bit numbers. If you wanted to store a 10 bit number, you would have to use an int data type, but int data types can store up to 16 bit numbers, so you would be wasting 6 bits of memory. Additionally, the pins of the Arduino are grouped together in clusters of 8 or less. On the Uno, the only full group of 8 are digital pins 0-7, this group is called PORTD. When writing Arduino code you can easily output 8 bits of data by setting the states of digital pins 0-7 all at once. In the code this is done by sending an 8 bit number to PORTD. For example:
PORTD = 255;
sets digital pins 0-7 HIGH, it is equivalent to the following:
digitalWrite(0,HIGH);
digitalWrite(1,HIGH);
digitalWrite(2,HIGH);
digitalWrite(3,HIGH);
digitalWrite(4,HIGH);
digitalWrite(5,HIGH);
digitalWrite(6,HIGH);
digitalWrite(7,HIGH);
but using the PORTD command sets all the pins simultaneously and is much faster. The following command would set digital pins 0-7 LOW:
PORTD = 0;
you can also use the PORTD command to set some of the pins high and others low. For example:
PORTD = 137;
137 in binary is 10001001, so sending 137 to PORTD will set pin 7 HIGH (because the first digit of the binary number is a "1"), pins 6-4 LOW (because the next 3 digits are "0"), pin 3 HIGH, pins 2 and 1 LOW, and pin 0 HIGH. You can read more about how this works on the Arduino website. You can even send binary numbers to PORTD, for example:
PORTD = B10001001;
is equivalent to PORTD = 137;
Finally, I'll talk about how we get this data into the TLC7528. The TLC7528 is called a parallel DAC. This means that all the data we send to the DAC is sent in parallel. 8 bit parallel DACs have eight data connections between the Arduino and DAC that send all 8 bits of data at the same time. The opposite of parallel is serial, in serial setups you use fewer data connections (usually three), but send only one bit over at a time. So, in order to transmit an 8 bit number via a serial connection you have to send eight 1 bit packages, one after the other, while in parallel setups you can send all 8 bits at the same time. This means that serial connections require faster data transfer than parallel connections. If you are not worried about using 8 digital pins of the Arduino, a parallel 8 bit DAC is a good option because it requires less clock speed and is simpler to code.












































Visit Our Store »
Go Pro Today »




Wow thanks! That still sounds a little bit fuzzier than I'd like for using samples though. Is there a way I could increase the quality some more? I'd like to use my instrument for recording, and possibly preforming. Also, could I just downgrade the quality to 8-bit using the one-bit audio? Might be cool
https://soundcloud.com/amanda-ghassaei/over-the-rainbow
If possible can you please suggest a circuit to convert 0 to 4v back to a true 0 - 5v?
http://en.wikipedia.org/wiki/Operational_amplifier#Non-inverting_amplifier
if you choose your resistors correctly, you can amplify the signal back up to 0-5V. I think it is something like R1 = 4*R2, but you should double check. does that make sense?
int output;
void setup()
{
for (byte i=0;i<8;i++){
pinMode(i, OUTPUT);//set digital pins 0-7 as outputs
}
//set up continuous sampling of analog pin 0
//clear ADCSRA and ADCSRB registers
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements
cli();//stop interrupts
//set timer1 interrupt at ~44.1kHz
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
// set compare match register for 1hz increments
OCR1A = 361;// = (16*10^6) / (44100*1) - 1 (must be <65536)
// turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS10 bit for 1 prescaler
TCCR1B |= (1 << CS10);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);
sei();//enable interrupts
}
ISR(TIMER1_COMPA_vect) //timer1 interrupt ~44.1kHz to send audio data (it is really 44.199kHz)
{
output = ADCH; //read the value from A0
PORTD = output; //send the output value to the DAC through digital pins 0-7
}
void loop(){}
int incomingAudio;//store incoming audio data
void setup(){
cli();//disable interrupts
//set up continuous sampling of analog pin 0
//clear ADCSRA and ADCSRB registers
ADCSRA = 0;
ADCSRB = 0;
ADMUX |= (1 << REFS0); //set reference voltage
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
ADCSRA |= (1 << ADPS2) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500kHz
ADCSRA |= (1 << ADATE); //enabble auto trigger
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
ADCSRA |= (1 << ADEN); //enable ADC
ADCSRA |= (1 << ADSC); //start ADC measurements
sei();//enable interrupts
//if you want to add other things to setup(), do it here
}
ISR(ADC_vect) {//when new ADC value ready
incomingAudio = ADCH;//update the variable incomingAudio with new value from A0 (between 0 and 255)
PORTD = incomingAudio;//send out audio
}
void loop(){
//do other stuff here
}
http://www.instructables.com/id/Arduino-Timer-Interrupts/
it gets called at a constant frequency to send out audio data- basically it takes care of sending out audio at a certain sampling rate.
reading A0 and sending out data should both be handled with interrupts. all the code for reading from portb should happen in your main loop. check out my vocal effects box for an example of this.
let me know if you have more questions!
amanda
For those who need a 10-bit solution, please have a look at http://sourceforge.net/projects/lxardoscope/files/accuracyInvestigation/ where the Arduino Uno drives a MAX503.
http://faz-voce-mesmo.blogspot.pt/2012/11/instructables-sofa-de-paletes-stereo.html