~gabe/flashlight-firmware/anduril2

« back to all changes in this revision

Viewing changes to JonnyC/STAR/Dual_switch/DualSwitch_1.0.c

  • Committer: Selene Scriven
  • Date: 2014-07-20 21:22:43 UTC
  • Revision ID: ubuntu@toykeeper.net-20140720212243-8ognv348vkxt1kyl
Added JonnyC's "STAR" family of firmwares.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * NANJG 105C Diagram
 
3
 *           ---
 
4
 *         -|   |- VCC
 
5
 *  Star 4 -|   |- Voltage ADC
 
6
 *  Star 3 -|   |- PWM
 
7
 *     GND -|   |- Star 2
 
8
 *           ---
 
9
 
 
10
 * CPU speed is 4.8Mhz without the 8x divider when low fuse is 0x75
 
11
 *
 
12
 * define F_CPU 4800000  CPU: 4.8MHz  PWM: 9.4kHz       ####### use low fuse: 0x75  #######
 
13
 *                             /8     PWM: 1.176kHz     ####### use low fuse: 0x65  #######
 
14
 * define F_CPU 9600000  CPU: 9.6MHz  PWM: 19kHz        ####### use low fuse: 0x7a  #######
 
15
 *                             /8     PWM: 2.4kHz       ####### use low fuse: 0x6a  #######
 
16
 * 
 
17
 * Above PWM speeds are for phase-correct PWM.  This program uses Fast-PWM, which when the CPU is 4.8MHz will be 18.75 kHz
 
18
 *
 
19
 * FUSES
 
20
 *              I use these fuse settings
 
21
 *              Low:  0x75
 
22
 *              High: 0xff
 
23
 *
 
24
 * STARS
 
25
 *              Star 2 = H-L if connected, L-H if not
 
26
 *              Star 3 = Memory if not connected
 
27
 *              Star 4 = Input for switch
 
28
 *
 
29
 * VOLTAGE
 
30
 *              Resistor values for voltage divider (reference BLF-VLD README for more info)
 
31
 *              Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
 
32
 *
 
33
 *           VCC
 
34
 *            |
 
35
 *           Vd (~.25 v drop from protection diode)
 
36
 *            |
 
37
 *          1912 (R1 19,100 ohms)
 
38
 *            |
 
39
 *            |---- PB2 from MCU
 
40
 *            |
 
41
 *          4701 (R2 4,700 ohms)
 
42
 *            |
 
43
 *           GND
 
44
 *
 
45
 *              ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
 
46
 *              125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
47
 *              121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
 
48
 *
 
49
 *              Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
 
50
 *              130 and 120
 
51
 *
 
52
 *              To find out what value to use, plug in the target voltage (V) to this equation
 
53
 *                      value = (V * 4700 * 255) / (23800 * 1.1)
 
54
 *
 
55
 *
 
56
 *  v1.0 - Initial try, there's bound to be bugs     
 
57
 */
 
58
#define F_CPU 4800000UL
 
59
 
 
60
/*
 
61
 * =========================================================================
 
62
 * Settings to modify per driver
 
63
 */
 
64
 
 
65
#define VOLTAGE_MON                     // Comment out to disable - ramp down and eventual shutoff when battery is low
 
66
#define MODES                   16,32,125,255   // Must be low to high, star determines which way we cycle through them
 
67
#define TURBO                           // Comment out to disable - full output with a step down after n number of seconds
 
68
                                                        // If turbo is enabled, it will be where 255 is listed in the modes above
 
69
#define TURBO_TIMEOUT   5625 // How many WTD ticks before before dropping down (.016 sec each)
 
70
                                                        // 90  = 5625
 
71
                                                        // 120 = 7500
 
72
                                                        
 
73
#define ADC_LOW                 130     // When do we start ramping
 
74
#define ADC_CRIT                120 // When do we shut the light off
 
75
#define ADC_DELAY               188     // Delay in ticks between low-bat rampdowns (188 ~= 3s)
 
76
 
 
77
/*
 
78
 * =========================================================================
 
79
 */
 
80
 
 
81
#include <avr/pgmspace.h>
 
82
#include <avr/io.h>
 
