~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

Viewing changes to Tom_E/eswitch.c

  • Committer: Selene Scriven
  • Date: 2014-10-29 02:38:49 UTC
  • Revision ID: ubuntu@toykeeper.net-20141029023849-8k8eduvjm0v3kcsg
Added a STAR-momentary from Tom E, with easy off and strobe.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//-------------------------------------------------------------------------------------
 
2
// eswitch.c - based on STAR_mom_1.0 (STAR firmware) from JonnyC
 
3
// =========
 
4
//  Modified by Tom E
 
5
// 
 
6
// Change History
 
7
// --------------
 
8
// 03/01/2014 - from JohnnyC
 
9
// 03/02/2014 - first update: added One-click turn off, shortened the long duration press,
 
10
//              add comments, re-format code
 
11
// 03/07/2014 - increase long press from 250 to 300 msecs
 
12
// 03/08/2014 - new mode sets for FET based Nanjg's added
 
13
// 05/04/2014 - add strobe mode (compile option)
 
14
// 
 
15
//-------------------------------------------------------------------------------------
 
16
#define F_CPU 4800000UL
 
17
 
 
18
/*
 
19
 * =========================================================================
 
20
 * Settings to modify per driver
 
21
 */
 
22
 
 
23
 
 
24
 
 
25
//-------------------------------------------------------------------------------------
 
26
// Used for FET based Phase Corrected PWM:
 
27
//-------------------------------------------------------------------------------------
 
28
//#define MODES 0,12,64,128,255 // 4 modes %: 5-25-50-100 (for Kenny's Warsun)
 
29
 
 
30
#define MODES   0,1,12,102,255          // 4 modes: %: ml-5-40-100 (for Michael G. w/strobe)
 
31
 
 
32
//#define MODES 0,1,4,16,100,255        // 5 modes
 
33
 
 
34
// RMM - used in a MT-G2 build (5 modes):
 
35
//#define MODES 0,1,4,25,120,255
 
36
 
 
37
// RMM - also used this set (6 modes):
 
38
//#define MODES 0,1,4,8,20,110,255
 
39
 
 
40
 
 
41
//-------------------------------------------------------------------------------------
 
42
// Used for 7135 based Phase Corrected PWM:
 
43
//-------------------------------------------------------------------------------------
 
44
//#define MODES 0,5,15,92,255           // 5=moonlight, 15=6%, 92=36%, 255=100%
 
45
 
 
46
// Original mode settings, used with Fast PWM:
 
47
//#define MODES 0,16,32,125,255 // Must be low to high, and must start with 0
 
48
 
 
49
//-------------------------------------------------------------------------------------
 
50
 
 
51
#define LONG_PRESS_DUR 21       // How long until a press is a long press (32 is ~0.5 sec), 21 = 0.336 secs
 
52
 
 
53
#define PWM_OPTION 0x21         // 0x21 for phase corrected (9.4 kHz) or 0x23 for fast-PWM (18 kHz)
 
54
 
 
55
// ----- One-Click Turn OFF option --------------------------------------------
 
56
#define IDLE_LOCK                       // Comment out to disable
 
57
#define IDLE_TIME 75            // make the time-out 1.2 seconds
 
58
// ----------------------------------------------------------------------------
 
59
 
 
60
// ----- Option Strobe Mode ---------------------------------------------------
 
61
#define ENABLE_STROBE
 
62
#define XLONG_PRESS_DUR 48      // 38=0.6 secs, 48=0.75 secs
 
63
 
 
64
// ----- Turbo Settings -------------------------------------------------------
 
65
//#define TURBO                 // Comment out to disable - full output, step downs after n number of secs
 
66
                                                                // If turbo is enabled, it will be where 255 is listed in the modes above
 
67
#define TURBO_TIMEOUT   5625    // time before dropping down (16 msecs per, 5625=90 secs)
 
68
// ----------------------------------------------------------------------------
 
69
                                                        
 
70
// ----- Voltage Monitoring Settings ------------------------------------------
 
71
#define VOLTAGE_MON             // Comment out to disable - ramp down and eventual shutoff when battery is low
 
