In the previous post on generating audio using Arduino Mega, R-2R DAC and different types of amplifying stages we have outputted audio sample from within the main loop. While for simple application that does not need timing precision it is acceptable, for application that needs high timing precision of sampled data output it may not be acceptable.
So the code used here as previously stated can be used in the following example tutorials.
- Audio from Arduino using R2R DAC and transistor amplifier
- Generate Audio using Arduino using R-2R DAC and LM358 Operational Amplifier
- Audio generate using Arduino, R-2R DAC and LM368
Below is the code we used earlier for outputting sampled data from the main loop() function.
#include "soundtest.h"
short i = 0;
void setup() {
DDRC = 255;
}
void loop() {
PORTC = pgm_read_byte(&(data[i++]));
if(i >= sizeof(data))
i = 0;
delayMicroseconds(62);
}
The reason that it does not provide precise timed sampled output data is that the instant when the sampled data is outputted depends upon the code size within the main loop() which can vary upon application.
The solution to this is to use Arduino built-in timer interrupt. Using such technique, the sampled data is outputted in regular timed period no matter what happens inside the main loop function.
An alternative code to output samples using timer interrupt is provided below.
#include "soundtest.h"
short i = 0;
void setup() {
//SET UP TIMER 1
cli(); //Disable global interrupts
TCCR1A = 0; //Reset Timer 1 Counter Control Register A
TCCR1B = 0; //Reset Timer 1 Counter Control Register B
//Set Timer 1 Output Compare Register A to desired frequency
//(16,000,000 / 16384) - 1 = 975
OCR1A = 975;
TCCR1B |= (1 << WGM12); //Turn on CTC mode
TCCR1B |= (1 << CS10); //Prescalar = 1 (no prescalar used)
TIMSK1 |= (1 << OCIE1A); //Enable timer interrupt
sei(); //Enable interrupts
DDRC = 255; //make PORTC output
}
void loop() {
}
ISR(TIMER1_COMPA_vect)
{
PORTC = pgm_read_byte(&(data[i++]));
if(i >= sizeof(data))
i = 0;
}
In the code above we have used the 16-bit Timer 1 in CTC mode with interrupt to output data at sampling rate of 16.384KHz. In the code, first we disable the global interrupt using the cli() function. Then the Timer Counter 1 Control Registers TCCR1A and TCCR1B are cleared to ensure that no unknown bits are sets because some bits in these registers may be set due to previous microcontroller usage. Then we calculate the value required to be loaded into the OCR1A register. To calculate this we need to know at what frequency we want to output data and use the equation as follows.
where N variable represents the prescaler factor (1, 8, 64, 256, or 1024)
We need to solve for OCR1A in the above equation.
Substituing 1 for N because we will not use Pre-scalar and rearranging, we get
OCR1A = fclk/2*foc1A - 1
Using fs = 2*foc1A, we get,
OCR1A = fclk/fs - 1
The sampled data used in our data was sampled at 16KHz, but for us we will use 16.384KHz or 16384Hz.
Now using fclk = 16MHz for Arduino Mega, and fs = 16384Hz in the above equation, we get,
OCR1A = 16MHz/16384Hz - 1
or, OCR1A = 16000000Hz/16384Hz - 1
that is OCR1A = 975.56
or, OCR1A = 975
So when we load OCR1A with value of 975 then the timer interrupt will fire sampled data to the output port C at 16.384Khz sampling rate.
The next step is to configure the Timer 1 in the CTC mode with no prescalar. This is done by setting the WGM12 bit in TCCR1B register for CTC(Clear Timer on Compare) mode and setting the CS10 bit also in the TCCR1B register for no prescalar. After that we enable the output compare interrupt enable bit(OCIE1A) in the TIMSK1 register to enable the interrupt function. Lastly for the Timer 1 configuration, we enable the global interrupt using the sei() function. The DDRC = 255; statement just sets up the PORT C as output port.
The ISR(Timer1_COMPA_vect) is interrupt service routine function that is called once the interrupt bit enabled previously is set. That is the interrupt is called every 16.284Khz or 61microseconts and each time one sampled data is outputted.
Circuit Diagram and Hardware Setup
The following shows the circuit diagram of interfacing Arduino Mega with R-2R DAC, low pass filter, Arduino audio amplifier with transistors.
The following shows Arduino mega, R-2R digital to analog converter, low pass filter and the transistor based Arduino audio amplifier with speaker assembled on a breadboard.