Arduino (ATmega) PWM audio DAC
About PWM on the Arduino (ATmega)
This page describes all the nitty gritty of getting your Arduino set up to output decent audio on its PWM via Timer1. For more in-depth information on what the settings mean, and what values are right for your application, please check out our PWM Tutorial on the main site.
Timer1 outputs its data on pins 9 and 10 on the Arduino (pin 9/10 are OC1A/B and PORTB1/2 on the ATmega328p). For all of the following, it will be assumed that pin 9 is used for Single PWM, and is the high byte for Dual PWM. Pin 10 is not used for Single PWM, and is the low byte for Dual PWM. You can also set up for Dual PWM, and output 2 different signals, one on each output.
Other timers can be similarly setup to run PWM, but we use Timer1 because it is a 16 bit timer, so it can do greater bit depth at lower frequencies (if desired). The register settings for other timers is similar, but check the ATmega328p datasheet to be make sure its all correct. Also, be careful when using Timer0 on the Arduino, as it is already in use for the delay() and millis() functions, and its overflow interrupt can not be reused.
Register settings
The following charts all assume a microcontroller clock frequency of 16MHz (standard on the Arduino). If you are using a different oscillator, just multiply the frequencies listed by Fcpu/16MHz, where Fcpu is your oscillator frequency. For example, if you were running at 8MHz, you would multiply by 1/2, and if you were running at 20MHz, you would multiply by 5/4. Everything else stays the same.
There are only 3 values to set in order to configure your PWM. they are:
PWM_FREQ - This is the frequency value taken from the table below.
PWM_MODE - Set to 1 for Fast mode, and 0 for Phase Correct mode.
PWM_QTY - Set to 1 for Single PWM, and 2 for Dual PWM.
|
Fast - PWM_MODE 1 |
Phase Correct - PWM_MODE 0 |
||||
Frequency |
PWM_FREQ |
Bit Depth |
PWM_FREQ |
Bit Depth |
||
Fcpu = 16MHz |
Value |
Single |
Dual |
Value |
Single |
Dual |
250kHz |
0x003F |
6 bit |
12 bit |
0x001F |
5 bit |
10 bit |
125kHz |
0x007F |
7 bit |
14 bit |
0x003F |
6 bit |
12 bit |
62.5kHz |
0x00FF |
8 bit |
16 bit |
0x007F |
7 bit |
14 bit |
31.3kHz |
0x01FF |
9 bit |
N/A |
0x00FF |
8 bit |
16 bit |
15.6kHz |
0x03FF |
10 bit |
N/A |
0x01FF |
9 bit |
N/A |
7.81kHz |
0x07FF |
11 bit |
N/A |
0x03FF |
10 bit |
N/A |
3.91kHz |
0x0FFF |
12 bit |
N/A |
0x07FF |
11 bit |
N/A |
1.95kHz |
0x1FFF |
13 bit |
N/A |
0x0FFF |
12 bit |
N/A |
976Hz |
0x3FFF |
14 bit |
N/A |
0x1FFF |
13 bit |
N/A |
488Hz |
0x7FFF |
15 bit |
N/A |
0x3FFF |
14 bit |
N/A |
244Hz |
0xFFFF |
16 bit |
N/A |
0x7FFF |
15 bit |
N/A |
122Hz |
N/A |
N/A |
N/A |
0xFFFF |
16 bit |
N/A |
Dual PWMs
Dual PWMs just mix an upper and lower value together, to make one analog value. This mixing occurs through 2 resistors, one of which is 2^n times the other, where n is the bit depth. For 2 - 8 bit PWMs (16b), use 1:256, or 3.9k and 1M (as shown below). For 2 - 7 bit PWMs (14b), use 1:128, or 3.9k and 499k, etc. The 3.9k is used so that the output resistance of the microcontroller is much less than the total resistance, reducing errors. And be sure to use 1% resistors for 14b and higher PWMs.
Example Arduino Sketch
This skecth uses the above chart to set the Arduino PWM to 31.25kHz, Dual PWM, Phase Correct, at 16 bit depth. It takes in audio on ADC0, and plays it out the PWM, so you can hear the sort of quality you can expect from 10 bit data (10 bits is the depth of the ADC). For ATmega programmers, the Arduino sketch is written in C, and is essentially usable if copied into your programming interface of choice. The following low-pass filter and summing circuit should be used, along with a the bias circuit for the ADC.
1 // adc_to_pwm.pde
2 // ADC to PWM converter
3 // guest - openmusiclabs.com - 1.9.13
4 // options table at http://wiki.openmusiclabs.com/wiki/PWMDAC
5 // takes in audio data from the ADC and plays it out on
6 // Timer1 PWM. 16b, Phase Correct, 31.25kHz - although ADC is 10b.
7
8 #define PWM_FREQ 0x00FF // pwm frequency - see table
9 #define PWM_MODE 0 // Fast (1) or Phase Correct (0)
10 #define PWM_QTY 2 // number of pwms, either 1 or 2
11
12 void setup() {
13 // setup ADC
14 ADMUX = 0x60; // left adjust, adc0, internal vcc
15 ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger
16 ADCSRB =0x07; // t1 capture for trigger
17 DIDR0 = 0x01; // turn off digital inputs for adc0
18
19 // setup PWM
20 TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); //
21 TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1
22 TIMSK1 = 0x20; // interrupt on capture interrupt
23 ICR1H = (PWM_FREQ >> 8);
24 ICR1L = (PWM_FREQ & 0xff);
25 DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs
26
27 sei(); // turn on interrupts - not really necessary with arduino
28 }
29
30 void loop() {
31 while(1); // gets rid of jitter
32 }
33
34 ISR(TIMER1_CAPT_vect) {
35
36 // get ADC data
37 unsigned int temp1 = ADCL; // you need to fetch the low byte first
38 unsigned int temp2 = ADCH;
39 // although ADCH and ADCL are 8b numbers, they are represented
40 // here by unsigned ints, just to demonstrate how you would
41 // use numbers larger than 8b. also, be sure you use unsigned
42 // ints for this operation. if you have a signed int (a regular
43 // int), add 0x8000 and cast it to an unsigned int before sending
44 // it out to OCR1AH or OCR1AL.
45 // example:
46 // int temp3 = 87;
47 // unsigned int temp4 = temp3 + 0x8000;
48 // OCR1AH = temp4 >> 8;
49 // OCR1AL = temp4;
50
51 // output high byte on OC1A
52 OCR1AH = temp2 >> 8; // takes top 8 bits
53 OCR1AL = temp2; // takes bottom 8 bits
54
55 // output low byte on OC1B
56 OCR1BH = temp1 >> 8;
57 OCR1BL = temp1;
58 }
59