#define __AVR_ATtiny2313__ 1 #define F_CPU 128000 #include "avr/io.h" #include "avr/interrupt.h" #include "avr/sleep.h" #include "util/delay.h" #define RD_PIN 0b00001 #define POS_PIN 0b00010 #define FD_PIN 0b00100 #define UP_PIN 0b01000 #define DOWN_PIN 0b10000 #define FRONT_UP_PIN 0b00001 #define FRONT_DOWN_PIN 0b00010 struct shift { char fd; char rd; }; /* * Multi-dimensional shift state array. * Indexed with shift direction, FD position, RD position. Results are * shift direction and count for the fd and rd. */ struct shift state[2][2][10] = { /* Up Shift */ { /* Little ring */ { {0, -1}, {0, -1}, {1, 2}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1} }, /* Big ring */ { {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1}, {0, -1} }, }, /* Down */ { /* Little ring */ { {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, }, /* Big ring */ { {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {-1, -2}, {0, 1}, {0, 1} }, } }; int fd = 1; /* Start in highest gear ratio. */ int rd = 0; int automode = 1; /* Start in auto mode. */ volatile int timeouts; ISR(WDT_OVERFLOW_vect) { if (timeouts) timeouts--; } /* * Sleep for approximately cnt * 32ms. This puts the cpu in a low power * state when we're sleeping for long periods. This uses the watchdog * timeout facility. */ void wdt_sleep(int cnt) { timeouts = cnt; sleep_enable(); while (timeouts) sleep_mode(); sleep_disable(); } void PORT_Init() { /* Disable Analog Comparator to save power. */ ACSR = ACD; /* * Enable watchdog with a 32ms interval. */ MCUSR &= ~(1 << WDRF); WDTCSR |= (1 << WDCE); WDTCSR = (1 << WDIE) | (1 << WDP0); sei(); /* Enable interrupts. */ set_sleep_mode(SLEEP_MODE_PWR_DOWN); /* * Configure ports. */ PORTB = 0b00000000; DDRB = FD_PIN | RD_PIN; /* Derailleur set to output. */ DDRD = 0b00000000; /* Turn on internal pull-ups. */ PORTD |= FRONT_UP_PIN | FRONT_DOWN_PIN; PORTB |= UP_PIN | DOWN_PIN; } static inline int button_up(void) { if (PINB & UP_PIN) return (0); return (1); } static inline int button_down(void) { if (PINB & DOWN_PIN) return (0); return (1); } static inline int button_both(void) { if ((PINB & (UP_PIN | DOWN_PIN)) == 0) return (1); return (0); } static inline int button_any(void) { if ((PINB & (UP_PIN | DOWN_PIN)) != (UP_PIN | DOWN_PIN)) return (1); return (0); } static inline int front_button_up(void) { if (PIND & FRONT_UP_PIN) return (0); return (1); } static inline int front_button_down(void) { if (PIND & FRONT_DOWN_PIN) return (0); return (1); } static inline int front_button_both(void) { if ((PIND & (FRONT_UP_PIN | FRONT_DOWN_PIN)) == 0) return (1); return (0); } static inline int front_button_any(void) { if ((PIND & (FRONT_UP_PIN | FRONT_DOWN_PIN)) != (FRONT_UP_PIN | FRONT_DOWN_PIN)) return (1); return (0); } static inline void hold(int line) { PORTB |= line; _delay_ms(40); PORTB &= ~line; _delay_ms(40); } static inline void pulse(int line) { int i; for (i = 0; i < 10; i++) { PORTB |= line; _delay_ms(2); PORTB &= ~line; _delay_ms(2); } _delay_ms(40); } static inline void upshift_fd(void) { if (fd > 0) fd--; hold(FD_PIN); } static inline void upshift_rd(void) { if (rd > 0) rd--; pulse(RD_PIN); } static inline void downshift_fd(void) { if (fd < 1) fd++; pulse(FD_PIN); } static inline void downshift_rd(void) { if (rd < 9) rd++; hold(RD_PIN); } void shift(struct shift *s) { int rd; if (s->fd < 0) upshift_fd(); else if (s->fd > 0) downshift_fd(); if (s->rd < 0) for (rd = 0; rd < -s->rd; rd++) upshift_rd(); else for (rd = 0; rd < s->rd; rd++) downshift_rd(); } int main() { PORT_Init(); while (1) { /* Wait for a button press. */ while (front_button_any() + button_any() == 0) wdt_sleep(1); if (automode) { /* * If the user presses any front button, terminate * auto mode and execute the requested action. */ if (front_button_any()) { automode = 0; continue; } else if (button_up()) shift(&state[0][fd][rd]); else if (button_down()) shift(&state[1][fd][rd]); } else { if (front_button_both()) automode = 1; else if (button_up()) upshift_rd(); else if (button_down()) downshift_rd(); else if (front_button_up()) upshift_fd(); else if (front_button_down()) downshift_fd(); } /* * Wait longer before allowing another shift to prevent * dropped chains or double-shifting. This also controls * the rate of multi-shifting. */ wdt_sleep(3); } }