~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

Viewing changes to ToyKeeper/STAR_noinit/STAR_noinit.c

  • Committer: Selene Scriven
  • Date: 2014-07-24 06:46:30 UTC
  • Revision ID: ubuntu@toykeeper.net-20140724064630-k1fk2o5ppso2owhj
Started adding contributions from DrJones.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* STAR_noinit version 1.3:1.0
2
 
 *
3
 
 * Changelog
4
 
 *
5
 
 * 1.0 Initial version
6
 
 * 1.1 Bug fix
7
 
 * 1.2 Added support for dual PWM outputs and selection of PWM mode per output level
8
 
 * 1.3 Added ability to have turbo ramp down gradually instead of step down
9
 
 * 1.3:1.0 Changed from off-time capacitor to "noinit" memory decay trick
10
 
 *
11
 
 */
12
 
 
13
 
/*
14
 
 * NANJG 105C Diagram
15
 
 *           ---
16
 
 *         -|   |- VCC
17
 
 *  Star 4 -|   |- Voltage ADC
18
 
 *  Star 3 -|   |- PWM
19
 
 *     GND -|   |- Star 2
20
 
 *           ---
21
 
 *
22
 
 * FUSES
23
 
 *      (check bin/flash*.sh for recommended values)
24
 
 *
25
 
 * STARS
26
 
 *              Star 2 = Moon if connected and alternate PWM output not used
27
 
 *              Star 3 = H-L if connected, L-H if not
28
 
 *              Star 4 = Capacitor for off-time
29
 
 *
30
 
 * VOLTAGE
31
 
 *              Resistor values for voltage divider (reference BLF-VLD README for more info)
32
 
 *              Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
33
 
 *
34
 
 *           VCC
35
 
 *            |
36
 
 *           Vd (~.25 v drop from protection diode)
37
 
 *            |
38
 
 *          1912 (R1 19,100 ohms)
39
 
 *            |
40
 
 *            |---- PB2 from MCU
41
 
 *            |
42
 
 *          4701 (R2 4,700 ohms)
43
 
 *            |
44
 
 *           GND
45
 
 *
46
 
 *              ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
47
 
 *              125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
48
 
 *              121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
49
 
 *
50
 
 *              Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
51
 
 *              130 and 120
52
 
 *
53
 
 *              To find out what value to use, plug in the target voltage (V) to this equation
54
 
 *                      value = (V * 4700 * 255) / (23800 * 1.1)
55
 
 *      
56
 
 */
57
 
#define F_CPU 4800000UL
58
 
 
59
 
/*
60
 
 * =========================================================================
61
 
 * Settings to modify per driver
62
 
 */
63
 
 
64
 
#define VOLTAGE_MON                     // Comment out to disable
65
 
 
66
 
#define MEMORY                          // Comment out to disable
67
 
 
68
 
//#define TICKS_250MS           // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
69
 
                                                        // Affects turbo timeout/rampdown timing
70
 
 
71
 
#define MODE_MOON                       3       // Can comment out to remove mode, but should be set through soldering stars
72
 
#define MODE_LOW                        14  // Can comment out to remove mode
73
 
#define MODE_MED                        39      // Can comment out to remove mode
74
 
//#define MODE_HIGH                     255     // Can comment out to remove mode
75
 
#define MODE_TURBO                      255     // Can comment out to remove mode
76
 
#define MODE_TURBO_LOW          140     // Level turbo ramps down to if turbo enabled
77
 
#define TURBO_TIMEOUT           240 // How many WTD ticks before before dropping down.  If ticks set for 500 ms, then 240 x .5 = 120 seconds.  Max value of 255 unless you change "ticks"
78
 
                                                                // variable to uint8_t
79
 
//#define TURBO_RAMP_DOWN                       // By default we will start to gradually ramp down, once TURBO_TIMEOUT ticks are reached, 1 PWM_LVL each tick until reaching MODE_TURBO_LOW PWM_LVL
80
 
                                                                // If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
81
 
 
82
 
#define FAST_PWM_START      8 // Above what output level should we switch from phase correct to fast-PWM?
83
 
//#define DUAL_PWM_START                8 // Above what output level should we switch from the alternate PWM output to both PWM outputs?  Comment out to disable alternate PWM output
84
 
 
85
 
#define ADC_LOW                         130     // When do we start ramping
86
 
#define ADC_CRIT                        120 // When do we shut the light off
87
 
 
88
 
/*
89
 
 * =========================================================================
90
 
 */
91
 
 
92
 
//#include <avr/pgmspace.h>
93
 
#include <avr/io.h>
94
 
#include <util/delay.h>
95
 