83
#include <util/delay.h>
 
84
#include <avr/interrupt.h>
 
85
#include <avr/wdt.h>    
 
86
#include <avr/eeprom.h>
 
87
#include <avr/sleep.h>
 
88
//#include <avr/power.h>
 
89
 
 
90
#define STAR2_PIN   PB0         // If not connected, will cycle L-H.  Connected, H-L
 
91
#define STAR3_PIN   PB4         // If not connected, will enable memory
 
92
#define SWITCH_PIN  PB3         // what pin the switch is connected to, which is Star 4
 
93
#define PWM_PIN     PB1
 
94
#define VOLTAGE_PIN PB2
 
95
#define ADC_CHANNEL 0x01        // MUX 01 corresponds with PB2
 
96
#define ADC_DIDR        ADC1D   // Digital input disable bit corresponding with PB2
 
97
#define ADC_PRSCL   0x06        // clk/64
 
98
 
 
99
#define PWM_LVL OCR0B       // OCR0B is the output compare register for PB1
 
100
 
 
101
#define DB_REL_DUR 0b00001111 // time before we consider the switch released after
 
102
                                                          // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
 
103
 
 
104
// Switch handling
 
105
#define LONG_PRESS_DUR   32 // How many WDT ticks until we consider a press a long press
 
106
                            // 32 is roughly .5 s       
 
107
 
 
108
/*
 
109
 * The actual program
 
110
 * =========================================================================
 
111
 */
 
112
 
 
113
/*
 
114
 * global variables
 
115
 */
 
116
PROGMEM  uint8_t modes[] = { MODES };
 
117
volatile uint8_t mode_idx = 0;
 
118
volatile uint8_t press_duration = 0;
 
119
volatile uint8_t low_to_high = 0;
 
120
 
 
121
// Mode storage
 
122
uint8_t eepos = 0;
 
123
uint8_t eep[32];
 
124
uint8_t memory = 0;
 
125
 
 
126
void store_mode_idx(uint8_t lvl) {  //central method for writing (with wear leveling)
 
127
        uint8_t oldpos=eepos;
 
128
        eepos=(eepos+1)&31;  //wear leveling, use next cell
 
129
        // Write the current mode
 
130
        EEARL=eepos;  EEDR=lvl; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
 
131
        while(EECR & 2); //wait for completion
 
132
        // Erase the last mode
 
133
        EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
 
134
}
 
135
inline void read_mode_idx() {
 
136
        eeprom_read_block(&eep, 0, 32);
 
137
        while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
 
138
        if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
 
139
        else eepos=0;
 
140
}
 
141
 
 
142
// Debounced switch press value
 
143
int is_pressed()
 
144
{
 
145
        // Keep track of last switch values polled
 
146
        static uint8_t buffer = 0x00;
 
147
        // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
 
148
        buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
 
149
        return (buffer & DB_REL_DUR);
 
150
}
 
151
 
 
152
void next_mode(uint8_t save) {
 
153
        if (++mode_idx >= sizeof(modes)) {
 
154
                // Wrap around
 
155
                mode_idx = 0;
 
156
        }
 
157
        if (memory && save)     {
 
158
                store_mode_idx(mode_idx);
 
159
        }
 
160
}
 
161
 
 
162
void prev_mode(uint8_t save) {
 
163
        if (mode_idx == 0) {
 
164
                // Wrap around
 
165
                mode_idx = sizeof(modes) - 1;
 
166
        } else {
 
167
                --mode_idx;
 
168
        }
 
169
        if (memory && save)     {
 
170
                store_mode_idx(mode_idx);
 
171
        }
 
172
}
 
173
/*
 
174
inline void PCINT_on() {
 
175
        // Enable pin change interrupts
 
176
        GIMSK |= (1 << PCIE);
 
177
}
 
178
 
 
179
inline void PCINT_off() {
 
180
        // Disable pin change interrupts
 
181
        GIMSK &= ~(1 << PCIE);
 
182
}
 
183
*/
 
184
 
 
185
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
 
186
// All logic of what to do when we wake up will be handled in the main loop.
 
187
EMPTY_INTERRUPT(PCINT0_vect);
 
