159.1.1
by Selene Scriven
Reverted blf-a6.c to Manker production values. |
1 |
/*
|
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.
|
|
7 |
*
|
|
8 |
* Copyright (C) 2015 Selene Scriven
|
|
9 |
*
|
|
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.
|
|
14 |
*
|
|
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.
|
|
19 |
*
|
|
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/>.
|
|
22 |
*
|
|
23 |
*
|
|
24 |
* NANJG 105C Diagram
|
|
25 |
* ---
|
|
26 |
* -| |- VCC
|
|
27 |
* OTC -| |- Voltage ADC
|
|
28 |
* Star 3 -| |- PWM (FET)
|
|
29 |
* GND -| |- PWM (1x7135)
|
|
30 |
* ---
|
|
31 |
*
|
|
32 |
* FUSES
|
|
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)
|
|
36 |
*
|
|
37 |
* For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
|
|
38 |
*
|
|
39 |
* STARS
|
|
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
|
|
43 |
*
|
|
44 |
* VOLTAGE
|
|
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
|
|
47 |
*
|
|
48 |
* VCC
|
|
49 |
* |
|
|
50 |
* Vd (~.25 v drop from protection diode)
|
|
51 |
* |
|
|
52 |
* 1912 (R1 19,100 ohms)
|
|
53 |
* |
|
|
54 |
* |---- PB2 from MCU
|
|
55 |
* |
|
|
56 |
* 4701 (R2 4,700 ohms)
|
|
57 |
* |
|
|
58 |
* GND
|
|
59 |
*
|
|
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.
|
|
64 |
*
|
|
65 |
* Same for off-time capacitor values. Measure, don't guess.
|
|
66 |
*/
|
|
67 |
// Choose your MCU here, or in the build script
|
|
68 |
//#define ATTINY 13
|
|
69 |
//#define ATTINY 25
|
|
70 |
||
71 |
||
72 |
// set some hardware-specific values...
|
|
73 |
// (while configuring this firmware, skip this section)
|
|
74 |
#if (ATTINY == 13)
|
|
75 |
#define F_CPU 4800000UL
|
|
76 |
#define EEPLEN 64
|
|
77 |
#elif (ATTINY == 25)
|
|
78 |
#define F_CPU 8000000UL
|
|
79 |
#define EEPLEN 128
|
|
80 |
#else
|
|
81 |
Hey, you need to define ATTINY. |
|
82 |
#endif
|
|
83 |
||
84 |
||
85 |
/*
|
|
86 |
* =========================================================================
|
|
87 |
* Settings to modify per driver
|
|
88 |
*/
|
|
89 |
||
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 |
|
94 |
||
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.
|
|
99 |
#if (ATTINY == 13)
|
|
100 |
#define DELAY_TWEAK 950
|
|
101 |
#elif (ATTINY == 25)
|
|
102 |
#define DELAY_TWEAK 2000
|
|
103 |
#endif
|
|
104 |
||
105 |
#define OFFTIM3 // Use short/med/long off-time presses |
|
106 |
// instead of just short/long
|
|
107 |
||
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
|
|
111 |
||
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
|
|
115 |
||
116 |
// Mode group 1
|
|
117 |
#define NUM_MODES1 7
|
|
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
|
|
127 |
// Mode group 2
|
|
128 |
#define NUM_MODES2 4
|
|
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 .
|
|
135 |
#define NUM_HIDDEN 4
|
|
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 |
|
139 |
||
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
|
|
148 |
||
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
|
|
154 |
||
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) |
|
165 |
||
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.
|
|
169 |
#ifdef OFFTIM3
|
|
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
|
|
173 |
#else
|
|
174 |
#define CAP_SHORT 115 // Anything higher than this is a short press, lower is a long press |
|
175 |
#endif
|
|
176 |
||
177 |
/*
|
|
178 |
* =========================================================================
|
|
179 |
*/
|
|
180 |
||
181 |
// Ignore a spurious warning, we did the cast on purpose
|
|
182 |
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
|
|
183 |
||
184 |
#ifdef OWN_DELAY
|
|
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) |
|
188 |
{
|
|
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); |
|
192 |
}
|
|
193 |
void _delay_s() // because it saves a bit of ROM space to do it this way |
|
194 |
{
|
|
195 |
_delay_ms(1000); |
|
196 |
}
|
|
197 |
#else
|
|
198 |
#include <util/delay.h> |
|
199 |
#endif
|
|
200 |
||
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>
|
|
207 |
||
208 |
#define STAR2_PIN PB0 // But note that there is no star 2. |
|
209 |
#define STAR3_PIN PB4
|
|
210 |
#define CAP_PIN PB3
|
|
211 |
#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) |
|
212 |
#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 |
|
213 |
#define PWM_PIN PB1
|
|
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 |
|
219 |
||
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 |
|
222 |
||
223 |
/*
|
|
224 |
* global variables
|
|
225 |
*/
|
|
226 |
||
227 |
// Config / state variables
|
|
228 |
uint8_t eepos = 0; |
|
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"))); |
|
235 |
||
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.
|
|
239 |
#define mode_dir 1
|
|
240 |
// total length of current mode group's array
|
|
241 |
uint8_t mode_cnt; |
|
242 |
// number of regular non-hidden modes in current mode group
|
|
243 |
uint8_t solid_modes; |
|
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
|
|
247 |
||
248 |
||
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 |
|
253 |
||
254 |
PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT }; |
|
255 |
PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT }; |
|
256 |
const uint8_t *modes1x; |
|
257 |
||
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; |
|
261 |
||
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 |
|
269 |
};
|
|
270 |
||
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
|
|
273 |
uint8_t eep; |
|
274 |
uint8_t oldpos=eepos; |
|
275 |
||
276 |
eepos = (eepos+1) & (EEPLEN-1); // wear leveling, use next cell |
|
277 |
||
278 |
#ifdef CONFIG_STARS
|
|
279 |
eep = mode_idx | (modegroup << 5); |
|
280 |
#else
|
|
281 |
eep = mode_idx | (modegroup << 5) | (memory << 6); |
|
282 |
#endif
|
|
283 |
eeprom_write_byte((uint8_t *)(eepos), eep); // save current state |
|
284 |
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state |
|
285 |
}
|
|
286 |
||
287 |
void restore_state() { |
|
288 |
uint8_t eep; |
|
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; |
|
293 |
}
|
|
294 |
// unpack the config data
|
|
295 |
if (eepos < EEPLEN) { |
|
296 |
mode_idx = eep & 0x0f; |
|
297 |
modegroup = (eep >> 5) & 1; |
|
298 |
#ifndef CONFIG_STARS
|
|
299 |
memory = (eep >> 6) & 1; |
|
300 |
#endif
|
|
301 |
}
|
|
302 |
// unnecessary, save_state handles wrap-around
|
|
303 |
// (and we don't really care about it skipping cell 0 once in a while)
|
|
304 |
//else eepos=0;
|
|
305 |
}
|
|
306 |
||
307 |
inline void next_mode() { |
|
308 |
mode_idx += 1; |
|
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)
|
|
312 |
mode_idx = 0; |
|
313 |
}
|
|
314 |
}
|
|
315 |
||
316 |
#ifdef OFFTIM3
|
|
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
|
|
320 |
mode_idx = 0; |
|
321 |
} else if (mode_idx > 0) { |
|
322 |
// Regular mode: is between 1 and TOTAL_MODES
|
|
323 |
mode_idx -= 1; |
|
324 |
} else { |
|
325 |
// Otherwise, wrap around (this allows entering hidden modes)
|
|
326 |
mode_idx = mode_cnt - 1; |
|
327 |
}
|
|
328 |
}
|
|
329 |
#endif
|
|
330 |
||
331 |
#ifdef CONFIG_STARS
|
|
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 |
|
336 |
// Moon
|
|
337 |
// enable moon mode?
|
|
338 |
if ((PINB & (1 << STAR2_PIN)) == 0) {
|
|
339 |
modes[mode_cnt++] = MODE_MOON;
|
|
340 |
}
|
|
341 |
#endif
|
|
342 |
#if 0 // Mode order not as important as mem/no-mem |
|
343 |
// Mode order
|
|
344 |
if ((PINB & (1 << STAR3_PIN)) == 0) {
|
|
345 |
// High to Low
|
|
346 |
mode_dir = -1;
|
|
347 |
} else {
|
|
348 |
mode_dir = 1;
|
|
349 |
}
|
|
350 |
#endif
|
|
351 |
// Memory
|
|
352 |
if ((PINB & (1 << STAR3_PIN)) == 0) { |
|
353 |
memory = 1; // solder to enable memory |
|
354 |
} else { |
|
355 |
memory = 0; // unsolder to disable memory |
|
356 |
}
|
|
357 |
}
|
|
358 |
#endif // ifdef CONFIG_STARS |
|
359 |
||
360 |
void count_modes() { |
|
361 |
/*
|
|
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.
|
|
365 |
*
|
|
366 |
* (this matters because we have more than one set of modes to choose
|
|
367 |
* from, so we need to count at runtime)
|
|
368 |
*/
|
|
369 |
if (modegroup == 0) { |
|
370 |
solid_modes = NUM_MODES1; |
|
371 |
modesNx = modesNx1; |
|
372 |
modes1x = modes1x1; |
|
373 |
modes_pwm = modes_pwm1; |
|
374 |
} else { |
|
375 |
solid_modes = NUM_MODES2; |
|
376 |
modesNx = modesNx2; |
|
377 |
modes1x = modes1x2; |
|
378 |
modes_pwm = modes_pwm2; |
|
379 |
}
|
|
380 |
mode_cnt = solid_modes + NUM_HIDDEN; |
|
381 |
}
|
|
382 |
||
383 |
#ifdef VOLTAGE_MON
|
|
384 |
inline void ADC_on() { |
|
385 |
DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption |
|
386 |
#if (ATTINY == 13)
|
|
387 |
ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 |
|
388 |
#elif (ATTINY == 25)
|
|
389 |
ADMUX = (1 << REFS1) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 |
|
390 |
#endif
|
|
391 |
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale |
|
392 |
}
|
|
393 |
#else
|
|
394 |
inline void ADC_off() { |
|
395 |
ADCSRA &= ~(1<<7); //ADC off |
|
396 |
}
|
|
397 |
#endif
|
|
398 |
||
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)) { |
|
402 |
TCCR0A = PHASE; |
|
403 |
}
|
|
404 |
PWM_LVL = pwm1; |
|
405 |
ALT_PWM_LVL = pwm2; |
|
406 |
}
|
|
407 |
||
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)); |
|
411 |
/*
|
|
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));
|
|
416 |
}
|
|
417 |
*/
|
|
418 |
}
|
|
419 |
||
420 |
#ifdef VOLTAGE_MON
|
|
421 |
uint8_t get_voltage() { |
|
422 |
// Start conversion
|
|
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
|
|
427 |
return ADCH; |
|
428 |
}
|
|
429 |
#endif
|
|
430 |
||
431 |
void blink(uint8_t val) |
|
432 |
{
|
|
433 |
for (; val>0; val--) |
|
434 |
{
|
|
435 |
set_output(BLINK_BRIGHTNESS); |
|
436 |
_delay_ms(100); |
|
437 |
set_output(0,0); |
|
438 |
_delay_ms(400); |
|
439 |
}
|
|
440 |
}
|
|
441 |
||
442 |
#ifndef CONFIG_STARS
|
|
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.
|
|
449 |
*var ^= 1; |
|
450 |
save_state(); |
|
451 |
blink(2); |
|
452 |
*var ^= 1; |
|
453 |
save_state(); |
|
454 |
_delay_s(); |
|
455 |
}
|
|
456 |
#endif // ifndef CONFIG_STARS |
|
457 |
||
458 |
int main(void) |
|
459 |
{
|
|
460 |
uint8_t cap_val; |
|
461 |
||
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 |
|
465 |
#if (ATTINY == 13)
|
|
466 |
ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3 |
|
467 |
#elif (ATTINY == 25)
|
|
468 |
ADMUX = (1 << REFS1) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 |
|
469 |
#endif
|
|
470 |
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale |
|
471 |
||
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 |
|
479 |
||
480 |
#ifdef CONFIG_STARS
|
|
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); |
|
485 |
#endif
|
|
486 |
||
487 |
// Set PWM pin to output
|
|
488 |
DDRB |= (1 << PWM_PIN); // enable main channel |
|
489 |
DDRB |= (1 << ALT_PWM_PIN); // enable second channel |
|
490 |
||
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...)
|
|
494 |
TCCR0A = PHASE; |
|
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...) |
|
497 |
||
498 |
// Read config values and saved state
|
|
499 |
#ifdef CONFIG_STARS
|
|
500 |
check_stars(); |
|
501 |
#endif
|
|
502 |
restore_state(); |
|
503 |
// Enable the current mode group
|
|
504 |
count_modes(); |
|
505 |
||
506 |
||
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; }
|
|
512 |
||
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 |
|
518 |
#ifdef OFFTIM3
|
|
519 |
} else if (cap_val > CAP_MED) { |
|
520 |
fast_presses = 0; |
|
521 |
// User did a medium press, go back one mode
|
|
522 |
prev_mode(); // Will handle "negative" modes and wrap-arounds |
|
523 |
#endif
|
|
524 |
} else { |
|
525 |
// Long press, keep the same mode
|
|
526 |
// ... or reset to the first mode
|
|
527 |
fast_presses = 0; |
|
528 |
if (! memory) { |
|
529 |
// Reset to the first mode
|
|
530 |
mode_idx = 0; |
|
531 |
}
|
|
532 |
}
|
|
533 |
save_state(); |
|
534 |
||
535 |
// Turn off ADC
|
|
536 |
//ADC_off();
|
|
537 |
||
538 |
// Charge up the capacitor by setting CAP_PIN to output
|
|
539 |
DDRB |= (1 << CAP_PIN); // Output |
|
540 |
PORTB |= (1 << CAP_PIN); // High |
|
541 |
||
542 |
// Turn features on or off as needed
|
|
543 |
#ifdef VOLTAGE_MON
|
|
544 |
ADC_on(); |
|
545 |
#else
|
|
546 |
ADC_off(); |
|
547 |
#endif
|
|
548 |
//ACSR |= (1<<7); //AC off
|
|
549 |
||
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
|
|
553 |
||
554 |
uint8_t output; |
|
555 |
#ifdef NON_WDT_TURBO
|
|
556 |
uint8_t ticks = 0; |
|
557 |
#endif
|
|
558 |
#ifdef VOLTAGE_MON
|
|
559 |
uint8_t lowbatt_cnt = 0; |
|
560 |
uint8_t i = 0; |
|
561 |
uint8_t voltage; |
|
562 |
// Make sure voltage reading is running for later
|
|
563 |
ADCSRA |= (1 << ADSC); |
|
564 |
#endif
|
|
565 |
while(1) { |
|
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 |
|
570 |
mode_idx = 0; |
|
571 |
||
572 |
#ifdef CONFIG_STARS
|
|
573 |
// Short/small version of the config mode
|
|
574 |
// Toggle the mode group, blink, then exit
|
|
575 |
modegroup ^= 1; |
|
576 |
save_state(); |
|
577 |
count_modes(); // reconfigure without a power cycle |
|
578 |
blink(1); |
|
579 |
#else
|
|
580 |
// Longer/larger version of the config mode
|
|
581 |
// Toggle the mode group, blink, un-toggle, continue
|
|
582 |
toggle(&modegroup); |
|
583 |
||
584 |
// Toggle memory, blink, untoggle, exit
|
|
585 |
toggle(&memory); |
|
586 |
#endif // ifdef CONFIG_STARS |
|
587 |
}
|
|
588 |
#ifdef STROBE
|
|
589 |
else if (output == STROBE) { |
|
590 |
// 10Hz tactical strobe
|
|
591 |
set_output(255,0); |
|
592 |
_delay_ms(50); |
|
593 |
set_output(0,0); |
|
594 |
_delay_ms(50); |
|
595 |
}
|
|
596 |
#endif // ifdef STROBE |
|
597 |
#ifdef BIKING_STROBE
|
|
598 |
else if (output == BIKING_STROBE) { |
|
599 |
// 2-level stutter beacon for biking and such
|
|
600 |
#ifdef FULL_BIKING_STROBE
|
|
601 |
// normal version
|
|
602 |
for(i=0;i<4;i++) { |
|
603 |
set_output(255,0); |
|
604 |
_delay_ms(5); |
|
605 |
set_output(0,255); |
|
606 |
_delay_ms(65); |
|
607 |
}
|
|
608 |
_delay_ms(720); |
|
609 |
#else
|
|
610 |
// small/minimal version
|
|
611 |
set_output(255,0); |
|
612 |
_delay_ms(10); |
|
613 |
set_output(0,255); |
|
614 |
_delay_s(); |
|
615 |
#endif
|
|
616 |
}
|
|
617 |
#endif // ifdef BIKING_STROBE |
|
618 |
#ifdef BATTCHECK
|
|
619 |
else if (output == BATTCHECK) { |
|
620 |
voltage = get_voltage(); |
|
621 |
// figure out how many times to blink
|
|
622 |
for (i=0; |
|
623 |
voltage > pgm_read_byte(voltage_blinks + i); |
|
624 |
i ++) {} |
|
625 |
||
626 |
// blink zero to five times to show voltage
|
|
627 |
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
|
|
628 |
blink(i); |
|
629 |
// wait between readouts
|
|
630 |
_delay_s(); _delay_s(); |
|
631 |
}
|
|
632 |
#endif // ifdef BATTCHECK |
|
633 |
else { // Regular non-hidden solid mode |
|
634 |
set_mode(mode_idx); |
|
635 |
// This part of the code will mostly replace the WDT tick code.
|
|
636 |
#ifdef NON_WDT_TURBO
|
|
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 |
|
643 |
set_mode(mode_idx); |
|
644 |
save_state(); |
|
645 |
}
|
|
646 |
#endif
|
|
647 |
// Otherwise, just sleep.
|
|
648 |
_delay_ms(500); |
|
649 |
||
650 |
// If we got this far, the user has stopped fast-pressing.
|
|
651 |
// So, don't enter config mode.
|
|
652 |
fast_presses = 0; |
|
653 |
}
|
|
654 |
#ifdef VOLTAGE_MON
|
|
655 |
#if 1
|
|
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) { |
|
661 |
lowbatt_cnt ++; |
|
662 |
} else { |
|
663 |
lowbatt_cnt = 0; |
|
664 |
}
|
|
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
|
|
673 |
i = 2; |
|
674 |
} else if (i > 0) { |
|
675 |
// step down from solid modes one at a time
|
|
676 |
i -= 1; |
|
677 |
} else { // Already at the lowest mode |
|
678 |
i = 0; |
|
679 |
// Turn off the light
|
|
680 |
set_output(0,0); |
|
681 |
// Power down as many components as possible
|
|
682 |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
|
683 |
sleep_mode(); |
|
684 |
}
|
|
685 |
set_mode(i); |
|
686 |
mode_idx = i; |
|
687 |
save_state(); |
|
688 |
lowbatt_cnt = 0; |
|
689 |
// Wait at least 2 seconds before lowering the level again
|
|
690 |
_delay_ms(250); // this will interrupt blinky modes |
|
691 |
}
|
|
692 |
||
693 |
// Make sure conversion is running for next time through
|
|
694 |
ADCSRA |= (1 << ADSC); |
|
695 |
}
|
|
696 |
#endif
|
|
697 |
#endif // ifdef VOLTAGE_MON |
|
698 |
//sleep_mode(); // incompatible with blinky modes
|
|
699 |
||
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
|
|
703 |
}
|
|
704 |
||
705 |
//return 0; // Standard Return Code
|
|
706 |
}
|