~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to ToyKeeper/blf-a6/blf-a6.c

  • Committer: Selene Scriven
  • Date: 2017-09-12 23:34:36 UTC
  • mto: (188.1.3 trunk)
  • mto: This revision was merged to the branch mainline in revision 331.
  • Revision ID: bzr@toykeeper.net-20170912233436-d3w6nln0ts1subue
Added Flintrock's Bistro-HD 1.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
// Choose your MCU here, or in the build script
 
68
//#define ATTINY 13
 
69
//#define ATTINY 25
 
70
// FIXME: make 1-channel vs 2-channel power a single #define option
 
71
#define FET_7135_LAYOUT  // specify an I/O pin layout
 
72
// Also, assign I/O pins in this file:
 
73
#include "../tk-attiny.h"
 
74
 
 
75
/*
 
76
 * =========================================================================
 
77
 * Settings to modify per driver
 
78
 */
 
79
 
 
80
//#define FAST 0x23           // fast PWM channel 1 only
 
81
//#define PHASE 0x21          // phase-correct PWM channel 1 only
 
82
#define FAST 0xA3           // fast PWM both channels
 
83
#define PHASE 0xA1          // phase-correct PWM both channels
 
84
 
 
85
#define VOLTAGE_MON         // Comment out to disable LVP
 
86
 
 
87
#define OFFTIM3             // Use short/med/long off-time presses
 
88
                            // instead of just short/long
 
89
 
 
90
// comment out to use extended config mode instead of a solderable star
 
91
// (controls whether mode memory is on the star or if it's a setting in config mode)
 
92
//#define CONFIG_STARS
 
93
 
 
94
// output to use for blinks on battery check mode (primary PWM level, alt PWM level)
 
95
// Use 20,0 for a single-channel driver or 0,20 for a two-channel driver
 
96
#define BLINK_BRIGHTNESS    0,20
 
97
 
 
98
// Mode group 1
 
99
#define NUM_MODES1          7
 
100
// PWM levels for the big circuit (FET or Nx7135)
 
101
#define MODESNx1            0,0,0,7,56,137,255
 
102
// PWM levels for the small circuit (1x7135)
 
103
#define MODES1x1            3,20,110,255,255,255,0
 
104
// My sample:     6=0..6,  7=2..11,  8=8..21(15..32)
 
105
// Krono sample:  6=5..21, 7=17..32, 8=33..96(50..78)
 
106
// Manker2:       2=21, 3=39, 4=47, ... 6?=68
 
107
// PWM speed for each mode
 
108
#define MODES_PWM1          PHASE,FAST,FAST,FAST,FAST,FAST,PHASE
 
109
// Mode group 2
 
110
#define NUM_MODES2          4
 
111
#define MODESNx2            0,0,90,255
 
112
#define MODES1x2            20,230,255,0
 
113
#define MODES_PWM2          FAST,FAST,FAST,PHASE
 
114
// Hidden modes are *before* the lowest (moon) mode, and should be specified
 
115
// in reverse order.  So, to go backward from moon to turbo to strobe to
 
116
// battcheck, use BATTCHECK,STROBE,TURBO .
 
117
#define NUM_HIDDEN          4
 
118
#define HIDDENMODES         BIKING_STROBE,BATTCHECK,STROBE,TURBO
 
119
#define HIDDENMODES_PWM     PHASE,PHASE,PHASE,PHASE
 
120
#define HIDDENMODES_ALT     0,0,0,0   // Zeroes, same length as NUM_HIDDEN
 
121
 
 
122
#define TURBO     255       // Convenience code for turbo mode
 
123
#define BATTCHECK 254       // Convenience code for battery check mode
 
124
// Uncomment to enable tactical strobe mode
 
125
#define STROBE    253       // Convenience code for strobe mode
 
126
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
 
127
#define BIKING_STROBE 252   // Convenience code for biking strobe mode
 
128
// comment out to use minimal version instead (smaller)
 
129
#define FULL_BIKING_STROBE
 