72
 
 
73
  #define ADC_LOW               130     // When do we start ramping (3.1v), 5 per 0.1v
 
74
  #define ADC_CRIT      115     // When do we shut the light off (2.80v)
 
75
  #define ADC_DELAY     188     // Delay in ticks between low-bat rampdowns (188 ~= 3s)
 
76
// ----------------------------------------------------------------------------
 
77
 
 
78
/*
 
79
 * =========================================================================
 
80
 */
 
81
 
 
82
#include <avr/pgmspace.h>
 
83
#include <avr/io.h>
 
84
#include <util/delay.h>
 
85
#include <avr/interrupt.h>
 
86
#include <avr/wdt.h>    
 
87
#include <avr/eeprom.h>
 
88
#include <avr/sleep.h>
 
89
//#include <avr/power.h>
 
90
 
 
91
#define STAR2_PIN   PB0         // Star 2, MCU pin #5
 
92
#define STAR3_PIN   PB4         // Star 3, MCU pin #3
 
93
#define SWITCH_PIN  PB3         // Star 4, MCU pin #2, pin the switch is connected to
 
94
#define PWM_PIN     PB1         // PWM Output, MCU pin #6
 
95
#define VOLTAGE_PIN PB2         // Voltage monitoring input, MCU pin #7
 
96
 
 
97
#define ADC_CHANNEL 0x01        // MUX 01 corresponds with PB2
 
98
#define ADC_DIDR        ADC1D           // Digital input disable bit corresponding with PB2
 
99
#define ADC_PRSCL   0x06        // clk/64
 
100
 
 
101
#define PWM_LVL OCR0B       // OCR0B is the output compare register for PB1
 
102
 
 
103
#define DB_REL_DUR 0b00001111 // time before we consider the switch released after
 
104
                                                          // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
 
105
 
 
106
 
 
107
/*
 
108
 * The actual program
 
109
 * =========================================================================
 
110
 */
 
111
 
 
112
/*
 
113
 * global variables
 
114
 */
 
115
PROGMEM  const uint8_t modes[] = { MODES };
 
116
 
 
117
volatile uint8_t modeIdx = 0;                           // current mode index (can be called a mode # (0 = OFF)
 
118
 
 
119
volatile uint8_t prevModeIdx = 0;               // used to restore the initial mode when exiting strobe mode
 
120
 
 
121
volatile uint8_t press_duration = 0;    // current press duration
 
122
 
 
123
volatile uint8_t mypwm=0;                                       // PWM output value, used in strobe mode
 
124
 
 
125
#ifdef IDLE_LOCK
 
126
 uint8_t byIdleTicks = 0;
 
127
#endif
 
128
 
 
129
 
 
130
/**************************************************************************************
 
131
* is_pressed - debounce the switch release, not the switch press
 
132
* ==========
 
133
**************************************************************************************/
 
134
int is_pressed()
 
135
{
 
136
        // Keep track of last switch values polled
 
137
        static uint8_t buffer = 0x00;
 
138
 
 
139
        // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
 
140
        buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
 
141
 
 
142
        // Return "true" if any of the last 4 polls detected a press. All last 4 polls have to
 
143
        //  be "not pressed" for this to return "false"
 
144
        return (buffer & DB_REL_DUR);
 
145
}
 
146
 
 
147
/**************************************************************************************
 
148
* PCINT_on - Enable pin change interrupts
 
149
* ========
 
150
**************************************************************************************/
 
151
inline void PCINT_on()
 
152
{
 
153
        GIMSK |= (1 << PCIE);
 
154
}
 
155
 
 
156
/**************************************************************************************
 
157
* PCINT_off - Disable pin change interrupts
 
158
* =========
 
159
**************************************************************************************/
 
160
inline void PCINT_off()
 
161
{
 
162
        GIMSK &= ~(1 << PCIE);
 
163
}
 
164
 
 
165
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
 
166
// All logic of what to do when we wake up will be handled in the main loop.
 
167
EMPTY_INTERRUPT(PCINT0_vect);
 
