SHOW:
|
|
- or go back to the newest paste.
| 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 | #define F_CPU 4800000UL | |
| 68 | ||
| 69 | /* | |
| 70 | * ========================================================================= | |
| 71 | * Settings to modify per driver | |
| 72 | */ | |
| 73 | ||
| 74 | //#define FAST 0x23 // fast PWM channel 1 only | |
| 75 | //#define PHASE 0x21 // phase-correct PWM channel 1 only | |
| 76 | #define FAST 0xA3 // fast PWM both channels | |
| 77 | #define PHASE 0xA1 // phase-correct PWM both channels | |
| 78 | ||
| 79 | #define VOLTAGE_MON // Comment out to disable LVP | |
| 80 | #define OWN_DELAY // Should we use the built-in delay or our own? | |
| 81 | // Adjust the timing per-driver, since the hardware has high variance | |
| 82 | // Higher values will run slower, lower values run faster. | |
| 83 | #define DELAY_TWEAK 950 | |
| 84 | ||
| 85 | //#define OFFTIM3 // Use short/med/long off-time presses | |
| 86 | // instead of just short/long | |
| 87 | ||
| 88 | ||
| 89 | // output to use for blinks on battery check mode (primary PWM level, alt PWM level) | |
| 90 | // Use 20,0 for a single-channel driver or 0,20 for a two-channel driver | |
| 91 | #define BLINK_BRIGHTNESS 20,20 | |
| 92 | ||
| 93 | // Mode group 1 | |
| 94 | #define NUM_MODES1 3 | |
| 95 | // PWM levels for the big circuit (FET or Nx7135) | |
| 96 | #define MODESNx1 0,255,TURBO | |
| 97 | // PWM levels for the small circuit (1x7135) | |
| 98 | #define MODES1x1 255,0,0 | |
| 99 | // PWM speed for each mode | |
| 100 | #define MODES_PWM1 PHASE,PHASE,PHASE | |
| 101 | // Mode group 2 | |
| 102 | #define NUM_MODES2 5 | |
| 103 | #define MODESNx2 0,0,15,255,TURBO | |
| 104 | #define MODES1x2 5,255,0,0,0 | |
| 105 | #define MODES_PWM2 PHASE,FAST,FAST,FAST,FAST | |
| 106 | // Hidden modes are *before* the lowest (moon) mode, and should be specified | |
| 107 | // in reverse order. So, to go backward from moon to turbo to strobe to | |
| 108 | // battcheck, use BATTCHECK,STROBE,TURBO . | |
| 109 | #define NUM_HIDDEN 0 | |
| 110 | #define HIDDENMODES 0 | |
| 111 | #define HIDDENMODES_PWM 0 | |
| 112 | #define HIDDENMODES_ALT 0 // Zeroes, same length as NUM_HIDDEN | |
| 113 | ||
| 114 | #define TURBO 245 // Convenience code for turbo mode | |
| 115 | #define BATTCHECK 254 // Convenience code for battery check mode | |
| 116 | // Uncomment to enable tactical strobe mode | |
| 117 | //#define STROBE 253 // Convenience code for strobe mode | |
| 118 | // Uncomment to unable a 2-level stutter beacon instead of a tactical strobe | |
| 119 | //#define BIKING_STROBE 252 // Convenience code for biking strobe mode | |
| 120 | // comment out to use minimal version instead (smaller) | |
| 121 | //#define FULL_BIKING_STROBE | |
| 122 | ||
| 123 | #define NON_WDT_TURBO // enable turbo step-down without WDT | |
| 124 | // How many timer ticks before before dropping down. | |
| 125 | // Each timer tick is 500ms, so "60" would be a 30-second stepdown. | |
| 126 | // Max value of 255 unless you change "ticks" | |
| 127 | #define TURBO_TIMEOUT 10 | |
| 128 | ||
| 129 | // These values were measured using wight's "A17HYBRID-S" driver built by DBCstm. | |
| 130 | // Your mileage may vary. | |
| 131 | #define ADC_42 174 // the ADC value we expect for 4.20 volts | |
| 132 | #define ADC_100 174 // the ADC value for 100% full (4.2V resting) | |
| 133 | #define ADC_75 166 // the ADC value for 75% full (4.0V resting) | |
| 134 | #define ADC_50 158 // the ADC value for 50% full (3.8V resting) | |
| 135 | #define ADC_25 145 // the ADC value for 25% full (3.5V resting) | |
| 136 | #define ADC_0 124 // the ADC value for 0% full (3.0V resting) | |
| 137 | #define ADC_LOW 116 // When do we start ramping down (2.8V) | |
| 138 | #define ADC_CRIT 112 // When do we shut the light off (2.7V) | |
| 139 | // These values were copied from s7.c. | |
| 140 | // Your mileage may vary. | |
| 141 | //#define ADC_42 185 // the ADC value we expect for 4.20 volts | |
| 142 | //#define ADC_100 185 // the ADC value for 100% full (4.2V resting) | |
| 143 | //#define ADC_75 175 // the ADC value for 75% full (4.0V resting) | |
| 144 | //#define ADC_50 164 // the ADC value for 50% full (3.8V resting) | |
| 145 | //#define ADC_25 154 // the ADC value for 25% full (3.5V resting) | |
| 146 | //#define ADC_0 139 // the ADC value for 0% full (3.0V resting) | |
| 147 | //#define ADC_LOW 123 // When do we start ramping down (2.8V) | |
| 148 | //#define ADC_CRIT 113 // When do we shut the light off (2.7V) | |
| 149 | // Values for testing only: | |
| 150 | //#define ADC_LOW 125 // When do we start ramping down (2.8V) | |
| 151 | //#define ADC_CRIT 124 // When do we shut the light off (2.7V) | |
| 152 | ||
| 153 | // the BLF EE A6 driver may have different offtime cap values than most other drivers | |
| 154 | // Values are between 1 and 255, and can be measured with offtime-cap.c | |
| 155 | // These #defines are the edge boundaries, not the center of the target. | |
| 156 | #ifdef OFFTIM3 | |
| 157 | #define CAP_SHORT 250 // Anything higher than this is a short press | |
| 158 | #define CAP_MED 190 // Between CAP_MED and CAP_SHORT is a medium press | |
| 159 | // Below CAP_MED is a long press | |
| 160 | #else | |
| 161 | #define CAP_SHORT 190 // Anything higher than this is a short press, lower is a long press | |
| 162 | #endif | |
| 163 | ||
| 164 | /* | |
| 165 | * ========================================================================= | |
| 166 | */ | |
| 167 | ||
| 168 | // Ignore a spurious warning, we did the cast on purpose | |
| 169 | #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" | |
| 170 | ||
| 171 | #ifdef OWN_DELAY | |
| 172 | #include <util/delay_basic.h> | |
| 173 | // Having own _delay_ms() saves some bytes AND adds possibility to use variables as input | |
| 174 | void _delay_ms(uint16_t n) | |
| 175 | {
| |
| 176 | // TODO: make this take tenths of a ms instead of ms, | |
| 177 | // for more precise timing? | |
| 178 | while(n-- > 0) _delay_loop_2(DELAY_TWEAK); | |
| 179 | } | |
| 180 | void _delay_s() // because it saves a bit of ROM space to do it this way | |
| 181 | {
| |
| 182 | _delay_ms(1000); | |
| 183 | } | |
| 184 | #else | |
| 185 | #include <util/delay.h> | |
| 186 | #endif | |
| 187 | ||
| 188 | #include <avr/pgmspace.h> | |
| 189 | //#include <avr/io.h> | |
| 190 | //#include <avr/interrupt.h> | |
| 191 | #include <avr/eeprom.h> | |
| 192 | #include <avr/sleep.h> | |
| 193 | //#include <avr/power.h> | |
| 194 | ||
| 195 | #define STAR2_PIN PB0 // But note that there is no star 2. | |
| 196 | #define FET_PIN PB4 | |
| 197 | #define CAP_PIN PB3 | |
| 198 | #define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) | |
| 199 | #define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 | |
| 200 | #define PWM_PIN PB1 | |
| 201 | #define ALT_PWM_PIN PB0 | |
| 202 | #define VOLTAGE_PIN PB2 | |
| 203 | #define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 | |
| 204 | #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 | |
| 205 | #define ADC_PRSCL 0x06 // clk/64 | |
| 206 | ||
| 207 | #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 | |
| 208 | #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 | |
| 209 | ||
| 210 | /* | |
| 211 | * global variables | |
| 212 | */ | |
| 213 | ||
| 214 | // Config / state variables | |
| 215 | uint8_t eepos = 0; | |
| 216 | uint8_t memory = 0; // mode memory, or not (set via soldered star) | |
| 217 | uint8_t modegroup = 0; // which mode group (set above in #defines) | |
| 218 | uint8_t mode_idx = 0; // current or last-used mode number | |
| 219 | // counter for entering config mode | |
| 220 | // (needs to be remembered while off, but only for up to half a second) | |
| 221 | uint8_t fast_presses __attribute__ ((section (".noinit")));
| |
| 222 | ||
| 223 | // NOTE: Only '1' is known to work; -1 will probably break and is untested. | |
| 224 | // In other words, short press goes to the next (higher) mode, | |
| 225 | // medium press goes to the previous (lower) mode. | |
| 226 | #define mode_dir 1 | |
| 227 | // total length of current mode group's array | |
| 228 | uint8_t mode_cnt; | |
| 229 | // number of regular non-hidden modes in current mode group | |
| 230 | uint8_t solid_modes; | |
| 231 | // number of hidden modes in the current mode group | |
| 232 | // (hardcoded because both groups have the same hidden modes) | |
| 233 | //uint8_t hidden_modes = NUM_HIDDEN; // this is never used | |
| 234 | ||
| 235 | ||
| 236 | // Modes (gets set when the light starts up based on saved config values) | |
| 237 | PROGMEM const uint8_t modesNx1[] = { MODESNx1, HIDDENMODES };
| |
| 238 | PROGMEM const uint8_t modesNx2[] = { MODESNx2, HIDDENMODES };
| |
| 239 | const uint8_t *modesNx; // gets pointed at whatever group is current | |
| 240 | ||
| 241 | PROGMEM const uint8_t modes1x1[] = { MODES1x1, HIDDENMODES_ALT };
| |
| 242 | PROGMEM const uint8_t modes1x2[] = { MODES1x2, HIDDENMODES_ALT };
| |
| 243 | const uint8_t *modes1x; | |
| 244 | ||
| 245 | PROGMEM const uint8_t modes_pwm1[] = { MODES_PWM1, HIDDENMODES_PWM };
| |
| 246 | PROGMEM const uint8_t modes_pwm2[] = { MODES_PWM2, HIDDENMODES_PWM };
| |
| 247 | const uint8_t *modes_pwm; | |
| 248 | ||
| 249 | PROGMEM const uint8_t voltage_blinks[] = {
| |
| 250 | ADC_0, // 1 blink for 0%-25% | |
| 251 | ADC_25, // 2 blinks for 25%-50% | |
| 252 | ADC_50, // 3 blinks for 50%-75% | |
| 253 | ADC_75, // 4 blinks for 75%-100% | |
| 254 | ADC_100, // 5 blinks for >100% | |
| 255 | 255, // Ceiling, don't remove | |
| 256 | }; | |
| 257 | ||
| 258 | void save_state() { // central method for writing (with wear leveling)
| |
| 259 | // a single 16-bit write uses less ROM space than two 8-bit writes | |
| 260 | uint8_t eep; | |
| 261 | uint8_t oldpos=eepos; | |
| 262 | ||
| 263 | eepos = (eepos+1) & 63; // wear leveling, use next cell | |
| 264 | ||
| 265 | ||
| 266 | eep = mode_idx | (modegroup << 5) | (memory << 6); | |
| 267 | eeprom_write_byte((uint8_t *)(eepos), eep); // save current state | |
| 268 | eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state | |
| 269 | } | |
| 270 | ||
| 271 | void restore_state() {
| |
| 272 | uint8_t eep; | |
| 273 | // find the config data | |
| 274 | for(eepos=0; eepos<64; eepos++) {
| |
| 275 | eep = eeprom_read_byte((const uint8_t *)eepos); | |
| 276 | if (eep != 0xff) break; | |
| 277 | } | |
| 278 | // unpack the config data | |
| 279 | if (eepos < 64) {
| |
| 280 | mode_idx = eep & 0x0f; | |
| 281 | modegroup = (eep >> 5) & 1; | |
| 282 | memory = (eep >> 6) & 1; | |
| 283 | } | |
| 284 | // unnecessary, save_state handles wrap-around | |
| 285 | // (and we don't really care about it skipping cell 0 once in a while) | |
| 286 | //else eepos=0; | |
| 287 | } | |
| 288 | ||
| 289 | inline void next_mode() {
| |
| 290 | mode_idx += 1; | |
| 291 | if (mode_idx >= solid_modes) {
| |
| 292 | // Wrap around, skipping the hidden modes | |
| 293 | // (note: this also applies when going "forward" from any hidden mode) | |
| 294 | mode_idx = 0; | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | #ifdef OFFTIM3 | |
| 299 | inline void prev_mode() {
| |
| 300 | if (mode_idx == solid_modes) {
| |
| 301 | // If we hit the end of the hidden modes, go back to moon | |
| 302 | mode_idx = 0; | |
| 303 | } else if (mode_idx > 0) {
| |
| 304 | // Regular mode: is between 1 and TOTAL_MODES | |
| 305 | mode_idx -= 1; | |
| 306 | } else {
| |
| 307 | // Otherwise, wrap around (this allows entering hidden modes) | |
| 308 | mode_idx = mode_cnt - 1; | |
| 309 | } | |
| 310 | } | |
| 311 | #endif | |
| 312 | ||
| 313 | #ifdef CONFIG_STARS | |
| 314 | inline void check_stars() {
| |
| 315 | // Configure options based on stars | |
| 316 | // 0 being low for soldered, 1 for pulled-up for not soldered | |
| 317 | #if 0 // not implemented, STAR2_PIN is used for second PWM channel | |
| 318 | // Moon | |
| 319 | // enable moon mode? | |
| 320 | if ((PINB & (1 << STAR2_PIN)) == 0) {
| |
| 321 | modes[mode_cnt++] = MODE_MOON; | |
| 322 | } | |
| 323 | #endif | |
| 324 | #if 0 // Mode order not as important as mem/no-mem | |
| 325 | // Mode order | |
| 326 | if ((PINB & (1 << FET_PIN)) == 0) {
| |
| 327 | // High to Low | |
| 328 | mode_dir = -1; | |
| 329 | } else {
| |
| 330 | mode_dir = 1; | |
| 331 | } | |
| 332 | #endif | |
| 333 | // Memory | |
| 334 | if ((PINB & (1 << FET_PIN)) == 0) {
| |
| 335 | memory = 1; // solder to enable memory | |
| 336 | } else {
| |
| 337 | memory = 0; // unsolder to disable memory | |
| 338 | } | |
| 339 | } | |
| 340 | #endif // ifdef CONFIG_STARS | |
| 341 | ||
| 342 | void count_modes() {
| |
| 343 | /* | |
| 344 | * Determine how many solid and hidden modes we have. | |
| 345 | * The modes_pwm array should have several values for regular modes | |
| 346 | * then some values for hidden modes. | |
| 347 | * | |
| 348 | * (this matters because we have more than one set of modes to choose | |
| 349 | * from, so we need to count at runtime) | |
| 350 | */ | |
| 351 | if (modegroup == 0) {
| |
| 352 | solid_modes = NUM_MODES1; | |
| 353 | modesNx = modesNx1; | |
| 354 | modes1x = modes1x1; | |
| 355 | modes_pwm = modes_pwm1; | |
| 356 | } else {
| |
| 357 | solid_modes = NUM_MODES2; | |
| 358 | modesNx = modesNx2; | |
| 359 | modes1x = modes1x2; | |
| 360 | modes_pwm = modes_pwm2; | |
| 361 | } | |
| 362 | mode_cnt = solid_modes + NUM_HIDDEN; | |
| 363 | } | |
| 364 | ||
| 365 | #ifdef VOLTAGE_MON | |
| 366 | inline void ADC_on() {
| |
| 367 | DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption | |
| 368 | ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2 | |
| 369 | ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale | |
| 370 | } | |
| 371 | #else | |
| 372 | inline void ADC_off() {
| |
| 373 | ADCSRA &= ~(1<<7); //ADC off | |
| 374 | } | |
| 375 | #endif | |
| 376 | ||
| 377 | void set_output(uint8_t pwm1, uint8_t pwm2) {
| |
| 378 | // Need PHASE to properly turn off the light | |
| 379 | if ((pwm1==0) && (pwm2==0)) {
| |
| 380 | TCCR0A = PHASE; | |
| 381 | } | |
| 382 | #ifdef TURBO | |
| 383 | if (pwm1 == TURBO) {
| |
| 384 | PWM_LVL = 0; | |
| 385 | ALT_PWM_LVL = 0; | |
| 386 | DDRB |= (1 << FET_PIN); | |
| 387 | PORTB |= (1 << FET_PIN); | |
| 388 | } else {
| |
| 389 | PWM_LVL = pwm1; | |
| 390 | ALT_PWM_LVL = pwm2; | |
| 391 | PORTB &= ~(1 << FET_PIN); | |
| 392 | } | |
| 393 | #else | |
| 394 | PWM_LVL = pwm1; | |
| 395 | ALT_PWM_LVL = pwm2; | |
| 396 | #endif //ifdef TURBO | |
| 397 | } | |
| 398 | ||
| 399 | void set_mode(uint8_t mode) {
| |
| 400 | TCCR0A = pgm_read_byte(modes_pwm + mode); | |
| 401 | set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode)); | |
| 402 | /* | |
| 403 | // Only set output for solid modes | |
| 404 | uint8_t out = pgm_read_byte(modesNx + mode); | |
| 405 | if ((out < 250) || (out == 255)) {
| |
| 406 | set_output(pgm_read_byte(modesNx + mode), pgm_read_byte(modes1x + mode)); | |
| 407 | } | |
| 408 | */ | |
| 409 | } | |
| 410 | ||
| 411 | #ifdef VOLTAGE_MON | |
| 412 | uint8_t get_voltage() {
| |
| 413 | // Start conversion | |
| 414 | ADCSRA |= (1 << ADSC); | |
| 415 | // Wait for completion | |
| 416 | while (ADCSRA & (1 << ADSC)); | |
| 417 | // See if voltage is lower than what we were looking for | |
| 418 | return ADCH; | |
| 419 | } | |
| 420 | #endif | |
| 421 | ||
| 422 | void blink(uint8_t val) | |
| 423 | {
| |
| 424 | for (; val>0; val--) | |
| 425 | {
| |
| 426 | set_output(BLINK_BRIGHTNESS); | |
| 427 | _delay_ms(100); | |
| 428 | set_output(0,0); | |
| 429 | _delay_ms(400); | |
| 430 | } | |
| 431 | } | |
| 432 | ||
| 433 | void toggle(uint8_t *var) {
| |
| 434 | // Used for extended config mode | |
| 435 | // Changes the value of a config option, waits for the user to "save" | |
| 436 | // by turning the light off, then changes the value back in case they | |
| 437 | // didn't save. Can be used repeatedly on different options, allowing | |
| 438 | // the user to change and save only one at a time. | |
| 439 | *var ^= 1; | |
| 440 | save_state(); | |
| 441 | blink(2); | |
| 442 | *var ^= 1; | |
| 443 | save_state(); | |
| 444 | _delay_s(); | |
| 445 | } | |
| 446 | ||
| 447 | int main(void) | |
| 448 | {
| |
| 449 | uint8_t cap_val; | |
| 450 | ||
| 451 | // Read the off-time cap *first* to get the most accurate reading | |
| 452 | // Start up ADC for capacitor pin | |
| 453 | DIDR0 |= (1 << CAP_DIDR); // disable digital input on ADC pin to reduce power consumption | |
| 454 | ADMUX = (1 << REFS0) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3 | |
| 455 | ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale | |
| 456 | ||
| 457 | // Wait for completion | |
| 458 | while (ADCSRA & (1 << ADSC)); | |
| 459 | // Start again as datasheet says first result is unreliable | |
| 460 | ADCSRA |= (1 << ADSC); | |
| 461 | // Wait for completion | |
| 462 | while (ADCSRA & (1 << ADSC)); | |
| 463 | cap_val = ADCH; // save this for later | |
| 464 | ||
| 465 | // Set PWM pin to output | |
| 466 | DDRB |= (1 << PWM_PIN); // enable main channel | |
| 467 | DDRB |= (1 << ALT_PWM_PIN); // enable second channel | |
| 468 | ||
| 469 | // Set timer to do PWM for correct output pin and set prescaler timing | |
| 470 | //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 | |
| 471 | //TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) | |
| 472 | TCCR0A = PHASE; | |
| 473 | // Set timer to do PWM for correct output pin and set prescaler timing | |
| 474 | TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) | |
| 475 | ||
| 476 | // Read config values and saved state | |
| 477 | restore_state(); | |
| 478 | // Enable the current mode group | |
| 479 | count_modes(); | |
| 480 | ||
| 481 | ||
| 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; }
| |
| 487 | ||
| 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 | |
| 493 | #ifdef OFFTIM3 | |
| 494 | } else if (cap_val > CAP_MED) {
| |
| 495 | fast_presses = 0; | |
| 496 | // User did a medium press, go back one mode | |
| 497 | prev_mode(); // Will handle "negative" modes and wrap-arounds | |
| 498 | #endif | |
| 499 | } else {
| |
| 500 | // Long press, keep the same mode | |
| 501 | // ... or reset to the first mode | |
| 502 | fast_presses = 0; | |
| 503 | if (! memory) {
| |
| 504 | // Reset to the first mode | |
| 505 | mode_idx = 0; | |
| 506 | } | |
| 507 | } | |
| 508 | save_state(); | |
| 509 | ||
| 510 | // Turn off ADC | |
| 511 | //ADC_off(); | |
| 512 | ||
| 513 | // Charge up the capacitor by setting CAP_PIN to output | |
| 514 | DDRB |= (1 << CAP_PIN); // Output | |
| 515 | PORTB |= (1 << CAP_PIN); // High | |
| 516 | ||
| 517 | // Turn features on or off as needed | |
| 518 | #ifdef VOLTAGE_MON | |
| 519 | ADC_on(); | |
| 520 | #else | |
| 521 | ADC_off(); | |
| 522 | #endif | |
| 523 | //ACSR |= (1<<7); //AC off | |
| 524 | ||
| 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 | |
| 528 | ||
| 529 | uint8_t output; | |
| 530 | #ifdef NON_WDT_TURBO | |
| 531 | uint8_t ticks = 0; | |
| 532 | #endif | |
| 533 | #ifdef VOLTAGE_MON | |
| 534 | uint8_t lowbatt_cnt = 0; | |
| 535 | uint8_t i = 0; | |
| 536 | uint8_t voltage; | |
| 537 | // Make sure voltage reading is running for later | |
| 538 | ADCSRA |= (1 << ADSC); | |
| 539 | #endif | |
| 540 | while(1) {
| |
| 541 | output = pgm_read_byte(modesNx + mode_idx); | |
| 542 | if (fast_presses > 0x0f) { // Config mode
| |
| 543 | _delay_s(); // wait for user to stop fast-pressing button | |
| 544 | fast_presses = 0; // exit this mode after one use | |
| 545 | mode_idx = 0; | |
| 546 | ||
| 547 | // Longer/larger version of the config mode | |
| 548 | // Toggle the mode group, blink, un-toggle, continue | |
| 549 | toggle(&modegroup); | |
| 550 | ||
| 551 | // Toggle memory, blink, untoggle, exit | |
| 552 | toggle(&memory); | |
| 553 | } | |
| 554 | ||
| 555 | #ifdef STROBE | |
| 556 | else if (output == STROBE) {
| |
| 557 | // 10Hz tactical strobe | |
| 558 | set_output(255,0); | |
| 559 | _delay_ms(50); | |
| 560 | set_output(0,0); | |
| 561 | _delay_ms(50); | |
| 562 | } | |
| 563 | #endif // ifdef STROBE | |
| 564 | #ifdef BIKING_STROBE | |
| 565 | else if (output == BIKING_STROBE) {
| |
| 566 | // 2-level stutter beacon for biking and such | |
| 567 | #ifdef FULL_BIKING_STROBE | |
| 568 | // normal version | |
| 569 | for(i=0;i<4;i++) {
| |
| 570 | set_output(255,0); | |
| 571 | _delay_ms(5); | |
| 572 | set_output(0,255); | |
| 573 | _delay_ms(65); | |
| 574 | } | |
| 575 | _delay_ms(720); | |
| 576 | #else | |
| 577 | // small/minimal version | |
| 578 | set_output(255,0); | |
| 579 | _delay_ms(10); | |
| 580 | set_output(0,255); | |
| 581 | _delay_s(); | |
| 582 | #endif | |
| 583 | } | |
| 584 | #endif // ifdef BIKING_STROBE | |
| 585 | #ifdef BATTCHECK | |
| 586 | else if (output == BATTCHECK) {
| |
| 587 | voltage = get_voltage(); | |
| 588 | // figure out how many times to blink | |
| 589 | for (i=0; | |
| 590 | voltage > pgm_read_byte(voltage_blinks + i); | |
| 591 | i ++) {}
| |
| 592 | ||
| 593 | // blink zero to five times to show voltage | |
| 594 | // (~0%, ~25%, ~50%, ~75%, ~100%, >100%) | |
| 595 | blink(i); | |
| 596 | // wait between readouts | |
| 597 | _delay_s(); _delay_s(); | |
| 598 | } | |
| 599 | #endif // ifdef BATTCHECK | |
| 600 | else { // Regular non-hidden solid mode
| |
| 601 | set_mode(mode_idx); | |
| 602 | // This part of the code will mostly replace the WDT tick code. | |
| 603 | #ifdef NON_WDT_TURBO | |
| 604 | // Do some magic here to handle turbo step-down | |
| 605 | //if (ticks < 255) ticks++; // don't roll over | |
| 606 | ticks ++; // actually, we don't care about roll-over prevention | |
| 607 | if ((ticks > TURBO_TIMEOUT) | |
| 608 | && (output == TURBO)) {
| |
| 609 | mode_idx = solid_modes - 2; // step down to second-highest mode | |
| 610 | set_mode(mode_idx); | |
| 611 | save_state(); | |
| 612 | } | |
| 613 | #endif | |
| 614 | // Otherwise, just sleep. | |
| 615 | _delay_ms(500); | |
| 616 | ||
| 617 | // If we got this far, the user has stopped fast-pressing. | |
| 618 | // So, don't enter config mode. | |
| 619 | fast_presses = 0; | |
| 620 | } | |
| 621 | #ifdef VOLTAGE_MON | |
| 622 | #if 1 | |
| 623 | if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
| |
| 624 | voltage = ADCH; // get_voltage(); | |
| 625 | // See if voltage is lower than what we were looking for | |
| 626 | //if (voltage < ((mode_idx <= 1) ? ADC_CRIT : ADC_LOW)) {
| |
| 627 | if (voltage < ADC_LOW) {
| |
| 628 | lowbatt_cnt ++; | |
| 629 | } else {
| |
| 630 | lowbatt_cnt = 0; | |
| 631 | } | |
| 632 | // See if it's been low for a while, and maybe step down | |
| 633 | if (lowbatt_cnt >= 8) {
| |
| 634 | // DEBUG: blink on step-down: | |
| 635 | //set_output(0,0); _delay_ms(100); | |
| 636 | i = mode_idx; // save space by not accessing mode_idx more than necessary | |
| 637 | // properly track hidden vs normal modes | |
| 638 | if (i >= solid_modes) {
| |
| 639 | // step down from blinky modes to medium | |
| 640 | i = 2; | |
| 641 | } else if (i > 0) {
| |
| 642 | // step down from solid modes one at a time | |
| 643 | i -= 1; | |
| 644 | } else { // Already at the lowest mode
| |
| 645 | i = 0; | |
| 646 | // Turn off the light | |
| 647 | set_output(0,0); | |
| 648 | // Power down as many components as possible | |
| 649 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); | |
| 650 | sleep_mode(); | |
| 651 | } | |
| 652 | set_mode(i); | |
| 653 | mode_idx = i; | |
| 654 | save_state(); | |
| 655 | lowbatt_cnt = 0; | |
| 656 | // Wait at least 2 seconds before lowering the level again | |
| 657 | _delay_ms(250); // this will interrupt blinky modes | |
| 658 | } | |
| 659 | ||
| 660 | // Make sure conversion is running for next time through | |
| 661 | ADCSRA |= (1 << ADSC); | |
| 662 | } | |
| 663 | #endif | |
| 664 | #endif // ifdef VOLTAGE_MON | |
| 665 | //sleep_mode(); // incompatible with blinky modes | |
| 666 | ||
| 667 | // If we got this far, the user has stopped fast-pressing. | |
| 668 | // So, don't enter config mode. | |
| 669 | //fast_presses = 0; // doesn't interact well with strobe, too fast | |
| 670 | } | |
| 671 | ||
| 672 | //return 0; // Standard Return Code | |
| 673 | } |