--- /dev/null
+/* Copyright (c) 2015 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * Demonstrate the use of AVR timer 2 to precisely control a pin.
+ * Test board has LEDs connected to pin 5 and 9 (PD5 and PB1) on an
+ * Arduino Nano board. Pin 5 is connected to timer2 and demonstrates
+ * control of the timer interval while pin 9 is connected to timer1
+ * and shows the PWM frequency control.
+ */
+
+#include <avr/pgmspace.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/power.h>
+#include <avr/sleep.h>
+#include <avr/wdt.h>
+#include <util/delay.h>
+
+/*
+ * timer2:
+ * Using a /64 prescaler with 1 16MHz clock and counting from
+ * 6 we get an interval of 1ms exactly between interrupts:
+ * (64.0 / 16e6) * (256 - 6) = 0.001s
+ *
+ * timer1:
+ * Using Phase/Freq correct mode we can control the PWM freq precisely.
+ * f = 16e6 / (2 * mul * TOP)
+ * TOP = 0xf000 yields 125 Hz
+ * TOP = 64 yields 125 kHz
+ * TOP = 16 yields 500 kHz
+ * TOP = 2 yields max of 4 MHz
+ * For IR: 36kHz use 222; 38kHz use 212
+ */
+const uint8_t TIMER_INIT = 6;
+const uint16_t PWM_TOP = 212;
+const uint16_t PWM_MATCH = 212/2; /* square wave, 50% */
+
+volatile uint16_t counter = 0;
+
+ISR(TIMER2_OVF_vect)
+{
+ cli();
+ TCNT2 = TIMER_INIT; /* reset the timer */
+ if (++counter >= 250)
+ {
+ PORTD ^= _BV(PD5);
+ OCR1A = (OCR1A == 0) ? PWM_MATCH : 0;
+ counter = 0;
+ }
+ sei();
+}
+
+static void init_timer2()
+{
+ cli();
+ /* 18.9: Disable the timer2 overflow interrupt for configuration */
+ TIMSK2 &= ~(1 << TOIE2);
+ /* 18.9b: Select clock source as internal i/o clock */
+ ASSR &= ~(1<<AS2);
+ /* Table 18.8: all 0 == normal operation with overflow */
+ TCCR2A &= ~((1<<WGM21) | (1<<WGM20));
+ TCCR2B &= ~(1<<WGM22);
+ /* Table 18-9: Configure prescaler to CPU/64 */
+ TCCR2B &= (1<<CS22) | (1<<CS21) | (1<<CS20); /* clear bits */
+ TCCR2B |= (1<< CS22); /* select /64 */
+ /* Delay is (prescale / cpu_freq) * (256 - tcnt2) */
+ TCNT2 = TIMER_INIT;
+ /* 18.9e: re-enable the timer overflow interrupt flag */
+ TIMSK2 |= (1 << TOIE2);
+ sei();
+}
+
+static void init_pwm_pin9()
+{
+ /*
+ * PD5,PD6 (5,6) are attached to timer0 (8 bit)
+ * PB1,PB2 (9,10) are attached to timer1 (16bit)
+ * PD3,PB3 (3 & 11) are attached to timer2 (8 bit)
+ *
+ * In Phase Correct PWM mode, the couner counts up to TOP
+ * then back down to 0. OCR is cleared on match ascending then
+ * set on match descending. In this mode the PWM interval is twice
+ * the prescaled clock interval.
+ *
+ * PWM cycle should be (1.0 / 16e6) * 256 == 16us
+ * with prescale 1. In Phase Correct mode we get 32us per cycle
+ * or 31.25kHz.
+ * freq = 16e6/(2 * prescaler * 256) == 31.25
+ * but we can change ICR1 to set alternate TOP other than 256.
+ * eg: ICR1 = 100 yields 80kHz
+ */
+ cli();
+ DDRB |= _BV(DDB1); /* set the pin PB1 (OC1A) as output. */
+ PORTB &= ~(_BV(PB1));
+ /* 16.11.1: select PWM mode: CTC mode */
+ TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
+ TCCR1A |= _BV(COM1A1);
+ /* 16.11.1 (table 16-4): select waveform mode: Phase correct 8 bit */
+ TCCR1A &= ~(_BV(WGM11) | _BV(WGM10));
+ TCCR1B &= ~(_BV(WGM13) | _BV(WGM12));
+ TCCR1A |= _BV(WGM11); TCCR1B |= _BV(WGM13);
+ /* 16.11.2: select clock prescaler: /1 */
+ TCCR1B &= ~(_BV(CS12) | _BV(CS11) | _BV(CS10));
+ TCCR1B |= _BV(CS10);
+ /* set duty cycle: 0 is off, 255 is full on */
+ ICR1 = PWM_TOP;
+ TCNT1 = 0;
+ OCR1A = PWM_MATCH;
+ sei();
+}
+
+int
+main(void)
+{
+ wdt_enable(WDTO_1S);
+
+ /* turn off all unused peripherals */
+ power_adc_disable();
+ power_spi_disable();
+ power_twi_disable();
+ power_usart0_disable();
+
+ /* set all unused pins to high-Z state */
+ DDRB = 0; PORTB = 0xff;
+ DDRC = 0; PORTC = 0xff;
+ DDRD = 0; PORTD = 0xff;
+
+ DDRD = _BV(DDD5); /* pins 5 is output */
+ init_timer2();
+ init_pwm_pin9();
+
+ while (1)
+ {
+ _delay_ms(250);
+ wdt_reset();
+ }
+}
\ No newline at end of file