~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

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