#include <avr/interrupt.h>
96
 
#include <avr/wdt.h>    
97
 
#include <avr/eeprom.h>
98
 
#include <avr/sleep.h>
99
 
//#include <avr/power.h>
100
 
 
101
 
#define STAR2_PIN   PB0
102
 
#define STAR3_PIN   PB4
103
 
#define PWM_PIN     PB1
104
 
#define VOLTAGE_PIN PB2
105
 
#define ADC_CHANNEL 0x01        // MUX 01 corresponds with PB2
106
 
#define ADC_DIDR        ADC1D   // Digital input disable bit corresponding with PB2
107
 
#define ADC_PRSCL   0x06        // clk/64
108
 
 
109
 
#define PWM_LVL         OCR0B   // OCR0B is the output compare register for PB1
110
 
#define ALT_PWM_LVL OCR0A   // OCR0A is the output compare register for PB0
111
 
 
112
 
/*
113
 
 * global variables
114
 
 */
115
 
 
116
 
// offtime detection
117
 
volatile uint8_t noinit_decay __attribute__ ((section (".noinit")));
118
 
 
119
 
// Mode storage
120
 
uint8_t eepos = 0;
121
 
uint8_t eep[32];
122
 
uint8_t memory = 0;
123
 
 
124
 
// Modes (gets set when the light starts up based on stars)
125
 
static uint8_t modes[10];  // Don't need 10, but keeping it high enough to handle all
126
 
volatile uint8_t mode_idx = 0;
127
 
int     mode_dir = 0; // 1 or -1. Determined when checking stars. Do we increase or decrease the idx when moving up to a higher mode.
128
 
uint8_t mode_cnt = 0;
129
 
 
130
 
uint8_t lowbatt_cnt = 0;
131
 
 
132
 
void store_mode_idx(uint8_t lvl) {  //central method for writing (with wear leveling)
133
 
        uint8_t oldpos=eepos;
134
 
        eepos=(eepos+1)&31;  //wear leveling, use next cell
135
 
        // Write the current mode
136
 
        EEARL=eepos;  EEDR=lvl; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
137
 
        while(EECR & 2); //wait for completion
138
 
        // Erase the last mode
139
 
        EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
140
 
}
141
 
inline void read_mode_idx() {
142
 
        eeprom_read_block(&eep, 0, 32);
143
 
        while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
144
 
        if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
145
 
        else eepos=0;
146
 
}
147
 
 
148
 
inline void next_mode() {
149
 
        if (mode_idx == 0 && mode_dir == -1) {
150
 
                // Wrap around
151
 
                mode_idx = mode_cnt - 1;
152
 
        } else {
153
 
                mode_idx += mode_dir;
154
 
                if (mode_idx > (mode_cnt - 1)) {
155
 
                        // Wrap around
156
 
                        mode_idx = 0;
157
 
                }
158
 
        }
159
 
}
160
 
 
161
 
inline void check_stars() {
162
 
        // Load up the modes based on stars
163
 
        // Always load up the modes array in order of lowest to highest mode
164
 
        // 0 being low for soldered, 1 for pulled-up for not soldered
165
 
        // Moon
166
 
#ifdef MODE_MOON
167
 
#ifndef DUAL_PWM_START
168
 
        if ((PINB & (1 << STAR2_PIN)) == 0) {
169
 
#endif
170
 
                modes[mode_cnt++] = MODE_MOON;
171
 
#ifndef DUAL_PWM_START
172
 
        }
173
 
#endif
174
 
#endif
175
 
#ifdef MODE_LOW
176
 
        modes[mode_cnt++] = MODE_LOW;
177
 
#endif
178
 
#ifdef MODE_MED
179
 
        modes[mode_cnt++] = MODE_MED;
180
 
#endif
181
 
#ifdef MODE_HIGH
182
 
        modes[mode_cnt++] = MODE_HIGH;
183
 
#endif
184
 
#ifdef MODE_TURBO
185
 
        modes[mode_cnt++] = MODE_TURBO;
186
 
#endif
187
 
        if ((PINB & (1 << STAR3_PIN)) == 0) {
188
 
                // High to Low
189
 
                mode_dir = -1;
190
 
        } else {
191
 
                mode_dir = 1;
192
 
        }
193
 
}
194
 
 
195
 
inline void WDT_on() {
196
 
        // Setup watchdog timer to only interrupt, not reset
197
 
        cli();                                                  // Disable interrupts
198
 
        wdt_reset();                                    // Reset the WDT
199
 
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
200
 
        #ifdef TICKS_250MS
201
 
        WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
202
 
        #else
203
 
        WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
204
 
        #endif
205
 
        sei();                                                  // Enable interrupts
206
 
}
207
 
 
208
 