168
 
 
169
/**************************************************************************************
 
170
* WDT_on - Setup watchdog timer to only interrupt, not reset, every 16ms
 
171
* ======
 
172
**************************************************************************************/
 
173
inline void WDT_on()
 
174
{
 
175
        cli();                                                  // Disable interrupts
 
176
        wdt_reset();                                    // Reset the WDT
 
177
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
178
        WDTCR = (1<<WDTIE);                     // Enable interrupt every 16ms
 
179
        sei();                                                  // Enable interrupts
 
180
}
 
181
 
 
182
/**************************************************************************************
 
183
* WDT_off - turn off the WatchDog timer
 
184
* =======
 
185
**************************************************************************************/
 
186
inline void WDT_off()
 
187
{
 
188
        cli();                                                  // Disable interrupts
 
189
        wdt_reset();                                    // Reset the WDT
 
190
        MCUSR &= ~(1<<WDRF);                    // Clear Watchdog reset flag
 
191
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
192
        WDTCR = 0x00;                                   // Disable WDT
 
193
        sei();                                                  // Enable interrupts
 
194
}
 
195
 
 
196
/**************************************************************************************
 
197
* ADC_on - Turn the AtoD Converter ON
 
198
* ======
 
199
**************************************************************************************/
 
200
inline void ADC_on()
 
201
{
 
202
        ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
203
    DIDR0 |= (1 << ADC_DIDR);                                                   // disable digital input on ADC pin to reduce power consumption
 
204
        ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
205
}
 
206
 
 
207
/**************************************************************************************
 
208
* ADC_off - Turn the AtoD Converter OFF
 
209
* =======
 
210
**************************************************************************************/
 
211
inline void ADC_off()
 
212
{
 
213
        ADCSRA &= ~(1<<7); //ADC off
 
214
}
 
215
 
 
216
/**************************************************************************************
 
217
* sleep_until_switch_press - only called with the light OFF
 
218
* ========================
 
219
**************************************************************************************/
 
220
void sleep_until_switch_press()
 
221
{
 
222
        // Turn the WDT off so it doesn't wake us from sleep
 
223
        // Will also ensure interrupts are on or we will never wake up
 
224
        WDT_off();
 
225
        
 
226
        // Make sure the switch is released otherwise we will wake when the user releases the switch
 
227
        while (is_pressed())
 
228
        {
 
229
                _delay_ms(16);
 
230
 
 
231
          #ifdef ENABLE_STROBE
 
232
                // If a long press got us here to go into OFF mode, check button is held for strobe mode 
 
233
                if (press_duration < 255)
 
234
                {
 
235
                        if (++press_duration == XLONG_PRESS_DUR)
 
236
                        {
 
237
                                modeIdx = 99;                   // Set strobe mode
 
238
                                WDT_on();
 
239
                                return;
 
240
                        }
 
241
                }
 
242
          #endif
 
243
        }
 
244
        
 
245
        press_duration = 0;     // Need to reset press duration since a button release wasn't recorded
 
246
        
 
247
        
 
248
        PCINT_on();             // Enable a pin change interrupt to wake us up
 
249
        
 
250
        // Put the MCU in a low power state
 
251
        sleep_mode();   // Now go to sleep
 
252
        
 
253
        // Hey, someone must have pressed the switch!!
 
254
        
 
255
        PCINT_off();    // Disable pin change interrupt because it's only used to wake us up
 
256
        
 
257
        WDT_on();               // Turn the WDT back on to check for switch presses
 
258
        // Go back to main program
 
259
}
 
260
 
 
261
/**************************************************************************************
 
262
* next_mode - switch's to next mode, higher output mode
 
263
* =========
 
264
**************************************************************************************/
 
265
inline void next_mode()
 
266
{
 
267
        prevModeIdx      = modeIdx;
 
268
        if (++modeIdx >= sizeof(modes))
 
269
        {
 
270
                modeIdx = 0;    // Wrap around
 
271
        }
 
272
}
 
273
 
 
274
/**************************************************************************************
 
275
* prev_mode - switch's to previous mode, lower output mode
 
276
* =========
 
277
**************************************************************************************/
 
278
inline void prev_mode()
 
