/* Name: main.c * Author: Toshi Nagata * Copyright: Copyright 2009 (c) Toshi Nagata * License: Modified BSD License * Pin 2 (in): IR receiver * Pin 3 (out): LED * Pin 5 (out): PWM (not in use yet) */ #include #include #include #define NBITS 16 #define CL 6.67 /* Timer clock in microseconds */ #define T0 ((short)(1500 / CL)) /* threshold for bit 0/1 */ #define T1 ((short)(20000 / CL)) /* interval within one chunk */ #define T2 ((short)(50000 / CL)) /* interval between 2 separate chunks */ volatile unsigned short t, start; volatile unsigned short tempData, oldData, data; volatile char tempCount; volatile char ready; ISR(TIM0_OVF_vect) { t += 256; if (t - start >= T2) { /* Long interval is detected */ /* Start waiting for the first interrupt */ /* (The incomplete data is discarded if present) */ tempCount = -1; oldData = 0; start = t = 0; } else if (t - start >= T1) { /* Short interval is detected */ if (tempCount >= 0 && tempCount < NBITS + 1) { /* Not waiting for end of chunk */ tempCount = -1; /* Start waiting for new data (may be overridden later; see below) */ /* Check the validity of the data */ /* (Specific for old DENON remote controller) */ if ((tempData & 0x1f) == 0x08) { tempData >>= 5; if (tempData & (1 << 9)) tempData = (~tempData) & 0x03ff; if (tempData == oldData) { data = tempData; ready = 1; /* Valid data */ tempCount = NBITS + 1; /* Skip until the end of chunk */ } oldData = tempData; } start = t = 0; } } } /* INT0 should be more convenient, but PCINT3 is used instead, to avoid changing pin wiring between programming and testing modes. */ ISR(PCINT0_vect) { if (PINB & 0b00001000) { /* pin 2 (bit 3) is low -> falling edge */ if (tempCount < 0) { /* Start data collection */ tempData = 0; tempCount = 0; } else if (tempCount < NBITS) { /* Read out one more bit */ if (t + (unsigned char)TCNT0 - start >= T0) tempData |= (1 << tempCount); tempCount++; } t = 0; start = (unsigned char)TCNT0; } } void blinkLED(void) { PORTB ^= 0b00010000; _delay_ms(50); _delay_ms(50); PORTB ^= 0b00010000; _delay_ms(50); _delay_ms(50); } int main(void) { char n; cli(); /* Disable interrupt globally */ DDRB = 0b00010001; /* pin 3 (bit 4) and pin 5 (bit 0) for output */ TCCR0A = 0b11000000 /* PWM: set OC0A on compare match and clear at TOP */ | 0b00000011; /* Fast PWM */ TCCR0B = 0b00000000 /* Fast PWM with TOP = MAX */ | 0b00000010; /* Clock prescale by 8 */ OCR0A = 255; /* PWM duty ratio */ TIMSK0 = 0b00000010; /* Timer0 overflow interrupt enabled */ /* MCUCR = 0b00000010; *//* INT0 interrupt on falling */ GIMSK = 0b00100000; /* PCINT enabled */ PCMSK = 0b00001000; /* PCINT3 (pin 2) enabled */ tempCount = -1; t = start = 0; sei(); /* Enable interrupt globally */ for (n = 0; n < 5; n++) blinkLED(); for (;;) { if (ready) { /* Data is available */ blinkLED(); switch (data) { case 0b1000010: /* button 1 */ OCR0A = 255; n = 1; break; case 0b1000011: /* button 2 */ OCR0A = 80; n = 2; break; case 0b1000100: /* button 3 */ OCR0A = 1; n = 3; break; case 0b1000101: /* button 4 */ OCR0A = 80; n = 4; break; default: n = 0; break; } ready = 0; /* This data is processed */ while (n >= 0) { blinkLED(); n--; } } } return 0; /* Never reached */ }