/* Name: main.c * Author: Toshi Nagata * Copyright: 2009-2018 (c) Toshi Nagata * License: Modified BSD License * * Clock: Internal 1.2MHz * Pin 7 (PB2): motor polarity (old: Pin 3) * Pin 5/6 (PB0/1): motor1/2 PWM control * Pin 3/7 (PB4/2): motor1/2 Phase control * Pin 2 (PB3): IR receiver (old: Pin 6 and 7) */ #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; signed char current1, current2; /* Masks for pin-change interrupt */ #define MASK1 0b00001000 #if 0 /* For debug */ void show_data(unsigned short data) { char i; cli(); for (i = 0; i < 16; i++) { if (data & 1) { PORTB |= 0b00000100; } else { PORTB |= 0b00010000; } _delay_ms(800); PORTB &= 0b11101011; _delay_ms(200); data >>= 1; } sei(); } #endif ISR(TIM0_OVF_vect) { if (tempCount < 0) return; /* Wait until the first trigger is detected */ t++; if (t >= T2 / 256) { /* No input for certain period (longer than GAP) */ data = 0; /* Indicate the button is released */ ready = 1; tempCount = -1; /* Start over */ oldData = 0; } else if (t >= T1 / 256) { /* Short interval is detected */ tempCount = NBITS; /* Start over (the key may be still pressed) */ } } ISR(PCINT0_vect) { unsigned char pin; pin = PINB & MASK1; if (last_pin && pin == 0) { /* Falling edge */ if (tempCount < 0 || tempCount >= NBITS) { /* Start data collection */ tempData = 0; tempCount = 0; } else { /* Read out one more bit */ unsigned short dt = t * 256 + (unsigned char)TCNT0 - start; if (dt >= T0) tempData |= (1 << tempCount); tempCount++; /* Are all bits received? */ if (tempCount == NBITS) { if ((tempData & 0x1f) == 0x10) { if (tempData & 0x4000) tempData ^= 0x7fe0; if (tempData == oldData) { data = tempData; ready = 1; /* Valid data */ } else oldData = tempData; } tempCount = NBITS; /* One data complete; acquisition will continue after the GAP */ } } t = 0; start = (unsigned char)TCNT0; } last_pin = pin; } void setCurrent(void) { unsigned char c; if (current1 < 0) { PORTB |= 0b00010000; c = 255 + current1 + current1; } else { PORTB &= 0b11101111; c = 255 - current1 - current1; } OCR0A = c; if (current2 < 0) { PORTB |= 0b00000100; c = 255 + current2 + current2; } else { PORTB &= 0b11111011; c = 255 - current2 - current2; } OCR0B = c; } int main(void) { cli(); /* Disable interrupt globally */ DDRB = 0b00010111; /* PB0/1/2/4 (pins 5/6/7/3) for output */ TCCR0A = 0b11110000 /* PWM: set OC0A/OC0B on compare match and clear at TOP */ | 0b00000011; /* Fast PWM */ TCCR0B = 0b00000000 /* Fast PWM with TOP = MAX */ | 0b00000010; /* Clock prescale by 8 */ current1 = current2 = 0; OCR0B = 255; /* PWM duty ratio */ OCR0A = 255; TIMSK0 = 0b00000010; /* Timer0 overflow interrupt enabled */ GIMSK = 0b00100000; /* PCINT enabled */ PCMSK = MASK1; /* PCINT3 (pin 2) enabled */ tempCount = -1; t = start = 0; lastData = 0; last_pin = MASK1; sei(); /* Enable interrupt globally */ for (;;) { unsigned short d; signed char m1, c1, c2, dc1, dc2; if (!ready) continue; if (data == 0 && current1 == 0 && current2 == 0) continue; ready = 0; /* Data is available */ d = data >> 5; if (d & 1) { /* Forward */ if (d & 8) { /* Forward + Backward = no move */ m1 = 0; } else m1 = 1; } else if (d & 8) { m1 = -1; } else m1 = 0; switch (d & 0b0110) { case 0b0010: /* left */ c1 = m1 * 64; c2 = m1 * 120; break; case 0b0100: /* right */ c1 = m1 * 120; c2 = m1 * 64; break; default: /* straight */ c1 = c2 = m1 * 120; break; } if (c1 > current1) dc1 = 8; else if (c1 < current1) dc1 = -8; else dc1 = 0; if (c2 > current2) dc2 = 8; else if (c2 < current2) dc2 = -8; else dc2 = 0; while (c1 != current1 || c2 != current2) { if (c1 != current1) current1 += dc1; if (c2 != current2) current2 += dc2; setCurrent(); _delay_ms(10); } } return 0; /* Never reached */ }