130
 
 
131
#define NON_WDT_TURBO            // enable turbo step-down without WDT
 
132
// How many timer ticks before before dropping down.
 
133
// Each timer tick is 500ms, so "60" would be a 30-second stepdown.
 
134
// Max value of 255 unless you change "ticks"
 
135
#define TURBO_TIMEOUT       90
 
136
 
 
137
// Calibrate voltage and OTC in this file:
 
138
#include "../tk-calibration.h"
 
139
 
 
140
/*
 
141
 * =========================================================================
 
142
 */
 
143
 
 
144
// Ignore a spurious warning, we did the cast on purpose
 
145
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
 
146
 
 
147
#include <avr/pgmspace.h>
 
148
//#include <avr/io.h>
 
149
//#include <avr/interrupt.h>
 
150
#include <avr/eeprom.h>
 
151
#include <avr/sleep.h>
 
152
//#include <avr/power.h>
 
153
 
 
154
#define OWN_DELAY           // Don't use stock delay functions.
 
155
#define USE_DELAY_MS        // use _delay_ms()
 
156
#define USE_DELAY_S         // Also use _delay_s(), not just _delay_ms()
 
157
#include "../tk-delay.h"
 
158
#define USE_BATTCHECK
 
159
//#define BATTCHECK_4bars
 
160
#define BATTCHECK_8bars
 
161
#define BLINK_SPEED 500
 
162
#include "../tk-voltage.h"
 
163
 
 
164
/*
 
165
 * global variables
 
166
 */
 
167
 
 
168
// Config / state variables
 
169
uint8_t eepos = 0;
 
170
uint8_t memory = 0;        // mode memory, or not (set via soldered star)
 
171
uint8_t modegroup = 0;     // which mode group (set above in #defines)
 
172
uint8_t mode_idx = 0;      // current or last-used mode number
 
173
// counter for entering config mode
 
174
// (needs to be remembered while off, but only for up to half a second)
 
175
uint8_t fast_presses __attribute__ ((section (".noinit")));
 
176
 
 
177
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
 
178
// In other words, short press goes to the next (higher) mode,
 
179
// medium press goes to the previous (lower) mode.
 
180
#define mode_dir 1
 
181
// total length of current mode group's array
 
182
uint8_t mode_cnt;
 
183
// number of regular non-hidden modes in current mode group
 
184
uint8_t solid_modes;
 
185
// number of hidden modes in the current mode group
 
186
// (hardcoded because both groups have the same hidden modes)
 
187
//uint8_t hidden_modes = NUM_HIDDEN;  // this is never used
 
188
 
 
189
 
 
190
// Modes (gets set when the light starts up based on saved config values)
 
191
PROGMEM const uint8_t modesNx1[] = { MODESNx1, HIDDENMODES };
 
192
PROGMEM const uint8_t modesNx2[] = { MODESNx2, HIDDENMODES };
 
193
const uint8_t *modesNx;  // gets pointed at whatever group is current
 
194
 
 
195
PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT };
 
196
PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT };
 
197
const uint8_t *modes1x;
 
198
 
 
199
PROGMEM const uint8_t modes_pwm1[] = { MODES_PWM1, HIDDENMODES_PWM };
 
200
PROGMEM const uint8_t modes_pwm2[] = { MODES_PWM2, HIDDENMODES_PWM };
 
201
const uint8_t *modes_pwm;
 
202
 
 
203
void save_state() {  // central method for writing (with wear leveling)
 
204
    // a single 16-bit write uses less ROM space than two 8-bit writes
 
205
    uint8_t eep;
 
206
    uint8_t oldpos=eepos;
 
207
 
 
208
    eepos = (eepos+1) & (EEPSIZE-1);  // wear leveling, use next cell
 
209
 
 
210
#ifdef CONFIG_STARS
 
211
    eep = mode_idx | (modegroup << 5);
 
212
#else
 
213
    eep = mode_idx | (modegroup << 5) | (memory << 6);
 
214
#endif
 
215
    eeprom_write_byte((uint8_t *)(eepos), eep);      // save current state
 
216
    eeprom_write_byte((uint8_t *)(oldpos), 0xff);    // erase old state
 
217
}
 