279
{
 
280
        prevModeIdx      = modeIdx;
 
281
        if ((modeIdx == 0) || (modeIdx > sizeof(modes)))
 
282
        {
 
283
                modeIdx = sizeof(modes) - 1;    // Wrap around
 
284
        }
 
285
        else
 
286
        {
 
287
                --modeIdx;
 
288
        }
 
289
}
 
290
 
 
291
/**************************************************************************************
 
292
* WDT_vect - The watchdog timer - this is invoked every 16ms
 
293
* ========
 
294
**************************************************************************************/
 
295
ISR(WDT_vect)
 
296
{
 
297
  #ifdef TURBO
 
298
        static uint16_t turbo_ticks = 0;
 
299
  #endif
 
300
 
 
301
  #ifdef VOLTAGE_MON
 
302
        static uint8_t  adc_ticks = ADC_DELAY;
 
303
        static uint8_t  lowbatt_cnt = 0;
 
304
  #endif
 
305
 
 
306
        if (is_pressed())
 
307
        {
 
308
                if (press_duration < 255)
 
309
                {
 
310
                        press_duration++;
 
311
                }
 
312
                
 
313
                if (press_duration == LONG_PRESS_DUR)
 
314
                {
 
315
                        // Long press, go to previous mode
 
316
                        prev_mode();
 
317
 
 
318
                  #ifdef IDLE_LOCK
 
319
                        byIdleTicks = 0;        // reset idle time
 
320
                  #endif
 
321
                }
 
322
 
 
323
          #ifdef ENABLE_STROBE
 
324
                if (press_duration == XLONG_PRESS_DUR)
 
325
                {
 
326
                        modeIdx = 99;           // make it the "special" strobe value
 
327
                }
 
328
          #endif
 
329
                
 
330
 
 
331
          #ifdef TURBO
 
332
                turbo_ticks = 0;                        // Always reset turbo timer whenever the button is pressed
 
333
          #endif
 
334
 
 
335
          #ifdef VOLTAGE_MON
 
336
                adc_ticks = ADC_DELAY;  // Same with the ramp down delay
 
337
          #endif
 
338
        }
 
339
        else            // Not pressed (debounced qualified)
 
340
        {
 
341
                if (press_duration > 0 && press_duration < LONG_PRESS_DUR)
 
342
                {
 
343
                        if (modeIdx == 99)
 
344
                        {
 
345
                                modeIdx = prevModeIdx;
 
346
                        }
 
347
                        else
 
348
                        {
 
349
                                next_mode();    // Short press, go to next mode
 
350
                                
 
351
                          #ifdef IDLE_LOCK
 
352
                                if (byIdleTicks >= IDLE_TIME)
 
353
                                modeIdx = 0;    // Turn OFF the light
 
354
                                byIdleTicks = 0;        // reset idle time
 
355
                          #endif
 
356
                        }
 
357
 
 
358
                }
 
359
                else
 
360
                {
 
361
                  #ifdef IDLE_LOCK
 
362
                        if (++byIdleTicks == 0)
 
363
                                byIdleTicks = 255;
 
364
                  #endif
 
365
 
 
366
                        // Only do turbo check when switch isn't pressed
 
367
                  #ifdef TURBO
 
368
                        if (pgm_read_byte(&modes[modeIdx]) == 255)
 
369
                        {
 
370
                                turbo_ticks++;
 
371
                                if (turbo_ticks > TURBO_TIMEOUT)
 
372
                                {
 
373
                                        prev_mode();    // Go to the previous mode
 
374
                                }
 
375
                        }
 
376
                  #endif
 
377
 
 
378
                        // Only do voltage monitoring when the switch isn't pressed
 
379
                  #ifdef VOLTAGE_MON
 
380
                        if (adc_ticks > 0)
 
381
                        {
 
382
                                --adc_ticks;
 
383
                        }
 
384
                        if (adc_ticks == 0)
 
385
                        {
 
386
                                // See if conversion is done
 
387
                                if (ADCSRA & (1 << ADIF))
 
388
                                {
 
389
                                        // See if voltage is lower than what we were looking for
 
390
                                        if (ADCH < ((modeIdx == 1) ? ADC_CRIT : ADC_LOW))
 
391
                                        {
 
392
                                                ++lowbatt_cnt;
 
393
                                        }
 
394
                                        else
 
395
                                        {
 
396
                                                lowbatt_cnt = 0;
 
397
                                        }
 
398
                                }
 
399
                                
 
400
                                // See if it's been low for a while
 
401
                                if (lowbatt_cnt >= 4)
 
402
                                {
 
403
                                        prev_mode();
 
404
                                        lowbatt_cnt = 0;
 
405
                                        // If we reach 0 here, main loop will go into sleep mode
 
406
                                        // Restart the counter to when we step down again
 
407
                                        adc_ticks = ADC_DELAY;
 
408
                                }
 
409
                                
 
410
                                // Make sure conversion is running for next time through
 
411
                                ADCSRA |= (1 << ADSC);
 
412
                        }
 
413
                  #endif
 
414
                }
 
415
                press_duration = 0;
 
416
        } // button released (not pressed)
 
417
}
 
