1
//-------------------------------------------------------------------------------------
2
// eswitch.c - based on STAR_mom_1.0 (STAR firmware) from JonnyC
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)
15
//-------------------------------------------------------------------------------------
16
#define F_CPU 4800000UL
19
* =========================================================================
20
* Settings to modify per driver
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)
30
#define MODES 0,1,12,102,255 // 4 modes: %: ml-5-40-100 (for Michael G. w/strobe)
32
//#define MODES 0,1,4,16,100,255 // 5 modes
34
// RMM - used in a MT-G2 build (5 modes):
35
//#define MODES 0,1,4,25,120,255
37
// RMM - also used this set (6 modes):
38
//#define MODES 0,1,4,8,20,110,255
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%
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
49
//-------------------------------------------------------------------------------------
51
#define LONG_PRESS_DUR 21 // How long until a press is a long press (32 is ~0.5 sec), 21 = 0.336 secs
53
#define PWM_OPTION 0x21 // 0x21 for phase corrected (9.4 kHz) or 0x23 for fast-PWM (18 kHz)
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
// ----------------------------------------------------------------------------
60
// ----- Option Strobe Mode ---------------------------------------------------
62
#define XLONG_PRESS_DUR 48 // 38=0.6 secs, 48=0.75 secs
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
// ----------------------------------------------------------------------------
70
// ----- Voltage Monitoring Settings ------------------------------------------
71
#define VOLTAGE_MON // Comment out to disable - ramp down and eventual shutoff when battery is low
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
// ----------------------------------------------------------------------------
79
* =========================================================================
82
#include <avr/pgmspace.h>
84
#include <util/delay.h>
85
#include <avr/interrupt.h>
87
#include <avr/eeprom.h>
88
#include <avr/sleep.h>
89
//#include <avr/power.h>
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
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
101
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
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
109
* =========================================================================
115
PROGMEM const uint8_t modes[] = { MODES };
117
volatile uint8_t modeIdx = 0; // current mode index (can be called a mode # (0 = OFF)
119
volatile uint8_t prevModeIdx = 0; // used to restore the initial mode when exiting strobe mode
121
volatile uint8_t press_duration = 0; // current press duration
123
volatile uint8_t mypwm=0; // PWM output value, used in strobe mode
126
uint8_t byIdleTicks = 0;
130
/**************************************************************************************
131
* is_pressed - debounce the switch release, not the switch press
133
**************************************************************************************/
136
// Keep track of last switch values polled
137
static uint8_t buffer = 0x00;
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);
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);
147
/**************************************************************************************
148
* PCINT_on - Enable pin change interrupts
150
**************************************************************************************/
151
inline void PCINT_on()
153
GIMSK |= (1 << PCIE);
156
/**************************************************************************************
157
* PCINT_off - Disable pin change interrupts
159
**************************************************************************************/
160
inline void PCINT_off()
162
GIMSK &= ~(1 << PCIE);
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);
169
/**************************************************************************************
170
* WDT_on - Setup watchdog timer to only interrupt, not reset, every 16ms
172
**************************************************************************************/
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
182
/**************************************************************************************
183
* WDT_off - turn off the WatchDog timer
185
**************************************************************************************/
186
inline void WDT_off()
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
196
/**************************************************************************************
197
* ADC_on - Turn the AtoD Converter ON
199
**************************************************************************************/
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
207
/**************************************************************************************
208
* ADC_off - Turn the AtoD Converter OFF
210
**************************************************************************************/
211
inline void ADC_off()
213
ADCSRA &= ~(1<<7); //ADC off
216
/**************************************************************************************
217
* sleep_until_switch_press - only called with the light OFF
218
* ========================
219
**************************************************************************************/
220
void sleep_until_switch_press()
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
226
// Make sure the switch is released otherwise we will wake when the user releases the switch
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)
235
if (++press_duration == XLONG_PRESS_DUR)
237
modeIdx = 99; // Set strobe mode
245
press_duration = 0; // Need to reset press duration since a button release wasn't recorded
248
PCINT_on(); // Enable a pin change interrupt to wake us up
250
// Put the MCU in a low power state
251
sleep_mode(); // Now go to sleep
253
// Hey, someone must have pressed the switch!!
255
PCINT_off(); // Disable pin change interrupt because it's only used to wake us up
257
WDT_on(); // Turn the WDT back on to check for switch presses
258
// Go back to main program
261
/**************************************************************************************
262
* next_mode - switch's to next mode, higher output mode
264
**************************************************************************************/
265
inline void next_mode()
267
prevModeIdx = modeIdx;
268
if (++modeIdx >= sizeof(modes))
270
modeIdx = 0; // Wrap around
274
/**************************************************************************************
275
* prev_mode - switch's to previous mode, lower output mode
277
**************************************************************************************/
278
inline void prev_mode()
280
prevModeIdx = modeIdx;
281
if ((modeIdx == 0) || (modeIdx > sizeof(modes)))
283
modeIdx = sizeof(modes) - 1; // Wrap around
291
/**************************************************************************************
292
* WDT_vect - The watchdog timer - this is invoked every 16ms
294
**************************************************************************************/
298
static uint16_t turbo_ticks = 0;
302
static uint8_t adc_ticks = ADC_DELAY;
303
static uint8_t lowbatt_cnt = 0;
308
if (press_duration < 255)
313
if (press_duration == LONG_PRESS_DUR)
315
// Long press, go to previous mode
319
byIdleTicks = 0; // reset idle time
324
if (press_duration == XLONG_PRESS_DUR)
326
modeIdx = 99; // make it the "special" strobe value
332
turbo_ticks = 0; // Always reset turbo timer whenever the button is pressed
336
adc_ticks = ADC_DELAY; // Same with the ramp down delay
339
else // Not pressed (debounced qualified)
341
if (press_duration > 0 && press_duration < LONG_PRESS_DUR)
345
modeIdx = prevModeIdx;
349
next_mode(); // Short press, go to next mode
352
if (byIdleTicks >= IDLE_TIME)
353
modeIdx = 0; // Turn OFF the light
354
byIdleTicks = 0; // reset idle time
362
if (++byIdleTicks == 0)
366
// Only do turbo check when switch isn't pressed
368
if (pgm_read_byte(&modes[modeIdx]) == 255)
371
if (turbo_ticks > TURBO_TIMEOUT)
373
prev_mode(); // Go to the previous mode
378
// Only do voltage monitoring when the switch isn't pressed
386
// See if conversion is done
387
if (ADCSRA & (1 << ADIF))
389
// See if voltage is lower than what we were looking for
390
if (ADCH < ((modeIdx == 1) ? ADC_CRIT : ADC_LOW))
400
// See if it's been low for a while
401
if (lowbatt_cnt >= 4)
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;
410
// Make sure conversion is running for next time through
411
ADCSRA |= (1 << ADSC);
416
} // button released (not pressed)
420
/**************************************************************************************
421
* main - main program loop. This is where it all happens...
423
**************************************************************************************/
426
// Set all ports to input, and turn pull-up resistors on for the inputs we are using
428
PORTB = (1 << SWITCH_PIN) | (1 << STAR2_PIN) | (1 << STAR3_PIN);
430
// Set the switch as an interrupt for when we turn pin change interrupts on
431
PCMSK = (1 << SWITCH_PIN);
433
// Set PWM pin to output
434
DDRB = (1 << PWM_PIN);
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...)
440
// Turn features on or off as needed
447
ACSR |= (1<<7); //AC off
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();
453
uint8_t last_mode_idx = 0;
455
while (1) // run forever
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)
462
last_mode_idx = modeIdx; // The WDT changed the mode.
464
//---------------------------------------------------
466
//---------------------------------------------------
469
if (modeIdx == 99) // strobe at 12.5Hz
474
PWM_LVL = mypwm; _delay_ms(20);
476
PWM_LVL = 0; _delay_ms(60);
478
// If the mode got changed, break out of here to process the mode change
479
if (modeIdx != last_mode_idx)
486
PWM_LVL = pgm_read_byte(&modes[modeIdx]);
489
_delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
491
sleep_until_switch_press(); // Go to sleep
495
} // mode change detected
499
return 0; // Standard Return Code (will never get here)
b'\\ No newline at end of file'