218
 
 
219
void restore_state() {
 
220
    uint8_t eep;
 
221
    // find the config data
 
222
    for(eepos=0; eepos<EEPSIZE; eepos++) {
 
223
        eep = eeprom_read_byte((const uint8_t *)eepos);
 
224
        if (eep != 0xff) break;
 
225
    }
 
226
    // unpack the config data
 
227
    if (eepos < EEPSIZE) {
 
228
        mode_idx = eep & 0x0f;
 
229
        modegroup = (eep >> 5) & 1;
 
230
#ifndef CONFIG_STARS
 
231
        memory = (eep >> 6) & 1;
 
232
#endif
 
233
    }
 
234
    // unnecessary, save_state handles wrap-around
 
235
    // (and we don't really care about it skipping cell 0 once in a while)
 
236
    //else eepos=0;
 
237
}
 
238
 
 
239
inline void next_mode() {
 
240
    mode_idx += 1;
 
241
    if (mode_idx >= solid_modes) {
 
242
        // Wrap around, skipping the hidden modes
 
243
        // (note: this also applies when going "forward" from any hidden mode)
 
244
        mode_idx = 0;
 
245
    }
 
246
}
 
247
 
 
248
#ifdef OFFTIM3
 
249
inline void prev_mode() {
 
250
    if (mode_idx == solid_modes) {
 
251
        // If we hit the end of the hidden modes, go back to moon
 
252
        mode_idx = 0;
 
253
    } else if (mode_idx > 0) {
 
254
        // Regular mode: is between 1 and TOTAL_MODES
 
255
        mode_idx -= 1;
 
256
    } else {
 
257
        // Otherwise, wrap around (this allows entering hidden modes)
 
258
        mode_idx = mode_cnt - 1;
 
259
    }
 
260
}
 
261
#endif
 
262
 
 
263
#ifdef CONFIG_STARS
 
264
inline void check_stars() {
 
265
    // Configure options based on stars
 
266
    // 0 being low for soldered, 1 for pulled-up for not soldered
 
267
#if 0  // not implemented, STAR2_PIN is used for second PWM channel
 
268
    // Moon
 
269
    // enable moon mode?
 
270
    if ((PINB & (1 << STAR2_PIN)) == 0) {
 
271
        modes[mode_cnt++] = MODE_MOON;
 
272
    }
 
273
#endif
 
274
#if 0  // Mode order not as important as mem/no-mem
 
275
    // Mode order
 
276
    if ((PINB & (1 << STAR3_PIN)) == 0) {
 
277
        // High to Low
 
278
        mode_dir = -1;
 
279
    } else {
 
280
        mode_dir = 1;
 
281
    }
 
282
#endif
 
283
    // Memory
 
284
    if ((PINB & (1 << STAR3_PIN)) == 0) {
 
285
        memory = 1;  // solder to enable memory
 
286
    } else {
 
287
        memory = 0;  // unsolder to disable memory
 
288
    }
 
289
}
 
290
#endif  // ifdef CONFIG_STARS
 
291
 
 
292
void count_modes() {
 
293
    /*
 
294
     * Determine how many solid and hidden modes we have.
 
295
     * The modes_pwm array should have several values for regular modes
 
296
     * then some values for hidden modes.
 
297
     *
 
298
     * (this matters because we have more than one set of modes to choose
 
299
     *  from, so we need to count at runtime)
 
300
     */
 
301
    if (modegroup == 0) {
 
302
        solid_modes = NUM_MODES1;
 
303
        modesNx = modesNx1;
 
304
        modes1x = modes1x1;
 
305
        modes_pwm = modes_pwm1;
 
306
    } else {
 
307
        solid_modes = NUM_MODES2;
 
308
        modesNx = modesNx2;
 
309
        modes1x = modes1x2;
 
310
        modes_pwm = modes_pwm2;
 
311
    }
 
312
    mode_cnt = solid_modes + NUM_HIDDEN;
 
313
}
 
