/* Name: main.c * Author: Toshi Nagata * Copyright: 2009-2018 (c) Toshi Nagata * License: Modified BSD License * * Clock: Internal 1.2MHz * Pin 5 (PB0): motor polarity (old: Pin 3) * Pin 6 (PB1): motor PWM control (old: Pin 5) * Pin 2, 3 (PB3, 4): IR receiver (old: Pin 6 and 7) * Pin 7 (PB2): LED */ #include #include #include #define NBITS 15 #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 */ #define T3 ((short)(200000 / CL)) /* minimum interval between 2 pushes */ volatile unsigned short t, start; volatile unsigned short tempData, oldData, data, lastData; volatile char tempCount; volatile char ready; volatile unsigned char last_pin = 1; char current; /* Masks for pin-change interrupt */ #define MASK1 0b00001000 #define MASK2 0b00010000 #if 0 /* For debug */ void show_data(unsigned short data) { char i; cli(); for (i = 0; i < 16; i++) { if (data & 1) { PORTB |= 1; } else { PORTB |= 0b00000100; } _delay_ms(800); PORTB &= 0b11111010; _delay_ms(200); data >>= 1; } sei(); } #endif ISR(TIM0_OVF_vect) { if (tempCount < 0) return; t++; if (t >= T2 / 256) { /* Long interval is detected */ /* Start waiting for the first interrupt */ /* (The incomplete data is discarded if present) */ if (t >= T3 / 256) { /* The button is released */ lastData = 0; } tempCount = -1; oldData = 0; start = t = 0; } else if (t >= T1 / 256) { /* 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) == 0x10) { if (tempData & 0x4000) tempData ^= 0x7fe0; if (tempData == oldData) { data = tempData; ready = 1; /* Valid data */ tempCount = NBITS + 1; /* Skip until the end of chunk */ } oldData = tempData; } start = t = 0; } } } ISR(PCINT0_vect) { unsigned char pin; if ((PINB & (MASK1 | MASK2)) == (MASK1 | MASK2)) { pin = 1; } else pin = 0; if (last_pin && pin == 0) { /* Falling edge */ if (tempCount < 0) { /* Start data collection */ tempData = 0; tempCount = 0; } else if (tempCount < NBITS) { /* Read out one more bit */ unsigned short dt = t * 256 + (unsigned char)TCNT0 - start; if (dt >= T0) tempData |= (1 << tempCount); tempCount++; } t = 0; start = (unsigned char)TCNT0; } last_pin = pin; } void setCurrent(void) { if (current < 0) { if ((PORTB & 0b00000001) == 0) { PORTB |= 0b00000001; _delay_ms(50); } OCR0B = 255 + current * 2; } else { if ((PORTB & 0b00000001) != 0) { PORTB &= 0b11111110; _delay_ms(50); } OCR0B = 255 - current * 2; } } int main(void) { cli(); /* Disable interrupt globally */ DDRB = 0b00000111; /* PB0/1/2 (pins 5/6/7) for output */ TCCR0A = 0b00110000 /* PWM: set OC0B on compare match and clear at TOP */ | 0b00000011; /* Fast PWM */ TCCR0B = 0b00000000 /* Fast PWM with TOP = MAX */ | 0b00000010; /* Clock prescale by 8 */ current = 0; OCR0B = 255; /* PWM duty ratio */ TIMSK0 = 0b00000010; /* Timer0 overflow interrupt enabled */ GIMSK = 0b00100000; /* PCINT enabled */ PCMSK = (MASK1 | MASK2); /* PCINT3/4 (pins 2/3) enabled */ tempCount = -1; t = start = 0; lastData = 0; sei(); /* Enable interrupt globally */ for (;;) { char c1; if (!ready) continue; ready = 0; if (data == lastData) continue; /* Data is available */ switch (data >> 5) { case 0b1000010: /* button 1 = forward */ c1 = 80; break; case 0b1000011: /* button 2 = backward */ c1 = -80; break; case 0b1000100: /* button 3 = stop */ c1 = 0; break; default: c1 = -127; /* No valid button */ break; } lastData = data; ready = 0; /* This data is processed */ if (c1 != -127) { PORTB |= 0b00000100; if (c1 != current) { do { if (c1 < current) current--; else current++; setCurrent(); _delay_ms(10); } while (c1 != current); } } else { PORTB &= 0b11111011; } } return 0; /* Never reached */ }