nuclear@0: /* PORT USAGE nuclear@0: * nuclear@0: * D0-D3: data bus, connected to the D0-D3 pins of all the 4511 chips nuclear@0: * C0-C5,D4,D5: directly driving the 8 speed graph LEDs nuclear@0: * B0-B2: connected to the decoder controlling the 4511 latches as such: nuclear@0: * 000 - speed most significant nuclear@0: * 001 - speed least significant nuclear@0: * 010 - dist most significant nuclear@0: * 011 - dist middle nuclear@0: * 100 - dist least significant nuclear@0: * 101 - all latches are off (high) nuclear@0: * B3-B5: ISP. B4 also acts as generic pushbutton input nuclear@0: * B6,B7: XTAL nuclear@0: * D6,D7: comparator, hall effect sensor input and threshold trimpot. nuclear@0: */ nuclear@0: nuclear@0: /* 16mhz resonator */ nuclear@2: #define F_CPU 16000000 nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@2: #include nuclear@0: #include nuclear@0: nuclear@3: nuclear@2: #define SEC_TICKS (F_CPU / 256 / 256) nuclear@2: nuclear@2: #define PRESCL_1 1 nuclear@2: #define PRESCL_8 2 nuclear@2: #define PRESCL_64 3 nuclear@2: #define PRESCL_256 4 nuclear@2: #define PRESCL_1024 5 nuclear@2: nuclear@2: nuclear@2: #define PB_CSEL_MASK 0x7 nuclear@2: #define PD_DATA_MASK 0xf nuclear@2: #define PD_LEDS_MASK 0xc0 nuclear@2: nuclear@4: /* 1800 2sec intervals per hour, \times perimeter, \times x, \over 100000 cm in a kilometer */ nuclear@4: #define RP2S_TO_KMH(x) ((18 * WHEEL_PERIMETER * (x)) / 1000) nuclear@2: nuclear@2: #define DEBOUNCE_TICKS 1 nuclear@2: nuclear@3: nuclear@3: /* wheel perimeter in centimeters */ nuclear@3: #define WHEEL_PERIMETER 194 nuclear@3: nuclear@2: #define nop() asm volatile("nop") nuclear@2: nuclear@3: nuclear@2: void init_timer(void); nuclear@0: void latch(int n); nuclear@4: void update_display(int idx); nuclear@2: void disp_speed(int n); nuclear@2: void disp_dist(int n); nuclear@2: void disp_leds(int n); nuclear@0: nuclear@0: int state; nuclear@2: int debug_mode = 0; nuclear@2: nuclear@2: volatile long ticks; nuclear@0: nuclear@0: unsigned long nrot; nuclear@4: unsigned long speed_rp2s; nuclear@0: nuclear@0: int main(void) nuclear@0: { nuclear@0: int i; nuclear@0: nuclear@0: DDRB = 0xf; /* only first four bits are outputs */ nuclear@0: DDRC = 0xff; /* speed leds 6 bits */ nuclear@2: DDRD = 0x3f; /* 4bits data bus, 2 bits speed leds, 2 bits comparator (input) */ nuclear@0: nuclear@0: /* zero-out the displays and disable all pullups except for the reset pin */ nuclear@0: PORTB = 0; nuclear@0: PORTC = 0x40; /* 6th bit set, C6 is reset */ nuclear@0: PORTD = 0; nuclear@0: nuclear@0: for(i=0; i<5; i++) { nuclear@0: latch(i); nuclear@0: } nuclear@0: nuclear@2: /* if the button at B4 is pressed during startup, enter debug mode */ nuclear@2: if((PINB >> 4) & 1) { nuclear@2: debug_mode = 1; nuclear@2: PORTC |= 3 << 4; nuclear@2: } nuclear@2: nuclear@2: init_timer(); nuclear@2: nuclear@2: /* disable digital input buffer for the AINn pins */ nuclear@2: DIDR1 = 0; nuclear@2: nuclear@0: /* read initial comparator state */ nuclear@0: state = (ACSR >> ACO) & 1; nuclear@0: nuclear@0: /* in debug mode we want an interrupt both on rising and falling edge nuclear@0: * to be able to blink leds when the wheel goes past. otherwise we only need nuclear@0: * a rising edge interrupt to increment the rotation counter. nuclear@0: */ nuclear@2: /* rising edge interrupt */ nuclear@2: /*ACSR |= (2 << ACIS0);*/ nuclear@2: nuclear@2: /* comparator interrupt enable */ nuclear@2: ACSR |= (1 << ACIE); nuclear@0: nuclear@0: sei(); nuclear@0: nuclear@2: for(;;) { nuclear@2: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@2: void init_timer(void) nuclear@2: { nuclear@2: power_timer0_enable(); nuclear@2: nuclear@2: TCCR0A = 0; nuclear@2: TCCR0B = PRESCL_256; nuclear@2: nuclear@2: /* enable timer overflow interrupt */ nuclear@2: TIMSK0 = 1; nuclear@2: } nuclear@2: nuclear@0: void latch(int n) nuclear@0: { nuclear@2: unsigned char upper = PORTB & ~PB_CSEL_MASK; nuclear@0: /* pull latch low */ nuclear@0: PORTB = upper | n; nuclear@0: asm volatile("nop"); nuclear@0: /* pull all latches high again */ nuclear@0: PORTB = upper | 5; nuclear@0: } nuclear@0: nuclear@0: ISR(ANALOG_COMP_vect) nuclear@0: { nuclear@2: volatile static long prev_time; nuclear@2: unsigned char aco; nuclear@2: nuclear@2: nop(); nuclear@2: nop(); nuclear@2: nop(); nuclear@2: nuclear@2: aco = (ACSR >> ACO) & 1; nuclear@2: nuclear@2: if(aco != state && (ticks - prev_time) > DEBOUNCE_TICKS) { nuclear@2: state = aco; nuclear@0: nuclear@0: if(state) { nuclear@0: nrot++; nuclear@2: nuclear@4: /*update_display();*/ nuclear@0: } nuclear@4: prev_time = ticks; nuclear@0: } nuclear@2: } nuclear@2: nuclear@2: ISR(TIMER0_OVF_vect) nuclear@2: { nuclear@4: volatile static int sec_ticks, sec; nuclear@4: volatile static unsigned long last_rot[4]; nuclear@2: nuclear@2: ticks++; nuclear@2: sec_ticks++; nuclear@2: nuclear@4: if(sec_ticks >= SEC_TICKS / 2) { nuclear@4: int idx = ++sec & 3; nuclear@2: nuclear@4: speed_rp2s = nrot - last_rot[idx]; nuclear@4: nuclear@4: update_display(idx); nuclear@2: nuclear@2: sec_ticks = 0; nuclear@4: last_rot[idx] = nrot; nuclear@2: } nuclear@2: nuclear@2: nuclear@2: if(debug_mode) { nuclear@2: unsigned char state = (ACSR >> ACO) & 1; nuclear@2: if(state) { nuclear@2: PORTD |= (1 << 5); nuclear@2: } else { nuclear@2: PORTD &= ~(1 << 5); nuclear@2: } nuclear@2: } nuclear@0: } nuclear@0: nuclear@4: void update_display(int idx) nuclear@0: { nuclear@2: if(debug_mode) { nuclear@2: disp_dist(nrot); nuclear@4: disp_speed(speed_rp2s); nuclear@0: nuclear@2: if(state) { nuclear@2: PORTD |= (1 << 5); nuclear@2: } else { nuclear@2: PORTD &= ~(1 << 5); nuclear@2: } nuclear@2: } else { nuclear@2: unsigned long dist_cm = (nrot * WHEEL_PERIMETER); nuclear@4: int speed_kmh = RP2S_TO_KMH(speed_rp2s); nuclear@2: nuclear@2: disp_dist(dist_cm / 10000); nuclear@2: disp_speed(speed_kmh); nuclear@2: disp_leds(speed_kmh * 8); nuclear@2: } nuclear@0: } nuclear@2: nuclear@2: void disp_speed(int n) nuclear@2: { nuclear@2: PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10); nuclear@2: latch(1); nuclear@2: n /= 10; nuclear@2: PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10); nuclear@2: latch(0); nuclear@2: } nuclear@2: nuclear@2: void disp_dist(int n) nuclear@2: { nuclear@2: PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10); nuclear@2: latch(4); nuclear@2: n /= 10; nuclear@2: PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10); nuclear@2: latch(3); nuclear@2: n /= 10; nuclear@2: PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10); nuclear@2: latch(2); nuclear@2: } nuclear@2: nuclear@2: void disp_leds(int n) nuclear@2: { nuclear@2: int i, count, bits; nuclear@2: nuclear@2: count = (n + 4) >> 5; nuclear@2: bits = 0; nuclear@2: nuclear@2: for(i=0; i> 2); nuclear@2: }