314
 
 
315
void set_output(uint8_t pwm1, uint8_t pwm2) {
 
316
    // Need PHASE to properly turn off the light
 
317
    if ((pwm1==0) && (pwm2==0)) {
 
318
        TCCR0A = PHASE;
 
319
    }
 
320
    PWM_LVL = pwm1;
 
321
    ALT_PWM_LVL = pwm2;
 
322
}
 
323
 
 
324
void set_mode(uint8_t mode) {
 
325
    TCCR0A = pgm_read_byte(modes_pwm + mode);
 
326
    set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
 
327
    /*
 
328
    // Only set output for solid modes
 
329
    uint8_t out = pgm_read_byte(modesNx + mode);
 
330
    if ((out < 250) || (out == 255)) {
 
331
        set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
 
332
    }
 
333
    */
 
334
}
 
335
 
 
336
void blink(uint8_t val)
 
337
{
 
338
    for (; val>0; val--)
 
339
    {
 
340
        set_output(BLINK_BRIGHTNESS);
 
341
        _delay_ms(BLINK_SPEED / 5);
 
342
        set_output(0,0);
 
343
        _delay_ms(BLINK_SPEED * 4 / 5);
 
344
    }
 
345
}
 
346
 
 
347
#ifndef CONFIG_STARS
 
348
void toggle(uint8_t *var) {
 
349
    // Used for extended config mode
 
350
    // Changes the value of a config option, waits for the user to "save"
 
351
    // by turning the light off, then changes the value back in case they
 
352
    // didn't save.  Can be used repeatedly on different options, allowing
 
353
    // the user to change and save only one at a time.
 
354
    *var ^= 1;
 
355
    save_state();
 
356
    blink(2);
 
357
    *var ^= 1;
 
358
    save_state();
 
359
    _delay_s();
 
360
}
 
361
#endif // ifndef CONFIG_STARS
 
362
 
 
363
int main(void)
 
