3
* This code runs on a single-channel or dual-channel driver (FET+7135)
4
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
6
* Copyright (C) 2015 Selene Scriven
8
* This program is free software: you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, either version 3 of the License, or
11
* (at your option) any later version.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
* ATTINY25/45/85 Diagram
25
* OTC -|2 7|- Voltage ADC
26
* Star 3 -|3 6|- PWM (FET, optional)
27
* GND -|4 5|- PWM (1x7135)
31
* I use these fuse settings on attiny25
36
* For more details on these settings:
37
* http://www.engbedded.com/cgi-bin/fcx.cgi?P_PREV=ATtiny25&P=ATtiny25&M_LOW_0x3F=0x12&M_HIGH_0x07=0x06&M_HIGH_0x20=0x00&B_SPIEN=P&B_SUT0=P&B_CKSEL3=P&B_CKSEL2=P&B_CKSEL0=P&B_BODLEVEL0=P&V_LOW=E2&V_HIGH=DE&V_EXTENDED=FF
44
* To find out what values to use, flash the driver with battcheck.hex
45
* and hook the light up to each voltage you need a value for. This is
46
* much more reliable than attempting to calculate the values from a
47
* theoretical formula.
49
* Same for off-time capacitor values. Measure, don't guess.
51
// Choose your MCU here, or in the build script
54
// FIXME: make 1-channel vs 2-channel power a single #define option
55
#define FET_7135_LAYOUT // specify an I/O pin layout
56
// Also, assign I/O pins in this file:
57
#include "../tk-attiny.h"
60
* =========================================================================
61
* Settings to modify per driver
64
// FIXME: make 1-channel vs 2-channel power a single #define option
65
//#define FAST 0x23 // fast PWM channel 1 only
66
//#define PHASE 0x21 // phase-correct PWM channel 1 only
67
#define FAST 0xA3 // fast PWM both channels
68
#define PHASE 0xA1 // phase-correct PWM both channels
70
#define VOLTAGE_MON // Comment out to disable LVP
72
#define OFFTIM3 // Use short/med/long off-time presses
73
// instead of just short/long
75
// comment out to use extended config mode instead of a solderable star
76
// (controls whether mode memory is on the star or if it's a setting in config mode)
77
//#define CONFIG_STARS
79
// ../../bin/level_calc.py 64 1 10 1400 y 3 0.25 140
82
//#define RAMP_7135 3,3,3,3,3,3,4,4,4,4,4,5,5,5,6,6,7,7,8,9,10,11,12,13,15,16,18,21,23,27,30,34,39,44,50,57,65,74,85,97,111,127,145,166,190,217,248,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
83
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,11,17,23,30,39,48,59,72,86,103,121,143,168,197,255
85
//#define RAMP_7135 3,5,8,12,17,24,32,41,51,63,75,90,105,121,139,158,178,200,223,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
86
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,6,9,12,16,19,22,26,30,33,37,41,45,50,54,59,63,68,73,78,84,89,94,100,106,111,117,123,130,136,142,149,156,162,169,176,184,191,198,206,214,221,255
88
#define RAMP_7135 3,3,4,5,7,8,10,13,16,20,25,30,36,42,50,59,68,78,90,103,116,131,148,165,184,204,226,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
89
#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,7,10,13,16,20,24,28,32,36,41,46,51,56,61,67,73,80,86,93,100,107,115,123,131,139,148,157,166,176,186,196,207,218,255
91
//#define RAMP_7135 3,3,3,4,4,5,5,6,7,8,10,11,13,15,18,21,24,28,33,38,44,50,57,66,75,85,96,108,122,137,154,172,192,213,237,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
92
//#define RAMP_FET 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,9,13,17,21,25,30,35,41,47,53,60,67,75,83,91,101,111,121,132,144,156,169,183,198,213,255
94
// output to use for blinks on battery check mode
95
#define BLINK_BRIGHTNESS RAMP_SIZE/4
97
// uncomment to ramp up/down to a mode instead of jumping directly
102
// PWM levels for the big circuit (FET or Nx7135)
103
#define MODES_G1 1,10,21,32,43,53,64
104
// PWM speed for each mode
105
#define MODES_PWM1 PHASE,FAST,FAST,FAST,FAST,FAST,PHASE
108
#define MODES_G2 10,28,46,64
109
#define MODES_PWM2 FAST,FAST,FAST,PHASE
110
// Hidden modes are *before* the lowest (moon) mode, and should be specified
111
// in reverse order. So, to go backward from moon to turbo to strobe to
112
// battcheck, use BATTCHECK,STROBE,TURBO .
114
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,RAMP,TURBO
115
#define HIDDENMODES_PWM PHASE,PHASE,PHASE,PHASE,PHASE
116
#define HIDDENMODES_ALT 0,0,0,0,0 // Zeroes, same length as NUM_HIDDEN
118
#define TURBO RAMP_SIZE // Convenience code for turbo mode
119
#define BATTCHECK 254 // Convenience code for battery check mode
120
// Uncomment to enable tactical strobe mode
121
#define STROBE 253 // Convenience code for strobe mode
122
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
123
#define BIKING_STROBE 252 // Convenience code for biking strobe mode
124
// comment out to use minimal version instead (smaller)
125
#define FULL_BIKING_STROBE
126
#define RAMP 251 // Convenience code for biking strobe mode
127
#define POLICE_STROBE 250
129
#define NON_WDT_TURBO // enable turbo step-down without WDT
130
// How many timer ticks before before dropping down.
131
// Each timer tick is 500ms, so "60" would be a 30-second stepdown.
132
// Max value of 255 unless you change "ticks"
133
#define TURBO_TIMEOUT 90
135
// Calibrate voltage and OTC in this file:
136
#include "../tk-calibration.h"
139
* =========================================================================
142
// Ignore a spurious warning, we did the cast on purpose
143
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
145
#include <avr/pgmspace.h>
146
//#include <avr/io.h>
147
//#include <avr/interrupt.h>
148
#include <avr/eeprom.h>
149
#include <avr/sleep.h>
150
//#include <avr/power.h>
152
#define OWN_DELAY // Don't use stock delay functions.
153
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
154
#include "../tk-delay.h"
155
#define USE_BATTCHECK
156
//#define BATTCHECK_4bars
157
//#define BATTCHECK_8bars
158
#define BATTCHECK_VpT
159
#define BLINK_SPEED 500
160
#include "../tk-voltage.h"
166
// Config / state variables
168
uint8_t memory = 0; // mode memory, or not (set via soldered star)
169
uint8_t modegroup = 0; // which mode group (set above in #defines)
171
uint8_t offtim3 = 1; // enable medium-press?
173
uint8_t mode_idx = 0; // current or last-used mode number
174
// counter for entering config mode
175
// (needs to be remembered while off, but only for up to half a second)
176
uint8_t fast_presses __attribute__ ((section (".noinit")));
178
// NOTE: Only '1' is known to work; -1 will probably break and is untested.
179
// In other words, short press goes to the next (higher) mode,
180
// medium press goes to the previous (lower) mode.
182
// total length of current mode group's array
184
// number of regular non-hidden modes in current mode group
186
// number of hidden modes in the current mode group
187
// (hardcoded because both groups have the same hidden modes)
188
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
191
// Modes (gets set when the light starts up based on saved config values)
192
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
193
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };
195
PROGMEM const uint8_t modes_g1[] = { MODES_G1, HIDDENMODES };
196
PROGMEM const uint8_t modes_g2[] = { MODES_G2, HIDDENMODES };
197
const uint8_t *modes; // gets pointed at whatever group is current
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_mode() { // save the current mode index (with wear leveling)
204
uint8_t oldpos=eepos;
206
eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell
208
eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
209
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
212
#define FIRSTBOOT 0b01010101
213
#define OPT_firstboot (EEPSIZE-1)
214
#define OPT_modegroup (EEPSIZE-2)
215
#define OPT_memory (EEPSIZE-3)
216
#define OPT_offtim3 (EEPSIZE-4)
217
void save_state() { // central method for writing complete state
219
eeprom_write_byte((uint8_t *)OPT_firstboot, FIRSTBOOT);
220
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
221
eeprom_write_byte((uint8_t *)OPT_memory, memory);
223
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
227
void restore_state() {
230
// check if this is the first time we have powered on
231
eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
232
if (eep != FIRSTBOOT) {
242
// find the mode index data
243
for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
244
eep = eeprom_read_byte((const uint8_t *)eepos);
251
// load other config values
252
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
253
memory = eeprom_read_byte((uint8_t *)OPT_memory);
255
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
258
// unnecessary, save_state handles wrap-around
259
// (and we don't really care about it skipping cell 0 once in a while)
263
inline void next_mode() {
265
if (mode_idx >= solid_modes) {
266
// Wrap around, skipping the hidden modes
267
// (note: this also applies when going "forward" from any hidden mode)
273
inline void prev_mode() {
274
if (mode_idx == solid_modes) {
275
// If we hit the end of the hidden modes, go back to moon
277
} else if (mode_idx > 0) {
278
// Regular mode: is between 1 and TOTAL_MODES
281
// Otherwise, wrap around (this allows entering hidden modes)
282
mode_idx = mode_cnt - 1;
288
inline void check_stars() {
289
// Configure options based on stars
290
// 0 being low for soldered, 1 for pulled-up for not soldered
291
#if 0 // not implemented, STAR2_PIN is used for second PWM channel
294
if ((PINB & (1 << STAR2_PIN)) == 0) {
295
modes[mode_cnt++] = MODE_MOON;
298
#if 0 // Mode order not as important as mem/no-mem
300
if ((PINB & (1 << STAR3_PIN)) == 0) {
308
if ((PINB & (1 << STAR3_PIN)) == 0) {
309
memory = 1; // solder to enable memory
311
memory = 0; // unsolder to disable memory
314
#endif // ifdef CONFIG_STARS
318
* Determine how many solid and hidden modes we have.
319
* The modes_pwm array should have several values for regular modes
320
* then some values for hidden modes.
322
* (this matters because we have more than one set of modes to choose
323
* from, so we need to count at runtime)
325
if (modegroup == 0) {
326
solid_modes = NUM_MODES1;
328
modes_pwm = modes_pwm1;
330
solid_modes = NUM_MODES2;
332
modes_pwm = modes_pwm2;
334
mode_cnt = solid_modes + NUM_HIDDEN;
337
void set_output(uint8_t pwm1, uint8_t pwm2) {
338
// Need PHASE to properly turn off the light
339
if ((pwm1==0) && (pwm2==0)) {
346
void set_level(uint8_t level) {
351
set_output(pgm_read_byte(ramp_FET + level),
352
pgm_read_byte(ramp_7135 + level));
356
void set_mode(uint8_t mode) {
358
static uint8_t actual_level = 0;
359
uint8_t target_level = pgm_read_byte(modes + mode);
363
// TODO: clean out these old implementation ideas
364
//shift_amount = ((target_level - actual_level) >> 2);
365
//shift_amount = ((target_level - actual_level) >> 2) | (target_level!=actual_level);
366
//if ((target_level != actual_level) && (! shift_amount)) { shift_amount = 1; }
367
//if (target_level != actual_level) { shift_amount |= 1; }
368
//shift_amount |= (target_level != actual_level);
369
diff = target_level - actual_level;
370
shift_amount = (diff >> 2) | (diff!=0);
371
actual_level += shift_amount;
373
if (target_level > actual_level) {
374
//shift_amount = 1 + ((target_level - actual_level) >> 2);
375
actual_level += 1 + shift_amount;
377
} else if (target_level < actual_level) {
378
//shift_amount = 1 + ((actual_level - target_level) >> 2);
379
actual_level -= 1 + shift_amount;
383
//set_level(pgm_read_byte(modes + mode));
384
//set_level(pgm_read_byte(modes + actual_mode));
385
set_level(actual_level);
386
//_delay_ms(RAMP_SIZE/20);
387
_delay_ms(RAMP_SIZE/4);
388
} while (target_level != actual_level);
390
//TCCR0A = pgm_read_byte(modes_pwm + mode);
391
//set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode));
392
set_level(pgm_read_byte(modes + mode));
396
void blink(uint8_t val, uint16_t speed)
401
set_output(BLINK_BRIGHTNESS);
402
_delay_ms(BLINK_SPEED / 5);
404
_delay_ms(BLINK_SPEED * 4 / 5);
406
set_level(BLINK_BRIGHTNESS);
414
void toggle(uint8_t *var, uint8_t num) {
415
// Used for extended config mode
416
// Changes the value of a config option, waits for the user to "save"
417
// by turning the light off, then changes the value back in case they
418
// didn't save. Can be used repeatedly on different options, allowing
419
// the user to change and save only one at a time.
420
blink(num, BLINK_SPEED/8); // indicate which option number this is
423
// "buzz" for a while to indicate the active toggle window
424
for(uint8_t i=0; i<32; i++) {
425
set_level(RAMP_SIZE/8);
430
// if the user didn't click, reset the value and return
435
#endif // ifndef CONFIG_STARS
441
// Read the off-time cap *first* to get the most accurate reading
442
// Start up ADC for capacitor pin
443
DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption
444
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
445
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
447
// Wait for completion
448
while (ADCSRA & (1 << ADSC));
449
// Start again as datasheet says first result is unreliable
450
ADCSRA |= (1 << ADSC);
451
// Wait for completion
452
while (ADCSRA & (1 << ADSC));
453
cap_val = ADCH; // save this for later
456
// All ports default to input, but turn pull-up resistors on for the stars (not the ADC input! Made that mistake already)
457
// only one star, because one is used for PWM channel 2
458
// and the other is used for the off-time capacitor
459
PORTB = (1 << STAR3_PIN);
462
// Set PWM pin to output
463
DDRB |= (1 << PWM_PIN); // enable main channel
464
DDRB |= (1 << ALT_PWM_PIN); // enable second channel
466
// Set timer to do PWM for correct output pin and set prescaler timing
467
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
468
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
470
// Set timer to do PWM for correct output pin and set prescaler timing
471
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
473
// Read config values and saved state
478
// Enable the current mode group
482
// memory decayed, reset it
483
// (should happen on med/long press instead
484
// because mem decay is *much* slower when the OTC is charged
485
// so let's not wait until it decays to reset it)
486
//if (fast_presses > 0x20) { fast_presses = 0; }
488
if (cap_val > CAP_SHORT) {
489
// We don't care what the value is as long as it's over 15
490
fast_presses = (fast_presses+1) & 0x1f;
491
// Indicates they did a short press, go to the next mode
492
next_mode(); // Will handle wrap arounds
494
} else if (offtim3 && (cap_val > CAP_MED)) {
496
// User did a medium press, go back one mode
497
prev_mode(); // Will handle "negative" modes and wrap-arounds
500
// Long press, keep the same mode
501
// ... or reset to the first mode
504
// Reset to the first mode
513
// Charge up the capacitor by setting CAP_PIN to output
514
DDRB |= (1 << CAP_PIN); // Output
515
PORTB |= (1 << CAP_PIN); // High
517
// Turn features on or off as needed
523
//ACSR |= (1<<7); //AC off
525
// Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
526
// Will allow us to go idle between WDT interrupts
527
//set_sleep_mode(SLEEP_MODE_IDLE); // not used due to blinky modes
529
//blink(modegroup + 1, BLINK_SPEED/4);
536
uint8_t lowbatt_cnt = 0;
539
// Make sure voltage reading is running for later
540
ADCSRA |= (1 << ADSC);
543
output = pgm_read_byte(modes + mode_idx);
544
if (fast_presses > 0x0f) { // Config mode
545
_delay_s(); // wait for user to stop fast-pressing button
546
fast_presses = 0; // exit this mode after one use
550
// Short/small version of the config mode
551
// Toggle the mode group, blink, then exit
554
count_modes(); // reconfigure without a power cycle
555
blink(1, BLINK_SPEED/4);
557
// Longer/larger version of the config mode
558
// Toggle the mode group, blink, un-toggle, continue
559
toggle(&modegroup, 1);
561
// Toggle memory, blink, untoggle, exit
564
// Toggle offtim3, blink, untoggle, exit
566
#endif // ifdef CONFIG_STARS
569
else if (output == STROBE) {
570
// 10Hz tactical strobe
571
set_level(RAMP_SIZE);
576
#endif // ifdef STROBE
578
else if (output == POLICE_STROBE) {
579
// police-like strobe
581
set_level(RAMP_SIZE);
587
set_level(RAMP_SIZE);
593
#endif // ifdef POLICE_STROBE
595
else if (output == BIKING_STROBE) {
596
// 2-level stutter beacon for biking and such
597
#ifdef FULL_BIKING_STROBE
607
// small/minimal version
614
#endif // ifdef BIKING_STROBE
616
else if (output == RAMP) {
618
// simple ramping test
619
for(r=1; r<=RAMP_SIZE; r++) {
623
for(r=RAMP_SIZE; r>0; r--) {
630
else if (output == BATTCHECK) {
632
// blink out volts and tenths
633
uint8_t result = battcheck();
634
blink(result >> 5, BLINK_SPEED/8);
635
_delay_ms(BLINK_SPEED);
637
_delay_ms(BLINK_SPEED*3/2);
638
blink(result & 0b00011111, BLINK_SPEED/8);
639
#else // ifdef BATTCHECK_VpT
640
// blink zero to five times to show voltage
641
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
642
blink(battcheck(), BLINK_SPEED/8);
643
#endif // ifdef BATTCHECK_VpT
644
// wait between readouts
645
_delay_s(); _delay_s();
647
#endif // ifdef BATTCHECK
648
else { // Regular non-hidden solid mode
650
// This part of the code will mostly replace the WDT tick code.
652
// Do some magic here to handle turbo step-down
653
//if (ticks < 255) ticks++; // don't roll over
654
ticks ++; // actually, we don't care about roll-over prevention
655
if ((ticks > TURBO_TIMEOUT)
656
&& (output == TURBO)) {
657
mode_idx = solid_modes - 2; // step down to second-highest mode
662
// Otherwise, just sleep.
665
// If we got this far, the user has stopped fast-pressing.
666
// So, don't enter config mode.
671
if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
672
voltage = ADCH; // get_voltage();
673
// See if voltage is lower than what we were looking for
674
//if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
675
if (voltage < ADC_LOW) {
680
// See if it's been low for a while, and maybe step down
681
if (lowbatt_cnt >= 8) {
682
// DEBUG: blink on step-down:
683
//set_level(0); _delay_ms(100);
684
i = mode_idx; // save space by not accessing mode_idx more than necessary
685
// properly track hidden vs normal modes
686
if (i >= solid_modes) {
687
// step down from blinky modes to medium
690
// step down from solid modes one at a time
692
} else { // Already at the lowest mode
694
// Turn off the light
696
// Power down as many components as possible
697
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
704
// Wait at least 2 seconds before lowering the level again
705
_delay_ms(250); // this will interrupt blinky modes
708
// Make sure conversion is running for next time through
709
ADCSRA |= (1 << ADSC);
712
#endif // ifdef VOLTAGE_MON
713
//sleep_mode(); // incompatible with blinky modes
715
// If we got this far, the user has stopped fast-pressing.
716
// So, don't enter config mode.
717
//fast_presses = 0; // doesn't interact well with strobe, too fast
720
//return 0; // Standard Return Code