-19 Removals
+21 Additions
/*/*
* "Bistro" firmware * "Bistro" firmware
* This code runs on a single-channel or dual-channel driver (FET+7135) * This code runs on a single-channel or dual-channel driver (FET+7135)
* with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC). * with an attiny25/45/85 MCU and a capacitor to measure offtime (OTC).
* *
* Copyright (C) 2015 Selene Scriven * Copyright (C) 2015 Selene Scriven
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* *
* ATTINY25/45/85 Diagram * ATTINY25/45/85 Diagram
* ---- * ----
* -|1 8|- VCC * -|1 8|- VCC
* OTC -|2 7|- Voltage ADC * OTC -|2 7|- Voltage ADC
* Star 3 -|3 6|- PWM (FET, optional) * Star 3 -|3 6|- PWM (FET, optional)
* GND -|4 5|- PWM (1x7135) * GND -|4 5|- PWM (1x7135)
* ---- * ----
* *
* FUSES * FUSES
* I use these fuse settings on attiny25 * I use these fuse settings on attiny25
* Low: 0xd2 * Low: 0xd2
* High: 0xde * High: 0xde
* Ext: 0xff * Ext: 0xff
* *
* For more details on these settings: * For more details on these settings:
* 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 * 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
* *
* STARS * STARS
* Star 3 = unused * Star 3 = unused
* *
* CALIBRATION * CALIBRATION
* *
* To find out what values to use, flash the driver with battcheck.hex * To find out what values to use, flash the driver with battcheck.hex
* and hook the light up to each voltage you need a value for. This is * and hook the light up to each voltage you need a value for. This is
* much more reliable than attempting to calculate the values from a * much more reliable than attempting to calculate the values from a
* theoretical formula. * theoretical formula.
* *
* Same for off-time capacitor values. Measure, don't guess. * Same for off-time capacitor values. Measure, don't guess.
*/ */
// Choose your MCU here, or in the build script// Choose your MCU here, or in the build script
//#define ATTINY 13//#define ATTINY 13
//#define ATTINY 25//#define ATTINY 25
// FIXME: make 1-channel vs 2-channel power a single #define option// FIXME: make 1-channel vs 2-channel power a single #define option
#define FET_7135_LAYOUT // specify an I/O pin layout#define FET_7135_LAYOUT // specify an I/O pin layout
// Also, assign I/O pins in this file:// Also, assign I/O pins in this file:
#include "tk-attiny.h"#include "tk-attiny.h"
/*/*
* ========================================================================= * =========================================================================
* Settings to modify per driver * Settings to modify per driver
*/ */
// FIXME: make 1-channel vs 2-channel power a single #define option// FIXME: make 1-channel vs 2-channel power a single #define option
//#define FAST 0x23 // fast PWM channel 1 only//#define FAST 0x23 // fast PWM channel 1 only
//#define PHASE 0x21 // phase-correct PWM channel 1 only//#define PHASE 0x21 // phase-correct PWM channel 1 only
#define FAST 0xA3 // fast PWM both channels#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels#define PHASE 0xA1 // phase-correct PWM both channels
#define VOLTAGE_MON // Comment out to disable LVP#define VOLTAGE_MON // Comment out to disable LVP
#define OFFTIM3 // Use short/med/long off-time presses#define OFFTIM3 // Use short/med/long off-time presses
// instead of just short/long // instead of just short/long
// ../../bin/level_calc.py 64 1 10 1400 y 3 0.25 140// ../../bin/level_calc.py 64 1 10 1300 y 3 0.23 140
#define RAMP_SIZE 64#define RAMP_SIZE 64
// log curve// log curve
//#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//#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
//#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//#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
// x**2 curve// x**2 curve
//#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//#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
//#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//#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
// x**3 curve// x**3 curve
#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#define RAMP_7135 3,3,4,5,6,8,10,12,15,19,23,28,33,40,47,55,63,73,84,95,108,122,137,153,171,190,210,232,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
#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#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,2,5,8,11,14,18,22,26,30,34,39,44,49,54,59,65,71,77,84,91,98,105,113,121,129,137,146,155,164,174,184,194,205,216,255
// x**5 curve// x**5 curve
//#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//#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
//#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//#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
// uncomment to ramp up/down to a mode instead of jumping directly// uncomment to ramp up/down to a mode instead of jumping directly
#define SOFT_START#define SOFT_START
// Enable battery indicator mode?// Enable battery indicator mode?
#define USE_BATTCHECK#define USE_BATTCHECK
// Choose a battery indicator style// Choose a battery indicator style
//#define BATTCHECK_4bars // up to 4 blinks//#define BATTCHECK_4bars // up to 4 blinks
//#define BATTCHECK_8bars // up to 8 blinks//#define BATTCHECK_8bars // up to 8 blinks
#define BATTCHECK_VpT // Volts + tenths#define BATTCHECK_VpT // Volts + tenths
// output to use for blinks on battery check (and other modes)// output to use for blinks on battery check (and other modes)
#define BLINK_BRIGHTNESS RAMP_SIZE/4#define BLINK_BRIGHTNESS RAMP_SIZE/4
// ms per normal-speed blink// ms per normal-speed blink
#define BLINK_SPEED 500#define BLINK_SPEED 500
// Hidden modes are *before* the lowest (moon) mode, and should be specified// Hidden modes are *before* the lowest (moon) mode, and should be specified
// in reverse order. So, to go backward from moon to turbo to strobe to// in reverse order. So, to go backward from moon to turbo to strobe to
// battcheck, use BATTCHECK,STROBE,TURBO .// battcheck, use BATTCHECK,STROBE,TURBO .
#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO#define HIDDENMODES BIKING_STROBE,BATTCHECK,POLICE_STROBE,TURBO
#define TURBO RAMP_SIZE // Convenience code for turbo mode#define TURBO RAMP_SIZE // Convenience code for turbo mode
#define BATTCHECK 254 // Convenience code for battery check mode#define BATTCHECK 254 // Convenience code for battery check mode
#define GROUP_SELECT_MODE 253#define GROUP_SELECT_MODE 253
#define TEMP_CAL_MODE 252#define TEMP_CAL_MODE 252
// Uncomment to enable tactical strobe mode// Uncomment to enable tactical strobe mode
//#define STROBE 251 // Convenience code for strobe mode//#define STROBE 251 // Convenience code for strobe mode
// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe// Uncomment to unable a 2-level stutter beacon instead of a tactical strobe
#define BIKING_STROBE 250 // Convenience code for biking strobe mode#define BIKING_STROBE 250 // Convenience code for biking strobe mode
// comment out to use minimal version instead (smaller)// comment out to use minimal version instead (smaller)
#define FULL_BIKING_STROBE#define FULL_BIKING_STROBE
//#define RAMP 249 // ramp test mode for tweaking ramp shape//#define RAMP 249 // ramp test mode for tweaking ramp shape
#define POLICE_STROBE 248#define POLICE_STROBE 248
//#define RANDOM_STROBE 247//#define RANDOM_STROBE 247
// thermal step-down// thermal step-down
#define TEMPERATURE_MON#define TEMPERATURE_MON
// Calibrate voltage and OTC in this file:// Calibrate voltage and OTC in this file:
#include "tk-calibration.h"#include "tk-calibration.h"
/*/*
* ========================================================================= * =========================================================================
*/ */
// Ignore a spurious warning, we did the cast on purpose// Ignore a spurious warning, we did the cast on purpose
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#include <avr/pgmspace.h>#include <avr/pgmspace.h>
//#include <avr/io.h>//#include <avr/io.h>
//#include <avr/interrupt.h>//#include <avr/interrupt.h>
#include <avr/eeprom.h>#include <avr/eeprom.h>
#include <avr/sleep.h>#include <avr/sleep.h>
//#include <avr/power.h>//#include <avr/power.h>
#include <string.h>#include <string.h>
#define OWN_DELAY // Don't use stock delay functions.#define OWN_DELAY // Don't use stock delay functions.
#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()#define USE_DELAY_S // Also use _delay_s(), not just _delay_ms()
#include "tk-delay.h"#include "tk-delay.h"
#include "tk-voltage.h"#include "tk-voltage.h"
#ifdef RANDOM_STROBE#ifdef RANDOM_STROBE
#include "tk-random.h"#include "tk-random.h"
#endif#endif
/*/*
* global variables * global variables
*/ */
// Config option variables// Config option variables
#define FIRSTBOOT 0b01010101#define FIRSTBOOT 0b01010101
uint8_t firstboot = FIRSTBOOT; // detect initial boot or factory resetuint8_t firstboot = FIRSTBOOT; // detect initial boot or factory reset
uint8_t modegroup = 5; // which mode group (set above in #defines)uint8_t modegroup = 5; // which mode group (set above in #defines)
uint8_t enable_moon = 1; // Should we add moon to the set of modes?uint8_t enable_moon = 1; // Should we add moon to the set of modes?
uint8_t reverse_modes = 0; // flip the mode order?uint8_t reverse_modes = 0; // flip the mode order?
uint8_t memory = 0; // mode memory, or not (set via soldered star)uint8_t memory = 0; // mode memory, or not (set via soldered star)
#ifdef OFFTIM3#ifdef OFFTIM3
uint8_t offtim3 = 1; // enable medium-press?uint8_t offtim3 = 1; // enable medium-press?
#endif#endif
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
uint8_t maxtemp = 79; // temperature step-down thresholduint8_t maxtemp = 79; // temperature step-down threshold
#endif#endif
uint8_t muggle_mode = 0; // simple mode designed for mugglesuint8_t muggle_mode = 0; // simple mode designed for muggles
// Other state variables// Other state variables
uint8_t mode_override = 0; // do we need to enter a special mode?uint8_t mode_override = 0; // do we need to enter a special mode?
uint8_t mode_idx = 0; // current or last-used mode numberuint8_t mode_idx = 0; // current or last-used mode number
uint8_t eepos = 0;uint8_t eepos = 0;
// counter for entering config mode// counter for entering config mode
// (needs to be remembered while off, but only for up to half a second)// (needs to be remembered while off, but only for up to half a second)
uint8_t fast_presses __attribute__ ((section (".noinit")));uint8_t fast_presses __attribute__ ((section (".noinit")));
// total length of current mode group's array// total length of current mode group's array
uint8_t mode_cnt;uint8_t mode_cnt;
// number of regular non-hidden modes in current mode group// number of regular non-hidden modes in current mode group
uint8_t solid_modes;uint8_t solid_modes;
// number of hidden modes in the current mode group// number of hidden modes in the current mode group
// (hardcoded because both groups have the same hidden modes)// (hardcoded because both groups have the same hidden modes)
//uint8_t hidden_modes = NUM_HIDDEN; // this is never used//uint8_t hidden_modes = NUM_HIDDEN; // this is never used
PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };PROGMEM const uint8_t hiddenmodes[] = { HIDDENMODES };
// default values calculated by group_calc.py// default values calculated by group_calc.py
// Each group must be 8 values long, but can be cut short with a zero.// Each group must be 8 values long, but can be cut short with a zero.
#define NUM_MODEGROUPS 8 // don't count muggle mode#define NUM_MODEGROUPS 9 // don't count muggle mode
PROGMEM const uint8_t modegroups[] = {PROGMEM const uint8_t modegroups[] = {
64, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0,
10, 64, 0, 0, 0, 0, 0, 0, 11, 64, 0, 0, 0, 0, 0, 0,
10, 37, 64, 0, 0, 0, 0, 0, 11, 35, 64, 0, 0, 0, 0, 0,
10, 28, 46, 64, 0, 0, 0, 0, 11, 26, 46, 64, 0, 0, 0, 0,
10, 23, 37, 50, 64, 0, 0, 0, 11, 23, 36, 50, 64, 0, 0, 0,
10, 20, 31, 42, 53, 64, 0, 0, 11, 20, 31, 41, 53, 64, 0, 0,
10, 19, 28, 37, 46, 54, 64, 0, 29, 64,POLICE_STROBE,0,0,0,0,0, // 7: special group A
10, 17, 25, 33, 40, 48, 56, 64, BIKING_STROBE,BATTCHECK,11,29,64,0,0,0, // 8: special group B
10, 30, 50, 0, // muggle mode, exception to "must be 8 bytes long" 9, 18, 29, 46, 64, 0, 0, 0, // 9: special group C
11, 29, 50, 0, // muggle mode, exception to "must be 8 bytes long"
};};
uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...uint8_t modes[] = { 1,2,3,4,5,6,7,8,9, HIDDENMODES }; // make sure this is long enough...
// Modes (gets set when the light starts up based on saved config values)// Modes (gets set when the light starts up based on saved config values)
PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };PROGMEM const uint8_t ramp_7135[] = { RAMP_7135 };
PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };PROGMEM const uint8_t ramp_FET[] = { RAMP_FET };
void save_mode() { // save the current mode index (with wear leveling)void save_mode() { // save the current mode index (with wear leveling)
uint8_t oldpos=eepos; uint8_t oldpos=eepos;
eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell eepos = (eepos+1) & ((EEPSIZE/2)-1); // wear leveling, use next cell
eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state eeprom_write_byte((uint8_t *)(eepos), mode_idx); // save current state
eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state eeprom_write_byte((uint8_t *)(oldpos), 0xff); // erase old state
}}
#define OPT_firstboot (EEPSIZE-1)#define OPT_firstboot (EEPSIZE-1)
#define OPT_modegroup (EEPSIZE-2)#define OPT_modegroup (EEPSIZE-2)
#define OPT_memory (EEPSIZE-3)#define OPT_memory (EEPSIZE-3)
#define OPT_offtim3 (EEPSIZE-4)#define OPT_offtim3 (EEPSIZE-4)
#define OPT_maxtemp (EEPSIZE-5)#define OPT_maxtemp (EEPSIZE-5)
#define OPT_mode_override (EEPSIZE-6)#define OPT_mode_override (EEPSIZE-6)
#define OPT_moon (EEPSIZE-7)#define OPT_moon (EEPSIZE-7)
#define OPT_revmodes (EEPSIZE-8)#define OPT_revmodes (EEPSIZE-8)
#define OPT_muggle (EEPSIZE-9)#define OPT_muggle (EEPSIZE-9)
void save_state() { // central method for writing complete statevoid save_state() { // central method for writing complete state
save_mode(); save_mode();
eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot); eeprom_write_byte((uint8_t *)OPT_firstboot, firstboot);
eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup); eeprom_write_byte((uint8_t *)OPT_modegroup, modegroup);
eeprom_write_byte((uint8_t *)OPT_memory, memory); eeprom_write_byte((uint8_t *)OPT_memory, memory);
#ifdef OFFTIM3#ifdef OFFTIM3
eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3); eeprom_write_byte((uint8_t *)OPT_offtim3, offtim3);
#endif#endif
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp); eeprom_write_byte((uint8_t *)OPT_maxtemp, maxtemp);
#endif#endif
eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override); eeprom_write_byte((uint8_t *)OPT_mode_override, mode_override);
eeprom_write_byte((uint8_t *)OPT_moon, enable_moon); eeprom_write_byte((uint8_t *)OPT_moon, enable_moon);
eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes); eeprom_write_byte((uint8_t *)OPT_revmodes, reverse_modes);
eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode); eeprom_write_byte((uint8_t *)OPT_muggle, muggle_mode);
}}
void restore_state() {void restore_state() {
uint8_t eep; uint8_t eep;
// check if this is the first time we have powered on // check if this is the first time we have powered on
eep = eeprom_read_byte((uint8_t *)OPT_firstboot); eep = eeprom_read_byte((uint8_t *)OPT_firstboot);
if (eep != FIRSTBOOT) { if (eep != FIRSTBOOT) {
// not much to do; the defaults should already be set // not much to do; the defaults should already be set
// while defining the variables above // while defining the variables above
save_state(); save_state();
return; return;
} }
// find the mode index data // find the mode index data
for(eepos=0; eepos<(EEPSIZE/2); eepos++) { for(eepos=0; eepos<(EEPSIZE/2); eepos++) {
eep = eeprom_read_byte((const uint8_t *)eepos); eep = eeprom_read_byte((const uint8_t *)eepos);
if (eep != 0xff) { if (eep != 0xff) {
mode_idx = eep; mode_idx = eep;
break; break;
} }
} }
// load other config values // load other config values
modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup); modegroup = eeprom_read_byte((uint8_t *)OPT_modegroup);
memory = eeprom_read_byte((uint8_t *)OPT_memory); memory = eeprom_read_byte((uint8_t *)OPT_memory);
#ifdef OFFTIM3#ifdef OFFTIM3
offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3); offtim3 = eeprom_read_byte((uint8_t *)OPT_offtim3);
#endif#endif
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp); maxtemp = eeprom_read_byte((uint8_t *)OPT_maxtemp);
#endif#endif
mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override); mode_override = eeprom_read_byte((uint8_t *)OPT_mode_override);
enable_moon = eeprom_read_byte((uint8_t *)OPT_moon); enable_moon = eeprom_read_byte((uint8_t *)OPT_moon);
reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes); reverse_modes = eeprom_read_byte((uint8_t *)OPT_revmodes);
muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle); muggle_mode = eeprom_read_byte((uint8_t *)OPT_muggle);
// unnecessary, save_state handles wrap-around // unnecessary, save_state handles wrap-around
// (and we don't really care about it skipping cell 0 once in a while) // (and we don't really care about it skipping cell 0 once in a while)
//else eepos=0; //else eepos=0;
}}
inline void next_mode() {inline void next_mode() {
mode_idx += 1; mode_idx += 1;
if (mode_idx >= solid_modes) { if (mode_idx >= solid_modes) {
// Wrap around, skipping the hidden modes // Wrap around, skipping the hidden modes
// (note: this also applies when going "forward" from any hidden mode) // (note: this also applies when going "forward" from any hidden mode)
// FIXME? Allow this to cycle through hidden modes? // FIXME? Allow this to cycle through hidden modes?
mode_idx = 0; mode_idx = 0;
} }
}}
#ifdef OFFTIM3#ifdef OFFTIM3
inline void prev_mode() {inline void prev_mode() {
// simple mode has no reverse // simple mode has no reverse
if (muggle_mode) { return next_mode(); } if (muggle_mode) { return next_mode(); }
if (mode_idx == solid_modes) { if (mode_idx == solid_modes) {
// If we hit the end of the hidden modes, go back to moon // If we hit the end of the hidden modes, go back to moon
mode_idx = 0; mode_idx = 0;
} else if (mode_idx > 0) { } else if (mode_idx > 0) {
// Regular mode: is between 1 and TOTAL_MODES // Regular mode: is between 1 and TOTAL_MODES
mode_idx -= 1; mode_idx -= 1;
} else { } else {
// Otherwise, wrap around (this allows entering hidden modes) // Otherwise, wrap around (this allows entering hidden modes)
mode_idx = mode_cnt - 1; mode_idx = mode_cnt - 1;
} }
}}
#endif#endif
void count_modes() {void count_modes() {
/* /*
* Determine how many solid and hidden modes we have. * Determine how many solid and hidden modes we have.
* *
* (this matters because we have more than one set of modes to choose * (this matters because we have more than one set of modes to choose
* from, so we need to count at runtime) * from, so we need to count at runtime)
*/ */
// copy config to local vars to avoid accidentally overwriting them in muggle mode // copy config to local vars to avoid accidentally overwriting them in muggle mode
// (also, it seems to reduce overall program size) // (also, it seems to reduce overall program size)
uint8_t my_modegroup = modegroup; uint8_t my_modegroup = modegroup;
uint8_t my_enable_moon = enable_moon; uint8_t my_enable_moon = enable_moon;
uint8_t my_reverse_modes = reverse_modes; uint8_t my_reverse_modes = reverse_modes;
// override config if we're in simple mode // override config if we're in simple mode
if (muggle_mode) { if (muggle_mode) {
my_modegroup = NUM_MODEGROUPS; my_modegroup = NUM_MODEGROUPS;
my_enable_moon = 0; my_enable_moon = 0;
my_reverse_modes = 0; my_reverse_modes = 0;
} }
uint8_t *dest; uint8_t *dest;
const uint8_t *src = modegroups + (my_modegroup<<3); const uint8_t *src = modegroups + (my_modegroup<<3);
dest = modes; dest = modes;
// Figure out how many modes are in this group // Figure out how many modes are in this group
//solid_modes = modegroup + 1; // Assume group N has N modes //solid_modes = modegroup + 1; // Assume group N has N modes
// No, how about actually counting the modes instead? // No, how about actually counting the modes instead?
// (in case anyone changes the mode groups above so they don't form a triangle) // (in case anyone changes the mode groups above so they don't form a triangle)
for(solid_modes=0; for(solid_modes=0;
(solid_modes<8) && pgm_read_byte(src + solid_modes); (solid_modes<8) && pgm_read_byte(src + solid_modes);
solid_modes++ ) {} solid_modes++ ) {}
// add moon mode (or not) if config says to add it // add moon mode (or not) if config says to add it
if (my_enable_moon) { if (my_enable_moon) {
modes[0] = 1; modes[0] = 1;
dest ++; dest ++;
} }
// add regular modes // add regular modes
memcpy_P(dest, src, solid_modes); memcpy_P(dest, src, solid_modes);
// add hidden modes // add hidden modes
memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes)); memcpy_P(dest + solid_modes, hiddenmodes, sizeof(hiddenmodes));
// final count // final count
mode_cnt = solid_modes + sizeof(hiddenmodes); mode_cnt = solid_modes + sizeof(hiddenmodes);
if (my_reverse_modes) { if (my_reverse_modes) {
// TODO: yuck, isn't there a better way to do this? // TODO: yuck, isn't there a better way to do this?
int8_t i; int8_t i;
src += solid_modes; src += solid_modes;
dest = modes; dest = modes;
for(i=0; i<solid_modes; i++) { for(i=0; i<solid_modes; i++) {
src --; src --;
*dest = pgm_read_byte(src); *dest = pgm_read_byte(src);
dest ++; dest ++;
} }
if (my_enable_moon) { if (my_enable_moon) {
*dest = 1; *dest = 1;
} }
mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo mode_cnt --; // get rid of last hidden mode, since it's a duplicate turbo
} }
if (my_enable_moon) { if (my_enable_moon) {
mode_cnt ++; mode_cnt ++;
solid_modes ++; solid_modes ++;
} }
}}
inline void set_output(uint8_t pwm1, uint8_t pwm2) {inline void set_output(uint8_t pwm1, uint8_t pwm2) {
/* This is no longer needed since we always use PHASE mode. /* This is no longer needed since we always use PHASE mode.
// Need PHASE to properly turn off the light // Need PHASE to properly turn off the light
if ((pwm1==0) && (pwm2==0)) { if ((pwm1==0) && (pwm2==0)) {
TCCR0A = PHASE; TCCR0A = PHASE;
} }
*/ */
PWM_LVL = pwm1; PWM_LVL = pwm1;
ALT_PWM_LVL = pwm2; ALT_PWM_LVL = pwm2;
}}
void set_level(uint8_t level) {void set_level(uint8_t level) {
if (level == 0) { if (level == 0) {
set_output(0,0); set_output(0,0);
} else { } else {
level -= 1; level -= 1;
set_output(pgm_read_byte(ramp_FET + level), set_output(pgm_read_byte(ramp_FET + level),
pgm_read_byte(ramp_7135 + level)); pgm_read_byte(ramp_7135 + level));
} }
}}
void set_mode(uint8_t mode) {void set_mode(uint8_t mode) {
#ifdef SOFT_START#ifdef SOFT_START
static uint8_t actual_level = 0; static uint8_t actual_level = 0;
uint8_t target_level = mode; uint8_t target_level = mode;
int8_t shift_amount; int8_t shift_amount;
int8_t diff; int8_t diff;
do { do {
diff = target_level - actual_level; diff = target_level - actual_level;
shift_amount = (diff >> 2) | (diff!=0); shift_amount = (diff >> 2) | (diff!=0);
actual_level += shift_amount; actual_level += shift_amount;
set_level(actual_level); set_level(actual_level);
//_delay_ms(RAMP_SIZE/20); // slow ramp //_delay_ms(RAMP_SIZE/20); // slow ramp
_delay_ms(RAMP_SIZE/4); // fast ramp _delay_ms(RAMP_SIZE/4); // fast ramp
} while (target_level != actual_level); } while (target_level != actual_level);
#else#else
set_level(mode); set_level(mode);
#endif // SOFT_START#endif // SOFT_START
}}
void blink(uint8_t val, uint16_t speed)void blink(uint8_t val, uint16_t speed)
{{
for (; val>0; val--) for (; val>0; val--)
{ {
set_level(BLINK_BRIGHTNESS); set_level(BLINK_BRIGHTNESS);
_delay_ms(speed); _delay_ms(speed);
set_level(0); set_level(0);
_delay_ms(speed<<2); _delay_ms(speed<<2);
} }
}}
void strobe(uint8_t ontime, uint8_t offtime) {void strobe(uint8_t ontime, uint8_t offtime) {
set_level(RAMP_SIZE); set_level(RAMP_SIZE);
_delay_ms(ontime); _delay_ms(ontime);
set_level(0); set_level(0);
_delay_ms(offtime); _delay_ms(offtime);
}}
void toggle(uint8_t *var, uint8_t num) {void toggle(uint8_t *var, uint8_t num) {
// Used for config mode // Used for config mode
// Changes the value of a config option, waits for the user to "save" // Changes the value of a config option, waits for the user to "save"
// by turning the light off, then changes the value back in case they // by turning the light off, then changes the value back in case they
// didn't save. Can be used repeatedly on different options, allowing // didn't save. Can be used repeatedly on different options, allowing
// the user to change and save only one at a time. // the user to change and save only one at a time.
blink(num, BLINK_SPEED/8); // indicate which option number this is blink(num, BLINK_SPEED/8); // indicate which option number this is
*var ^= 1; *var ^= 1;
save_state(); save_state();
// "buzz" for a while to indicate the active toggle window // "buzz" for a while to indicate the active toggle window
for(uint8_t i=0; i<32; i++) { for(uint8_t i=0; i<32; i++) {
set_level(BLINK_BRIGHTNESS * 3 / 4); set_level(BLINK_BRIGHTNESS * 3 / 4);
_delay_ms(20); _delay_ms(20);
set_level(0); set_level(0);
_delay_ms(20); _delay_ms(20);
} }
// if the user didn't click, reset the value and return // if the user didn't click, reset the value and return
*var ^= 1; *var ^= 1;
save_state(); save_state();
_delay_s(); _delay_s();
}}
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
uint8_t get_temperature() {uint8_t get_temperature() {
ADC_on_temperature(); ADC_on_temperature();
// average a few values; temperature is noisy // average a few values; temperature is noisy
uint16_t temp = 0; uint16_t temp = 0;
uint8_t i; uint8_t i;
get_voltage(); get_voltage();
for(i=0; i<16; i++) { for(i=0; i<16; i++) {
temp += get_voltage(); temp += get_voltage();
_delay_ms(5); _delay_ms(5);
} }
temp >>= 4; temp >>= 4;
return temp; return temp;
}}
#endif // TEMPERATURE_MON#endif // TEMPERATURE_MON
inline uint8_t read_otc() {inline uint8_t read_otc() {
// Read and return the off-time cap value // Read and return the off-time cap value
// Start up ADC for capacitor pin // Start up ADC for capacitor pin
// disable digital input on ADC pin to reduce power consumption // disable digital input on ADC pin to reduce power consumption
DIDR0 |= (1 << CAP_DIDR); DIDR0 |= (1 << CAP_DIDR);
// 1.1v reference, left-adjust, ADC3/PB3 // 1.1v reference, left-adjust, ADC3/PB3
ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; ADMUX = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL;
// enable, start, prescale // enable, start, prescale
ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
// Wait for completion // Wait for completion
while (ADCSRA & (1 << ADSC)); while (ADCSRA & (1 << ADSC));
// Start again as datasheet says first result is unreliable // Start again as datasheet says first result is unreliable
ADCSRA |= (1 << ADSC); ADCSRA |= (1 << ADSC);
// Wait for completion // Wait for completion
while (ADCSRA & (1 << ADSC)); while (ADCSRA & (1 << ADSC));
// ADCH should have the value we wanted // ADCH should have the value we wanted
return ADCH; return ADCH;
}}
int main(void)int main(void)
{{
// check the OTC immediately before it has a chance to charge or discharge // check the OTC immediately before it has a chance to charge or discharge
uint8_t cap_val = read_otc(); // save it for later uint8_t cap_val = read_otc(); // save it for later
// Set PWM pin to output // Set PWM pin to output
DDRB |= (1 << PWM_PIN); // enable main channel DDRB |= (1 << PWM_PIN); // enable main channel
DDRB |= (1 << ALT_PWM_PIN); // enable second channel DDRB |= (1 << ALT_PWM_PIN); // enable second channel
// Set timer to do PWM for correct output pin and set prescaler timing // Set timer to do PWM for correct output pin and set prescaler timing
//TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23 //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
//TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) //TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
TCCR0A = PHASE; TCCR0A = PHASE;
// Set timer to do PWM for correct output pin and set prescaler timing // Set timer to do PWM for correct output pin and set prescaler timing
TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
// Read config values and saved state // Read config values and saved state
restore_state(); restore_state();
// Enable the current mode group // Enable the current mode group
count_modes(); count_modes();
// TODO: Enable this? (might prevent some corner cases, but requires extra room) // TODO: Enable this? (might prevent some corner cases, but requires extra room)
// memory decayed, reset it // memory decayed, reset it
// (should happen on med/long press instead // (should happen on med/long press instead
// because mem decay is *much* slower when the OTC is charged // because mem decay is *much* slower when the OTC is charged
// so let's not wait until it decays to reset it) // so let's not wait until it decays to reset it)
//if (fast_presses > 0x20) { fast_presses = 0; } //if (fast_presses > 0x20) { fast_presses = 0; }
// check button press time, unless the mode is overridden // check button press time, unless the mode is overridden
if (! mode_override) { if (! mode_override) {
if (cap_val > CAP_SHORT) { if (cap_val > CAP_SHORT) {
// Indicates they did a short press, go to the next mode // Indicates they did a short press, go to the next mode
// We don't care what the fast_presses value is as long as it's over 15 // We don't care what the fast_presses value is as long as it's over 15
fast_presses = (fast_presses+1) & 0x1f; fast_presses = (fast_presses+1) & 0x1f;
next_mode(); // Will handle wrap arounds next_mode(); // Will handle wrap arounds
#ifdef OFFTIM3#ifdef OFFTIM3
} else if (cap_val > CAP_MED) { } else if (cap_val > CAP_MED) {
// User did a medium press, go back one mode // User did a medium press, go back one mode
fast_presses = 0; fast_presses = 0;
if (offtim3) { if (offtim3) {
prev_mode(); // Will handle "negative" modes and wrap-arounds prev_mode(); // Will handle "negative" modes and wrap-arounds
} else { } else {
next_mode(); // disabled-med-press acts like short-press next_mode(); // disabled-med-press acts like short-press
// (except that fast_presses isn't reliable then) // (except that fast_presses isn't reliable then)
} }
#endif#endif
} else { } else {
// Long press, keep the same mode // Long press, keep the same mode
// ... or reset to the first mode // ... or reset to the first mode
fast_presses = 0; fast_presses = 0;
if (muggle_mode || (! memory)) { if (muggle_mode || (! memory)) {
// Reset to the first mode // Reset to the first mode
mode_idx = 0; mode_idx = 0;
} }
} }
} }
save_mode(); save_mode();
// Charge up the capacitor by setting CAP_PIN to output // Charge up the capacitor by setting CAP_PIN to output
DDRB |= (1 << CAP_PIN); // Output DDRB |= (1 << CAP_PIN); // Output
PORTB |= (1 << CAP_PIN); // High PORTB |= (1 << CAP_PIN); // High
// Turn features on or off as needed // Turn features on or off as needed
#ifdef VOLTAGE_MON #ifdef VOLTAGE_MON
ADC_on(); ADC_on();
#else #else
ADC_off(); ADC_off();
#endif #endif
uint8_t output; uint8_t output;
uint8_t actual_level; uint8_t actual_level;
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
uint8_t overheat_count = 0; uint8_t overheat_count = 0;
#endif#endif
#ifdef VOLTAGE_MON#ifdef VOLTAGE_MON
uint8_t lowbatt_cnt = 0; uint8_t lowbatt_cnt = 0;
uint8_t i = 0; uint8_t i = 0;
uint8_t voltage; uint8_t voltage;
// Make sure voltage reading is running for later // Make sure voltage reading is running for later
ADCSRA |= (1 << ADSC); ADCSRA |= (1 << ADSC);
#endif#endif
//output = pgm_read_byte(modes + mode_idx); //output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx]; output = modes[mode_idx];
actual_level = output; actual_level = output;
// handle mode overrides, like mode group selection and temperature calibration // handle mode overrides, like mode group selection and temperature calibration
if (mode_override) { if (mode_override) {
// do nothing; mode is already set // do nothing; mode is already set
//mode_idx = mode_override; //mode_idx = mode_override;
fast_presses = 0; fast_presses = 0;
output = mode_idx; output = mode_idx;
} }
while(1) { while(1) {
if (fast_presses > 0x0f) { // Config mode if (fast_presses > 0x0f) { // Config mode
_delay_s(); // wait for user to stop fast-pressing button _delay_s(); // wait for user to stop fast-pressing button
fast_presses = 0; // exit this mode after one use fast_presses = 0; // exit this mode after one use
mode_idx = 0; mode_idx = 0;
// Enter or leave "muggle mode"? // Enter or leave "muggle mode"?
toggle(&muggle_mode, 1); toggle(&muggle_mode, 1);
if (muggle_mode) { continue; }; // don't offer other options in muggle mode if (muggle_mode) { continue; }; // don't offer other options in muggle mode
toggle(&memory, 2); toggle(&memory, 2);
toggle(&enable_moon, 3); toggle(&enable_moon, 3);
toggle(&reverse_modes, 4); toggle(&reverse_modes, 4);
// Enter the mode group selection mode? // Enter the mode group selection mode?
mode_idx = GROUP_SELECT_MODE; mode_idx = GROUP_SELECT_MODE;
toggle(&mode_override, 5); toggle(&mode_override, 5);
mode_idx = 0; mode_idx = 0;
#ifdef OFFTIM3#ifdef OFFTIM3
toggle(&offtim3, 6); toggle(&offtim3, 6);
#endif#endif
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
// Enter temperature calibration mode? // Enter temperature calibration mode?
mode_idx = TEMP_CAL_MODE; mode_idx = TEMP_CAL_MODE;
toggle(&mode_override, 7); toggle(&mode_override, 7);
mode_idx = 0; mode_idx = 0;
#endif#endif
toggle(&firstboot, 8); toggle(&firstboot, 8);
//output = pgm_read_byte(modes + mode_idx); //output = pgm_read_byte(modes + mode_idx);
output = modes[mode_idx]; output = modes[mode_idx];
actual_level = output; actual_level = output;
} }
#ifdef STROBE#ifdef STROBE
else if (output == STROBE) { else if (output == STROBE) {
// 10Hz tactical strobe // 10Hz tactical strobe
strobe(50,50); strobe(50,50);
} }
#endif // ifdef STROBE#endif // ifdef STROBE
#ifdef POLICE_STROBE#ifdef POLICE_STROBE
else if (output == POLICE_STROBE) { else if (output == POLICE_STROBE) {
// police-like strobe // police-like strobe
for(i=0;i<8;i++) { for(i=0;i<8;i++) {
strobe(20,40); strobe(20,40);
} }
for(i=0;i<8;i++) { for(i=0;i<8;i++) {
strobe(40,80); strobe(40,80);
} }
} }
#endif // ifdef POLICE_STROBE#endif // ifdef POLICE_STROBE
#ifdef RANDOM_STROBE#ifdef RANDOM_STROBE
else if (output == RANDOM_STROBE) { else if (output == RANDOM_STROBE) {
// pseudo-random strobe // pseudo-random strobe
uint8_t ms = 34 + (pgm_rand() & 0x3f); uint8_t ms = 34 + (pgm_rand() & 0x3f);
strobe(ms, ms); strobe(ms, ms);
strobe(ms, ms); strobe(ms, ms);
} }
#endif // ifdef RANDOM_STROBE#endif // ifdef RANDOM_STROBE
#ifdef BIKING_STROBE#ifdef BIKING_STROBE
else if (output == BIKING_STROBE) { else if (output == BIKING_STROBE) {
// 2-level stutter beacon for biking and such // 2-level stutter beacon for biking and such
#ifdef FULL_BIKING_STROBE#ifdef FULL_BIKING_STROBE
// normal version // normal version
for(i=0;i<4;i++) { for(i=0;i<4;i++) {
set_output(255,0); set_output(255,0);
_delay_ms(5); _delay_ms(5);
set_output(0,255); set_output(0,255);
_delay_ms(65); _delay_ms(65);
} }
_delay_ms(720); _delay_ms(720);
#else#else
// small/minimal version // small/minimal version
set_output(255,0); set_output(255,0);
_delay_ms(10); _delay_ms(10);
set_output(0,255); set_output(0,255);
_delay_s(); _delay_s();
#endif#endif
} }
#endif // ifdef BIKING_STROBE#endif // ifdef BIKING_STROBE
#ifdef RAMP#ifdef RAMP
else if (output == RAMP) { else if (output == RAMP) {
int8_t r; int8_t r;
// simple ramping test // simple ramping test
for(r=1; r<=RAMP_SIZE; r++) { for(r=1; r<=RAMP_SIZE; r++) {
set_level(r); set_level(r);
_delay_ms(25); _delay_ms(25);
} }
for(r=RAMP_SIZE; r>0; r--) { for(r=RAMP_SIZE; r>0; r--) {
set_level(r); set_level(r);
_delay_ms(25); _delay_ms(25);
} }
} }
#endif // ifdef RAMP#endif // ifdef RAMP
#ifdef BATTCHECK#ifdef BATTCHECK
else if (output == BATTCHECK) { else if (output == BATTCHECK) {
#ifdef BATTCHECK_VpT#ifdef BATTCHECK_VpT
// blink out volts and tenths // blink out volts and tenths
uint8_t result = battcheck(); uint8_t result = battcheck();
blink(result >> 5, BLINK_SPEED/8); blink(result >> 5, BLINK_SPEED/8);
_delay_ms(BLINK_SPEED); _delay_ms(BLINK_SPEED);
blink(1,5); blink(1,5);
_delay_ms(BLINK_SPEED*3/2); _delay_ms(BLINK_SPEED*3/2);
blink(result & 0b00011111, BLINK_SPEED/8); blink(result & 0b00011111, BLINK_SPEED/8);
#else // ifdef BATTCHECK_VpT#else // ifdef BATTCHECK_VpT
// blink zero to five times to show voltage // blink zero to five times to show voltage
// (~0%, ~25%, ~50%, ~75%, ~100%, >100%) // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
blink(battcheck(), BLINK_SPEED/8); blink(battcheck(), BLINK_SPEED/8);
#endif // ifdef BATTCHECK_VpT#endif // ifdef BATTCHECK_VpT
// wait between readouts // wait between readouts
_delay_s(); _delay_s(); _delay_s(); _delay_s();
} }
#endif // ifdef BATTCHECK#endif // ifdef BATTCHECK
else if (output == GROUP_SELECT_MODE) { else if (output == GROUP_SELECT_MODE) {
// exit this mode after one use // exit this mode after one use
mode_idx = 0; mode_idx = 0;
mode_override = 0; mode_override = 0;
for(i=0; i<NUM_MODEGROUPS; i++) { for(i=0; i<NUM_MODEGROUPS; i++) {
modegroup = i; modegroup = i;
save_state(); save_state();
blink(1, BLINK_SPEED/3); blink(1, BLINK_SPEED/3);
} }
_delay_s(); _delay_s(); _delay_s(); _delay_s();
} }
#ifdef TEMP_CAL_MODE#ifdef TEMP_CAL_MODE
else if (output == TEMP_CAL_MODE) { else if (output == TEMP_CAL_MODE) {
// make sure we don't stay in this mode after button press // make sure we don't stay in this mode after button press
mode_idx = 0; mode_idx = 0;
mode_override = 0; mode_override = 0;
// Allow the user to turn off thermal regulation if they want // Allow the user to turn off thermal regulation if they want
maxtemp = 255; maxtemp = 255;
save_state(); save_state();
set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode set_mode(RAMP_SIZE/4); // start somewhat dim during turn-off-regulation mode
_delay_s(); _delay_s(); _delay_s(); _delay_s();
// run at highest output level, to generate heat // run at highest output level, to generate heat
set_mode(RAMP_SIZE); set_mode(RAMP_SIZE);
// measure, save, wait... repeat // measure, save, wait... repeat
while(1) { while(1) {
maxtemp = get_temperature(); maxtemp = get_temperature();
save_state(); save_state();
_delay_s(); _delay_s(); _delay_s(); _delay_s();
} }
} }
#endif // TEMP_CAL_MODE#endif // TEMP_CAL_MODE
else { // Regular non-hidden solid mode else { // Regular non-hidden solid mode
set_mode(actual_level); set_mode(actual_level);
#ifdef TEMPERATURE_MON#ifdef TEMPERATURE_MON
uint8_t temp = get_temperature(); uint8_t temp = get_temperature();
// step down? (or step back up?) // step down? (or step back up?)
if (temp >= maxtemp) { if (temp >= maxtemp) {
overheat_count ++; overheat_count ++;
// reduce noise, and limit the lowest step-down level // reduce noise, and limit the lowest step-down level
if ((overheat_count > 15) && (actual_level > (RAMP_SIZE/8))) { if ((overheat_count > 15) && (actual_level > (RAMP_SIZE/8))) {
actual_level --; actual_level --;
//_delay_ms(5000); // don't ramp down too fast //_delay_ms(5000); // don't ramp down too fast
overheat_count = 0; // don't ramp down too fast overheat_count = 0; // don't ramp down too fast
} }
} else { } else {
// if we're not overheated, ramp up to the user-requested level // if we're not overheated, ramp up to the user-requested level
overheat_count = 0; overheat_count = 0;
if ((temp < maxtemp - 2) && (actual_level < output)) { if ((temp < maxtemp - 2) && (actual_level < output)) {
actual_level ++; actual_level ++;
} }
} }
set_mode(actual_level); set_mode(actual_level);
ADC_on(); // return to voltage mode ADC_on(); // return to voltage mode
#endif#endif
// Otherwise, just sleep. // Otherwise, just sleep.
_delay_ms(500); _delay_ms(500);
// If we got this far, the user has stopped fast-pressing. // If we got this far, the user has stopped fast-pressing.
// So, don't enter config mode. // So, don't enter config mode.
fast_presses = 0; fast_presses = 0;
} }
#ifdef VOLTAGE_MON#ifdef VOLTAGE_MON
if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready if (ADCSRA & (1 << ADIF)) { // if a voltage reading is ready
voltage = ADCH; // get the waiting value voltage = ADCH; // get the waiting value
// See if voltage is lower than what we were looking for // See if voltage is lower than what we were looking for
if (voltage < ADC_LOW) { if (voltage < ADC_LOW) {
lowbatt_cnt ++; lowbatt_cnt ++;
} else { } else {
lowbatt_cnt = 0; lowbatt_cnt = 0;
} }
// See if it's been low for a while, and maybe step down // See if it's been low for a while, and maybe step down
if (lowbatt_cnt >= 8) { if (lowbatt_cnt >= 8) {
// DEBUG: blink on step-down: // DEBUG: blink on step-down:
//set_level(0); _delay_ms(100); //set_level(0); _delay_ms(100);
if (actual_level > RAMP_SIZE) { // hidden / blinky modes if (actual_level > RAMP_SIZE) { // hidden / blinky modes
// step down from blinky modes to medium // step down from blinky modes to medium
actual_level = RAMP_SIZE / 2; actual_level = RAMP_SIZE / 2;
} else if (actual_level > 1) { // regular solid mode } else if (actual_level > 1) { // regular solid mode
// step down from solid modes somewhat gradually // step down from solid modes somewhat gradually
// drop by 25% each time // drop by 25% each time
actual_level = (actual_level >> 2) + (actual_level >> 1); actual_level = (actual_level >> 2) + (actual_level >> 1);
// drop by 50% each time // drop by 50% each time
//actual_level = (actual_level >> 1); //actual_level = (actual_level >> 1);
} else { // Already at the lowest mode } else { // Already at the lowest mode
//mode_idx = 0; // unnecessary; we never leave this clause //mode_idx = 0; // unnecessary; we never leave this clause
//actual_level = 0; // unnecessary; we never leave this clause //actual_level = 0; // unnecessary; we never leave this clause
// Turn off the light // Turn off the light
set_level(0); set_level(0);
// Power down as many components as possible // Power down as many components as possible
set_sleep_mode(SLEEP_MODE_PWR_DOWN); set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode(); sleep_mode();
} }
set_mode(actual_level); set_mode(actual_level);
output = actual_level; output = actual_level;
//save_mode(); // we didn't actually change the mode //save_mode(); // we didn't actually change the mode
lowbatt_cnt = 0; lowbatt_cnt = 0;
// Wait at least 2 seconds before lowering the level again // Wait at least 2 seconds before lowering the level again
_delay_ms(250); // this will interrupt blinky modes _delay_ms(250); // this will interrupt blinky modes
} }
// Make sure conversion is running for next time through // Make sure conversion is running for next time through
ADCSRA |= (1 << ADSC); ADCSRA |= (1 << ADSC);
} }
#endif // ifdef VOLTAGE_MON#endif // ifdef VOLTAGE_MON
} }
//return 0; // Standard Return Code //return 0; // Standard Return Code
}}
Editor
Clear
Original Text
Changed Text