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@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@2: #define RPS_TO_KMH(x) ((36 * WHEEL_PERIMETER * (x)) / 1000) nuclear@2: nuclear@2: #define DEBOUNCE_TICKS 1 nuclear@2: nuclear@2: #define nop() asm volatile("nop") nuclear@2: nuclear@2: void init_timer(void); nuclear@0: void latch(int n); nuclear@0: void update_display(void); 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@2: unsigned long speed_rps; nuclear@0: nuclear@0: /* wheel perimeter in centimeters */ nuclear@2: #define WHEEL_PERIMETER 220 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@2: update_display(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@2: prev_time = ticks; nuclear@2: } nuclear@2: nuclear@2: ISR(TIMER0_OVF_vect) nuclear@2: { nuclear@2: volatile static int sec_ticks; nuclear@2: volatile static unsigned long last_rot; nuclear@2: nuclear@2: ticks++; nuclear@2: sec_ticks++; nuclear@2: nuclear@2: if(sec_ticks >= SEC_TICKS) { nuclear@2: speed_rps = nrot - last_rot; nuclear@2: nuclear@2: update_display(); nuclear@2: nuclear@2: sec_ticks = 0; nuclear@2: last_rot = 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@0: void update_display(void) nuclear@0: { nuclear@2: if(debug_mode) { nuclear@2: disp_dist(nrot); nuclear@2: disp_speed(speed_rps); 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@2: int speed_kmh = RPS_TO_KMH(speed_rps); 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: }