188
 
 
189
inline void WDT_on() {
 
190
        // Setup watchdog timer to only interrupt, not reset, every 16ms.
 
191
        cli();                                                  // Disable interrupts
 
192
        wdt_reset();                                    // Reset the WDT
 
193
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
194
        WDTCR = (1<<WDTIE);                             // Enable interrupt every 16ms
 
195
        sei();                                                  // Enable interrupts
 
196
}
 
197
/*
 
198
inline void WDT_off()
 
199
{
 
200
        cli();                                                  // Disable interrupts
 
201
        wdt_reset();                                    // Reset the WDT
 
202
        MCUSR &= ~(1<<WDRF);                    // Clear Watchdog reset flag
 
203
        WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
 
204
        WDTCR = 0x00;                                   // Disable WDT
 
205
        sei();                                                  // Enable interrupts
 
206
}
 
207
*/
 
208
 
 
209
inline void ADC_on() {
 
210
        ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
 
211
    DIDR0 |= (1 << ADC_DIDR);                                                   // disable digital input on ADC pin to reduce power consumption
 
212
        ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
 
213
}
 
214
 
 
215
inline void ADC_off() {
 
216
        ADCSRA &= ~(1<<7); //ADC off
 
217
}
 
218
 
 
219
/*
 
220
void sleep_until_switch_press()
 
221
{
 
222
        // This routine takes up a lot of program memory :(
 
223
        // Turn the WDT off so it doesn't wake us from sleep
 
224
        // Will also ensure interrupts are on or we will never wake up
 
225
        WDT_off();
 
226
        // Need to reset press duration since a button release wasn't recorded
 
227
        press_duration = 0;
 
228
        // Enable a pin change interrupt to wake us up
 
229
        // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
 
230
        while (is_pressed()) {
 
231
                _delay_ms(16);
 
232
        }
 
233
        PCINT_on();
 
234
        // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
 
235
        //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
236
        // Now go to sleep
 
237
        sleep_mode();
 
238
        // Hey, someone must have pressed the switch!!
 
239
        // Disable pin change interrupt because it's only used to wake us up
 
240
        PCINT_off();
 
241
        // Turn the WDT back on to check for switch presses
 
242
        WDT_on();
 
243
        // Go back to main program
 
244
}
 
245
*/
 
246
 
 
247
// The watchdog timer is called every 16ms
 
248
ISR(WDT_vect) {
 
249
 
 
250
        //static uint8_t  press_duration = 0;  // Pressed or not pressed
 
251
        static uint16_t turbo_ticks = 0;
 
252
        static uint8_t  adc_ticks = ADC_DELAY;
 
253
        static uint8_t  lowbatt_cnt = 0;
 
254
 
 
255
        if (is_pressed()) {
 
256
                if (press_duration < 255) {
 
257
                        press_duration++;
 
258
                }
 
259
                
 
260
                if (press_duration == LONG_PRESS_DUR) {
 
261
                        // Long press
 
262
                        if (low_to_high) {
 
263
                                prev_mode(1);
 
264
                        } else {
 
265
                                next_mode(1);
 
266
                        }                       
 
267
                }
 
268
                // Just always reset turbo timer whenever the button is pressed
 
269
                turbo_ticks = 0;
 
270
                // Same with the ramp down delay
 
271
                adc_ticks = ADC_DELAY;
 
272
        } else {
 
273
                // Not pressed
 
274
                if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
 
275
                        // Short press
 
276
                        if (low_to_high) {
 
277
                                next_mode(1);
 
278
                        } else {
 
279
                                prev_mode(1);
 
280
                        }       
 
281
                } else {
 
282
                        // Only do turbo check when switch isn't pressed
 
283
                #ifdef TURBO
 
284
                        if (pgm_read_byte(&modes[mode_idx]) == 255) {
 
285
                                turbo_ticks++;
 
286
                                if (turbo_ticks > TURBO_TIMEOUT) {
 
287
                                        // Go to the previous mode
 
288
                                        prev_mode(0);
 
289
                                }
 
290
                        }
 
291
                #endif
 
292
                        // Only do voltage monitoring when the switch isn't pressed and we aren't at the lowest level
 
293
                #ifdef VOLTAGE_MON
 
294
                        if (mode_idx > 0) {
 
295
                                if (adc_ticks > 0) {
 
296
                                        --adc_ticks;
 
297
                                }
 
298
                                if (adc_ticks == 0) {
 
299
                                        // See if conversion is done
 
300
                                        if (ADCSRA & (1 << ADIF)) {
 
301
                                                // See if voltage is lower than what we were looking for
 
302
                                                if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
 
303
                                                        ++lowbatt_cnt;
 
304
                                                } else {
 
305
                                                        lowbatt_cnt = 0;
 
306
                                                }
 
307
                                        }
 
308
                                
 
309
                                        // See if it's been low for a while
 
310
                                        if (lowbatt_cnt >= 4) {
 
311
                                                prev_mode(0);
 
312
                                                lowbatt_cnt = 0;
 
313
                                                // Restart the counter to when we step down again
 
314
                                                adc_ticks = ADC_DELAY;
 
315
                                        }
 
316
                                
 
317
                                        // Make sure conversion is running for next time through
 
318
                                        ADCSRA |= (1 << ADSC);
 
319
                                }
 
320
                        }                       
 
321
                #endif
 
322
                }
 