418
 
 
419
 
 
420
/**************************************************************************************
 
421
* main - main program loop. This is where it all happens...
 
422
* ====
 
423
**************************************************************************************/
 
424
int main(void)
 
425
{       
 
426
        // Set all ports to input, and turn pull-up resistors on for the inputs we are using
 
427
        DDRB = 0x00;
 
428
        PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
 
429
 
 
430
        // Set the switch as an interrupt for when we turn pin change interrupts on
 
431
        PCMSK = (1 << SWITCH_PIN);
 
432
        
 
433
        // Set PWM pin to output
 
434
        DDRB = (1 << PWM_PIN);
 
435
 
 
436
        // Set timer to do PWM for correct output pin and set prescaler timing
 
437
        TCCR0A = PWM_OPTION; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
438
        TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
439
        
 
440
        // Turn features on or off as needed
 
441
  #ifdef VOLTAGE_MON
 
442
        ADC_on();
 
443
  #else
 
444
        ADC_off();
 
445
  #endif
 
446
 
 
447
        ACSR   |=  (1<<7); //AC off
 
448
        
 
449
        // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
 
450
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
451
        sleep_until_switch_press();
 
452
        
 
453
        uint8_t last_mode_idx = 0;
 
454
        
 
455
        while (1)       // run forever
 
456
        {
 
457
                // We will never leave this loop.  The WDT will interrupt to check for switch presses and 
 
458
                // will change the mode if needed.  If this loop detects that the mode has changed, run the
 
459
                // logic for that mode while continuing to check for a mode change.
 
460
                if (modeIdx != last_mode_idx)
 
461
                {
 
462
                        last_mode_idx = modeIdx;        // The WDT changed the mode.
 
463
 
 
464
                        //---------------------------------------------------
 
465
                        // Mode Handling
 
466
                        //---------------------------------------------------
 
467
 
 
468
                  #ifdef ENABLE_STROBE
 
469
                        if (modeIdx == 99)      // strobe at 12.5Hz
 
470
                        {
 
471
                                mypwm = 255;
 
472
                                while (1)
 
473
                                {
 
474
                                        PWM_LVL = mypwm; _delay_ms(20);
 
475
                                        
 
476
                                        PWM_LVL = 0; _delay_ms(60);
 
477
                                        
 
478
                                        // If the mode got changed, break out of here to process the mode change
 
479
                                        if (modeIdx != last_mode_idx)
 
480
                                                break;
 
481
                                } // run forever
 
482
                        }
 
483
                        else
 
484
                  #endif
 
485
                        {
 
486
                                PWM_LVL = pgm_read_byte(&modes[modeIdx]);
 
487
                                if (PWM_LVL == 0)
 
488
                                {
 
489
                                        _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
 
490
                                        
 
491
                                        sleep_until_switch_press();     // Go to sleep
 
492
                                }
 
493
                        }
 
494
 
 
495
                } // mode change detected
 
496
 
 
497
        } // while
 
498
 
 
499
        return 0; // Standard Return Code (will never get here)
 
500
}
 
 
b'\\ No newline at end of file'