=== 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 [[http://www.openmusiclabs.com/learning/digital/pwm-dac/|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 [[http://www.atmel.com/Images/doc8161.pdf|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. {{attachment:pwm_schem_sm.jpg}} * [[attachment:adc_to_pwm.pde|ADC to PWM Arduino Sketch]] {{{#!highlight c // adc_to_pwm.pde // ADC to PWM converter // guest - openmusiclabs.com - 1.9.13 // options table at http://wiki.openmusiclabs.com/wiki/PWMDAC // takes in audio data from the ADC and plays it out on // Timer1 PWM. 16b, Phase Correct, 31.25kHz - although ADC is 10b. #define PWM_FREQ 0x00FF // pwm frequency - see table #define PWM_MODE 0 // Fast (1) or Phase Correct (0) #define PWM_QTY 2 // number of pwms, either 1 or 2 void setup() { // setup ADC ADMUX = 0x60; // left adjust, adc0, internal vcc ADCSRA = 0xe5; // turn on adc, ck/32, auto trigger ADCSRB =0x07; // t1 capture for trigger DIDR0 = 0x01; // turn off digital inputs for adc0 // setup PWM TCCR1A = (((PWM_QTY - 1) << 5) | 0x80 | (PWM_MODE << 1)); // TCCR1B = ((PWM_MODE << 3) | 0x11); // ck/1 TIMSK1 = 0x20; // interrupt on capture interrupt ICR1H = (PWM_FREQ >> 8); ICR1L = (PWM_FREQ & 0xff); DDRB |= ((PWM_QTY << 1) | 0x02); // turn on outputs sei(); // turn on interrupts - not really necessary with arduino } void loop() { while(1); // gets rid of jitter } ISR(TIMER1_CAPT_vect) { // get ADC data unsigned int temp1 = ADCL; // you need to fetch the low byte first unsigned int temp2 = ADCH; // although ADCH and ADCL are 8b numbers, they are represented // here by unsigned ints, just to demonstrate how you would // use numbers larger than 8b. also, be sure you use unsigned // ints for this operation. if you have a signed int (a regular // int), add 0x8000 and cast it to an unsigned int before sending // it out to OCR1AH or OCR1AL. // example: // int temp3 = 87; // unsigned int temp4 = temp3 + 0x8000; // OCR1AH = temp4 >> 8; // OCR1AL = temp4; // output high byte on OC1A OCR1AH = temp2 >> 8; // takes top 8 bits OCR1AL = temp2; // takes bottom 8 bits // output low byte on OC1B OCR1BH = temp1 >> 8; OCR1BL = temp1; } }}}