117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
1 |
/* STAR_noinit version 1.3:1.0
|
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
2 |
*
|
3 |
* Changelog
|
|
4 |
*
|
|
5 |
* 1.0 Initial version
|
|
6 |
* 1.1 Bug fix
|
|
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
|
|
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
9 |
* 1.3:1.0 Changed from off-time capacitor to "noinit" memory decay trick
|
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
10 |
*
|
11 |
*/
|
|
12 |
||
13 |
/*
|
|
14 |
* NANJG 105C Diagram
|
|
15 |
* ---
|
|
16 |
* -| |- VCC
|
|
17 |
* Star 4 -| |- Voltage ADC
|
|
18 |
* Star 3 -| |- PWM
|
|
19 |
* GND -| |- Star 2
|
|
20 |
* ---
|
|
21 |
*
|
|
22 |
* FUSES
|
|
188.4.9
by Selene Scriven
Replaced outdated fuse value messages with a pointer to more actively maintained scripts. |
23 |
* (check bin/flash*.sh for recommended values)
|
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
24 |
*
|
25 |
* STARS
|
|
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
|
|
29 |
*
|
|
30 |
* VOLTAGE
|
|
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
|
|
33 |
*
|
|
34 |
* VCC
|
|
35 |
* |
|
|
36 |
* Vd (~.25 v drop from protection diode)
|
|
37 |
* |
|
|
38 |
* 1912 (R1 19,100 ohms)
|
|
39 |
* |
|
|
40 |
* |---- PB2 from MCU
|
|
41 |
* |
|
|
42 |
* 4701 (R2 4,700 ohms)
|
|
43 |
* |
|
|
44 |
* GND
|
|
45 |
*
|
|
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 )
|
|
49 |
*
|
|
50 |
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
|
|
51 |
* 130 and 120
|
|
52 |
*
|
|
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)
|
|
55 |
*
|
|
56 |
*/
|
|
57 |
#define F_CPU 4800000UL
|
|
58 |
||
59 |
/*
|
|
60 |
* =========================================================================
|
|
61 |
* Settings to modify per driver
|
|
62 |
*/
|
|
63 |
||
64 |
#define VOLTAGE_MON // Comment out to disable |
|
65 |
||
66 |
#define MEMORY // Comment out to disable |
|
67 |
||
68 |
//#define TICKS_250MS // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
|
|
69 |
// Affects turbo timeout/rampdown timing
|
|
70 |
||
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
|
|
81 |
||
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
|
|
84 |
||
85 |
#define ADC_LOW 130 // When do we start ramping |
|
86 |
#define ADC_CRIT 120 // When do we shut the light off |
|
87 |
||
88 |
/*
|
|
89 |
* =========================================================================
|
|
90 |
*/
|
|
91 |
||
92 |
//#include <avr/pgmspace.h>
|
|
93 |
#include <avr/io.h> |
|
94 |
#include <util/delay.h> |
|
95 |
#include <avr/interrupt.h> |
|
96 |
#include <avr/wdt.h> |
|
97 |
#include <avr/eeprom.h> |
|
98 |
#include <avr/sleep.h> |
|
99 |
//#include <avr/power.h>
|
|
100 |
||
101 |
#define STAR2_PIN PB0
|
|
102 |
#define STAR3_PIN PB4
|
|
103 |
#define PWM_PIN PB1
|
|
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 |
|
108 |
||
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 |
|
111 |
||
112 |
/*
|
|
113 |
* global variables
|
|
114 |
*/
|
|
115 |
||
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
116 |
// offtime detection
|
117 |
volatile uint8_t noinit_decay __attribute__ ((section (".noinit"))); |
|
118 |
||
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
119 |
// Mode storage
|
120 |
uint8_t eepos = 0; |
|
121 |
uint8_t eep[32]; |
|
122 |
uint8_t memory = 0; |
|
123 |
||
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; |
|
129 |
||
130 |
uint8_t lowbatt_cnt = 0; |
|
131 |
||
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 |
|
140 |
}
|
|
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? |
|
145 |
else eepos=0; |
|
146 |
}
|
|
147 |
||
148 |
inline void next_mode() { |
|
149 |
if (mode_idx == 0 && mode_dir == -1) { |
|
150 |
// Wrap around
|
|
151 |
mode_idx = mode_cnt - 1; |
|
152 |
} else { |
|
153 |
mode_idx += mode_dir; |
|
154 |
if (mode_idx > (mode_cnt - 1)) { |
|
155 |
// Wrap around
|
|
156 |
mode_idx = 0; |
|
157 |
}
|
|
158 |
}
|
|
159 |
}
|
|
160 |
||
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
|
|
165 |
// Moon
|
|
166 |
#ifdef MODE_MOON
|
|
167 |
#ifndef DUAL_PWM_START
|
|
168 |
if ((PINB & (1 << STAR2_PIN)) == 0) { |
|
169 |
#endif
|
|
170 |
modes[mode_cnt++] = MODE_MOON; |
|
171 |
#ifndef DUAL_PWM_START
|
|
172 |
}
|
|
173 |
#endif
|
|
174 |
#endif
|
|
175 |
#ifdef MODE_LOW
|
|
176 |
modes[mode_cnt++] = MODE_LOW; |
|
177 |
#endif
|
|
178 |
#ifdef MODE_MED
|
|
179 |
modes[mode_cnt++] = MODE_MED; |
|
180 |
#endif
|
|
181 |
#ifdef MODE_HIGH
|
|
182 |
modes[mode_cnt++] = MODE_HIGH; |
|
183 |
#endif
|
|
184 |
#ifdef MODE_TURBO
|
|
185 |
modes[mode_cnt++] = MODE_TURBO; |
|
186 |
#endif
|
|
187 |
if ((PINB & (1 << STAR3_PIN)) == 0) { |
|
188 |
// High to Low
|
|
189 |
mode_dir = -1; |
|
190 |
} else { |
|
191 |
mode_dir = 1; |
|
192 |
}
|
|
193 |
}
|
|
194 |
||
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 |
|
200 |
#ifdef TICKS_250MS
|
|
201 |
WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms |
|
202 |
#else
|
|
203 |
WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms |
|
204 |
#endif
|
|
205 |
sei(); // Enable interrupts |
|
206 |
}
|
|
207 |
||
208 |
inline void WDT_off() |
|
209 |
{
|
|
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 |
|
216 |
}
|
|
217 |
||
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 |
|
222 |
}
|
|
223 |
||
224 |
inline void ADC_off() { |
|
225 |
ADCSRA &= ~(1<<7); //ADC off |
|
226 |
}
|
|
227 |
||
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
|
|
232 |
PWM_LVL = pwm_lvl; |
|
233 |
} else { |
|
234 |
PWM_LVL = 0; |
|
235 |
}
|
|
236 |
#else
|
|
237 |
PWM_LVL = pwm_lvl; |
|
238 |
#endif
|
|
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; |
|
243 |
}
|
|
244 |
||
245 |
#ifdef VOLTAGE_MON
|
|
246 |
uint8_t low_voltage(uint8_t voltage_val) { |
|
247 |
// Start conversion
|
|
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) { |
|
255 |
lowbatt_cnt = 0; |
|
256 |
return 1; |
|
257 |
}
|
|
258 |
} else { |
|
259 |
lowbatt_cnt = 0; |
|
260 |
}
|
|
261 |
return 0; |
|
262 |
}
|
|
263 |
#endif
|
|
264 |
||
265 |
ISR(WDT_vect) { |
|
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++;
|
|
271 |
||
272 |
#ifdef MODE_TURBO
|
|
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); |
|
277 |
#else
|
|
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); |
|
285 |
}
|
|
286 |
#endif
|
|
287 |
}
|
|
288 |
#endif
|
|
289 |
||
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
290 |
// for some reason, it behaves like on-time mem without this here
|
291 |
// (but it shouldn't be needed)
|
|
292 |
noinit_decay = 0; |
|
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
293 |
}
|
294 |
||
295 |
int main(void) |
|
296 |
{
|
|
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); |
|
300 |
#else
|
|
301 |
PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN); |
|
302 |
#endif
|
|
303 |
||
304 |
// Determine what mode we should fire up
|
|
305 |
// Read the last mode that was saved
|
|
306 |
read_mode_idx(); |
|
307 |
||
308 |
check_stars(); // Moving down here as it might take a bit for the pull-up to turn on? |
|
309 |
||
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
310 |
if (! noinit_decay) { |
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
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); |
|
314 |
} else { |
|
315 |
// Didn't have a short press, keep the same mode
|
|
316 |
#ifndef MEMORY
|
|
317 |
// Reset to the first mode
|
|
318 |
mode_idx = ((mode_dir == 1) ? 0 : (mode_cnt - 1)); |
|
319 |
store_mode_idx(mode_idx); |
|
320 |
#endif
|
|
321 |
}
|
|
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
322 |
// set noinit data for next boot
|
323 |
noinit_decay = 0; // will decay to non-zero after being off for a while |
|
324 |
||
117.1.1
by Selene Scriven
Copied STAR_offtime to STAR_noinit, so I can mod it to use memory decay instead of OTC. |
325 |
// Set PWM pin to output
|
326 |
DDRB |= (1 << PWM_PIN); |
|
327 |
#ifdef DUAL_PWM_START
|
|
328 |
DDRB |= (1 << STAR2_PIN); |
|
329 |
#endif
|
|
330 |
||
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...) |
|
334 |
||
335 |
// Turn features on or off as needed
|
|
336 |
#ifdef VOLTAGE_MON
|
|
337 |
ADC_on(); |
|
338 |
#else
|
|
339 |
ADC_off(); |
|
340 |
#endif
|
|
341 |
ACSR |= (1<<7); //AC off |
|
342 |
||
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); |
|
346 |
||
347 |
uint8_t prev_mode_idx = mode_idx; |
|
348 |
||
349 |
WDT_on(); |
|
350 |
||
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 |
|
356 |
#else
|
|
357 |
TCCR0A = 0b00100011; // fast-PWM normal output |
|
358 |
#endif
|
|
359 |
} else { |
|
360 |
#ifdef DUAL_PWM_START
|
|
361 |
TCCR0A = 0b10100001; // phase corrected PWM both outputs |
|
362 |
#else
|
|
363 |
TCCR0A = 0b00100001; // phase corrected PWM normal output |
|
364 |
#endif
|
|
365 |
}
|
|
366 |
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) |
|
367 |
||
368 |
set_output(modes[mode_idx]); |
|
369 |
||
370 |
uint8_t i = 0; |
|
371 |
uint8_t hold_pwm; |
|
372 |
while(1) { |
|
373 |
#ifdef VOLTAGE_MON
|
|
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)); |
|
380 |
i = 0; |
|
381 |
while (i++<10) { |
|
382 |
set_output(0); |
|
383 |
_delay_ms(250); |
|
384 |
set_output(modes[0]); |
|
385 |
_delay_ms(500); |
|
386 |
}
|
|
387 |
// Turn off the light
|
|
388 |
set_output(0); |
|
389 |
// Disable WDT so it doesn't wake us up
|
|
390 |
WDT_off(); |
|
391 |
// Power down as many components as possible
|
|
392 |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
|
393 |
sleep_mode(); |
|
394 |
} else { |
|
395 |
// Flash 3 times before lowering
|
|
396 |
hold_pwm = ALT_PWM_LVL; |
|
397 |
i = 0; |
|
398 |
while (i++<3) { |
|
399 |
set_output(0); |
|
400 |
_delay_ms(250); |
|
401 |
set_output(hold_pwm); |
|
402 |
_delay_ms(500); |
|
403 |
}
|
|
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]); |
|
407 |
mode_idx = 0; |
|
408 |
} else { |
|
409 |
set_output(ALT_PWM_LVL >> 1); |
|
410 |
}
|
|
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
|
|
414 |
mode_idx--; |
|
415 |
}
|
|
416 |
}
|
|
417 |
// Wait 3 seconds before lowering the level again
|
|
418 |
_delay_ms(3000); |
|
419 |
}
|
|
420 |
#endif
|
|
421 |
sleep_mode(); |
|
422 |
}
|
|
423 |
||
424 |
return 0; // Standard Return Code |
|
117.1.2
by Selene Scriven
Switched from off-time capacitor to memory decay trick, to measure off time. |
425 |
}
|