2
* dth Momentary version 1.1
4
* Created: 1/11/2015 7:15:48 PM
7
* Firmware for NANJG 105C-based LED drivers using ATTINY13A MCU
8
* using momentary switch for mode change.
10
* Portions of the code and the comment section below are based upon
11
* STAR_momentary version 1.6.
16
* Short press turns ON to last mode.
17
* Long press turns ON to moon.
18
* Double press turn ON to turbo.
21
* Short press turns OFF.
22
* Long press cycles to next mode. Keep pressing to continue cycling.
23
* Double press cycles to previous mode. Keep pressing to continue cycling.
26
* There is no previous mode from moon. This is to prevent jarring transition from moon to turbo.
34
* 1.1 Modified UI, added blink-on-power option from ToyKeeper, optimizations
42
* Star 4 -| |- Voltage ADC
48
* I use these fuse settings
49
* Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
52
* For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
57
* Star 4 = Switch input
60
* Resistor values for voltage divider (reference BLF-VLD README for more info)
61
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
65
* Vd (~.25 v drop from protection diode)
67
* 1912 (R1 19,100 ohms)
71
* 4701 (R2 4,700 ohms)
75
* ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
76
* 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
77
* 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
79
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
82
* To find out what value to use, plug in the target voltage (V) to this equation
83
* value = (V * 4700 * 255) / (23800 * 1.1)
91
// clock frequency used in util/delay.h
92
#define F_CPU 4800000UL
96
* Feature configuration
98
#define VOLTAGE_MON 1 // set to 0 to disable - ramp down and eventual shutoff when battery is low
99
#define TURBO_MODE 1 // enable turbo mode with ramp down after timeout
100
#define DEBOUNCE_BOTH 1 // set to 1 to debounce both press and release, else debounce only press
101
#define BLINK_ON_POWER 1 // blink once when power is received
107
#define ALT_PWM_PIN PB0
109
#define VOLTAGE_PIN PB2
110
#define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4
111
#define STAR3_PIN PB4 // If not connected, will cycle L-H. Connected, H-L
112
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
118
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
119
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
120
#define ADC_PRSCL 0x06 // clk/64
121
#define ADC_LOW 130 // When do we start ramping
122
#define ADC_CRIT 120 // When do we shut the light off
123
#define ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
129
#define PWM_PHASE 0b00100001
130
#define PWM_FAST 0b00100011
136
#define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed)
137
#define DB_REL_DUR 0b00000011 // time before we consider the switch released
141
* Switch and mode handling
143
#define LONG_PRESS_DUR 32 // How many WDT ticks until we consider a press a long press. 32 is roughly 1/2 s
144
#define DOUBLE_PRESS_DUR 8 // How many WDT ticks below which we consider a press a double press
145
#define TURBO_TIMEOUT 3750 // How many WTD ticks before before dropping down (.016 sec each)
148
#define SLEEP_TIMEOUT 125 // How many WTD ticks after switch released before entering sleep mode (.016 sec each)
152
* include header files
154
#include <avr/pgmspace.h>
156
#include <util/delay.h>
157
#include <avr/interrupt.h>
159
#include <avr/eeprom.h>
160
#include <avr/sleep.h>
168
// main state variables
171
uint8_t on_off_state; // 0 = off, 1 = on
172
uint8_t mode_idx; // brightness level: 0 to num_modes-1
173
uint8_t ticks_until_sleep; // number of ticks left before going to sleep
174
uint8_t ticks_since_last_release; // count number of ticks since last switch released
175
uint8_t press_duration; // number of 16ms ticks that switch has been pressed
176
uint8_t double_press_detected; // detect double press
177
uint8_t long_press_disable; // disable long press detection
178
uint8_t debounce_buffer; // previous switch status used for debounce
180
uint16_t turbo_ticks; // number of ticks in TURBO mode
183
uint8_t adc_ticks; // count down timer used to check ADC
184
uint8_t lowbatt_cnt; // counter used for low battery detection
194
// put mode tables into EEPROM to save program memory
195
uint8_t EEMEM mode_level[] = {1, 4, 15, 42, 127, 255};
196
uint8_t EEMEM mode_phase[] = {PWM_PHASE, PWM_PHASE, PWM_FAST, PWM_FAST, PWM_FAST, PWM_PHASE};
197
uint8_t EEMEM mode_next[] = {1, 2, 3, 4, 5, 1};
198
uint8_t EEMEM mode_prev[] = {0, 5, 1, 2, 3, 4};
199
const uint8_t num_modes_minus1 = sizeof(mode_level) - 1;
206
// Use initializer here to reduce size of reset_state().
207
volatile state_t state =
210
START_MODE, // mode_idx
211
SLEEP_TIMEOUT, // ticks_until_sleep
212
DOUBLE_PRESS_DUR, // ticks_since_last_release
214
0, // double_press_detected
215
0, // long_press_disable
216
0xff, // debounce_buffer
221
ADC_DELAY, // adc_ticks
228
* HW configuration routines
232
// Setup and enable watchdog timer to only interrupt, not reset, every 16ms.
235
cli(); // Disable interrupts
236
wdt_reset(); // Reset the WDT
237
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
238
WDTCR = (1<<WDTIE); // Enable interrupt every 16ms
239
sei(); // Enable interrupts
243
// Disable watchdog timer
244
inline void WDT_off()
246
cli(); // Disable interrupts
247
wdt_reset(); // Reset the WDT
248
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
249
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
250
WDTCR = 0x00; // Disable WDT
251
sei(); // Enable interrupts
256
// Setup ADC for voltage monitoring
259
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
260
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
261
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
267
inline void ADC_off()
269
ADCSRA &= ~(1<<7); //ADC off
273
// Enable pin change interrupts
274
inline void PCINT_on() {
275
GIMSK |= (1 << PCIE);
279
// Disable pin change interrupts
280
inline void PCINT_off() {
281
GIMSK &= ~(1 << PCIE);
286
inline void set_pwm_mode()
288
if (state.on_off_state)
290
// read mode phase and level from EEPROM
291
TCCR0A = eeprom_read_byte(&mode_phase[state.mode_idx]);
292
PWM_LVL = eeprom_read_byte(&mode_level[state.mode_idx]);
296
// use phase-accurate PWM mode with level 0 to get LED fully off
303
// Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
304
// All logic of what to do when we wake up will be handled in the main loop.
305
EMPTY_INTERRUPT(PCINT0_vect);
308
// initialize MCU functions
309
inline void initialize_mcu()
311
// turn off watchdog timer as a precaution
314
// Set all ports to input, and turn pull-up resistors on for the inputs we are using
316
PORTB = (1 << SWITCH_PIN);
319
// Set the switch as an interrupt for when we turn pin change interrupts on
320
PCMSK = (1 << SWITCH_PIN);
322
// Set PWM pin to output
323
DDRB = (1 << PWM_PIN);
326
// Set timer to do PWM for correct output pin and set prescaler timing
327
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
330
// Turn features on or off as needed
336
ACSR |= (1<<7); //AC off
339
// Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
340
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
345
* Read state of switch with debounce
349
uint8_t debounce_pressed;
353
// retrieve debounce_pressed from MSB of state.debounce_buffer
354
debounce_pressed = state.debounce_buffer & 128;
358
// Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
359
state.debounce_buffer = ((state.debounce_buffer << 1) & 0x7f) | ((~(PINB >> SWITCH_PIN)) & 1);
363
debounce_pressed = (debounce_pressed ? ((state.debounce_buffer & DB_REL_DUR) != 0) : ((state.debounce_buffer & DB_PRES_DUR) == DB_PRES_DUR)) ? 128 : 0;
364
// store debounce_pressed in MSB of state.debounce_buffer
365
state.debounce_buffer |= debounce_pressed;
367
debounce_pressed = (state.debounce_buffer & DB_REL_DUR);
371
return debounce_pressed;
375
inline void next_mode()
377
state.mode_idx = eeprom_read_byte(&mode_next[state.mode_idx]);
381
inline void prev_mode()
383
state.mode_idx = eeprom_read_byte(&mode_prev[state.mode_idx]);
387
inline void reset_state()
389
state.ticks_until_sleep = SLEEP_TIMEOUT;
392
// start by simulating a key press
393
state.debounce_buffer = 0xff;
394
state.press_duration = 1;
395
state.ticks_since_last_release = DOUBLE_PRESS_DUR;
399
void sleep_until_switch_press()
401
// Turn the WDT off so it doesn't wake us from sleep
402
// Will also ensure interrupts are on or we will never wake up
404
// Enable a pin change interrupt to wake us up
408
// Hey, someone must have pressed the switch!!
409
// Disable pin change interrupt because it's only used to wake us up
411
// reset state and simulate a key press
413
// Turn the WDT back on to check for switch presses
415
// Go back to main program
421
* The watchdog timer is called every 16ms.
425
if (state.ticks_until_sleep == 0)
427
// do nothing if we are in sleep mode
432
uint8_t change_pwm_mode = 0; // signal when to change PWM mode
434
// Process switch input and UI mode selection
437
// detect double press when switch is first pressed
438
if ((state.press_duration++ == 0) && (state.ticks_since_last_release < DOUBLE_PRESS_DUR))
440
state.double_press_detected = 1;
441
if (state.on_off_state)
444
state.mode_idx = num_modes_minus1;
445
// disable further long press actions
446
state.long_press_disable = 1;
451
state.on_off_state = 1;
457
if ((state.press_duration == LONG_PRESS_DUR) && (state.double_press_detected == 0) && (state.on_off_state == 0))
459
// long press and previously off so turn on in lowest mode
461
state.on_off_state = 1;
463
// disable further long press actions
464
state.long_press_disable = 1;
468
if (!state.long_press_disable)
470
if ((state.press_duration == LONG_PRESS_DUR) || (state.press_duration == 2*LONG_PRESS_DUR))
472
if (state.double_press_detected == 0)
485
// detect really long key press and treat as a sequence of long key presses
486
if (state.press_duration >= 2*LONG_PRESS_DUR)
488
state.press_duration = LONG_PRESS_DUR;
492
// reset relevant counters here
494
state.adc_ticks = ADC_DELAY;
496
state.ticks_since_last_release = 0;
498
state.turbo_ticks = 0;
503
if (state.press_duration > 0 && state.press_duration < LONG_PRESS_DUR)
506
if (!state.double_press_detected)
508
// toggle on_off_state
509
state.on_off_state = state.on_off_state ^ 1;
510
change_pwm_mode = state.on_off_state;
514
// we delay turning off until DOUBLE_PRESS_DUR has passed to avoid a blink when double press from ON
515
if (state.on_off_state == 0 && state.ticks_since_last_release == DOUBLE_PRESS_DUR)
522
// Only do turbo check when switch isn't pressed and light is on
523
if (state.on_off_state && state.mode_idx == num_modes_minus1)
525
if (++state.turbo_ticks > TURBO_TIMEOUT)
533
state.turbo_ticks = 0;
539
// Only do voltage monitoring when the switch isn't pressed and light is on
540
if (state.on_off_state)
542
if (state.adc_ticks > 0)
548
// See if conversion is done
549
if (ADCSRA & (1 << ADIF))
551
// See if voltage is lower than what we were looking for
552
state.lowbatt_cnt = ADCH < ((state.mode_idx == 0) ? ADC_CRIT : ADC_LOW) ? state.lowbatt_cnt+1 : 0;
554
// See if it's been low for a while
555
if (state.lowbatt_cnt >= 4)
557
if (state.mode_idx == 0)
559
state.on_off_state = 0;
565
state.lowbatt_cnt = 0;
566
state.adc_ticks = ADC_DELAY;
569
// Make sure conversion is running for next time through
570
ADCSRA |= (1 << ADSC);
575
state.lowbatt_cnt = 0;
576
state.adc_ticks = ADC_DELAY;
581
// reset relevant counters here
582
state.press_duration = 0;
583
state.double_press_detected = 0;
584
state.long_press_disable = 0;
585
if (state.ticks_since_last_release < 255)
587
++state.ticks_since_last_release;
596
if (state.on_off_state)
598
state.ticks_until_sleep = SLEEP_TIMEOUT;
600
else if (state.ticks_until_sleep > 0)
602
--state.ticks_until_sleep;
613
PWM_LVL = eeprom_read_byte(&mode_level[num_modes_minus1]);
620
sleep_until_switch_press();
625
if (!state.ticks_until_sleep)
628
sleep_until_switch_press();
b'\\ No newline at end of file'