inline void WDT_off()
209
 
{
210
 
        cli();                                                  // Disable interrupts
211
 
        wdt_reset();                                    // Reset the WDT
212
 
        MCUSR &= ~(1<<WDRF);                    // Clear Watchdog reset flag
213
 
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
214
 
        WDTCR = 0x00;                                   // Disable WDT
215
 
        sei();                                                  // Enable interrupts
216
 
}
217
 
 
218
 
inline void ADC_on() {
219
 
        DIDR0 |= (1 << ADC_DIDR);                                                       // disable digital input on ADC pin to reduce power consumption
220
 
        ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
221
 
        ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
222
 
}
223
 
 
224
 
inline void ADC_off() {
225
 
        ADCSRA &= ~(1<<7); //ADC off
226
 
}
227
 
 
228
 
void set_output(uint8_t pwm_lvl) {
229
 
        #ifdef DUAL_PWM_START
230
 
        if (pwm_lvl > DUAL_PWM_START) {
231
 
                // Using the normal output along with the alternate
232
 
                PWM_LVL = pwm_lvl;
233
 
        } else {
234
 
                PWM_LVL = 0;
235
 
        }
236
 
        #else
237
 
        PWM_LVL = pwm_lvl;
238
 
        #endif
239
 
        // Always set alternate PWM value even if not compiled for dual output as we will use this value
240
 
        // throughout the code when trying to see what the current output level is.  Setting this wont affect
241
 
        // the output when alternate output is disabled.
242
 
        ALT_PWM_LVL = pwm_lvl;
243
 
}
244
 
 
245
 
#ifdef VOLTAGE_MON
246
 
uint8_t low_voltage(uint8_t voltage_val) {
247
 
        // Start conversion
248
 
        ADCSRA |= (1 << ADSC);
249
 
        // Wait for completion
250
 
        while (ADCSRA & (1 << ADSC));
251
 
        // See if voltage is lower than what we were looking for
252
 
        if (ADCH < voltage_val) {
253
 
                // See if it's been low for a while
254
 
                if (++lowbatt_cnt > 8) {
255
 
                        lowbatt_cnt = 0;
256
 
                        return 1;
257
 
                }
258
 
        } else {
259
 
                lowbatt_cnt = 0;
260
 
        }
261
 
        return 0;
262
 
}
263
 
#endif
264
 
 
265
 
ISR(WDT_vect) {
266
 
        static uint8_t ticks = 0;
267
 
        if (ticks < 255) ticks++;
268
 
        // If you want more than 255 for longer turbo timeouts
269
 
        //static uint16_t ticks = 0;
270
 
        //if (ticks < 60000) ticks++;
271
 
        
272
 
#ifdef MODE_TURBO       
273
 
        //if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
274
 
        if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
275
 
                #ifdef TURBO_RAMP_DOWN
276
 
                set_output(PWM_LVL - 1);
277
 
                #else
278
 
                // Turbo mode is always at end
279
 
                set_output(MODE_TURBO_LOW);
280
 
                if (MODE_TURBO_LOW <= modes[mode_idx-1]) {
281
 
                        // Dropped at or below the previous mode, so set it to the stored mode
282
 
                        // Kept this as it was the same functionality as before.  For the TURBO_RAMP_DOWN feature
283
 
                        // it doesn't do this logic because I don't know what makes the most sense
284
 
                        store_mode_idx(--mode_idx);
285
 
                }
286
 
                #endif
287
 
        }
288
 
#endif
289
 
 
290
 
        // for some reason, it behaves like on-time mem without this here
291
 
        // (but it shouldn't be needed)
292
 
        noinit_decay = 0;
293
 
}
294
 
 
295
 
int main(void)
296
 
{       
297
 
        // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
298
 
        #ifdef DUAL_PWM_START
299
 
        PORTB = (1 << STAR3_PIN);
300
 
        #else
301
 
        PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN);
302
 
        #endif
303
 
        
304
 
        // Determine what mode we should fire up
305
 
        // Read the last mode that was saved
306
 
        read_mode_idx();
307
 
        
308
 
        check_stars(); // Moving down here as it might take a bit for the pull-up to turn on?
309
 
        
310
 
        if (! noinit_decay) {
311
 
                // Indicates they did a short press, go to the next mode
312
 
                next_mode(); // Will handle wrap arounds
313
 
                store_mode_idx(mode_idx);
314
 
        } else {
315
 
                // Didn't have a short press, keep the same mode
316
 
        #ifndef MEMORY
317
 
                // Reset to the first mode
318
 
                mode_idx = ((mode_dir == 1) ? 0 : (mode_cnt - 1));
319
 
                store_mode_idx(mode_idx);
320
 
        #endif
321
 
        }
