1
/* STAR_noinit version 1.3:1.0
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
* 1.3:1.0 Changed from off-time capacitor to "noinit" memory decay trick
17
* Star 4 -| |- Voltage ADC
23
* (check bin/flash*.sh for recommended values)
26
* Star 2 = Moon if connected and alternate PWM output not used
27
* Star 3 = H-L if connected, L-H if not
28
* Star 4 = Capacitor for off-time
31
* Resistor values for voltage divider (reference BLF-VLD README for more info)
32
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
36
* Vd (~.25 v drop from protection diode)
38
* 1912 (R1 19,100 ohms)
42
* 4701 (R2 4,700 ohms)
46
* ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
47
* 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
48
* 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
50
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
53
* To find out what value to use, plug in the target voltage (V) to this equation
54
* value = (V * 4700 * 255) / (23800 * 1.1)
57
#define F_CPU 4800000UL
60
* =========================================================================
61
* Settings to modify per driver
64
#define VOLTAGE_MON // Comment out to disable
66
#define MEMORY // Comment out to disable
68
//#define TICKS_250MS // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
69
// Affects turbo timeout/rampdown timing
71
#define MODE_MOON 3 // Can comment out to remove mode, but should be set through soldering stars
72
#define MODE_LOW 14 // Can comment out to remove mode
73
#define MODE_MED 39 // Can comment out to remove mode
74
//#define MODE_HIGH 255 // Can comment out to remove mode
75
#define MODE_TURBO 255 // Can comment out to remove mode
76
#define MODE_TURBO_LOW 140 // Level turbo ramps down to if turbo enabled
77
#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"
78
// variable to uint8_t
79
//#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
80
// If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
82
#define FAST_PWM_START 8 // Above what output level should we switch from phase correct to fast-PWM?
83
//#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
85
#define ADC_LOW 130 // When do we start ramping
86
#define ADC_CRIT 120 // When do we shut the light off
89
* =========================================================================
92
//#include <avr/pgmspace.h>
94
#include <util/delay.h>
95
#include <avr/interrupt.h>
97
#include <avr/eeprom.h>
98
#include <avr/sleep.h>
99
//#include <avr/power.h>
101
#define STAR2_PIN PB0
102
#define STAR3_PIN PB4
104
#define VOLTAGE_PIN PB2
105
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
106
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
107
#define ADC_PRSCL 0x06 // clk/64
109
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
110
#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
117
volatile uint8_t noinit_decay __attribute__ ((section (".noinit")));
124
// Modes (gets set when the light starts up based on stars)
125
static uint8_t modes[10]; // Don't need 10, but keeping it high enough to handle all
126
volatile uint8_t mode_idx = 0;
127
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.
128
uint8_t mode_cnt = 0;
130
uint8_t lowbatt_cnt = 0;
132
void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
133
uint8_t oldpos=eepos;
134
eepos=(eepos+1)&31; //wear leveling, use next cell
135
// Write the current mode
136
EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
137
while(EECR & 2); //wait for completion
138
// Erase the last mode
139
EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
141
inline void read_mode_idx() {
142
eeprom_read_block(&eep, 0, 32);
143
while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
144
if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
148
inline void next_mode() {
149
if (mode_idx == 0 && mode_dir == -1) {
151
mode_idx = mode_cnt - 1;
153
mode_idx += mode_dir;
154
if (mode_idx > (mode_cnt - 1)) {
161
inline void check_stars() {
162
// Load up the modes based on stars
163
// Always load up the modes array in order of lowest to highest mode
164
// 0 being low for soldered, 1 for pulled-up for not soldered
167
#ifndef DUAL_PWM_START
168
if ((PINB & (1 << STAR2_PIN)) == 0) {
170
modes[mode_cnt++] = MODE_MOON;
171
#ifndef DUAL_PWM_START
176
modes[mode_cnt++] = MODE_LOW;
179
modes[mode_cnt++] = MODE_MED;
182
modes[mode_cnt++] = MODE_HIGH;
185
modes[mode_cnt++] = MODE_TURBO;
187
if ((PINB & (1 << STAR3_PIN)) == 0) {
195
inline void WDT_on() {
196
// Setup watchdog timer to only interrupt, not reset
197
cli(); // Disable interrupts
198
wdt_reset(); // Reset the WDT
199
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
201
WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
203
WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
205
sei(); // Enable interrupts
208
inline void WDT_off()
210
cli(); // Disable interrupts
211
wdt_reset(); // Reset the WDT
212
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
213
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
214
WDTCR = 0x00; // Disable WDT
215
sei(); // Enable interrupts
218
inline void ADC_on() {
219
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
220
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
221
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
224
inline void ADC_off() {
225
ADCSRA &= ~(1<<7); //ADC off
228
void set_output(uint8_t pwm_lvl) {
229
#ifdef DUAL_PWM_START
230
if (pwm_lvl > DUAL_PWM_START) {
231
// Using the normal output along with the alternate
239
// Always set alternate PWM value even if not compiled for dual output as we will use this value
240
// throughout the code when trying to see what the current output level is. Setting this wont affect
241
// the output when alternate output is disabled.
242
ALT_PWM_LVL = pwm_lvl;
246
uint8_t low_voltage(uint8_t voltage_val) {
248
ADCSRA |= (1 << ADSC);
249
// Wait for completion
250
while (ADCSRA & (1 << ADSC));
251
// See if voltage is lower than what we were looking for
252
if (ADCH < voltage_val) {
253
// See if it's been low for a while
254
if (++lowbatt_cnt > 8) {
266
static uint8_t ticks = 0;
267
if (ticks < 255) ticks++;
268
// If you want more than 255 for longer turbo timeouts
269
//static uint16_t ticks = 0;
270
//if (ticks < 60000) ticks++;
273
//if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
274
if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
275
#ifdef TURBO_RAMP_DOWN
276
set_output(PWM_LVL - 1);
278
// Turbo mode is always at end
279
set_output(MODE_TURBO_LOW);
280
if (MODE_TURBO_LOW <= modes[mode_idx-1]) {
281
// Dropped at or below the previous mode, so set it to the stored mode
282
// Kept this as it was the same functionality as before. For the TURBO_RAMP_DOWN feature
283
// it doesn't do this logic because I don't know what makes the most sense
284
store_mode_idx(--mode_idx);
290
// for some reason, it behaves like on-time mem without this here
291
// (but it shouldn't be needed)
297
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
298
#ifdef DUAL_PWM_START
299
PORTB = (1 << STAR3_PIN);
301
PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN);
304
// Determine what mode we should fire up
305
// Read the last mode that was saved
308
check_stars(); // Moving down here as it might take a bit for the pull-up to turn on?
310
if (! noinit_decay) {
311
// Indicates they did a short press, go to the next mode
312
next_mode(); // Will handle wrap arounds
313
store_mode_idx(mode_idx);
315
// Didn't have a short press, keep the same mode
317
// Reset to the first mode
318
mode_idx = ((mode_dir == 1) ? 0 : (mode_cnt - 1));
319
store_mode_idx(mode_idx);
322
// set noinit data for next boot
323
noinit_decay = 0; // will decay to non-zero after being off for a while
325
// Set PWM pin to output
326
DDRB |= (1 << PWM_PIN);
327
#ifdef DUAL_PWM_START
328
DDRB |= (1 << STAR2_PIN);
331
// Set timer to do PWM for correct output pin and set prescaler timing
332
TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
333
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
335
// Turn features on or off as needed
341
ACSR |= (1<<7); //AC off
343
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
344
// Will allow us to go idle between WDT interrupts
345
set_sleep_mode(SLEEP_MODE_IDLE);
347
uint8_t prev_mode_idx = mode_idx;
351
// Now just fire up the mode
352
// Set timer to do PWM for correct output pin and set prescaler timing
353
if (modes[mode_idx] > FAST_PWM_START) {
354
#ifdef DUAL_PWM_START
355
TCCR0A = 0b10100011; // fast-PWM both outputs
357
TCCR0A = 0b00100011; // fast-PWM normal output
360
#ifdef DUAL_PWM_START
361
TCCR0A = 0b10100001; // phase corrected PWM both outputs
363
TCCR0A = 0b00100001; // phase corrected PWM normal output
366
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
368
set_output(modes[mode_idx]);
374
if (low_voltage(ADC_LOW)) {
375
// We need to go to a lower level
376
if (mode_idx == 0 && ALT_PWM_LVL <= modes[mode_idx]) {
377
// Can't go any lower than the lowest mode
378
// Wait until we hit the critical level before flashing 10 times and turning off
379
while (!low_voltage(ADC_CRIT));
384
set_output(modes[0]);
387
// Turn off the light
389
// Disable WDT so it doesn't wake us up
391
// Power down as many components as possible
392
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
395
// Flash 3 times before lowering
396
hold_pwm = ALT_PWM_LVL;
401
set_output(hold_pwm);
404
// Lower the mode by half, but don't go below lowest level
405
if ((ALT_PWM_LVL >> 1) < modes[0]) {
406
set_output(modes[0]);
409
set_output(ALT_PWM_LVL >> 1);
411
// See if we should change the current mode level if we've gone under the current mode.
412
if (ALT_PWM_LVL < modes[mode_idx]) {
413
// Lower our recorded mode
417
// Wait 3 seconds before lowering the level again
424
return 0; // Standard Return Code