323
                press_duration = 0;
 
324
        }
 
325
}
 
326
 
 
327
int main(void)
 
328
{       
 
329
        // Set all ports to input, and turn pull-up resistors on for the inputs we are using
 
330
        DDRB = 0x00;
 
331
        PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
 
332
 
 
333
        // Set the switch as an interrupt for when we turn pin change interrupts on
 
334
        PCMSK = (1 << SWITCH_PIN);
 
335
        
 
336
    // Set PWM pin to output
 
337
    DDRB = (1 << PWM_PIN);
 
338
 
 
339
    // Set timer to do PWM for correct output pin and set prescaler timing
 
340
    TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
 
341
    TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
 
342
        
 
343
        // Turn features on or off as needed
 
344
        #ifdef VOLTAGE_MON
 
345
        ADC_on();
 
346
        #else
 
347
        ADC_off();
 
348
        #endif
 
349
        ACSR   |=  (1<<7); //AC off
 
350
        
 
351
        // Determine if we are going L-H, or H-L based on STAR 2
 
352
        if ((PINB & (1 << STAR2_PIN)) == 0) {
 
353
                // High to Low
 
354
                low_to_high = 0;
 
355
        } else {
 
356
                low_to_high = 1;
 
357
        }
 
358
        // Not soldered (1) should enable memory
 
359
        memory = ((PINB & (1 << STAR3_PIN)) > 0) ? 1 : 0;
 
360
        
 
361
        // Don't think we want to ever go to sleep
 
362
        // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
 
363
        //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
364
        //sleep_until_switch_press();
 
365
        
 
366
        WDT_on();
 
367
        
 
368
        // Determine what mode we should fire up
 
369
        // Read the last mode that was saved
 
370
        if (memory) {
 
371
                read_mode_idx();
 
372
        } else {
 
373
                if (low_to_high) {
 
374
                        mode_idx = 0;
 
375
                } else {
 
376
                        mode_idx = sizeof(modes);
 
377
                }               
 
378
        }
 
379
        
 
380
        PWM_LVL = pgm_read_byte(&modes[mode_idx]);
 
381
        
 
382
        uint8_t last_mode_idx = mode_idx;
 
383
        
 
384
        while(1) {
 
385
                // We will never leave this loop.  The WDT will interrupt to check for switch presses and 
 
386
                // will change the mode if needed.  If this loop detects that the mode has changed, run the
 
387
                // logic for that mode while continuing to check for a mode change.
 
388
                if (mode_idx != last_mode_idx) {
 
389
                        // Save the new mode
 
390
                        last_mode_idx = mode_idx;
 
391
                        // The WDT changed the mode.
 
392
                        PWM_LVL = pgm_read_byte(&modes[mode_idx]);
 
393
                }
 
394
        }
 
395
 
 
396
    return 0; // Standard Return Code
 
397
}
 
 
b'\\ No newline at end of file'