322
 
        // set noinit data for next boot
323
 
        noinit_decay = 0;  // will decay to non-zero after being off for a while
324
 
 
325
 
    // Set PWM pin to output
326
 
    DDRB |= (1 << PWM_PIN);
327
 
        #ifdef DUAL_PWM_START
328
 
        DDRB |= (1 << STAR2_PIN);
329
 
        #endif
330
 
 
331
 
    // Set timer to do PWM for correct output pin and set prescaler timing
332
 
    TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
333
 
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
334
 
        
335
 
        // Turn features on or off as needed
336
 
        #ifdef VOLTAGE_MON
337
 
        ADC_on();
338
 
        #else
339
 
        ADC_off();
340
 
        #endif
341
 
        ACSR   |=  (1<<7); //AC off
342
 
        
343
 
        // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
344
 
        // Will allow us to go idle between WDT interrupts
345
 
        set_sleep_mode(SLEEP_MODE_IDLE);
346
 
        
347
 
        uint8_t prev_mode_idx = mode_idx;
348
 
        
349
 
        WDT_on();
350
 
        
351
 
        // Now just fire up the mode
352
 
    // Set timer to do PWM for correct output pin and set prescaler timing
353
 
        if (modes[mode_idx] > FAST_PWM_START) {
354
 
                #ifdef DUAL_PWM_START
355
 
                TCCR0A = 0b10100011; // fast-PWM both outputs
356
 
                #else
357
 
                TCCR0A = 0b00100011; // fast-PWM normal output
358
 
                #endif
359
 
        } else {
360
 
                #ifdef DUAL_PWM_START
361
 
                TCCR0A = 0b10100001; // phase corrected PWM both outputs
362
 
                #else
363
 
                TCCR0A = 0b00100001; // phase corrected PWM normal output
364
 
                #endif
365
 
        }
366
 
        TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
367
 
        
368
 
        set_output(modes[mode_idx]);
369
 
        
370
 
        uint8_t i = 0;
371
 
        uint8_t hold_pwm;
372
 
        while(1) {
373
 
        #ifdef VOLTAGE_MON
374
 
                if (low_voltage(ADC_LOW)) {
375
 
                        // We need to go to a lower level
376
 
                        if (mode_idx == 0 && ALT_PWM_LVL <= modes[mode_idx]) {
377
 
                                // Can't go any lower than the lowest mode
378
 
                                // Wait until we hit the critical level before flashing 10 times and turning off
379
 
                                while (!low_voltage(ADC_CRIT));
380
 
                                i = 0;
381
 
                                while (i++<10) {
382
 
                                        set_output(0);
383
 
                                        _delay_ms(250);
384
 
                                        set_output(modes[0]);
385
 
                                        _delay_ms(500);
386
 
                                }
387
 
                                // Turn off the light
388
 
                                set_output(0);
389
 
                                // Disable WDT so it doesn't wake us up
390
 
                                WDT_off();
391
 
                                // Power down as many components as possible
392
 
                                set_sleep_mode(SLEEP_MODE_PWR_DOWN);
393
 
                                sleep_mode();
394
 
                        } else {
395
 
                                // Flash 3 times before lowering
396
 
                                hold_pwm = ALT_PWM_LVL;
397
 
                                i = 0;
398
 
                                while (i++<3) {
399
 
                                        set_output(0);
400
 
                                        _delay_ms(250);
401
 
                                        set_output(hold_pwm);
402
 
                                        _delay_ms(500);
403
 
                                }
404
 
                                // Lower the mode by half, but don't go below lowest level
405
 
                                if ((ALT_PWM_LVL >> 1) < modes[0]) {
406
 
                                        set_output(modes[0]);
407
 
                                        mode_idx = 0;
408
 
                                } else {                                        
409
 
                                        set_output(ALT_PWM_LVL >> 1);
410
 
                                }                                       
411
 
                                // See if we should change the current mode level if we've gone under the current mode.
412
 
                                if (ALT_PWM_LVL < modes[mode_idx]) {
413
 
                                        // Lower our recorded mode
414
 
                                        mode_idx--;
415
 
                                }
416
 
                        }
417
 
                        // Wait 3 seconds before lowering the level again
418
 
                        _delay_ms(3000);
419
 
                }
420
 
        #endif
421
 
                sleep_mode();
422
 
        }
423
 
 
424
 
    return 0; // Standard Return Code
425
 
}