SHOW:
|
|
- or go back to the newest paste.
1 | /* | |
2 | * BLF EE A6 firmware (special-edition group buy light) | |
3 | * This light uses a FET+1 style driver, with a FET on the main PWM channel | |
4 | * for the brightest high modes and a single 7135 chip on the secondary PWM | |
5 | * channel so we can get stable, efficient low / medium modes. It also | |
6 | * includes a capacitor for measuring off time. | |
7 | * | |
8 | * Copyright (C) 2015 Selene Scriven | |
9 | * | |
10 | * This program is free software: you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation, either version 3 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | * | |
23 | * | |
24 | * NANJG 105C Diagram | |
25 | * --- | |
26 | * -| |- VCC | |
27 | * OTC -| |- Voltage ADC | |
28 | * Star 3 -| |- PWM (FET) | |
29 | * GND -| |- PWM (1x7135) | |
30 | * --- | |
31 | * | |
32 | * FUSES | |
33 | * I use these fuse settings | |
34 | * Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM) | |
35 | * High: 0xfd (to enable brownout detection) | |
36 | * | |
37 | * For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency | |
38 | * | |
39 | * STARS | |
40 | * Star 2 = second PWM output channel | |
41 | * Star 3 = mode memory if soldered, no memory by default | |
42 | * Star 4 = Capacitor for off-time | |
43 | * | |
44 | * VOLTAGE | |
45 | * Resistor values for voltage divider (reference BLF-VLD README for more info) | |
46 | * Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate | |
47 | * | |
48 | * VCC | |
49 | * | | |
50 | * Vd (~.25 v drop from protection diode) | |
51 | * | | |
52 | * 1912 (R1 19,100 ohms) | |
53 | * | | |
54 | * |---- PB2 from MCU | |
55 | * | | |
56 | * 4701 (R2 4,700 ohms) | |
57 | * | | |
58 | * GND | |
59 | * | |
60 | * To find out what values to use, flash the driver with battcheck.hex | |
61 | * and hook the light up to each voltage you need a value for. This is | |
62 | * much more reliable than attempting to calculate the values from a | |
63 | * theoretical formula. | |
64 | * | |
65 | * Same for off-time capacitor values. Measure, don't guess. | |
66 | */ | |
67 | #define F_CPU 4800000UL | |
68 | ||
69 | /* | |
70 | * ========================================================================= | |
71 | * Settings to modify per driver | |
72 | */ | |
73 | ||
74 | //#define FAST 0x23 // fast PWM channel 1 only | |
75 | //#define PHASE 0x21 // phase-correct PWM channel 1 only | |
76 | #define FAST 0xA3 // fast PWM both channels | |
77 | #define PHASE 0xA1 // phase-correct PWM both channels | |
78 | ||
79 | #define VOLTAGE_MON // Comment out to disable LVP | |
80 | #define OWN_DELAY // Should we use the built-in delay or our own? | |
81 | // Adjust the timing per-driver, since the hardware has high variance | |
82 | // Higher values will run slower, lower values run faster. | |
83 | #define DELAY_TWEAK 950 | |
84 | ||
85 | //#define OFFTIM3 // Use short/med/long off-time presses | |
86 | // instead of just short/long | |
87 | ||
88 | ||
89 | // output to use for blinks on battery check mode (primary PWM level, alt PWM level) | |
90 | // Use 20,0 for a single-channel driver or 0,20 for a two-channel driver | |
91 | #define BLINK_BRIGHTNESS 20,20 | |
92 | ||
93 | // Mode group 1 | |
94 | #define NUM_MODES1 3 | |
95 | // PWM levels for the big circuit (FET or Nx7135) | |
96 | #define MODESNx1 0,255,TURBO | |
97 | // PWM levels for the small circuit (1x7135) | |
98 | #define MODES1x1 255,0,0 | |
99 | // PWM speed for each mode | |
100 | #define MODES_PWM1 PHASE,PHASE,PHASE | |
101 | // Mode group 2 | |
102 | #define NUM_MODES2 5 | |
103 | #define MODESNx2 0,0,15,255,TURBO | |
104 | #define MODES1x2 5,255,0,0,0 | |
105 | #define MODES_PWM2 PHASE,FAST,FAST,FAST,FAST | |
106 | // Hidden modes are *before* the lowest (moon) mode, and should be specified | |
107 | // in reverse order. So, to go backward from moon to turbo to strobe to | |
108 | // battcheck, use BATTCHECK,STROBE,TURBO . | |
109 | #define NUM_HIDDEN 0 | |
110 | #define HIDDENMODES 0 | |
111 | #define HIDDENMODES_PWM 0 | |
112 | #define HIDDENMODES_ALT 0 // Zeroes, same length as NUM_HIDDEN | |
113 | ||
114 | #define TURBO 245 // Convenience code for turbo mode | |
115 | #define BATTCHECK 254 // Convenience code for battery check mode | |
116 | // Uncomment to enable tactical strobe mode | |
117 | //#define STROBE 253 // Convenience code for strobe mode | |
118 | // Uncomment to unable a 2-level stutter beacon instead of a tactical strobe | |
119 | //#define BIKING_STROBE 252 // Convenience code for biking strobe mode | |
120 | // comment out to use minimal version instead (smaller) | |
121 | //#define FULL_BIKING_STROBE | |
122 | ||
123 | #define NON_WDT_TURBO // enable turbo step-down without WDT | |
124 | // How many timer ticks before before dropping down. | |
125 | // Each timer tick is 500ms, so "60" would be a 30-second stepdown. | |
126 | // Max value of 255 unless you change "ticks" | |
127 | #define TURBO_TIMEOUT 10 | |
128 | ||
129 | // These values were measured using wight's "A17HYBRID-S" driver built by DBCstm. | |
130 | // Your mileage may vary. | |
131 | #define ADC_42 174 // the ADC value we expect for 4.20 volts | |
132 | #define ADC_100 174 // the ADC value for 100% full (4.2V resting) | |
133 | #define ADC_75 166 // the ADC value for 75% full (4.0V resting) | |
134 | #define ADC_50 158 // the ADC value for 50% full (3.8V resting) | |
135 | #define ADC_25 145 // the ADC value for 25% full (3.5V resting) | |
136 | #define ADC_0 124 // the ADC value for 0% full (3.0V resting) | |
137 | #define ADC_LOW 116 // When do we start ramping down (2.8V) | |
138 | #define ADC_CRIT 112 // When do we shut the light off (2.7V) | |
139 | // These values were copied from s7.c. | |
140 | // Your mileage may vary. | |
141 | //#define ADC_42 185 // the ADC value we expect for 4.20 volts | |
142 | //#define ADC_100 185 // the ADC value for 100% full (4.2V resting) | |
143 | //#define ADC_75 175 // the ADC value for 75% full (4.0V resting) | |
144 | //#define ADC_50 164 // the ADC value for 50% full (3.8V resting) | |
145 | //#define ADC_25 154 // the ADC value for 25% full (3.5V resting) | |
146 | //#define ADC_0 139 // the ADC value for 0% full (3.0V resting) | |
147 | //#define ADC_LOW 123 // When do we start ramping down (2.8V) | |
148 | //#define ADC_CRIT 113 // When do we shut the light off (2.7V) | |
149 | // Values for testing only: | |
150 | //#define ADC_LOW 125 // When do we start ramping down (2.8V) | |
151 | //#define ADC_CRIT 124 // When do we shut the light off (2.7V) | |
152 | ||
153 | // the BLF EE A6 driver may have different offtime cap values than most other drivers | |
154 | // Values are between 1 and 255, and can be measured with offtime-cap.c | |
155 | // These #defines are the edge boundaries, not the center of the target. | |
156 | #ifdef OFFTIM3 | |
157 | #define CAP_SHORT 250 // Anything higher than this is a short press | |
158 | #define CAP_MED 190 // Between CAP_MED and CAP_SHORT is a medium press | |
159 | // Below CAP_MED is a long press | |
160 | #else | |
161 | #define CAP_SHORT 190 // Anything higher than this is a short press, lower is a long press | |
162 | #endif | |
163 | ||
164 | /* | |
165 | * ========================================================================= | |
166 | */ | |
167 | ||
168 | // Ignore a spurious warning, we did the cast on purpose | |
169 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" | |
170 | ||
171 | #ifdef OWN_DELAY | |
172 | #include <util/delay_basic.h> | |
173 | // Having own _delay_ms() saves some bytes AND adds possibility to use variables as input | |
174 | void _delay_ms(uint16_t n) | |
175 | { | |
176 | // TODO: make this take tenths of a ms instead of ms, | |
177 | // for more precise timing? | |
178 | while(n-- > 0) _delay_loop_2(DELAY_TWEAK); | |
179 | } | |
180 | void _delay_s() // because it saves a bit of ROM space to do it this way | |
181 | { | |
182 | _delay_ms(1000); | |
183 | } | |
184 | #else | |
185 | #include <util/delay.h> | |
186 | #endif | |
187 | ||
188 | #include <avr/pgmspace.h> | |
189 | //#include <avr/io.h> | |
190 | //#include <avr/interrupt.h> | |
191 | #include <avr/eeprom.h> | |
192 | #include <avr/sleep.h> | |
193 | //#include <avr/power.h> | |
194 | ||
195 | #define STAR2_PIN PB0 // But note that there is no star 2. | |
196 | #define FET_PIN PB4 | |
197 | #define CAP_PIN PB3 | |
198 | #define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) | |
199 | #define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 | |
200 | #define PWM_PIN PB1 | |
201 | #define ALT_PWM_PIN PB0 | |
202 | #define VOLTAGE_PIN PB2 | |
203 | #define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 | |
204 | #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 | |
205 | #define ADC_PRSCL 0x06 // clk/64 | |
206 | ||
207 | #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 | |
208 | #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 | |
209 | ||
210 | /* | |
211 | * global variables | |
212 | */ | |
213 | ||
214 | // Config / state variables | |
215 | uint8_t eepos = 0; | |
216 | uint8_t memory = 0; // mode memory, or not (set via soldered star) | |
217 | uint8_t modegroup = 0; // which mode group (set above in #defines) | |
218 | uint8_t mode_idx = 0; // current or last-used mode number | |
219 | // counter for entering config mode | |
220 | // (needs to be remembered while off, but only for up to half a second) | |
221 | uint8_t fast_presses __attribute__ ((section (".noinit"))); | |
222 | ||
223 | // NOTE: Only '1' is known to work; -1 will probably break and is untested. | |
224 | // In other words, short press goes to the next (higher) mode, | |
225 | // medium press goes to the previous (lower) mode. | |
226 | #define mode_dir 1 | |
227 | // total length of current mode group's array | |
228 | uint8_t mode_cnt; | |
229 | // number of regular non-hidden modes in current mode group | |
230 | uint8_t solid_modes; | |
231 | // number of hidden modes in the current mode group | |
232 | // (hardcoded because both groups have the same hidden modes) | |
233 | //uint8_t hidden_modes = NUM_HIDDEN; // this is never used | |
234 | ||
235 | ||
236 | // Modes (gets set when the light starts up based on saved config values) | |
237 | PROGMEM const uint8_t modesNx1[] = { MODESNx1, HIDDENMODES }; | |
238 | PROGMEM const uint8_t modesNx2[] = { MODESNx2, HIDDENMODES }; | |
239 | const uint8_t *modesNx; // gets pointed at whatever group is current | |
240 | ||
241 | PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT }; | |
242 | PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT }; | |
243 | const uint8_t *modes1x; | |
244 | ||
245 | PROGMEM const uint8_t modes_pwm1[] = { MODES_PWM1, HIDDENMODES_PWM }; | |
246 | PROGMEM const uint8_t modes_pwm2[] = { MODES_PWM2, HIDDENMODES_PWM }; | |
247 | const uint8_t *modes_pwm; | |
248 | ||
249 | PROGMEM const uint8_t voltage_blinks[] = { | |
250 | ADC_0, // 1 blink for 0%-25% | |
251 | ADC_25, // 2 blinks for 25%-50% | |
252 | ADC_50, // 3 blinks for 50%-75% | |
253 | ADC_75, // 4 blinks for 75%-100% | |
254 | ADC_100, // 5 blinks for >100% | |
255 | 255, // Ceiling, don't remove | |
256 | }; | |
257 | ||
258 | void save_state() { // central method for writing (with wear leveling) | |
259 | // a single 16-bit write uses less ROM space than two 8-bit writes | |
260 | uint8_t eep; | |
261 | uint8_t oldpos=eepos; | |
262 | ||
263 | eepos = (eepos+1) & 63; // wear leveling, use next cell | |
264 | ||
265 | ||
266 | eep = mode_idx | (modegroup << 5) | (memory << 6); | |
267 | eeprom_write_byte((uint8_t *)(eepos), eep); // save current state | |
268 | eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state | |
269 | } | |
270 | ||
271 | void restore_state() { | |
272 | uint8_t eep; | |
273 | // find the config data | |
274 | for(eepos=0; eepos<64; eepos++) { | |
275 | eep = eeprom_read_byte((const uint8_t *)eepos); | |
276 | if (eep != 0xff) break; | |
277 | } | |
278 | // unpack the config data | |
279 | if (eepos < 64) { | |
280 | mode_idx = eep & 0x0f; | |
281 | modegroup = (eep >> 5) & 1; | |
282 | memory = (eep >> 6) & 1; | |
283 | } | |
284 | // unnecessary, save_state handles wrap-around | |
285 | // (and we don't really care about it skipping cell 0 once in a while) | |
286 | //else eepos=0; | |
287 | } | |
288 | ||
289 | inline void next_mode() { | |
290 | mode_idx += 1; | |
291 | if (mode_idx >= solid_modes) { | |
292 | // Wrap around, skipping the hidden modes | |
293 | // (note: this also applies when going "forward" from any hidden mode) | |
294 | mode_idx = 0; | |
295 | } | |
296 | } | |
297 | ||
298 | #ifdef OFFTIM3 | |
299 | inline void prev_mode() { | |
300 | if (mode_idx == solid_modes) { | |
301 | // If we hit the end of the hidden modes, go back to moon | |
302 | mode_idx = 0; | |
303 | } else if (mode_idx > 0) { | |
304 | // Regular mode: is between 1 and TOTAL_MODES | |
305 | mode_idx -= 1; | |
306 | } else { | |
307 | // Otherwise, wrap around (this allows entering hidden modes) | |
308 | mode_idx = mode_cnt - 1; | |
309 | } | |
310 | } | |
311 | #endif | |
312 | ||
313 | #ifdef CONFIG_STARS | |
314 | inline void check_stars() { | |
315 | // Configure options based on stars | |
316 | // 0 being low for soldered, 1 for pulled-up for not soldered | |
317 | #if 0 // not implemented, STAR2_PIN is used for second PWM channel | |
318 | // Moon | |
319 | // enable moon mode? | |
320 | if ((PINB & (1 << STAR2_PIN)) == 0) { | |
321 | modes[mode_cnt++] = MODE_MOON; | |
322 | } | |
323 | #endif | |
324 | #if 0 // Mode order not as important as mem/no-mem | |
325 | // Mode order | |
326 | if ((PINB & (1 << FET_PIN)) == 0) { | |
327 | // High to Low | |
328 | mode_dir = -1; | |
329 | } else { | |
330 | mode_dir = 1; | |
331 | } | |
332 | #endif | |
333 | // Memory | |
334 | if ((PINB & (1 << FET_PIN)) == 0) { | |
335 | memory = 1; // solder to enable memory | |
336 | } else { | |
337 | memory = 0; // unsolder to disable memory | |
338 | } | |
339 | } | |
340 | #endif // ifdef CONFIG_STARS | |
341 | ||
342 | void count_modes() { | |
343 | /* | |
344 | * Determine how many solid and hidden modes we have. | |
345 | * The modes_pwm array should have several values for regular modes | |
346 | * then some values for hidden modes. | |
347 | * | |
348 | * (this matters because we have more than one set of modes to choose | |
349 | * from, so we need to count at runtime) | |
350 | */ | |
351 | if (modegroup == 0) { | |
352 | solid_modes = NUM_MODES1; | |
353 | modesNx = modesNx1; | |
354 | modes1x = modes1x1; | |
355 | modes_pwm = modes_pwm1; | |
356 | } else { | |
357 | solid_modes = NUM_MODES2; | |
358 | modesNx = modesNx2; | |
359 | modes1x = modes1x2; | |
360 | modes_pwm = modes_pwm2; | |
361 | } | |
362 | mode_cnt = solid_modes + NUM_HIDDEN; | |
363 | } | |
364 | ||
365 | #ifdef VOLTAGE_MON | |
366 | inline void ADC_on() { | |
367 | DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption | |
368 | ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 | |
369 | ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale | |
370 | } | |
371 | #else | |
372 | inline void ADC_off() { | |
373 | ADCSRA &= ~(1<<7); //ADC off | |
374 | } | |
375 | #endif | |
376 | ||
377 | void set_output(uint8_t pwm1, uint8_t pwm2) { | |
378 | // Need PHASE to properly turn off the light | |
379 | if ((pwm1==0) && (pwm2==0)) { | |
380 | TCCR0A = PHASE; | |
381 | } | |
382 | #ifdef TURBO | |
383 | if (pwm1 == TURBO) { | |
384 | PWM_LVL = 0; | |
385 | ALT_PWM_LVL = 0; | |
386 | DDRB |= (1 << FET_PIN); | |
387 | PORTB |= (1 << FET_PIN); | |
388 | } else { | |
389 | PWM_LVL = pwm1; | |
390 | ALT_PWM_LVL = pwm2; | |
391 | PORTB &= ~(1 << FET_PIN); | |
392 | } | |
393 | #else | |
394 | PWM_LVL = pwm1; | |
395 | ALT_PWM_LVL = pwm2; | |
396 | #endif //ifdef TURBO | |
397 | } | |
398 | ||
399 | void set_mode(uint8_t mode) { | |
400 | TCCR0A = pgm_read_byte(modes_pwm + mode); | |
401 | set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode)); | |
402 | /* | |
403 | // Only set output for solid modes | |
404 | uint8_t out = pgm_read_byte(modesNx + mode); | |
405 | if ((out < 250) || (out == 255)) { | |
406 | set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode)); | |
407 | } | |
408 | */ | |
409 | } | |
410 | ||
411 | #ifdef VOLTAGE_MON | |
412 | uint8_t get_voltage() { | |
413 | // Start conversion | |
414 | ADCSRA |= (1 << ADSC); | |
415 | // Wait for completion | |
416 | while (ADCSRA & (1 << ADSC)); | |
417 | // See if voltage is lower than what we were looking for | |
418 | return ADCH; | |
419 | } | |
420 | #endif | |
421 | ||
422 | void blink(uint8_t val) | |
423 | { | |
424 | for (; val>0; val--) | |
425 | { | |
426 | set_output(BLINK_BRIGHTNESS); | |
427 | _delay_ms(100); | |
428 | set_output(0,0); | |
429 | _delay_ms(400); | |
430 | } | |
431 | } | |
432 | ||
433 | void toggle(uint8_t *var) { | |
434 | // Used for extended config mode | |
435 | // Changes the value of a config option, waits for the user to "save" | |
436 | // by turning the light off, then changes the value back in case they | |
437 | // didn't save. Can be used repeatedly on different options, allowing | |
438 | // the user to change and save only one at a time. | |
439 | *var ^= 1; | |
440 | save_state(); | |
441 | blink(2); | |
442 | *var ^= 1; | |
443 | save_state(); | |
444 | _delay_s(); | |
445 | } | |
446 | ||
447 | int main(void) | |
448 | { | |
449 | uint8_t cap_val; | |
450 | ||
451 | // Read the off-time cap *first* to get the most accurate reading | |
452 | // Start up ADC for capacitor pin | |
453 | DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption | |
454 | ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3 | |
455 | ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale | |
456 | ||
457 | // Wait for completion | |
458 | while (ADCSRA & (1 << ADSC)); | |
459 | // Start again as datasheet says first result is unreliable | |
460 | ADCSRA |= (1 << ADSC); | |
461 | // Wait for completion | |
462 | while (ADCSRA & (1 << ADSC)); | |
463 | cap_val = ADCH; // save this for later | |
464 | ||
465 | // Set PWM pin to output | |
466 | DDRB |= (1 << PWM_PIN); // enable main channel | |
467 | DDRB |= (1 << ALT_PWM_PIN); // enable second channel | |
468 | ||
469 | // Set timer to do PWM for correct output pin and set prescaler timing | |
470 | //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 | |
471 | //TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) | |
472 | TCCR0A = PHASE; | |
473 | // Set timer to do PWM for correct output pin and set prescaler timing | |
474 | TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) | |
475 | ||
476 | // Read config values and saved state | |
477 | restore_state(); | |
478 | // Enable the current mode group | |
479 | count_modes(); | |
480 | ||
481 | ||
482 | // memory decayed, reset it | |
483 | // (should happen on med/long press instead | |
484 | // because mem decay is *much* slower when the OTC is charged | |
485 | // so let's not wait until it decays to reset it) | |
486 | //if (fast_presses > 0x20) { fast_presses = 0; } | |
487 | ||
488 | if (cap_val > CAP_SHORT) { | |
489 | // We don't care what the value is as long as it's over 15 | |
490 | fast_presses = (fast_presses+1) & 0x1f; | |
491 | // Indicates they did a short press, go to the next mode | |
492 | next_mode(); // Will handle wrap arounds | |
493 | #ifdef OFFTIM3 | |
494 | } else if (cap_val > CAP_MED) { | |
495 | fast_presses = 0; | |
496 | // User did a medium press, go back one mode | |
497 | prev_mode(); // Will handle "negative" modes and wrap-arounds | |
498 | #endif | |
499 | } else { | |
500 | // Long press, keep the same mode | |
501 | // ... or reset to the first mode | |
502 | fast_presses = 0; | |
503 | if (! memory) { | |
504 | // Reset to the first mode | |
505 | mode_idx = 0; | |
506 | } | |
507 | } | |
508 | save_state(); | |
509 | ||
510 | // Turn off ADC | |
511 | //ADC_off(); | |
512 | ||
513 | // Charge up the capacitor by setting CAP_PIN to output | |
514 | DDRB |= (1 << CAP_PIN); // Output | |
515 | PORTB |= (1 << CAP_PIN); // High | |
516 | ||
517 | // Turn features on or off as needed | |
518 | #ifdef VOLTAGE_MON | |
519 | ADC_on(); | |
520 | #else | |
521 | ADC_off(); | |
522 | #endif | |
523 | //ACSR |= (1<<7); //AC off | |
524 | ||
525 | // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command. | |
526 | // Will allow us to go idle between WDT interrupts | |
527 | //set_sleep_mode(SLEEP_MODE_IDLE); // not used due to blinky modes | |
528 | ||
529 | uint8_t output; | |
530 | #ifdef NON_WDT_TURBO | |
531 | uint8_t ticks = 0; | |
532 | #endif | |
533 | #ifdef VOLTAGE_MON | |
534 | uint8_t lowbatt_cnt = 0; | |
535 | uint8_t i = 0; | |
536 | uint8_t voltage; | |
537 | // Make sure voltage reading is running for later | |
538 | ADCSRA |= (1 << ADSC); | |
539 | #endif | |
540 | while(1) { | |
541 | output = pgm_read_byte(modesNx + mode_idx); | |
542 | if (fast_presses > 0x0f) { // Config mode | |
543 | _delay_s(); // wait for user to stop fast-pressing button | |
544 | fast_presses = 0; // exit this mode after one use | |
545 | mode_idx = 0; | |
546 | ||
547 | // Longer/larger version of the config mode | |
548 | // Toggle the mode group, blink, un-toggle, continue | |
549 | toggle(&modegroup); | |
550 | ||
551 | // Toggle memory, blink, untoggle, exit | |
552 | toggle(&memory); | |
553 | } | |
554 | ||
555 | #ifdef STROBE | |
556 | else if (output == STROBE) { | |
557 | // 10Hz tactical strobe | |
558 | set_output(255,0); | |
559 | _delay_ms(50); | |
560 | set_output(0,0); | |
561 | _delay_ms(50); | |
562 | } | |
563 | #endif // ifdef STROBE | |
564 | #ifdef BIKING_STROBE | |
565 | else if (output == BIKING_STROBE) { | |
566 | // 2-level stutter beacon for biking and such | |
567 | #ifdef FULL_BIKING_STROBE | |
568 | // normal version | |
569 | for(i=0;i<4;i++) { | |
570 | set_output(255,0); | |
571 | _delay_ms(5); | |
572 | set_output(0,255); | |
573 | _delay_ms(65); | |
574 | } | |
575 | _delay_ms(720); | |
576 | #else | |
577 | // small/minimal version | |
578 | set_output(255,0); | |
579 | _delay_ms(10); | |
580 | set_output(0,255); | |
581 | _delay_s(); | |
582 | #endif | |
583 | } | |
584 | #endif // ifdef BIKING_STROBE | |
585 | #ifdef BATTCHECK | |
586 | else if (output == BATTCHECK) { | |
587 | voltage = get_voltage(); | |
588 | // figure out how many times to blink | |
589 | for (i=0; | |
590 | voltage > pgm_read_byte(voltage_blinks + i); | |
591 | i ++) {} | |
592 | ||
593 | // blink zero to five times to show voltage | |
594 | // (~0%, ~25%, ~50%, ~75%, ~100%, >100%) | |
595 | blink(i); | |
596 | // wait between readouts | |
597 | _delay_s(); _delay_s(); | |
598 | } | |
599 | #endif // ifdef BATTCHECK | |
600 | else { // Regular non-hidden solid mode | |
601 | set_mode(mode_idx); | |
602 | // This part of the code will mostly replace the WDT tick code. | |
603 | #ifdef NON_WDT_TURBO | |
604 | // Do some magic here to handle turbo step-down | |
605 | //if (ticks < 255) ticks++; // don't roll over | |
606 | ticks ++; // actually, we don't care about roll-over prevention | |
607 | if ((ticks > TURBO_TIMEOUT) | |
608 | && (output == TURBO)) { | |
609 | mode_idx = solid_modes - 2; // step down to second-highest mode | |
610 | set_mode(mode_idx); | |
611 | save_state(); | |
612 | } | |
613 | #endif | |
614 | // Otherwise, just sleep. | |
615 | _delay_ms(500); | |
616 | ||
617 | // If we got this far, the user has stopped fast-pressing. | |
618 | // So, don't enter config mode. | |
619 | fast_presses = 0; | |
620 | } | |
621 | #ifdef VOLTAGE_MON | |
622 | #if 1 | |
623 | if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready | |
624 | voltage = ADCH; // get_voltage(); | |
625 | // See if voltage is lower than what we were looking for | |
626 | //if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) { | |
627 | if (voltage < ADC_LOW) { | |
628 | lowbatt_cnt ++; | |
629 | } else { | |
630 | lowbatt_cnt = 0; | |
631 | } | |
632 | // See if it's been low for a while, and maybe step down | |
633 | if (lowbatt_cnt >= 8) { | |
634 | // DEBUG: blink on step-down: | |
635 | //set_output(0,0); _delay_ms(100); | |
636 | i = mode_idx; // save space by not accessing mode_idx more than necessary | |
637 | // properly track hidden vs normal modes | |
638 | if (i >= solid_modes) { | |
639 | // step down from blinky modes to medium | |
640 | i = 2; | |
641 | } else if (i > 0) { | |
642 | // step down from solid modes one at a time | |
643 | i -= 1; | |
644 | } else { // Already at the lowest mode | |
645 | i = 0; | |
646 | // Turn off the light | |
647 | set_output(0,0); | |
648 | // Power down as many components as possible | |
649 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
650 | sleep_mode(); | |
651 | } | |
652 | set_mode(i); | |
653 | mode_idx = i; | |
654 | save_state(); | |
655 | lowbatt_cnt = 0; | |
656 | // Wait at least 2 seconds before lowering the level again | |
657 | _delay_ms(250); // this will interrupt blinky modes | |
658 | } | |
659 | ||
660 | // Make sure conversion is running for next time through | |
661 | ADCSRA |= (1 << ADSC); | |
662 | } | |
663 | #endif | |
664 | #endif // ifdef VOLTAGE_MON | |
665 | //sleep_mode(); // incompatible with blinky modes | |
666 | ||
667 | // If we got this far, the user has stopped fast-pressing. | |
668 | // So, don't enter config mode. | |
669 | //fast_presses = 0; // doesn't interact well with strobe, too fast | |
670 | } | |
671 | ||
672 | //return 0; // Standard Return Code | |
673 | } |