2
* BLF EE A6 firmware (special-edition group buy light)
3
* This light uses a FET+1 style driver, with a FET on the main PWM channel
4
* for the brightest high modes and a single 7135 chip on the secondary PWM
5
* channel so we can get stable, efficient low / medium modes. It also
6
* includes a capacitor for measuring off time.
8
* Copyright (C) 2015 Selene Scriven
10
* This program is free software: you can redistribute it and/or modify
11
* it under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* This program is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
* GNU General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with this program. If not, see <http://www.gnu.org/licenses/>.
27
* OTC -| |- Voltage ADC
28
* Star 3 -| |- PWM (FET)
29
* GND -| |- PWM (1x7135)
33
* I use these fuse settings
34
* Low: 0x75 (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
35
* High: 0xfd (to enable brownout detection)
37
* For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
40
* Star 2 = second PWM output channel
41
* Star 3 = mode memory if soldered, no memory by default
42
* Star 4 = Capacitor for off-time
45
* Resistor values for voltage divider (reference BLF-VLD README for more info)
46
* Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
50
* Vd (~.25 v drop from protection diode)
52
* 1912 (R1 19,100 ohms)
56
* 4701 (R2 4,700 ohms)
60
* To find out what values to use, flash the driver with battcheck.hex
61
* and hook the light up to each voltage you need a value for. This is
62
* much more reliable than attempting to calculate the values from a
63
* theoretical formula.
65
* Same for off-time capacitor values. Measure, don't guess.
67
// Choose your MCU here, or in the build script
72
// set some hardware-specific values...
73
// (while configuring this firmware, skip this section)
75
#define F_CPU 4800000UL
78
#define F_CPU 8000000UL
81
Hey, you need to define ATTINY.
86
* =========================================================================
87
* Settings to modify per driver
90
//#define FAST 0x23 // fast PWM channel 1 only
91
//#define PHASE 0x21 // phase-correct PWM channel 1 only
92
#define FAST 0xA3 // fast PWM both channels
93
#define PHASE 0xA1 // phase-correct PWM both channels
95
#define VOLTAGE_MON // Comment out to disable LVP
96
#define OWN_DELAY // Should we use the built-in delay or our own?
97
// Adjust the timing per-driver, since the hardware has high variance
98
// Higher values will run slower, lower values run faster.
100
#define DELAY_TWEAK 950
102
#define DELAY_TWEAK 2000
105
#define OFFTIM3 // Use short/med/long off-time presses
106
// instead of just short/long
108
// comment out to use extended config mode instead of a solderable star
109
// (controls whether mode memory is on the star or if it's a setting in config mode)
110
//#define CONFIG_STARS
112
// output to use for blinks on battery check mode (primary PWM level, alt PWM level)
113
// Use 20,0 for a single-channel driver or 0,20 for a two-channel driver
114
#define BLINK_BRIGHTNESS 0,20
118
// PWM levels for the big circuit (FET or Nx7135)
119
#define MODESNx1 0,0,0,7,56,137,255
120
// PWM levels for the small circuit (1x7135)
121
#define MODES1x1 3,20,110,255,255,255,0
122
// My sample: 6=0..6, 7=2..11, 8=8..21(15..32)
123
// Krono sample: 6=5..21, 7=17..32, 8=33..96(50..78)
124
// Manker2: 2=21, 3=39, 4=47, ... 6?=68
125
// PWM speed for each mode
126
#define MODES_PWM1 PHASE,FAST,FAST,FAST,FAST,FAST,PHASE
129
#define MODESNx2 0,0,90,255
130
#define MODES1x2 20,230,255,0
131
#define MODES_PWM2 FAST,FAST,FAST,PHASE
132
// Hidden modes are *before* the lowest (moon) mode, and should be specified
133
// in reverse order. So, to go backward from moon to turbo to strobe to
134
// battcheck, use BATTCHECK,STROBE,TURBO .
136
#define HIDDENMODES BIKING_STROBE,BATTCHECK,STROBE,TURBO
137
#define HIDDENMODES_PWM PHASE,PHASE,PHASE,PHASE
138
#define HIDDENMODES_ALT 0,0,0,0 // Zeroes, same length as NUM_HIDDEN
140
#define TURBO 255 // Convenience code for turbo mode
141
#define BATTCHECK 254 // Convenience code for battery check mode
142
// Uncomment to enable tactical strobe mode
143
#define STROBE 253 // Convenience code for strobe mode
144
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
145
#define BIKING_STROBE 252 // Convenience code for biking strobe mode
146
// comment out to use minimal version instead (smaller)
147
#define FULL_BIKING_STROBE
149
#define NON_WDT_TURBO // enable turbo step-down without WDT
150
// How many timer ticks before before dropping down.
151
// Each timer tick is 500ms, so "60" would be a 30-second stepdown.
152
// Max value of 255 unless you change "ticks"
153
#define TURBO_TIMEOUT 90
155
// These values were measured using wight's "A17HYBRID-S" driver built by DBCstm.
156
// Your mileage may vary.
157
#define ADC_42 195 // the ADC value we expect for 4.20 volts
158
#define ADC_100 195 // the ADC value for 100% full (4.2V resting)
159
#define ADC_75 186 // the ADC value for 75% full (4.0V resting)
160
#define ADC_50 176 // the ADC value for 50% full (3.8V resting)
161
#define ADC_25 162 // the ADC value for 25% full (3.5V resting)
162
#define ADC_0 138 // the ADC value for 0% full (3.0V resting)
163
#define ADC_LOW 129 // When do we start ramping down (2.8V)
164
#define ADC_CRIT 124 // When do we shut the light off (2.7V)
166
// the BLF EE A6 driver may have different offtime cap values than most other drivers
167
// Values are between 1 and 255, and can be measured with offtime-cap.c
168
// These #defines are the edge boundaries, not the center of the target.
170
#define CAP_SHORT 190 // Anything higher than this is a short press
171
#define CAP_MED 94 // Between CAP_MED and CAP_SHORT is a medium press
172
// Below CAP_MED is a long press
174
#define CAP_SHORT 115 // Anything higher than this is a short press, lower is a long press
178
* =========================================================================
181
// Ignore a spurious warning, we did the cast on purpose
182
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
185
#include <util/delay_basic.h>
186
// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
187
void _delay_ms(uint16_t n)
189
// TODO: make this take tenths of a ms instead of ms,
190
// for more precise timing?
191
while(n-- > 0) _delay_loop_2(DELAY_TWEAK);
193
void _delay_s() // because it saves a bit of ROM space to do it this way
198
#include <util/delay.h>
201
#include <avr/pgmspace.h>
202
//#include <avr/io.h>
203
//#include <avr/interrupt.h>
204
#include <avr/eeprom.h>
205
#include <avr/sleep.h>
206
//#include <avr/power.h>
208
#define STAR2_PIN PB0 // But note that there is no star 2.
209
#define STAR3_PIN PB4
211
#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4)
212
#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3
214
#define ALT_PWM_PIN PB0
215
#define VOLTAGE_PIN PB2
216
#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
217
#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
218
#define ADC_PRSCL 0x06 // clk/64
220
#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
221
#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
227
// Config / state variables
229
uint8_t memory = 0; // mode memory, or not (set via soldered star)
230
uint8_t modegroup = 0; // which mode group (set above in #defines)
231
uint8_t mode_idx = 0; // current or last-used mode number
232
// counter for entering config mode
233
// (needs to be remembered while off, but only for up to half a second)
234
uint8_t fast_presses __attribute__ ((section (".noinit")));
236
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
237
// In other words, short press goes to the next (higher) mode,
238
// medium press goes to the previous (lower) mode.
240
// total length of current mode group's array
242
// number of regular non-hidden modes in current mode group
244
// number of hidden modes in the current mode group
245
// (hardcoded because both groups have the same hidden modes)
246
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
249
// Modes (gets set when the light starts up based on saved config values)
250
PROGMEM const uint8_t modesNx1[] = { MODESNx1, HIDDENMODES };
251
PROGMEM const uint8_t modesNx2[] = { MODESNx2, HIDDENMODES };
252
const uint8_t *modesNx; // gets pointed at whatever group is current
254
PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT };
255
PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT };
256
const uint8_t *modes1x;
258
PROGMEM const uint8_t modes_pwm1[] = { MODES_PWM1, HIDDENMODES_PWM };
259
PROGMEM const uint8_t modes_pwm2[] = { MODES_PWM2, HIDDENMODES_PWM };
260
const uint8_t *modes_pwm;
262
PROGMEM const uint8_t voltage_blinks[] = {
263
ADC_0, // 1 blink for 0%-25%
264
ADC_25, // 2 blinks for 25%-50%
265
ADC_50, // 3 blinks for 50%-75%
266
ADC_75, // 4 blinks for 75%-100%
267
ADC_100, // 5 blinks for >100%
268
255, // Ceiling, don't remove
271
void save_state() { // central method for writing (with wear leveling)
272
// a single 16-bit write uses less ROM space than two 8-bit writes
274
uint8_t oldpos=eepos;
276
eepos = (eepos+1) & (EEPLEN-1); // wear leveling, use next cell
279
eep = mode_idx | (modegroup << 5);
281
eep = mode_idx | (modegroup << 5) | (memory << 6);
283
eeprom_write_byte((uint8_t *)(eepos), eep); // save current state
284
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
287
void restore_state() {
289
// find the config data
290
for(eepos=0; eepos<EEPLEN; eepos++) {
291
eep = eeprom_read_byte((const uint8_t *)eepos);
292
if (eep != 0xff) break;
294
// unpack the config data
295
if (eepos < EEPLEN) {
296
mode_idx = eep & 0x0f;
297
modegroup = (eep >> 5) & 1;
299
memory = (eep >> 6) & 1;
302
// unnecessary, save_state handles wrap-around
303
// (and we don't really care about it skipping cell 0 once in a while)
307
inline void next_mode() {
309
if (mode_idx >= solid_modes) {
310
// Wrap around, skipping the hidden modes
311
// (note: this also applies when going "forward" from any hidden mode)
317
inline void prev_mode() {
318
if (mode_idx == solid_modes) {
319
// If we hit the end of the hidden modes, go back to moon
321
} else if (mode_idx > 0) {
322
// Regular mode: is between 1 and TOTAL_MODES
325
// Otherwise, wrap around (this allows entering hidden modes)
326
mode_idx = mode_cnt - 1;
332
inline void check_stars() {
333
// Configure options based on stars
334
// 0 being low for soldered, 1 for pulled-up for not soldered
335
#if 0 // not implemented, STAR2_PIN is used for second PWM channel
338
if ((PINB & (1 << STAR2_PIN)) == 0) {
339
modes[mode_cnt++] = MODE_MOON;
342
#if 0 // Mode order not as important as mem/no-mem
344
if ((PINB & (1 << STAR3_PIN)) == 0) {
352
if ((PINB & (1 << STAR3_PIN)) == 0) {
353
memory = 1; // solder to enable memory
355
memory = 0; // unsolder to disable memory
358
#endif // ifdef CONFIG_STARS
362
* Determine how many solid and hidden modes we have.
363
* The modes_pwm array should have several values for regular modes
364
* then some values for hidden modes.
366
* (this matters because we have more than one set of modes to choose
367
* from, so we need to count at runtime)
369
if (modegroup == 0) {
370
solid_modes = NUM_MODES1;
373
modes_pwm = modes_pwm1;
375
solid_modes = NUM_MODES2;
378
modes_pwm = modes_pwm2;
380
mode_cnt = solid_modes + NUM_HIDDEN;
384
inline void ADC_on() {
385
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
387
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
389
ADMUX = (1 << REFS1) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
391
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
394
inline void ADC_off() {
395
ADCSRA &= ~(1<<7); //ADC off
399
void set_output(uint8_t pwm1, uint8_t pwm2) {
400
// Need PHASE to properly turn off the light
401
if ((pwm1==0) && (pwm2==0)) {
408
void set_mode(uint8_t mode) {
409
TCCR0A = pgm_read_byte(modes_pwm + mode);
410
set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
412
// Only set output for solid modes
413
uint8_t out = pgm_read_byte(modesNx + mode);
414
if ((out < 250) || (out == 255)) {
415
set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
421
uint8_t get_voltage() {
423
ADCSRA |= (1 << ADSC);
424
// Wait for completion
425
while (ADCSRA & (1 << ADSC));
426
// See if voltage is lower than what we were looking for
431
void blink(uint8_t val)
435
set_output(BLINK_BRIGHTNESS);
443
void toggle(uint8_t *var) {
444
// Used for extended config mode
445
// Changes the value of a config option, waits for the user to "save"
446
// by turning the light off, then changes the value back in case they
447
// didn't save. Can be used repeatedly on different options, allowing
448
// the user to change and save only one at a time.
456
#endif // ifndef CONFIG_STARS
462
// Read the off-time cap *first* to get the most accurate reading
463
// Start up ADC for capacitor pin
464
DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption
466
ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
468
ADMUX = (1 << REFS1) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
470
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
472
// Wait for completion
473
while (ADCSRA & (1 << ADSC));
474
// Start again as datasheet says first result is unreliable
475
ADCSRA |= (1 << ADSC);
476
// Wait for completion
477
while (ADCSRA & (1 << ADSC));
478
cap_val = ADCH; // save this for later
481
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
482
// only one star, because one is used for PWM channel 2
483
// and the other is used for the off-time capacitor
484
PORTB = (1 << STAR3_PIN);
487
// Set PWM pin to output
488
DDRB |= (1 << PWM_PIN); // enable main channel
489
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
491
// Set timer to do PWM for correct output pin and set prescaler timing
492
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
493
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
495
// Set timer to do PWM for correct output pin and set prescaler timing
496
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
498
// Read config values and saved state
503
// Enable the current mode group
507
// memory decayed, reset it
508
// (should happen on med/long press instead
509
// because mem decay is *much* slower when the OTC is charged
510
// so let's not wait until it decays to reset it)
511
//if (fast_presses > 0x20) { fast_presses = 0; }
513
if (cap_val > CAP_SHORT) {
514
// We don't care what the value is as long as it's over 15
515
fast_presses = (fast_presses+1) & 0x1f;
516
// Indicates they did a short press, go to the next mode
517
next_mode(); // Will handle wrap arounds
519
} else if (cap_val > CAP_MED) {
521
// User did a medium press, go back one mode
522
prev_mode(); // Will handle "negative" modes and wrap-arounds
525
// Long press, keep the same mode
526
// ... or reset to the first mode
529
// Reset to the first mode
538
// Charge up the capacitor by setting CAP_PIN to output
539
DDRB |= (1 << CAP_PIN); // Output
540
PORTB |= (1 << CAP_PIN); // High
542
// Turn features on or off as needed
548
//ACSR |= (1<<7); //AC off
550
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
551
// Will allow us to go idle between WDT interrupts
552
//set_sleep_mode(SLEEP_MODE_IDLE); // not used due to blinky modes
559
uint8_t lowbatt_cnt = 0;
562
// Make sure voltage reading is running for later
563
ADCSRA |= (1 << ADSC);
566
output = pgm_read_byte(modesNx + mode_idx);
567
if (fast_presses > 0x0f) { // Config mode
568
_delay_s(); // wait for user to stop fast-pressing button
569
fast_presses = 0; // exit this mode after one use
573
// Short/small version of the config mode
574
// Toggle the mode group, blink, then exit
577
count_modes(); // reconfigure without a power cycle
580
// Longer/larger version of the config mode
581
// Toggle the mode group, blink, un-toggle, continue
584
// Toggle memory, blink, untoggle, exit
586
#endif // ifdef CONFIG_STARS
589
else if (output == STROBE) {
590
// 10Hz tactical strobe
596
#endif // ifdef STROBE
598
else if (output == BIKING_STROBE) {
599
// 2-level stutter beacon for biking and such
600
#ifdef FULL_BIKING_STROBE
610
// small/minimal version
617
#endif // ifdef BIKING_STROBE
619
else if (output == BATTCHECK) {
620
voltage = get_voltage();
621
// figure out how many times to blink
623
voltage > pgm_read_byte(voltage_blinks + i);
626
// blink zero to five times to show voltage
627
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
629
// wait between readouts
630
_delay_s(); _delay_s();
632
#endif // ifdef BATTCHECK
633
else { // Regular non-hidden solid mode
635
// This part of the code will mostly replace the WDT tick code.
637
// Do some magic here to handle turbo step-down
638
//if (ticks < 255) ticks++; // don't roll over
639
ticks ++; // actually, we don't care about roll-over prevention
640
if ((ticks > TURBO_TIMEOUT)
641
&& (output == TURBO)) {
642
mode_idx = solid_modes - 2; // step down to second-highest mode
647
// Otherwise, just sleep.
650
// If we got this far, the user has stopped fast-pressing.
651
// So, don't enter config mode.
656
if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
657
voltage = ADCH; // get_voltage();
658
// See if voltage is lower than what we were looking for
659
//if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
660
if (voltage < ADC_LOW) {
665
// See if it's been low for a while, and maybe step down
666
if (lowbatt_cnt >= 8) {
667
// DEBUG: blink on step-down:
668
//set_output(0,0); _delay_ms(100);
669
i = mode_idx; // save space by not accessing mode_idx more than necessary
670
// properly track hidden vs normal modes
671
if (i >= solid_modes) {
672
// step down from blinky modes to medium
675
// step down from solid modes one at a time
677
} else { // Already at the lowest mode
679
// Turn off the light
681
// Power down as many components as possible
682
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
689
// Wait at least 2 seconds before lowering the level again
690
_delay_ms(250); // this will interrupt blinky modes
693
// Make sure conversion is running for next time through
694
ADCSRA |= (1 << ADSC);
697
#endif // ifdef VOLTAGE_MON
698
//sleep_mode(); // incompatible with blinky modes
700
// If we got this far, the user has stopped fast-pressing.
701
// So, don't enter config mode.
702
//fast_presses = 0; // doesn't interact well with strobe, too fast
705
//return 0; // Standard Return Code