364
{
 
365
    uint8_t cap_val;
 
366
 
 
367
    // Read the off-time cap *first* to get the most accurate reading
 
368
    // Start up ADC for capacitor pin
 
369
    DIDR0 |= (1 << CAP_DIDR);                           // disable digital input on ADC pin to reduce power consumption
 
370
    ADMUX  = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
 
371
    ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
372
 
 
373
    // Wait for completion
 
374
    while (ADCSRA & (1 << ADSC));
 
375
    // Start again as datasheet says first result is unreliable
 
376
    ADCSRA |= (1 << ADSC);
 
377
    // Wait for completion
 
378
    while (ADCSRA & (1 << ADSC));
 
379
    cap_val = ADCH; // save this for later
 
380
 
 
381
#ifdef CONFIG_STARS
 
382
    // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
 
383
    // only one star, because one is used for PWM channel 2
 
384
    // and the other is used for the off-time capacitor
 
385
    PORTB = (1 << STAR3_PIN);
 
386
#endif
 
387
 
 
388
    // Set PWM pin to output
 
389
    DDRB |= (1 << PWM_PIN);     // enable main channel
 
390
    DDRB |= (1 << ALT_PWM_PIN); // enable second channel
 
391
 
 
392
    // Set timer to do PWM for correct output pin and set prescaler timing
 
393
    //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
394
    //TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
395
    TCCR0A = PHASE;
 
396
    // Set timer to do PWM for correct output pin and set prescaler timing
 
397
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
398
 
 
399
    // Read config values and saved state
 
400
#ifdef CONFIG_STARS
 
401
    check_stars();
 
402
#endif
 
403
    restore_state();
 
404
    // Enable the current mode group
 
405
    count_modes();
 
406
 
 
407
 
 
408
    // memory decayed, reset it
 
409
    // (should happen on med/long press instead
 
410
    //  because mem decay is *much* slower when the OTC is charged
 
411
    //  so let's not wait until it decays to reset it)
 
412
    //if (fast_presses > 0x20) { fast_presses = 0; }
 
413
 
 
414
    if (cap_val > CAP_SHORT) {
 
415
        // We don't care what the value is as long as it's over 15
 
416
        fast_presses = (fast_presses+1) & 0x1f;
 
417
        // Indicates they did a short press, go to the next mode
 
418
        next_mode(); // Will handle wrap arounds
 
419
#ifdef OFFTIM3
 
420
    } else if (cap_val > CAP_MED) {
 
421
        fast_presses = 0;
 
422
        // User did a medium press, go back one mode
 
423
        prev_mode(); // Will handle "negative" modes and wrap-arounds
 
424
#endif
 
425
    } else {
 
426
        // Long press, keep the same mode
 
427
        // ... or reset to the first mode
 
428
        fast_presses = 0;
 
429
        if (! memory) {
 
430
            // Reset to the first mode
 
431
            mode_idx = 0;
 
432
        }
 
433
    }
 
434
    save_state();
 
435
 
 
436
    // Turn off ADC
 
437
    //ADC_off();
 
438
 
 
439
    // Charge up the capacitor by setting CAP_PIN to output
 
440
    DDRB  |= (1 << CAP_PIN);    // Output
 
441
    PORTB |= (1 << CAP_PIN);    // High
 
442
 
 
443
    // Turn features on or off as needed
 
444
    #ifdef VOLTAGE_MON
 
445
    ADC_on();
 
446
    #else
 
447
    ADC_off();
 
448
    #endif
 
449
    //ACSR   |=  (1<<7); //AC off
 
450
 
 
451
    // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
 
452
    // Will allow us to go idle between WDT interrupts
 
453
    //set_sleep_mode(SLEEP_MODE_IDLE);  // not used due to blinky modes
 
454
 
 
455
    uint8_t output;
 
456
#ifdef NON_WDT_TURBO
 
457
    uint8_t ticks = 0;
 
458
#endif
 
459
#ifdef VOLTAGE_MON
 
460
    uint8_t lowbatt_cnt = 0;
 
461
    uint8_t i = 0;
 
462
    uint8_t voltage;
 
463
    // Make sure voltage reading is running for later
 
464
    ADCSRA |= (1 << ADSC);
 
465
#endif
 
466
    while(1) {
 
467
        output = pgm_read_byte(modesNx + mode_idx);
 
468
        if (fast_presses > 0x0f) {  // Config mode
 
469
            _delay_s();       // wait for user to stop fast-pressing button
 
470
            fast_presses = 0; // exit this mode after one use
 
471
            mode_idx = 0;
 
472
 
 
473
#ifdef CONFIG_STARS
 
474
            // Short/small version of the config mode
 
475
            // Toggle the mode group, blink, then exit
 
476
            modegroup ^= 1;
 
477
            save_state();
 
478
            count_modes();  // reconfigure without a power cycle
 
479
            blink(1);
 
480
#else
 
481
            // Longer/larger version of the config mode
 
482
            // Toggle the mode group, blink, un-toggle, continue
 
483
            toggle(&modegroup);
 
484
 
 
485
            // Toggle memory, blink, untoggle, exit
 
486
            toggle(&memory);
 
487
#endif  // ifdef CONFIG_STARS
 
488
        }
 
489
#ifdef STROBE
 
490
        else if (output == STROBE) {
 
491
            // 10Hz tactical strobe
 
492
            set_output(255,0);
 
493
            _delay_ms(50);
 
494
            set_output(0,0);
 
495
            _delay_ms(50);
 
496
        }
 
497
#endif // ifdef STROBE
 
498
#ifdef BIKING_STROBE
 
499
        else if (output == BIKING_STROBE) {
 
500
            // 2-level stutter beacon for biking and such
 
501
#ifdef FULL_BIKING_STROBE
 
502
            // normal version
 
503
            for(i=0;i<4;i++) {
 
504
                set_output(255,0);
 
505
                _delay_ms(5);
 
506
                set_output(0,255);
 
507
                _delay_ms(65);
 
508
            }
 
509
            _delay_ms(720);
 
510
#else
 
511
            // small/minimal version
 
512
            set_output(255,0);
 
513
            _delay_ms(10);
 
514
            set_output(0,255);
 
515
            _delay_s();
 
516
#endif
 
517
        }
 
518
#endif  // ifdef BIKING_STROBE
 
519
#ifdef BATTCHECK
 
520
        else if (output == BATTCHECK) {
 
521
            // blink zero to five times to show voltage
 
522
            // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
 
523
            blink(battcheck());
 
524
            // wait between readouts
 
525
            _delay_s(); _delay_s();
 
526
        }
 
527
#endif // ifdef BATTCHECK
 
528
        else {  // Regular non-hidden solid mode
 
529
            set_mode(mode_idx);
 
530
            // This part of the code will mostly replace the WDT tick code.
 
531
#ifdef NON_WDT_TURBO
 
532
            // Do some magic here to handle turbo step-down
 
533
            //if (ticks < 255) ticks++;  // don't roll over
 
534
            ticks ++;  // actually, we don't care about roll-over prevention
 
535
            if ((ticks > TURBO_TIMEOUT) 
 
536
                    && (output == TURBO)) {
 
537
                mode_idx = solid_modes - 2; // step down to second-highest mode
 
538
                set_mode(mode_idx);
 
539
                save_state();
 
540
            }
 
541
#endif
 
542
            // Otherwise, just sleep.
 
543
            _delay_ms(500);
 
544
 
 
545
            // If we got this far, the user has stopped fast-pressing.
 
546
            // So, don't enter config mode.
 
547
            fast_presses = 0;
 
548
        }
 
549
#ifdef VOLTAGE_MON
 
550
#if 1
 
551
        if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
 
552
            voltage = ADCH; // get_voltage();
 
553
            // See if voltage is lower than what we were looking for
 
554
            //if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
 
555
            if (voltage < ADC_LOW) {
 
556
                lowbatt_cnt ++;
 
557
            } else {
 
558
                lowbatt_cnt = 0;
 
559
            }
 
560
            // See if it's been low for a while, and maybe step down
 
561
            if (lowbatt_cnt >= 8) {
 
562
                // DEBUG: blink on step-down:
 
563
                //set_output(0,0);  _delay_ms(100);
 
564
                i = mode_idx; // save space by not accessing mode_idx more than necessary
 
565
                // properly track hidden vs normal modes
 
566
                if (i >= solid_modes) {
 
567
                    // step down from blinky modes to medium
 
568
                    i = 2;
 
569
                } else if (i > 0) {
 
570
                    // step down from solid modes one at a time
 
571
                    i -= 1;
 
572
                } else { // Already at the lowest mode
 
573
                    i = 0;
 
574
                    // Turn off the light
 
575
                    set_output(0,0);
 
576
                    // Power down as many components as possible
 
577
                    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
578
                    sleep_mode();
 
579
                }
 
580
                set_mode(i);
 
581
                mode_idx = i;
 
582
                save_state();
 
583
                lowbatt_cnt = 0;
 
584
                // Wait at least 2 seconds before lowering the level again
 
585
                _delay_ms(250);  // this will interrupt blinky modes
 
586
            }
 
587
 
 
588
            // Make sure conversion is running for next time through
 
589
            ADCSRA |= (1 << ADSC);
 
590
        }
 
591
#endif
 
592
#endif  // ifdef VOLTAGE_MON
 
593
        //sleep_mode();  // incompatible with blinky modes
 
594
 
 
595
        // If we got this far, the user has stopped fast-pressing.
 
596
        // So, don't enter config mode.
 
597
        //fast_presses = 0;  // doesn't interact well with strobe, too fast
 
598
    }
 
599
 
 
600
    //return 0; // Standard Return Code
 
601
}