rev |
line source |
nuclear@0
|
1 /* PORT USAGE
|
nuclear@0
|
2 *
|
nuclear@0
|
3 * D0-D3: data bus, connected to the D0-D3 pins of all the 4511 chips
|
nuclear@0
|
4 * C0-C5,D4,D5: directly driving the 8 speed graph LEDs
|
nuclear@0
|
5 * B0-B2: connected to the decoder controlling the 4511 latches as such:
|
nuclear@0
|
6 * 000 - speed most significant
|
nuclear@0
|
7 * 001 - speed least significant
|
nuclear@0
|
8 * 010 - dist most significant
|
nuclear@0
|
9 * 011 - dist middle
|
nuclear@0
|
10 * 100 - dist least significant
|
nuclear@0
|
11 * 101 - all latches are off (high)
|
nuclear@0
|
12 * B3-B5: ISP. B4 also acts as generic pushbutton input
|
nuclear@0
|
13 * B6,B7: XTAL
|
nuclear@0
|
14 * D6,D7: comparator, hall effect sensor input and threshold trimpot.
|
nuclear@0
|
15 */
|
nuclear@0
|
16
|
nuclear@0
|
17 /* 16mhz resonator */
|
nuclear@2
|
18 #define F_CPU 16000000
|
nuclear@0
|
19
|
nuclear@0
|
20 #include <avr/io.h>
|
nuclear@0
|
21 #include <avr/interrupt.h>
|
nuclear@2
|
22 #include <avr/power.h>
|
nuclear@0
|
23 #include <util/delay.h>
|
nuclear@0
|
24
|
nuclear@3
|
25
|
nuclear@2
|
26 #define SEC_TICKS (F_CPU / 256 / 256)
|
nuclear@2
|
27
|
nuclear@2
|
28 #define PRESCL_1 1
|
nuclear@2
|
29 #define PRESCL_8 2
|
nuclear@2
|
30 #define PRESCL_64 3
|
nuclear@2
|
31 #define PRESCL_256 4
|
nuclear@2
|
32 #define PRESCL_1024 5
|
nuclear@2
|
33
|
nuclear@2
|
34
|
nuclear@2
|
35 #define PB_CSEL_MASK 0x7
|
nuclear@2
|
36 #define PD_DATA_MASK 0xf
|
nuclear@2
|
37 #define PD_LEDS_MASK 0xc0
|
nuclear@2
|
38
|
nuclear@4
|
39 /* 1800 2sec intervals per hour, \times perimeter, \times x, \over 100000 cm in a kilometer */
|
nuclear@4
|
40 #define RP2S_TO_KMH(x) ((18 * WHEEL_PERIMETER * (x)) / 1000)
|
nuclear@2
|
41
|
nuclear@2
|
42 #define DEBOUNCE_TICKS 1
|
nuclear@2
|
43
|
nuclear@3
|
44
|
nuclear@3
|
45 /* wheel perimeter in centimeters */
|
nuclear@3
|
46 #define WHEEL_PERIMETER 194
|
nuclear@3
|
47
|
nuclear@2
|
48 #define nop() asm volatile("nop")
|
nuclear@2
|
49
|
nuclear@3
|
50
|
nuclear@2
|
51 void init_timer(void);
|
nuclear@0
|
52 void latch(int n);
|
nuclear@4
|
53 void update_display(int idx);
|
nuclear@2
|
54 void disp_speed(int n);
|
nuclear@2
|
55 void disp_dist(int n);
|
nuclear@2
|
56 void disp_leds(int n);
|
nuclear@0
|
57
|
nuclear@0
|
58 int state;
|
nuclear@2
|
59 int debug_mode = 0;
|
nuclear@2
|
60
|
nuclear@2
|
61 volatile long ticks;
|
nuclear@0
|
62
|
nuclear@0
|
63 unsigned long nrot;
|
nuclear@4
|
64 unsigned long speed_rp2s;
|
nuclear@0
|
65
|
nuclear@0
|
66 int main(void)
|
nuclear@0
|
67 {
|
nuclear@0
|
68 int i;
|
nuclear@0
|
69
|
nuclear@0
|
70 DDRB = 0xf; /* only first four bits are outputs */
|
nuclear@0
|
71 DDRC = 0xff; /* speed leds 6 bits */
|
nuclear@2
|
72 DDRD = 0x3f; /* 4bits data bus, 2 bits speed leds, 2 bits comparator (input) */
|
nuclear@0
|
73
|
nuclear@0
|
74 /* zero-out the displays and disable all pullups except for the reset pin */
|
nuclear@0
|
75 PORTB = 0;
|
nuclear@0
|
76 PORTC = 0x40; /* 6th bit set, C6 is reset */
|
nuclear@0
|
77 PORTD = 0;
|
nuclear@0
|
78
|
nuclear@0
|
79 for(i=0; i<5; i++) {
|
nuclear@0
|
80 latch(i);
|
nuclear@0
|
81 }
|
nuclear@0
|
82
|
nuclear@2
|
83 /* if the button at B4 is pressed during startup, enter debug mode */
|
nuclear@2
|
84 if((PINB >> 4) & 1) {
|
nuclear@2
|
85 debug_mode = 1;
|
nuclear@2
|
86 PORTC |= 3 << 4;
|
nuclear@2
|
87 }
|
nuclear@2
|
88
|
nuclear@2
|
89 init_timer();
|
nuclear@2
|
90
|
nuclear@2
|
91 /* disable digital input buffer for the AINn pins */
|
nuclear@2
|
92 DIDR1 = 0;
|
nuclear@2
|
93
|
nuclear@0
|
94 /* read initial comparator state */
|
nuclear@0
|
95 state = (ACSR >> ACO) & 1;
|
nuclear@0
|
96
|
nuclear@0
|
97 /* in debug mode we want an interrupt both on rising and falling edge
|
nuclear@0
|
98 * to be able to blink leds when the wheel goes past. otherwise we only need
|
nuclear@0
|
99 * a rising edge interrupt to increment the rotation counter.
|
nuclear@0
|
100 */
|
nuclear@2
|
101 /* rising edge interrupt */
|
nuclear@2
|
102 /*ACSR |= (2 << ACIS0);*/
|
nuclear@2
|
103
|
nuclear@2
|
104 /* comparator interrupt enable */
|
nuclear@2
|
105 ACSR |= (1 << ACIE);
|
nuclear@0
|
106
|
nuclear@0
|
107 sei();
|
nuclear@0
|
108
|
nuclear@2
|
109 for(;;) {
|
nuclear@2
|
110 }
|
nuclear@0
|
111 return 0;
|
nuclear@0
|
112 }
|
nuclear@0
|
113
|
nuclear@2
|
114 void init_timer(void)
|
nuclear@2
|
115 {
|
nuclear@2
|
116 power_timer0_enable();
|
nuclear@2
|
117
|
nuclear@2
|
118 TCCR0A = 0;
|
nuclear@2
|
119 TCCR0B = PRESCL_256;
|
nuclear@2
|
120
|
nuclear@2
|
121 /* enable timer overflow interrupt */
|
nuclear@2
|
122 TIMSK0 = 1;
|
nuclear@2
|
123 }
|
nuclear@2
|
124
|
nuclear@0
|
125 void latch(int n)
|
nuclear@0
|
126 {
|
nuclear@2
|
127 unsigned char upper = PORTB & ~PB_CSEL_MASK;
|
nuclear@0
|
128 /* pull latch low */
|
nuclear@0
|
129 PORTB = upper | n;
|
nuclear@0
|
130 asm volatile("nop");
|
nuclear@0
|
131 /* pull all latches high again */
|
nuclear@0
|
132 PORTB = upper | 5;
|
nuclear@0
|
133 }
|
nuclear@0
|
134
|
nuclear@0
|
135 ISR(ANALOG_COMP_vect)
|
nuclear@0
|
136 {
|
nuclear@2
|
137 volatile static long prev_time;
|
nuclear@2
|
138 unsigned char aco;
|
nuclear@2
|
139
|
nuclear@2
|
140 nop();
|
nuclear@2
|
141 nop();
|
nuclear@2
|
142 nop();
|
nuclear@2
|
143
|
nuclear@2
|
144 aco = (ACSR >> ACO) & 1;
|
nuclear@2
|
145
|
nuclear@2
|
146 if(aco != state && (ticks - prev_time) > DEBOUNCE_TICKS) {
|
nuclear@2
|
147 state = aco;
|
nuclear@0
|
148
|
nuclear@0
|
149 if(state) {
|
nuclear@0
|
150 nrot++;
|
nuclear@2
|
151
|
nuclear@4
|
152 /*update_display();*/
|
nuclear@0
|
153 }
|
nuclear@4
|
154 prev_time = ticks;
|
nuclear@0
|
155 }
|
nuclear@2
|
156 }
|
nuclear@2
|
157
|
nuclear@2
|
158 ISR(TIMER0_OVF_vect)
|
nuclear@2
|
159 {
|
nuclear@4
|
160 volatile static int sec_ticks, sec;
|
nuclear@4
|
161 volatile static unsigned long last_rot[4];
|
nuclear@2
|
162
|
nuclear@2
|
163 ticks++;
|
nuclear@2
|
164 sec_ticks++;
|
nuclear@2
|
165
|
nuclear@4
|
166 if(sec_ticks >= SEC_TICKS / 2) {
|
nuclear@4
|
167 int idx = ++sec & 3;
|
nuclear@2
|
168
|
nuclear@4
|
169 speed_rp2s = nrot - last_rot[idx];
|
nuclear@4
|
170
|
nuclear@4
|
171 update_display(idx);
|
nuclear@2
|
172
|
nuclear@2
|
173 sec_ticks = 0;
|
nuclear@4
|
174 last_rot[idx] = nrot;
|
nuclear@2
|
175 }
|
nuclear@2
|
176
|
nuclear@2
|
177
|
nuclear@2
|
178 if(debug_mode) {
|
nuclear@2
|
179 unsigned char state = (ACSR >> ACO) & 1;
|
nuclear@2
|
180 if(state) {
|
nuclear@2
|
181 PORTD |= (1 << 5);
|
nuclear@2
|
182 } else {
|
nuclear@2
|
183 PORTD &= ~(1 << 5);
|
nuclear@2
|
184 }
|
nuclear@2
|
185 }
|
nuclear@0
|
186 }
|
nuclear@0
|
187
|
nuclear@4
|
188 void update_display(int idx)
|
nuclear@0
|
189 {
|
nuclear@2
|
190 if(debug_mode) {
|
nuclear@2
|
191 disp_dist(nrot);
|
nuclear@4
|
192 disp_speed(speed_rp2s);
|
nuclear@0
|
193
|
nuclear@2
|
194 if(state) {
|
nuclear@2
|
195 PORTD |= (1 << 5);
|
nuclear@2
|
196 } else {
|
nuclear@2
|
197 PORTD &= ~(1 << 5);
|
nuclear@2
|
198 }
|
nuclear@2
|
199 } else {
|
nuclear@2
|
200 unsigned long dist_cm = (nrot * WHEEL_PERIMETER);
|
nuclear@4
|
201 int speed_kmh = RP2S_TO_KMH(speed_rp2s);
|
nuclear@2
|
202
|
nuclear@2
|
203 disp_dist(dist_cm / 10000);
|
nuclear@2
|
204 disp_speed(speed_kmh);
|
nuclear@2
|
205 disp_leds(speed_kmh * 8);
|
nuclear@2
|
206 }
|
nuclear@0
|
207 }
|
nuclear@2
|
208
|
nuclear@2
|
209 void disp_speed(int n)
|
nuclear@2
|
210 {
|
nuclear@2
|
211 PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10);
|
nuclear@2
|
212 latch(1);
|
nuclear@2
|
213 n /= 10;
|
nuclear@2
|
214 PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10);
|
nuclear@2
|
215 latch(0);
|
nuclear@2
|
216 }
|
nuclear@2
|
217
|
nuclear@2
|
218 void disp_dist(int n)
|
nuclear@2
|
219 {
|
nuclear@2
|
220 PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10);
|
nuclear@2
|
221 latch(4);
|
nuclear@2
|
222 n /= 10;
|
nuclear@2
|
223 PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10);
|
nuclear@2
|
224 latch(3);
|
nuclear@2
|
225 n /= 10;
|
nuclear@2
|
226 PORTD = (PORTD & ~PD_DATA_MASK) | (n % 10);
|
nuclear@2
|
227 latch(2);
|
nuclear@2
|
228 }
|
nuclear@2
|
229
|
nuclear@2
|
230 void disp_leds(int n)
|
nuclear@2
|
231 {
|
nuclear@2
|
232 int i, count, bits;
|
nuclear@2
|
233
|
nuclear@2
|
234 count = (n + 4) >> 5;
|
nuclear@2
|
235 bits = 0;
|
nuclear@2
|
236
|
nuclear@2
|
237 for(i=0; i<count; i++) {
|
nuclear@2
|
238 bits |= (1 << i);
|
nuclear@2
|
239 }
|
nuclear@2
|
240
|
nuclear@2
|
241 PORTC = bits;
|
nuclear@2
|
242 PORTD = (PORTD & PD_DATA_MASK) | ((bits & PD_LEDS_MASK) >> 2);
|
nuclear@2
|
243 }
|