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
70
// FIXME: make 1-channel vs 2-channel power a single #define option
71
#define FET_7135_LAYOUT // specify an I/O pin layout
72
// Also, assign I/O pins in this file:
73
#include "../tk-attiny.h"
76
* =========================================================================
77
* Settings to modify per driver
80
//#define FAST 0x23 // fast PWM channel 1 only
81
//#define PHASE 0x21 // phase-correct PWM channel 1 only
82
#define FAST 0xA3 // fast PWM both channels
83
#define PHASE 0xA1 // phase-correct PWM both channels
85
#define VOLTAGE_MON // Comment out to disable LVP
87
#define OFFTIM3 // Use short/med/long off-time presses
88
// instead of just short/long
90
// comment out to use extended config mode instead of a solderable star
91
// (controls whether mode memory is on the star or if it's a setting in config mode)
92
//#define CONFIG_STARS
94
// output to use for blinks on battery check mode (primary PWM level, alt PWM level)
95
// Use 20,0 for a single-channel driver or 0,20 for a two-channel driver
96
#define BLINK_BRIGHTNESS 0,20
100
// PWM levels for the big circuit (FET or Nx7135)
101
#define MODESNx1 0,0,0,7,56,137,255
102
// PWM levels for the small circuit (1x7135)
103
#define MODES1x1 3,20,110,255,255,255,0
104
// My sample: 6=0..6, 7=2..11, 8=8..21(15..32)
105
// Krono sample: 6=5..21, 7=17..32, 8=33..96(50..78)
106
// Manker2: 2=21, 3=39, 4=47, ... 6?=68
107
// PWM speed for each mode
108
#define MODES_PWM1 PHASE,FAST,FAST,FAST,FAST,FAST,PHASE
111
#define MODESNx2 0,0,90,255
112
#define MODES1x2 20,230,255,0
113
#define MODES_PWM2 FAST,FAST,FAST,PHASE
114
// Hidden modes are *before* the lowest (moon) mode, and should be specified
115
// in reverse order. So, to go backward from moon to turbo to strobe to
116
// battcheck, use BATTCHECK,STROBE,TURBO .
118
#define HIDDENMODES BIKING_STROBE,BATTCHECK,STROBE,TURBO
119
#define HIDDENMODES_PWM PHASE,PHASE,PHASE,PHASE
120
#define HIDDENMODES_ALT 0,0,0,0 // Zeroes, same length as NUM_HIDDEN
122
#define TURBO 255 // Convenience code for turbo mode
123
#define BATTCHECK 254 // Convenience code for battery check mode
124
// Uncomment to enable tactical strobe mode
125
#define STROBE 253 // Convenience code for strobe mode
126
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
127
#define BIKING_STROBE 252 // Convenience code for biking strobe mode
128
// comment out to use minimal version instead (smaller)
129
#define FULL_BIKING_STROBE
131
#define NON_WDT_TURBO // enable turbo step-down without WDT
132
// How many timer ticks before before dropping down.
133
// Each timer tick is 500ms, so "60" would be a 30-second stepdown.
134
// Max value of 255 unless you change "ticks"
135
#define TURBO_TIMEOUT 90
137
// Calibrate voltage and OTC in this file:
138
#include "../tk-calibration.h"
141
* =========================================================================
144
// Ignore a spurious warning, we did the cast on purpose
145
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
147
#include <avr/pgmspace.h>
148
//#include <avr/io.h>
149
//#include <avr/interrupt.h>
150
#include <avr/eeprom.h>
151
#include <avr/sleep.h>
152
//#include <avr/power.h>
154
#define OWN_DELAY // Don't use stock delay functions.
155
#define USE_DELAY_MS // use _delay_ms()
156
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
157
#include "../tk-delay.h"
158
#define USE_BATTCHECK
159
//#define BATTCHECK_4bars
160
#define BATTCHECK_8bars
161
#define BLINK_SPEED 500
162
#include "../tk-voltage.h"
168
// Config / state variables
170
uint8_t memory = 0; // mode memory, or not (set via soldered star)
171
uint8_t modegroup = 0; // which mode group (set above in #defines)
172
uint8_t mode_idx = 0; // current or last-used mode number
173
// counter for entering config mode
174
// (needs to be remembered while off, but only for up to half a second)
175
uint8_t fast_presses __attribute__ ((section (".noinit")));
177
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
178
// In other words, short press goes to the next (higher) mode,
179
// medium press goes to the previous (lower) mode.
181
// total length of current mode group's array
183
// number of regular non-hidden modes in current mode group
185
// number of hidden modes in the current mode group
186
// (hardcoded because both groups have the same hidden modes)
187
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
190
// Modes (gets set when the light starts up based on saved config values)
191
PROGMEM const uint8_t modesNx1[] = { MODESNx1, HIDDENMODES };
192
PROGMEM const uint8_t modesNx2[] = { MODESNx2, HIDDENMODES };
193
const uint8_t *modesNx; // gets pointed at whatever group is current
195
PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT };
196
PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT };
197
const uint8_t *modes1x;
199
PROGMEM const uint8_t modes_pwm1[] = { MODES_PWM1, HIDDENMODES_PWM };
200
PROGMEM const uint8_t modes_pwm2[] = { MODES_PWM2, HIDDENMODES_PWM };
201
const uint8_t *modes_pwm;
203
void save_state() { // central method for writing (with wear leveling)
204
// a single 16-bit write uses less ROM space than two 8-bit writes
206
uint8_t oldpos=eepos;
208
eepos = (eepos+1) & (EEPSIZE-1); // wear leveling, use next cell
211
eep = mode_idx | (modegroup << 5);
213
eep = mode_idx | (modegroup << 5) | (memory << 6);
215
eeprom_write_byte((uint8_t *)(eepos), eep); // save current state
216
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
219
void restore_state() {
221
// find the config data
222
for(eepos=0; eepos<EEPSIZE; eepos++) {
223
eep = eeprom_read_byte((const uint8_t *)eepos);
224
if (eep != 0xff) break;
226
// unpack the config data
227
if (eepos < EEPSIZE) {
228
mode_idx = eep & 0x0f;
229
modegroup = (eep >> 5) & 1;
231
memory = (eep >> 6) & 1;
234
// unnecessary, save_state handles wrap-around
235
// (and we don't really care about it skipping cell 0 once in a while)
239
inline void next_mode() {
241
if (mode_idx >= solid_modes) {
242
// Wrap around, skipping the hidden modes
243
// (note: this also applies when going "forward" from any hidden mode)
249
inline void prev_mode() {
250
if (mode_idx == solid_modes) {
251
// If we hit the end of the hidden modes, go back to moon
253
} else if (mode_idx > 0) {
254
// Regular mode: is between 1 and TOTAL_MODES
257
// Otherwise, wrap around (this allows entering hidden modes)
258
mode_idx = mode_cnt - 1;
264
inline void check_stars() {
265
// Configure options based on stars
266
// 0 being low for soldered, 1 for pulled-up for not soldered
267
#if 0 // not implemented, STAR2_PIN is used for second PWM channel
270
if ((PINB & (1 << STAR2_PIN)) == 0) {
271
modes[mode_cnt++] = MODE_MOON;
274
#if 0 // Mode order not as important as mem/no-mem
276
if ((PINB & (1 << STAR3_PIN)) == 0) {
284
if ((PINB & (1 << STAR3_PIN)) == 0) {
285
memory = 1; // solder to enable memory
287
memory = 0; // unsolder to disable memory
290
#endif // ifdef CONFIG_STARS
294
* Determine how many solid and hidden modes we have.
295
* The modes_pwm array should have several values for regular modes
296
* then some values for hidden modes.
298
* (this matters because we have more than one set of modes to choose
299
* from, so we need to count at runtime)
301
if (modegroup == 0) {
302
solid_modes = NUM_MODES1;
305
modes_pwm = modes_pwm1;
307
solid_modes = NUM_MODES2;
310
modes_pwm = modes_pwm2;
312
mode_cnt = solid_modes + NUM_HIDDEN;
315
void set_output(uint8_t pwm1, uint8_t pwm2) {
316
// Need PHASE to properly turn off the light
317
if ((pwm1==0) && (pwm2==0)) {
324
void set_mode(uint8_t mode) {
325
TCCR0A = pgm_read_byte(modes_pwm + mode);
326
set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
328
// Only set output for solid modes
329
uint8_t out = pgm_read_byte(modesNx + mode);
330
if ((out < 250) || (out == 255)) {
331
set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
336
void blink(uint8_t val)
340
set_output(BLINK_BRIGHTNESS);
341
_delay_ms(BLINK_SPEED / 5);
343
_delay_ms(BLINK_SPEED * 4 / 5);
348
void toggle(uint8_t *var) {
349
// Used for extended config mode
350
// Changes the value of a config option, waits for the user to "save"
351
// by turning the light off, then changes the value back in case they
352
// didn't save. Can be used repeatedly on different options, allowing
353
// the user to change and save only one at a time.
361
#endif // ifndef CONFIG_STARS
367
// Read the off-time cap *first* to get the most accurate reading
368
// Start up ADC for capacitor pin
369
DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption
370
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
371
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
373
// Wait for completion
374
while (ADCSRA & (1 << ADSC));
375
// Start again as datasheet says first result is unreliable
376
ADCSRA |= (1 << ADSC);
377
// Wait for completion
378
while (ADCSRA & (1 << ADSC));
379
cap_val = ADCH; // save this for later
382
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
383
// only one star, because one is used for PWM channel 2
384
// and the other is used for the off-time capacitor
385
PORTB = (1 << STAR3_PIN);
388
// Set PWM pin to output
389
DDRB |= (1 << PWM_PIN); // enable main channel
390
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
392
// Set timer to do PWM for correct output pin and set prescaler timing
393
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
394
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
396
// Set timer to do PWM for correct output pin and set prescaler timing
397
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
399
// Read config values and saved state
404
// Enable the current mode group
408
// memory decayed, reset it
409
// (should happen on med/long press instead
410
// because mem decay is *much* slower when the OTC is charged
411
// so let's not wait until it decays to reset it)
412
//if (fast_presses > 0x20) { fast_presses = 0; }
414
if (cap_val > CAP_SHORT) {
415
// We don't care what the value is as long as it's over 15
416
fast_presses = (fast_presses+1) & 0x1f;
417
// Indicates they did a short press, go to the next mode
418
next_mode(); // Will handle wrap arounds
420
} else if (cap_val > CAP_MED) {
422
// User did a medium press, go back one mode
423
prev_mode(); // Will handle "negative" modes and wrap-arounds
426
// Long press, keep the same mode
427
// ... or reset to the first mode
430
// Reset to the first mode
439
// Charge up the capacitor by setting CAP_PIN to output
440
DDRB |= (1 << CAP_PIN); // Output
441
PORTB |= (1 << CAP_PIN); // High
443
// Turn features on or off as needed
449
//ACSR |= (1<<7); //AC off
451
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
452
// Will allow us to go idle between WDT interrupts
453
//set_sleep_mode(SLEEP_MODE_IDLE); // not used due to blinky modes
460
uint8_t lowbatt_cnt = 0;
463
// Make sure voltage reading is running for later
464
ADCSRA |= (1 << ADSC);
467
output = pgm_read_byte(modesNx + mode_idx);
468
if (fast_presses > 0x0f) { // Config mode
469
_delay_s(); // wait for user to stop fast-pressing button
470
fast_presses = 0; // exit this mode after one use
474
// Short/small version of the config mode
475
// Toggle the mode group, blink, then exit
478
count_modes(); // reconfigure without a power cycle
481
// Longer/larger version of the config mode
482
// Toggle the mode group, blink, un-toggle, continue
485
// Toggle memory, blink, untoggle, exit
487
#endif // ifdef CONFIG_STARS
490
else if (output == STROBE) {
491
// 10Hz tactical strobe
497
#endif // ifdef STROBE
499
else if (output == BIKING_STROBE) {
500
// 2-level stutter beacon for biking and such
501
#ifdef FULL_BIKING_STROBE
511
// small/minimal version
518
#endif // ifdef BIKING_STROBE
520
else if (output == BATTCHECK) {
521
// blink zero to five times to show voltage
522
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
524
// wait between readouts
525
_delay_s(); _delay_s();
527
#endif // ifdef BATTCHECK
528
else { // Regular non-hidden solid mode
530
// This part of the code will mostly replace the WDT tick code.
532
// Do some magic here to handle turbo step-down
533
//if (ticks < 255) ticks++; // don't roll over
534
ticks ++; // actually, we don't care about roll-over prevention
535
if ((ticks > TURBO_TIMEOUT)
536
&& (output == TURBO)) {
537
mode_idx = solid_modes - 2; // step down to second-highest mode
542
// Otherwise, just sleep.
545
// If we got this far, the user has stopped fast-pressing.
546
// So, don't enter config mode.
551
if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
552
voltage = ADCH; // get_voltage();
553
// See if voltage is lower than what we were looking for
554
//if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
555
if (voltage < ADC_LOW) {
560
// See if it's been low for a while, and maybe step down
561
if (lowbatt_cnt >= 8) {
562
// DEBUG: blink on step-down:
563
//set_output(0,0); _delay_ms(100);
564
i = mode_idx; // save space by not accessing mode_idx more than necessary
565
// properly track hidden vs normal modes
566
if (i >= solid_modes) {
567
// step down from blinky modes to medium
570
// step down from solid modes one at a time
572
} else { // Already at the lowest mode
574
// Turn off the light
576
// Power down as many components as possible
577
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
584
// Wait at least 2 seconds before lowering the level again
585
_delay_ms(250); // this will interrupt blinky modes
588
// Make sure conversion is running for next time through
589
ADCSRA |= (1 << ADSC);
592
#endif // ifdef VOLTAGE_MON
593
//sleep_mode(); // incompatible with blinky modes
595
// If we got this far, the user has stopped fast-pressing.
596
// So, don't enter config mode.
597
//fast_presses = 0; // doesn't interact well with strobe, too fast
600
//return 0; // Standard Return Code