5
* Star 4 -| |- Voltage ADC
11
* I use these fuse settings
12
* Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
15
* For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
18
* Star 2 = second PWM output channel
19
* Star 3 = mode memory if soldered, no memory by default
20
* Star 4 = Capacitor for off-time
23
* Resistor values for voltage divider (reference BLF-VLD README for more info)
24
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
28
* Vd (~.25 v drop from protection diode)
30
* 1912 (R1 19,100 ohms)
34
* 4701 (R2 4,700 ohms)
38
* ADC = ((V_bat - V_diode) * R2 * 255) / ((R1 + R2 ) * V_ref)
39
* 125 = ((3.0 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
40
* 121 = ((2.9 - .25 ) * 4700 * 255) / ((19100 + 4700) * 1.1 )
42
* Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
45
* To find out what value to use, plug in the target voltage (V) to this equation
46
* value = (V * 4700 * 255) / (23800 * 1.1)
49
#define F_CPU 4800000UL
52
* =========================================================================
53
* Settings to modify per driver
56
//#define FAST 0x23 // fast PWM channel 1 only
57
//#define PHASE 0x21 // phase-correct PWM channel 1 only
58
#define FAST 0xA3 // fast PWM both channels
59
#define PHASE 0xA1 // phase-correct PWM both channels
61
#define VOLTAGE_MON // Comment out to disable
62
#define OWN_DELAY // Should we use the built-in delay or our own?
63
// Adjust the timing per-driver, since the hardware has high variance
64
// Higher values will run slower, lower values run faster.
65
#define DELAY_TWEAK 950
67
//#define TICKS_250MS // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
68
// Affects turbo timeout/rampdown timing
70
#define OFFTIM3 // Use short/med/long off-time presses
71
// instead of just short/long
73
// PWM levels for the big circuit (FET or Nx7135)
74
#define MODESNx 0,0,0,70,255
75
// PWM levels for the small circuit (1x7135)
76
// (if the big circuit is a FET, use 0 for high modes here instead of 255)
77
#define MODES1x 3,15,128,255,255
78
#define MODES_PWM PHASE,FAST,FAST,FAST,PHASE
79
// Hidden modes are *before* the lowest (moon) mode, and should be specified
80
// in reverse order. So, to go backward from moon to turbo to strobe to
81
// battcheck, use BATTCHECK,STROBE,255 .
82
#define HIDDENMODES BATTCHECK,STROBE,255
83
#define HIDDENMODES_PWM PHASE,PHASE,PHASE
85
#define MODE_TURBO_LOW 140 // Level turbo ramps down to if turbo enabled
86
#define TURBO // comment out to disable turbo step-down
87
#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"
88
// variable to uint8_t
89
//#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
90
// If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
92
// These values were measured using "Moonlight Special" driver from RMM.
93
// Your mileage may vary.
94
#define ADC_42 195 // the ADC value we expect for 4.20 volts
95
#define ADC_100 195 // the ADC value for 100% full (4.2V resting)
96
#define ADC_75 184 // the ADC value for 75% full (4.0V resting)
97
#define ADC_50 174 // the ADC value for 50% full (3.8V resting)
98
#define ADC_25 159 // the ADC value for 25% full (3.5V resting)
99
#define ADC_0 133 // the ADC value for 0% full (3.0V resting)
100
#define ADC_LOW 123 // When do we start ramping down (2.8V)
101
#define ADC_CRIT 118 // When do we shut the light off (2.7V)
102
// These were JonnyC's original values
103
//#define ADC_LOW 130 // When do we start ramping
104
//#define ADC_CRIT 120 // When do we shut the light off
107
#define CAP_SHORT 190 // Value between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we consider it a short press to move to the next mode
108
#define CAP_MED 100 // Value between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we consider it a short press to move to the next mode
110
#define CAP_SHORT 130 // Value between 1 and 255 corresponding with cap voltage (0 - 1.1v) where we consider it a short press to move to the next mode
111
// Not sure the lowest you can go before getting bad readings, but with a value of 70 and a 1uF cap, it seemed to switch sometimes
112
// even when waiting 10 seconds between presses.
115
// Comment these out to disable the mode and save space
116
#define STROBE 254 // Convenience code for strobe mode
117
#define BATTCHECK 253 // Convenience code for battery check mode
120
* =========================================================================
124
#include <util/delay_basic.h>
125
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
126
static void _delay_ms(uint16_t n)
128
// TODO: make this take tenths of a ms instead of ms,
129
// for more precise timing?
130
while(n-- > 0) _delay_loop_2(DELAY_TWEAK);
133
#include <util/delay.h>
136
#include <avr/pgmspace.h>
138
#include <avr/interrupt.h>
140
#include <avr/eeprom.h>
141
#include <avr/sleep.h>
142
//#include <avr/power.h>
144
#define STAR2_PIN PB0
145
#define STAR3_PIN PB4
147
#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4)
148
#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3
150
#define VOLTAGE_PIN PB2
151
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
152
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
153
#define ADC_PRSCL 0x06 // clk/64
155
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
156
#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
167
// Modes (gets set when the light starts up based on stars)
168
PROGMEM const uint8_t modesNx[] = { MODESNx , HIDDENMODES };
169
PROGMEM const uint8_t modes1x[] = { MODES1x , HIDDENMODES };
170
PROGMEM const uint8_t modes_pwm[] = { MODES_PWM , HIDDENMODES_PWM };
171
volatile uint8_t mode_idx = 0;
172
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
173
// In other words, short press goes to the next (higher) mode,
174
// medium press goes to the previous (lower) mode.
176
uint8_t mode_cnt = sizeof(modesNx);
178
uint8_t lowbatt_cnt = 0;
180
PROGMEM const uint8_t voltage_blinks[] = {
181
ADC_0, // 1 blink for 0%-25%
182
ADC_25, // 2 blinks for 25%-50%
183
ADC_50, // 3 blinks for 50%-75%
184
ADC_75, // 4 blinks for 75%-100%
185
ADC_100, // 5 blinks for >100%
188
void store_mode_idx(uint8_t lvl) { //central method for writing (with wear leveling)
189
uint8_t oldpos=eepos;
190
eepos=(eepos+1)&31; //wear leveling, use next cell
191
// Write the current mode
192
EEARL=eepos; EEDR=lvl; EECR=32+4; EECR=32+4+2; //WRITE //32:write only (no erase) 4:enable 2:go
193
while(EECR & 2); //wait for completion
194
// Erase the last mode
195
EEARL=oldpos; EECR=16+4; EECR=16+4+2; //ERASE //16:erase only (no write) 4:enable 2:go
197
inline void read_mode_idx() {
198
eeprom_read_block(&eep, 0, 32);
199
while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
200
if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
204
inline void next_mode() {
205
mode_idx += mode_dir;
206
if (mode_idx >= mode_cnt) {
213
inline void prev_mode() {
215
// Regular mode: is between 1 and TOTAL_MODES
216
mode_idx -= mode_dir;
218
// Otherwise, wrap around
219
mode_idx = mode_cnt - 1;
222
// FIXME: use a different mechanism for hidden modes
223
if ((0x40 > mode_idx) && (mode_idx > 0)) {
224
// Regular mode: is between 1 and TOTAL_MODES
225
mode_idx -= mode_dir;
226
// FIXME: use a different mechanism for hidden modes
227
} else if ((mode_idx&0x3f) < sizeof(neg_modes)) {
228
// "Negative" mode (uses 0x40 bit to indicate "negative")
229
mode_idx = (mode_idx|0x40) + mode_dir;
231
// Otherwise, always reset to first mode
232
// (mode was too negative or otherwise out of range)
239
inline void check_stars() {
240
// Configure options based on stars
241
// 0 being low for soldered, 1 for pulled-up for not soldered
242
#if 0 // not implemented, STAR2_PIN is used for second PWM channel
245
if ((PINB & (1 << STAR2_PIN)) == 0) {
246
modes[mode_cnt++] = MODE_MOON;
249
#if 0 // Mode order not as important as mem/no-mem
251
if ((PINB & (1 << STAR3_PIN)) == 0) {
259
if ((PINB & (1 << STAR3_PIN)) == 0) {
260
memory = 1; // solder to enable memory
262
memory = 0; // unsolder to disable memory
266
inline void WDT_on() {
267
// Setup watchdog timer to only interrupt, not reset
268
cli(); // Disable interrupts
269
wdt_reset(); // Reset the WDT
270
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
272
WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
274
WDTCR = (1<<WDTIE) | (1<<WDP2) | (1<<WDP0); // Enable interrupt every 500ms
276
sei(); // Enable interrupts
279
inline void WDT_off()
281
cli(); // Disable interrupts
282
wdt_reset(); // Reset the WDT
283
MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
284
WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
285
WDTCR = 0x00; // Disable WDT
286
sei(); // Enable interrupts
289
inline void ADC_on() {
290
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
291
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
292
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
295
inline void ADC_off() {
296
ADCSRA &= ~(1<<7); //ADC off
299
void set_output(uint8_t pwm1, uint8_t pwm2) {
300
// Need PHASE to properly turn off the light
301
if ((pwm1==0) && (pwm2==0)) {
308
void set_mode(uint8_t mode) {
309
TCCR0A = pgm_read_byte(modes_pwm + mode);
310
// Only set output for solid modes
311
uint8_t out = pgm_read_byte(modesNx + mode);
312
if ((out < 250) || (out == 255)) {
313
set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
318
uint8_t get_voltage() {
320
ADCSRA |= (1 << ADSC);
321
// Wait for completion
322
while (ADCSRA & (1 << ADSC));
323
// See if voltage is lower than what we were looking for
327
uint8_t low_voltage(uint8_t voltage_val) {
328
uint8_t voltage = get_voltage();
329
// See if voltage is lower than what we were looking for
330
if (voltage < voltage_val) {
331
// See if it's been low for a while
332
if (++lowbatt_cnt > 8) {
336
_delay_ms(100); // don't take a reading *too* often
345
static uint8_t ticks = 0;
346
if (ticks < 255) ticks++;
347
// If you want more than 255 for longer turbo timeouts
348
//static uint16_t ticks = 0;
349
//if (ticks < 60000) ticks++;
352
//if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
353
if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
354
#ifdef TURBO_RAMP_DOWN
355
set_output(PWM_LVL - 1, PWM_LVL - 1);
357
// Turbo mode is always at end
358
set_output(MODE_TURBO_LOW, MODE_TURBO_LOW);
360
if (MODE_TURBO_LOW <= modes[mode_idx-1]) {
361
// Dropped at or below the previous mode, so set it to the stored mode
362
// Kept this as it was the same functionality as before. For the TURBO_RAMP_DOWN feature
363
// it doesn't do this logic because I don't know what makes the most sense
364
store_mode_idx(--mode_idx);
375
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
376
// only one star, because one is used for PWM channel 2
377
// and the other is used for the off-time capacitor
378
PORTB = (1 << STAR3_PIN);
380
// Determine what mode we should fire up
381
// Read the last mode that was saved
384
check_stars(); // Moving down here as it might take a bit for the pull-up to turn on?
386
// Start up ADC for capacitor pin
387
DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption
388
ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
389
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
391
// Wait for completion
392
while (ADCSRA & (1 << ADSC));
393
// Start again as datasheet says first result is unreliable
394
ADCSRA |= (1 << ADSC);
395
// Wait for completion
396
while (ADCSRA & (1 << ADSC));
397
if (ADCH > CAP_SHORT) {
398
// Indicates they did a short press, go to the next mode
399
next_mode(); // Will handle wrap arounds
400
store_mode_idx(mode_idx);
402
} else if (ADCH > CAP_MED) {
403
// User did a medium press, go back one mode
404
prev_mode(); // Will handle "negative" modes and wrap-arounds
405
store_mode_idx(mode_idx);
408
// Didn't have a short press, keep the same mode
409
// ... or reset to the first mode
411
// Reset to the first mode
413
store_mode_idx(mode_idx);
419
// Charge up the capacitor by setting CAP_PIN to output
420
DDRB |= (1 << CAP_PIN); // Output
421
PORTB |= (1 << CAP_PIN); // High
423
// Set PWM pin to output
424
DDRB |= (1 << PWM_PIN); // enable main channel
425
DDRB |= (1 << STAR2_PIN); // enable second channel
427
// Set timer to do PWM for correct output pin and set prescaler timing
428
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
429
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
431
// Turn features on or off as needed
437
ACSR |= (1<<7); //AC off
439
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
440
// Will allow us to go idle between WDT interrupts
441
//set_sleep_mode(SLEEP_MODE_IDLE); // not used due to blinky modes
445
// Now just fire up the mode
446
// Set timer to do PWM for correct output pin and set prescaler timing
447
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
460
output = pgm_read_byte(modesNx + mode_idx);
461
if (0) {} // placeholder in case STROBE isn't defined
463
else if (output == STROBE) {
471
else if (output == BATTCHECK) {
473
// turn off and wait one second before showing the value
474
// (also, ensure voltage is measured while not under load)
477
voltage = get_voltage();
478
voltage = get_voltage(); // the first one is unreliable
479
// division takes too much flash space
480
//voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
481
// a table uses less space than 5 logic clauses
482
for (i=0; i<sizeof(voltage_blinks); i++) {
483
if (voltage > pgm_read_byte(voltage_blinks + i)) {
488
// blink up to five times to show voltage
489
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
490
for(i=0; i<blinks; i++) {
497
_delay_ms(1000); // wait at least 1 second between readouts
501
if (low_voltage(ADC_LOW)) {
502
// We need to go to a lower level
503
if (mode_idx == 0 && ALT_PWM_LVL <= modes1x[mode_idx]) {
504
// Can't go any lower than the lowest mode
505
// Wait until we hit the critical level before flashing 10 times and turning off
506
while (!low_voltage(ADC_CRIT));
514
// Turn off the light
516
// Disable WDT so it doesn't wake us up
518
// Power down as many components as possible
519
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
522
// Flash 3 times before lowering
523
hold_pwm = ALT_PWM_LVL;
528
set_output(hold_pwm,hold_pwm);
531
// Lower the mode by half, but don't go below lowest level
532
if ((ALT_PWM_LVL >> 1) < modes1x[0]) {
536
set_output(0,ALT_PWM_LVL >> 1);
538
// See if we should change the current mode level if we've gone under the current mode.
539
if (ALT_PWM_LVL < modes1x[mode_idx]) {
540
// Lower our recorded mode
544
// Wait 3 seconds before lowering the level again
548
//sleep_mode(); // incompatible with blinky modes
551
return 0; // Standard Return Code