diff options
73 files changed, 2561 insertions, 680 deletions
diff --git a/bin/build.sh b/bin/build.sh index fbb24ea..3992c38 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -14,7 +14,7 @@ export PROGRAM=$1 ; shift export MCU=attiny$ATTINY export CC=avr-gcc export OBJCOPY=avr-objcopy -export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -fgnu89-inline -DATTINY=$ATTINY -I.. -I../.. -I../../.. -fshort-enums" +export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -fgnu89-inline -fwhole-program -DATTINY=$ATTINY -I.. -I../.. -I../../.. -fshort-enums" export OFLAGS="-Wall -g -Os -mmcu=$MCU" export LDFLAGS="-fgnu89-inline" export OBJCOPYFLAGS='--set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex' diff --git a/bin/level_calc.py b/bin/level_calc.py index 6f07621..a780405 100755 --- a/bin/level_calc.py +++ b/bin/level_calc.py @@ -8,6 +8,8 @@ interactive = False # supported shapes: ninth, seventh, fifth, cube, square, log #ramp_shape = 'cube' +max_pwm = 255 + def main(args): """Calculates PWM levels for visually-linear steps. @@ -22,7 +24,7 @@ def main(args): (str, 'type', '7135', 'Type of channel - 7135 or FET:'), (int, 'pwm_min', 6, 'Lowest visible PWM level:'), (float, 'lm_min', 0.25, 'How bright is the lowest level, in lumens?'), - #(int, 'pwm_max', 255, 'Highest PWM level:'), + #(int, 'pwm_max', max_pwm, 'Highest PWM level:'), (float, 'lm_max', 1000, 'How bright is the highest level, in lumens?'), ] @@ -48,7 +50,7 @@ def main(args): if not args: print('===== Channel %s =====' % (chan_num+1)) chan = Empty() - chan.pwm_max = 255 + chan.pwm_max = max_pwm ask(questions_per_channel, chan) chan.type = chan.type.upper() if chan.type not in ('7135', 'FET'): @@ -152,6 +154,16 @@ def multi_pwm(answers, channels): (cnum+1, ','.join([str(int(round(i))) for i in channel.modes]))) + # Show highest level for each channel before next channel starts + for cnum, channel in enumerate(channels[:-1]): + prev = 0 + i = 1 + while (i < answers.num_levels) \ + and (channel.modes[i] >= channel.modes[i-1]) \ + and (channels[cnum+1].modes[i] == 0): + i += 1 + print('Ch%i max: %i (%.2f/%s)' % (cnum, i, channel.modes[i-1], max_pwm)) + def get_value(text, default, args): """Get input from the user, or from the command line args.""" diff --git a/hwdef-BLF_GT.h b/hwdef-BLF_GT.h index 01dbdbd..51b391d 100644 --- a/hwdef-BLF_GT.h +++ b/hwdef-BLF_GT.h @@ -34,24 +34,24 @@ #ifndef VOLTAGE_PIN #define VOLTAGE_PIN PB2 // pin 7, voltage ADC #define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2 -#define VOLTAGE_ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 +#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2 +// inherited from tk-attiny.h +//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1 // 1.1V reference, left-adjust, ADC1/PB2 //#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | (1 << ADLAR) | VOLTAGE_CHANNEL) // 1.1V reference, no left-adjust, ADC1/PB2 #define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL) #endif -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line) #ifndef ADC_44 -#define ADC_44 184 +#define ADC_44 (184*4) #endif #ifndef ADC_22 -#define ADC_22 92 +#define ADC_22 (92*4) #endif -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/hwdef-Emisar_D18.h b/hwdef-Emisar_D18.h index 638dadb..3a238eb 100644 --- a/hwdef-Emisar_D18.h +++ b/hwdef-Emisar_D18.h @@ -33,16 +33,13 @@ #ifndef AUXLED_PIN #define AUXLED_PIN PB2 // pin 7 #endif -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR #define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V #endif -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/hwdef-Emisar_D4.h b/hwdef-Emisar_D4.h index be499f1..d062d6f 100644 --- a/hwdef-Emisar_D4.h +++ b/hwdef-Emisar_D4.h @@ -34,16 +34,13 @@ //#define VOLTAGE_PIN PB2 // pin 7, voltage ADC //#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 //#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR #define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V #endif -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/hwdef-Emisar_D4Sv2.h b/hwdef-Emisar_D4Sv2.h new file mode 100644 index 0000000..d68b7ac --- /dev/null +++ b/hwdef-Emisar_D4Sv2.h @@ -0,0 +1,122 @@ +#ifndef HWDEF_EMISAR_D4SV2_H +#define HWDEF_EMISAR_D4SV2_H + +/* Emisar D4Sv2 driver layout (attiny1634) + * (same layout as D4v2, except it's a FET+3+1 instead of FET+1) + * + * Pin / Name / Function + * 1 PA6 FET PWM (PWM1B) + * 2 PA5 red aux LED (PWM0B) + * 3 PA4 green aux LED + * 4 PA3 blue aux LED + * 5 PA2 e-switch + * 6 PA1 (none) + * 7 PA0 (none) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 3x7135 PWM (PWM0A) + * 16 PB3 1x7135 PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI + * 19 PB0 (none) + * 20 PA7 (none) + * ADC12 thermal sensor + */ + +#ifdef ATTINY +#undef ATTINY +#endif +#define ATTINY 1634 +#include <avr/io.h> + +#define SWITCH_PIN PA2 // pin 5 +#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC + +#define PWM_CHANNELS 3 + +#define PWM1_PIN PB3 // pin 16, 1x7135 PWM +#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 + +#define PWM2_PIN PC0 // pin 15, 3x7135 PWM +#define PWM2_LVL OCR0A // OCR0A is the output compare register for PC0 + +#define PWM3_PIN PA6 // pin 1, FET PWM +#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB1 + + +#define ADC_PRSCL 0x07 // clk/128 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V) +#endif + +// this light has aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC +#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC +#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC + +// with so many pins, doing this all with #ifdefs gets awkward... +// ... so just hardcode it in each hwdef file instead +inline void hwdef_setup() { + // enable output ports + // FET, aux R/G/B + DDRA = (1 << PWM3_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + // 1x7135 + DDRB = (1 << PWM1_PIN); + // 3x7135 + DDRC = (1 << PWM2_PIN); + + // configure PWM + // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter + // pre-scale for timer: N = 1 + // WGM1[3:0]: 0,0,0,1: PWM, Phase Correct, 8-bit (DS table 12-5) + // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) + // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) + // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4) + TCCR1A = (0<<WGM11) | (1<<WGM10) // 8-bit (TOP=0xFF) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5) + ; + + // WGM0[2:0]: 0,0,1: PWM, Phase Correct (DS table 11-8) + // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9) + // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4) + // COM0B[1:0]: 0,0: OC0B disabled (DS table 11-7) + // TCCR0A: COM0A1, COM0A0, COM0B1, COM0B0, -, -, WGM01, WGM00 + TCCR0A = (0<<WGM01) | (1<<WGM00) // PWM, Phase Correct, TOP=0xFF (DS table 11-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 0A in normal direction (DS table 11-4) + | (0<<COM1B1) | (0<<COM1B0) // PWM 0B disabled (DS table 11-7) + ; + // TCCR0B: FOC0A, FOC0B, -, -, WGM02, CS02, CS01, CS00 + TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9) + | (0<<WGM02) // PWM, Phase Correct, TOP=0xFF (DS table 11-8) + ; + + // set up e-switch + //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC? + PUEA = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-Emisar_D4v2.h b/hwdef-Emisar_D4v2.h index 0460fad..fc95e22 100644 --- a/hwdef-Emisar_D4v2.h +++ b/hwdef-Emisar_D4v2.h @@ -9,7 +9,7 @@ * 3 PA4 green aux LED * 4 PA3 blue aux LED * 5 PA2 e-switch - * 6 PA1 (none) + * 6 PA1 button LED * 7 PA0 (none) * 8 GND GND * 9 VCC VCC @@ -48,15 +48,13 @@ #define PWM2_LVL OCR1B // OCR1B is the output compare register for PB1 -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR #define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V) #endif -#define TEMP_CHANNEL 0b00001111 - // this light has aux LEDs under the optic #define AUXLED_R_PIN PA5 // pin 2 #define AUXLED_G_PIN PA4 // pin 3 @@ -65,17 +63,23 @@ #define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC #define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC +#define BUTTON_LED_PIN PA1 // pin 6 +#define BUTTON_LED_PORT PORTA // for all "PA" pins +#define BUTTON_LED_DDR DDRA // for all "PA" pins +#define BUTTON_LED_PUE PUEA // for all "PA" pins + // with so many pins, doing this all with #ifdefs gets awkward... // ... so just hardcode it in each hwdef file instead inline void hwdef_setup() { // enable output ports // 7135 DDRB = (1 << PWM1_PIN); - // FET, aux R/G/B + // FET, aux R/G/B, button LED DDRA = (1 << PWM2_PIN) | (1 << AUXLED_R_PIN) | (1 << AUXLED_G_PIN) | (1 << AUXLED_B_PIN) + | (1 << BUTTON_LED_PIN) ; // configure PWM diff --git a/hwdef-FW3A.h b/hwdef-FW3A.h index 0b94635..e8875d7 100644 --- a/hwdef-FW3A.h +++ b/hwdef-FW3A.h @@ -35,16 +35,13 @@ //#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 //#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 #endif -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR #define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V #endif -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/hwdef-Mateminco_MF01-Mini.h b/hwdef-Mateminco_MF01-Mini.h new file mode 100644 index 0000000..c0de533 --- /dev/null +++ b/hwdef-Mateminco_MF01-Mini.h @@ -0,0 +1,49 @@ +#ifndef HWDEF_MF01_MINI_H +#define HWDEF_MF01_MINI_H + +/* MF01-Mini driver layout + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- aux LEDs + * FET PWM -|3 6|- PWM (7x7135) + * GND -|4 5|- PWM (1x7135) + * ---- + */ + +#define PWM_CHANNELS 3 + +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#endif + +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +#ifndef PWM1_PIN +#define PWM1_PIN PB0 // pin 5, 1x7135 PWM +#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0 +#endif +#ifndef PWM2_PIN +#define PWM2_PIN PB1 // pin 6, 7x7135 PWM +#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1 +#endif +#ifndef PWM3_PIN +#define PWM3_PIN PB4 // pin 3, FET PWM +#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4 +#endif + +#define ADC_PRSCL 0x07 // clk/128 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V +#endif + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-Mateminco_MF01S.h b/hwdef-Mateminco_MF01S.h index ab1c5bf..78a64a6 100644 --- a/hwdef-Mateminco_MF01S.h +++ b/hwdef-Mateminco_MF01S.h @@ -34,24 +34,24 @@ #ifndef VOLTAGE_PIN #define VOLTAGE_PIN PB2 // pin 7, voltage ADC #define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2 -#define VOLTAGE_ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 +#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2 +// inherited from tk-attiny.h +//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1 // 1.1V reference, left-adjust, ADC1/PB2 //#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | (1 << ADLAR) | VOLTAGE_CHANNEL) // 1.1V reference, no left-adjust, ADC1/PB2 #define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL) #endif -#define ADC_PRSCL 0x06 // clk/64 +#define ADC_PRSCL 0x07 // clk/128 // Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line) #ifndef ADC_44 -#define ADC_44 234 +#define ADC_44 (234*4) #endif #ifndef ADC_22 -#define ADC_22 117 +#define ADC_22 (117*4) #endif -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/hwdef-Noctigon_K1-12V.h b/hwdef-Noctigon_K1-12V.h new file mode 100644 index 0000000..8781d8e --- /dev/null +++ b/hwdef-Noctigon_K1-12V.h @@ -0,0 +1,138 @@ +#ifndef HWDEF_NOCTIGON_K1_12V_H +#define HWDEF_NOCTIGON_K1_12V_H + +/* Noctigon K1 driver layout (attiny1634) + * (originally known as Emisar D1S V2) + * + * Pin / Name / Function + * 1 PA6 (none) (PWM1B) (reserved for DD drivers) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 (none) (reserved for L: button LED (on some models)) + * 6 PA1 (none) + * 7 PA0 (none) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 boost PMIC enable (PWM0A not used) + * 16 PB3 main LED PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp power + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + * + * Main LED power uses one pin to turn the Opamp on/off, + * and one pin to control Opamp power level. + * All brightness control uses the power level pin, with 4 kHz 10-bit PWM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + */ + +#ifdef ATTINY +#undef ATTINY +#endif +#define ATTINY 1634 +#include <avr/io.h> + +#define PWM_CHANNELS 1 +#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz +#define PWM_TOP 1023 + +#define SWITCH_PIN PA7 // pin 20 +#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC + +#define PWM1_PIN PB3 // pin 16, Opamp reference +#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 + +#define LED_ENABLE_PIN PB0 // pin 19, Opamp power +#define LED_ENABLE_PORT PORTB // control port for PB0 + +#define LED_ENABLE2_PIN PC0 // pin 15, boost PMIC enable +#define LED_ENABLE2_PORT PORTC // control port for PC0 + + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 +// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D +// DS tables 19-3, 19-4 +// Bit 7 6 5 4 3 2 1 0 +// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 +// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 +// divided by ... +// REFS[1:0] = 1, 0 for internal 1.1V reference +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b10000110 +#define ADC_PRSCL 0x07 // clk/128 + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1) +// D1, R1, R2 = 0, 330, 100 +#ifndef ADC_44 +//#define ADC_44 981 // raw value at 4.40V +#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 +#endif +#ifndef ADC_22 +//#define ADC_22 489 // raw value at 2.20V +#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 +#endif + +// this light has aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC +#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC +#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC + +// with so many pins, doing this all with #ifdefs gets awkward... +// ... so just hardcode it in each hwdef file instead +inline void hwdef_setup() { + // enable output ports + // boost PMIC on/off + DDRC = (1 << LED_ENABLE2_PIN); + // Opamp level and Opamp on/off + DDRB = (1 << PWM1_PIN) + | (1 << LED_ENABLE_PIN); + // aux R/G/B + DDRA = (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + + // configure PWM + // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter + // pre-scale for timer: N = 1 + // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5) + // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) + // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) + // COM1B[1:0]: 0,0: PWM OC1B disabled (DS table 12-4) + TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (0<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5) + ; + + // set up e-switch + PUEA = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-Noctigon_K1-SBT90.h b/hwdef-Noctigon_K1-SBT90.h new file mode 100644 index 0000000..c19a4a6 --- /dev/null +++ b/hwdef-Noctigon_K1-SBT90.h @@ -0,0 +1,143 @@ +#ifndef HWDEF_NOCTIGON_K1SBT90_H +#define HWDEF_NOCTIGON_K1SBT90_H + +/* Noctigon K1-SBT90.2 driver layout (attiny1634) + * (mostly the same as KR4 driver) + * + * Pin / Name / Function + * 1 PA6 FET PWM (direct drive) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 (none) + * 6 PA1 (none) + * 7 PA0 (none) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 (none) PWM0A + * 16 PB3 main LED PWM (linear) (PWM1A) + * 17 PB2 MISO / e-switch (PCINT10) + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp power + * 20 PA7 (none) + * ADC12 thermal sensor + * + * Main LED power uses one pin to turn the Opamp on/off, + * and one pin to control Opamp power level. + * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + * Also has a direct-drive FET for turbo. + */ + +#ifdef ATTINY +#undef ATTINY +#endif +#define ATTINY 1634 +#include <avr/io.h> + +#define PWM_CHANNELS 2 +#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz +#define PWM_TOP 1023 + +#define SWITCH_PIN PB2 // pin 17 +#define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt +#define SWITCH_PCIE PCIE1 // PCIE1 is for PCINT[11:8] +#define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] +#define SWITCH_PORT PINB // PINA or PINB or PINC +#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] + +#define PWM1_PIN PB3 // pin 16, Opamp reference +#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 + +#define PWM2_PIN PA6 // pin 1, DD FET PWM +#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6 + +#define LED_ENABLE_PIN PB0 // pin 19, Opamp power +#define LED_ENABLE_PORT PORTB // control port for PB0 + + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 +// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D +// DS tables 19-3, 19-4 +// Bit 7 6 5 4 3 2 1 0 +// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 +// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 +// divided by ... +// REFS[1:0] = 1, 0 for internal 1.1V reference +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b10000110 +#define ADC_PRSCL 0x07 // clk/128 + +// TODO: calibrate this +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1) +// D1, R1, R2 = 0, 330, 100 +#ifndef ADC_44 +//#define ADC_44 981 // raw value at 4.40V +#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 +#endif +#ifndef ADC_22 +//#define ADC_22 489 // raw value at 2.20V +#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 +#endif + +#define TEMP_CHANNEL 0b00001111 + +// this light has aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC +#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC +#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC + +// with so many pins, doing this all with #ifdefs gets awkward... +// ... so just hardcode it in each hwdef file instead +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << PWM1_PIN) + | (1 << LED_ENABLE_PIN); + // DD FET PWM, aux R/G/B + DDRA = (1 << PWM2_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + + // configure PWM + // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter + // pre-scale for timer: N = 1 + // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5) + // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) + // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) + // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4) + TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5) + ; + + // set up e-switch + //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC? + PUEB = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-Noctigon_K1.h b/hwdef-Noctigon_K1.h new file mode 100644 index 0000000..4c0ce05 --- /dev/null +++ b/hwdef-Noctigon_K1.h @@ -0,0 +1,134 @@ +#ifndef HWDEF_NOCTIGON_K1_H +#define HWDEF_NOCTIGON_K1_H + +/* Noctigon K1 driver layout (attiny1634) + * (originally known as Emisar D1S V2) + * + * Pin / Name / Function + * 1 PA6 (none) (PWM1B) (reserved for DD drivers) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 (none) (reserved for L: button LED (on some models)) + * 6 PA1 (none) + * 7 PA0 (none) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 (none) PWM0A + * 16 PB3 main LED PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp power + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + * + * Main LED power uses one pin to turn the Opamp on/off, + * and one pin to control Opamp power level. + * All brightness control uses the power level pin, with 4 kHz 10-bit PWM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + */ + +#ifdef ATTINY +#undef ATTINY +#endif +#define ATTINY 1634 +#include <avr/io.h> + +#define PWM_CHANNELS 1 +#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz +#define PWM_TOP 1023 + +#define SWITCH_PIN PA7 // pin 20 +#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC + +#define PWM1_PIN PB3 // pin 16, Opamp reference +#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 + +#define LED_ENABLE_PIN PB0 // pin 19, Opamp power +#define LED_ENABLE_PORT PORTB // control port for PB0 + + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 +// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D +// DS tables 19-3, 19-4 +// Bit 7 6 5 4 3 2 1 0 +// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 +// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 +// divided by ... +// REFS[1:0] = 1, 0 for internal 1.1V reference +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b10000110 +#define ADC_PRSCL 0x07 // clk/128 + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1) +// D1, R1, R2 = 0, 330, 100 +#ifndef ADC_44 +//#define ADC_44 981 // raw value at 4.40V +#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 +#endif +#ifndef ADC_22 +//#define ADC_22 489 // raw value at 2.20V +#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 +#endif + +// this light has aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC +#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC +#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC + +// with so many pins, doing this all with #ifdefs gets awkward... +// ... so just hardcode it in each hwdef file instead +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << PWM1_PIN) + | (1 << LED_ENABLE_PIN); + // aux R/G/B + DDRA = (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + + // configure PWM + // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter + // pre-scale for timer: N = 1 + // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5) + // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) + // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) + // COM1B[1:0]: 0,0: PWM OC1B disabled (DS table 12-4) + TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (0<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5) + ; + + // set up e-switch + //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC? + PUEA = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-Noctigon_KR4.h b/hwdef-Noctigon_KR4.h new file mode 100644 index 0000000..073fe27 --- /dev/null +++ b/hwdef-Noctigon_KR4.h @@ -0,0 +1,145 @@ +#ifndef HWDEF_NOCTIGON_KR4_H +#define HWDEF_NOCTIGON_KR4_H + +/* Noctigon KR4 driver layout (attiny1634) + * + * Pin / Name / Function + * 1 PA6 FET PWM (direct drive) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 (none) + * 6 PA1 (none) + * 7 PA0 (none) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 (none) PWM0A + * 16 PB3 main LED PWM (linear) (PWM1A) + * 17 PB2 MISO / e-switch (PCINT10) + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp power + * 20 PA7 (none) + * ADC12 thermal sensor + * + * Main LED power uses one pin to turn the Opamp on/off, + * and one pin to control Opamp power level. + * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + * Some models also have a direct-drive FET for turbo. + */ + +#ifdef ATTINY +#undef ATTINY +#endif +#define ATTINY 1634 +#include <avr/io.h> + +#define PWM_CHANNELS 2 +#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz +#define PWM_TOP 1023 + +#define SWITCH_PIN PB2 // pin 17 +#define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt +#define SWITCH_PCIE PCIE1 // PCIE1 is for PCINT[11:8] +#define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8] +#define SWITCH_PORT PINB // PINA or PINB or PINC +#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] + +// the button tends to short out the voltage divider, +// so ignore voltage while the button is being held +//#define NO_LVP_WHILE_BUTTON_PRESSED + + +#define PWM1_PIN PB3 // pin 16, Opamp reference +#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 + +#define PWM2_PIN PA6 // pin 1, DD FET PWM +#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6 + +#define LED_ENABLE_PIN PB0 // pin 19, Opamp power +#define LED_ENABLE_PORT PORTB // control port for PB0 + + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1 +// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D +// DS tables 19-3, 19-4 +// Bit 7 6 5 4 3 2 1 0 +// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0 +// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1 +// divided by ... +// REFS[1:0] = 1, 0 for internal 1.1V reference +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b10000110 +#define ADC_PRSCL 0x07 // clk/128 + +// TODO: calibrate this +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1) +// D1, R1, R2 = 0, 330, 100 +#ifndef ADC_44 +//#define ADC_44 981 // raw value at 4.40V +#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2 +#endif +#ifndef ADC_22 +//#define ADC_22 489 // raw value at 2.20V +#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2 +#endif + +// this light has aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC +#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC +#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC + +// with so many pins, doing this all with #ifdefs gets awkward... +// ... so just hardcode it in each hwdef file instead +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << PWM1_PIN) + | (1 << LED_ENABLE_PIN); + // DD FET PWM, aux R/G/B + DDRA = (1 << PWM2_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + + // configure PWM + // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter + // pre-scale for timer: N = 1 + // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5) + // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6) + // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4) + // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4) + TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5) + | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4) + | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4) + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5) + ; + + // set up e-switch + //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC? + PUEB = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + +#define LAYOUT_DEFINED + +#endif diff --git a/hwdef-TK_Saber.h b/hwdef-TK_Saber.h index 3f49d30..e6476b8 100644 --- a/hwdef-TK_Saber.h +++ b/hwdef-TK_Saber.h @@ -23,14 +23,11 @@ #define SWITCH_PIN PB2 // pin 7 #define SWITCH_PCINT PCINT2 // pin 7 pin change interrupt -#define ADC_PRSCL 0x06 // clk/64 (no need to be super fast) +#define ADC_PRSCL 0x07 // clk/128 // average drop across diode on this hardware #define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - #define FAST 0xA3 // fast PWM both channels #define PHASE 0xA1 // phase-correct PWM both channels diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt index 3f17f41..97e6589 100644 --- a/spaghetti-monster/anduril/anduril-manual.txt +++ b/spaghetti-monster/anduril/anduril-manual.txt @@ -147,7 +147,7 @@ In more detail, this is what each blinky / utility mode does: can reach before it will start doing thermal regulation to keep itself from overheating. Click once per degree C above 30. For example, to set the limit to 50 C, click 20 times. The default - is 45 C. + is 45 C, and the highest value it will allow is 70 C. Strobe / Mood Modes diff --git a/spaghetti-monster/anduril/anduril-ui.png b/spaghetti-monster/anduril/anduril-ui.png Binary files differindex 3c032bc..70f6be0 100644 --- a/spaghetti-monster/anduril/anduril-ui.png +++ b/spaghetti-monster/anduril/anduril-ui.png diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 8ab66f5..01904dc 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -22,7 +22,7 @@ // Anduril config file name (set it here or define it at the gcc command line) //#define CONFIGFILE cfg-blf-q8.h -#define USE_LVP // FIXME: won't build when this option is turned off +#define USE_LVP // parameters for this defined below or per-driver #define USE_THERMAL_REGULATION @@ -35,6 +35,8 @@ // (currently incompatible with factory reset) //#define START_AT_MEMORIZED_LEVEL +// include a function to blink out the firmware version +#define USE_VERSION_CHECK // short blip when crossing from "click" to "hold" from off // (helps the user hit moon mode exactly, instead of holding too long @@ -77,6 +79,9 @@ // enable beacon mode #define USE_BEACON_MODE +// enable momentary mode +#define USE_MOMENTARY_MODE + //Muggle mode for easy UI #define USE_MUGGLE_MODE @@ -88,6 +93,12 @@ // so don't enable them at the same time as any of the above strobes) //#define USE_POLICE_STROBE_MODE //#define USE_SOS_MODE +//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode +//#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group + +// cut clock speed at very low modes for better efficiency +// (defined here so config files can override it) +#define USE_DYNAMIC_UNDERCLOCKING /***** specific settings for known driver types *****/ #include "tk.h" @@ -128,7 +139,6 @@ #endif #endif #define USE_IDLE_MODE // reduce power use while awake and no tasks are pending -#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency // full FET strobe can be a bit much... use max regulated level instead, // if there's a bright enough regulated level @@ -144,7 +154,7 @@ #define USE_STROBE_STATE #endif -#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE) +#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP) #define USE_BORING_STROBE_STATE #endif @@ -268,6 +278,7 @@ void sos_blink(uint8_t num, uint8_t dah); uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION +#define USE_BLINK_NUM uint8_t tempcheck_state(Event event, uint16_t arg); uint8_t thermal_config_state(Event event, uint16_t arg); #endif @@ -280,15 +291,21 @@ uint8_t goodnight_state(Event event, uint16_t arg); uint8_t beacon_state(Event event, uint16_t arg); uint8_t beacon_config_state(Event event, uint16_t arg); #endif +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +// automatic SOS emergency signal +uint8_t sos_state(Event event, uint16_t arg); +#endif // soft lockout #define MOON_DURING_LOCKOUT_MODE // if enabled, 2nd lockout click goes to the other ramp's floor level #define LOCKOUT_MOON_FANCY uint8_t lockout_state(Event event, uint16_t arg); +#ifdef USE_MOMENTARY_MODE // momentary / signalling mode uint8_t momentary_state(Event event, uint16_t arg); uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe uint8_t momentary_active = 0; // boolean, true if active *right now* +#endif #ifdef USE_MUGGLE_MODE // muggle mode, super-simple, hard to exit uint8_t muggle_state(Event event, uint16_t arg); @@ -306,7 +323,9 @@ void blip(); void indicator_blink(uint8_t arg); #endif #if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) +uint8_t setting_rgb_mode_now = 0; void rgb_led_update(uint8_t mode, uint8_t arg); +void rgb_led_voltage_readout(uint8_t bright); /* * 0: R * 1: RG @@ -318,6 +337,15 @@ void rgb_led_update(uint8_t mode, uint8_t arg); * 7: rainbow * 8: voltage */ +const PROGMEM uint8_t rgb_led_colors[] = { + 0b00000001, // 0: red + 0b00000101, // 1: yellow + 0b00000100, // 2: green + 0b00010100, // 3: cyan + 0b00010000, // 4: blue + 0b00010001, // 5: purple + 0b00010101, // 6: white +}; #define RGB_LED_NUM_COLORS 10 #define RGB_LED_NUM_PATTERNS 4 #ifndef RGB_LED_OFF_DEFAULT @@ -327,6 +355,9 @@ void rgb_led_update(uint8_t mode, uint8_t arg); #ifndef RGB_LED_LOCKOUT_DEFAULT #define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow #endif +#ifndef RGB_RAINBOW_SPEED +#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames +#endif uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT; uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT; #endif @@ -462,7 +493,7 @@ volatile strobe_mode_te strobe_type = 0; #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) // party / tactical strobe timing -volatile uint8_t strobe_delays[] = { 40, 67 }; // party strobe, tactical strobe +volatile uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz #endif // bike mode config options @@ -483,6 +514,12 @@ uint8_t triangle_wave(uint8_t phase); volatile uint8_t beacon_seconds = 2; #endif +#ifdef USE_VERSION_CHECK +#define USE_BLINK_DIGIT +#include "version.h" +const PROGMEM uint8_t version_number[] = VERSION_NUMBER; +uint8_t version_check_state(Event event, uint16_t arg); +#endif uint8_t off_state(Event event, uint16_t arg) { // turn emitter off when entering state @@ -617,12 +654,14 @@ uint8_t off_state(Event event, uint16_t arg) { set_state(lockout_state, 0); return MISCHIEF_MANAGED; } + #ifdef USE_MOMENTARY_MODE // 5 clicks: momentary mode else if (event == EV_5clicks) { blink_confirm(1); set_state(momentary_state, 0); return MISCHIEF_MANAGED; } + #endif #ifdef USE_MUGGLE_MODE // 6 clicks: muggle mode else if (event == EV_6clicks) { @@ -661,6 +700,7 @@ uint8_t off_state(Event event, uint16_t arg) { } // 7 clicks (hold last): change RGB aux LED color else if (event == EV_click7_hold) { + setting_rgb_mode_now = 1; if (0 == (arg & 0x3f)) { uint8_t mode = (rgb_led_off_mode & 0x0f) + 1; mode = mode % RGB_LED_NUM_COLORS; @@ -671,17 +711,25 @@ uint8_t off_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } else if (event == EV_click7_hold_release) { + setting_rgb_mode_now = 0; save_config(); return MISCHIEF_MANAGED; } #endif // end 7 clicks - #ifdef USE_TENCLICK_THERMAL_CONFIG + #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION) // 10 clicks: thermal config mode else if (event == EV_10clicks) { push_state(thermal_config_state, 0); return MISCHIEF_MANAGED; } #endif + #ifdef USE_VERSION_CHECK + // 15+ clicks: show the version number + else if (event == EV_15clicks) { + set_state(version_check_state, 0); + return MISCHIEF_MANAGED; + } + #endif #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET) // 13 clicks and hold the last click: invoke factory reset (reboot) else if (event == EV_click13_hold) { @@ -713,7 +761,9 @@ uint8_t steady_state(Event event, uint16_t arg) { // turn LED on when we first enter the mode if ((event == EV_enter_state) || (event == EV_reenter_state)) { + #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE) momentary_mode = 0; // 0 = ramping, 1 = strobes + #endif // if we just got back from config mode, go back to memorized level if (event == EV_reenter_state) { arg = memorized_level; @@ -792,6 +842,15 @@ uint8_t steady_state(Event event, uint16_t arg) { // (off->hold->stepped_min->release causes this state) else if (actual_level <= mode_min) { ramp_direction = 1; } } + // if the button is stuck, err on the side of safety and ramp down + else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) { + ramp_direction = -1; + } + // if the button is still stuck, lock the light + else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) { + blip(); + set_state(lockout_state, 0); + } memorized_level = nearest_level((int16_t)actual_level \ + (ramp_step_size * ramp_direction)); #else @@ -919,59 +978,34 @@ uint8_t steady_state(Event event, uint16_t arg) { if (arg == TICKS_PER_SECOND) ramp_direction = 1; #endif #ifdef USE_SET_LEVEL_GRADUALLY - // make thermal adjustment speed scale with magnitude - // also, adjust slower when going up - if ((arg & 1) && - ((actual_level < THERM_FASTER_LEVEL) || - (actual_level < gradual_target))) { - return MISCHIEF_MANAGED; // adjust slower when not a high mode - } - #ifdef THERM_HARD_TURBO_DROP - else if ((! (actual_level < THERM_FASTER_LEVEL)) - && (actual_level > gradual_target)) { - gradual_tick(); - } - else { - #endif - // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; - // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; - // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] - uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; - uint8_t diff; - static uint8_t ticks_since_adjust = 0; - if (gradual_target > actual_level) { - // rise at half speed (skip half the frames) - if (arg & 2) return MISCHIEF_MANAGED; - diff = gradual_target - actual_level; - } else { - diff = actual_level - gradual_target; - } - ticks_since_adjust ++; - // if there's any adjustment to be made, make it + int16_t diff = gradual_target - actual_level; + static uint16_t ticks_since_adjust = 0; + ticks_since_adjust++; if (diff) { - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if ((actual_level >= THERM_FASTER_LEVEL) - && (actual_level > gradual_target)) { magnitude ++; } - #endif + uint16_t ticks_per_adjust = 256; + if (diff < 0) { + //diff = -diff; + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef THERM_HARD_TURBO_DROP + ticks_per_adjust >>= 2; + #endif + ticks_per_adjust >>= 2; + } + } else { + // rise at half speed + ticks_per_adjust <<= 1; + } while (diff) { - magnitude ++; - diff >>= 1; + ticks_per_adjust >>= 1; + //diff >>= 1; + diff /= 2; // because shifting produces weird behavior } - uint8_t ticks_per_adjust = intervals[magnitude]; if (ticks_since_adjust > ticks_per_adjust) { gradual_tick(); ticks_since_adjust = 0; } - //if (!(arg % ticks_per_adjust)) gradual_tick(); } - #ifdef THERM_HARD_TURBO_DROP - } - #endif #endif // ifdef USE_SET_LEVEL_GRADUALLY return MISCHIEF_MANAGED; } @@ -1024,6 +1058,18 @@ uint8_t steady_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } + #ifdef USE_SET_LEVEL_GRADUALLY + // temperature is within target window + // (so stop trying to adjust output) + else if (event == EV_temperature_okay) { + // if we're still adjusting output... stop after the current step + if (gradual_target > actual_level) + gradual_target = actual_level + 1; + else if (gradual_target < actual_level) + gradual_target = actual_level - 1; + return MISCHIEF_MANAGED; + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY #endif // ifdef USE_THERMAL_REGULATION return EVENT_NOT_HANDLED; } @@ -1100,7 +1146,9 @@ uint8_t strobe_state(Event event, uint16_t arg) { // (maybe I should just make it nonvolatile?) strobe_mode_te st = strobe_type; + #ifdef USE_MOMENTARY_MODE momentary_mode = 1; // 0 = ramping, 1 = strobes + #endif #ifdef USE_CANDLE_MODE // pass all events to candle mode, when it's active @@ -1232,8 +1280,12 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) { if (0) {} // placeholde0 #ifdef USE_PARTY_STROBE_MODE else if (st == party_strobe_e) { // party strobe + #ifdef PARTY_STROBE_ONTIME + nice_delay_ms(PARTY_STROBE_ONTIME); + #else if (del < 42) delay_zero(); else nice_delay_ms(1); + #endif } #endif #ifdef USE_TACTICAL_STROBE_MODE @@ -1484,8 +1536,29 @@ inline void police_strobe_iter() { } } #endif +#endif // #ifdef USE_BORING_STROBE_STATE #ifdef USE_SOS_MODE +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +uint8_t sos_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return MISCHIEF_MANAGED; + } + // 2 clicks: next mode + else if (event == EV_2clicks) { + #ifdef USE_THERMAL_REGULATION + set_state(tempcheck_state, 0); + #else + set_state(battcheck_state, 0); + #endif + return MISCHIEF_MANAGED; + } + return EVENT_NOT_HANDLED; +} +#endif + void sos_blink(uint8_t num, uint8_t dah) { #define DIT_LENGTH 200 for (; num > 0; num--) { @@ -1499,19 +1572,19 @@ void sos_blink(uint8_t num, uint8_t dah) { nice_delay_ms(DIT_LENGTH); } // three "off" dits (or one "dah") between letters - nice_delay_ms(DIT_LENGTH*2); + // (except for SOS, which is collectively treated as a single "letter") + //nice_delay_ms(DIT_LENGTH*2); } inline void sos_mode_iter() { // one iteration of main loop() - nice_delay_ms(1000); + //nice_delay_ms(1000); sos_blink(3, 0); // S sos_blink(3, 1); // O sos_blink(3, 0); // S - nice_delay_ms(1000); + nice_delay_ms(2000); } #endif // #ifdef USE_SOS_MODE -#endif // #ifdef USE_BORING_STROBE_STATE #ifdef USE_BATTCHECK @@ -1521,11 +1594,25 @@ uint8_t battcheck_state(Event event, uint16_t arg) { set_state(off_state, 0); return MISCHIEF_MANAGED; } + #ifdef USE_GOODNIGHT_MODE // 2 clicks: goodnight mode else if (event == EV_2clicks) { set_state(goodnight_state, 0); return MISCHIEF_MANAGED; } + #elif defined(USE_BEACON_MODE) + // 2 clicks: beacon mode + else if (event == EV_2clicks) { + set_state(beacon_state, 0); + return MISCHIEF_MANAGED; + } + #elif defined(USE_THERMAL_REGULATION) + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + set_state(tempcheck_state, 0); + return MISCHIEF_MANAGED; + } + #endif return EVENT_NOT_HANDLED; } #endif @@ -1538,11 +1625,13 @@ uint8_t tempcheck_state(Event event, uint16_t arg) { set_state(off_state, 0); return MISCHIEF_MANAGED; } + #ifdef USE_BATTCHECK // 2 clicks: battcheck mode else if (event == EV_2clicks) { set_state(battcheck_state, 0); return MISCHIEF_MANAGED; } + #endif // 4 clicks: thermal config mode else if (event == EV_4clicks) { push_state(thermal_config_state, 0); @@ -1562,11 +1651,13 @@ uint8_t beacon_state(Event event, uint16_t arg) { } // TODO: use sleep ticks to measure time between pulses, // to save power - // 2 clicks: tempcheck mode + // 2 clicks: next mode else if (event == EV_2clicks) { - #ifdef USE_THERMAL_REGULATION + #ifdef USE_SOS_MODE_IN_BLINKY_GROUP + set_state(sos_state, 0); + #elif defined(USE_THERMAL_REGULATION) set_state(tempcheck_state, 0); - #else + #elif defined(USE_BATTCHECK) set_state(battcheck_state, 0); #endif return MISCHIEF_MANAGED; @@ -1597,9 +1688,15 @@ uint8_t goodnight_state(Event event, uint16_t arg) { set_state(off_state, 0); return MISCHIEF_MANAGED; } - // 2 clicks: beacon mode + // 2 clicks: next mode else if (event == EV_2clicks) { + #ifdef USE_BEACON_MODE set_state(beacon_state, 0); + #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP) + set_state(sos_state, 0); + #elif defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #endif return MISCHIEF_MANAGED; } // tick: step down (maybe) or off (maybe) @@ -1725,6 +1822,7 @@ uint8_t lockout_state(Event event, uint16_t arg) { } // click, click, hold: change RGB aux LED color else if (event == EV_click3_hold) { + setting_rgb_mode_now = 1; if (0 == (arg & 0x3f)) { uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1; mode = mode % RGB_LED_NUM_COLORS; @@ -1736,6 +1834,7 @@ uint8_t lockout_state(Event event, uint16_t arg) { } // click, click, hold, release: save new color else if (event == EV_click3_hold_release) { + setting_rgb_mode_now = 0; save_config(); return MISCHIEF_MANAGED; } @@ -1751,13 +1850,16 @@ uint8_t lockout_state(Event event, uint16_t arg) { } +#ifdef USE_MOMENTARY_MODE uint8_t momentary_state(Event event, uint16_t arg) { // TODO: momentary strobe here? (for light painting) // init strobe mode, if relevant + #ifdef USE_STROBE_STATE if ((event == EV_enter_state) && (momentary_mode == 1)) { strobe_state(event, arg); } + #endif // light up when the button is pressed; go dark otherwise // button is being held @@ -1783,6 +1885,7 @@ uint8_t momentary_state(Event event, uint16_t arg) { // disconnected for several seconds, so we want to be awake when that // happens to speed up the process) else if (event == EV_tick) { + #ifdef USE_STROBE_STATE if (momentary_active) { // 0 = ramping, 1 = strobes if (momentary_mode == 1) { @@ -1790,16 +1893,25 @@ uint8_t momentary_state(Event event, uint16_t arg) { } } else { - if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds + #endif + if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? + // turn off lighted button + #ifdef USE_INDICATOR_LED + indicator_led(0); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(0, 0); + #endif } + #ifdef USE_STROBE_STATE } + #endif return MISCHIEF_MANAGED; } return EVENT_NOT_HANDLED; } +#endif #ifdef USE_MUGGLE_MODE @@ -1919,6 +2031,9 @@ uint8_t muggle_state(Event event, uint16_t arg) { // turn off, but don't go to the main "off" state if (muggle_off_mode) { if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second + #ifdef USE_AUX_RGB_LEDS_WHILE_ON + rgb_led_set(0); + #endif go_to_standby = 1; // sleep while light is off } } @@ -1940,6 +2055,7 @@ uint8_t muggle_state(Event event, uint16_t arg) { return MISCHIEF_MANAGED; } #endif + #ifdef USE_LVP // low voltage is handled specially in muggle mode else if(event == EV_voltage_low) { uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); @@ -1950,7 +2066,15 @@ uint8_t muggle_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } + #endif + + return EVENT_NOT_HANDLED; +} +#endif + +#ifdef USE_VERSION_CHECK +uint8_t version_check_state(Event event, uint16_t arg) { return EVENT_NOT_HANDLED; } #endif @@ -2247,6 +2371,27 @@ void indicator_blink(uint8_t arg) { #endif #if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) +uint8_t voltage_to_rgb() { + uint8_t levels[] = { + // voltage, color + 0, 0, // 0, R + 33, 1, // 1, R+G + 35, 2, // 2, G + 37, 3, // 3, G+B + 39, 4, // 4, B + 41, 5, // 5, R + B + 44, 6, // 6, R+G+B // skip; looks too similar to G+B + 255, 6, // 7, R+G+B + }; + uint8_t volts = voltage; + if (volts < 29) return 0; + + uint8_t i; + for (i = 0; volts >= levels[i]; i += 2) {} + uint8_t color_num = levels[(i - 2) + 1]; + return pgm_read_byte(rgb_led_colors + color_num); +} + // do fancy stuff with the RGB aux LEDs // mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color // arg: time slice number @@ -2257,7 +2402,13 @@ void rgb_led_update(uint8_t mode, uint8_t arg) { // turn off aux LEDs when battery is empty // (but if voltage==0, that means we just booted and don't know yet) uint8_t volts = voltage; // save a few bytes by caching volatile value - if ((volts) && (volts < VOLTAGE_LOW)) { rgb_led_set(0); return; } + if ((volts) && (volts < VOLTAGE_LOW)) { + rgb_led_set(0); + #ifdef USE_BUTTON_LED + button_led_set(0); + #endif + return; + } uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more? uint8_t color = mode & 0x0f; @@ -2266,36 +2417,31 @@ void rgb_led_update(uint8_t mode, uint8_t arg) { if ((! go_to_standby) && (pattern > 2)) { pattern = 2; } - uint8_t colors[] = { - 0b00000001, // 0: red - 0b00000101, // 1: yellow - 0b00000100, // 2: green - 0b00010100, // 3: cyan - 0b00010000, // 4: blue - 0b00010001, // 5: purple - 0b00010101, // 6: white - }; + const uint8_t *colors = rgb_led_colors; uint8_t actual_color = 0; if (color < 7) { // normal color - actual_color = colors[color]; + actual_color = pgm_read_byte(colors + color); } else if (color == 7) { // rainbow - if (0 == (arg & 0x03)) { + uint8_t speed = 0x03; // awake speed + if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed + if (0 == (arg & speed)) { rainbow = (rainbow + 1) % 6; } - actual_color = colors[rainbow]; + actual_color = pgm_read_byte(colors + rainbow); } else { // voltage // show actual voltage while asleep... if (go_to_standby) { + actual_color = voltage_to_rgb(); // choose a color based on battery voltage - if (volts >= 38) actual_color = colors[4]; - else if (volts >= 33) actual_color = colors[2]; - else actual_color = colors[0]; + //if (volts >= 38) actual_color = pgm_read_byte(colors + 4); + //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2); + //else actual_color = pgm_read_byte(colors + 0); } // ... but during preview, cycle colors quickly else { - actual_color = colors[((arg>>1) % 3) << 1]; + actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1)); } } @@ -2307,17 +2453,40 @@ void rgb_led_update(uint8_t mode, uint8_t arg) { frame = (frame + 1) % sizeof(animation); pattern = animation[frame]; } + uint8_t result; + #ifdef USE_BUTTON_LED + uint8_t button_led_result; + #endif switch (pattern) { case 0: // off - rgb_led_set(0); + result = 0; + #ifdef USE_BUTTON_LED + button_led_result = 0; + #endif break; case 1: // low - rgb_led_set(actual_color); + result = actual_color; + #ifdef USE_BUTTON_LED + button_led_result = 1; + #endif break; - case 2: // high - rgb_led_set(actual_color << 1); + default: // high + result = (actual_color << 1); + #ifdef USE_BUTTON_LED + button_led_result = 2; + #endif break; } + rgb_led_set(result); + #ifdef USE_BUTTON_LED + button_led_set(button_led_result); + #endif +} + +void rgb_led_voltage_readout(uint8_t bright) { + uint8_t color = voltage_to_rgb(); + if (bright) color = color << 1; + rgb_led_set(color); } #endif @@ -2326,7 +2495,7 @@ void rgb_led_update(uint8_t mode, uint8_t arg) { void factory_reset() { // display a warning for a few seconds before doing the actual reset, // so the user has time to abort if they want - #define SPLODEY_TIME 3000 + #define SPLODEY_TIME 2500 #define SPLODEY_STEPS 64 #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS) uint8_t bright; @@ -2334,9 +2503,9 @@ void factory_reset() { // wind up to an explosion for (bright=0; bright<SPLODEY_STEPS; bright++) { set_level(bright); - delay_4ms(SPLODEY_TIME_PER_STEP/2/4); + nice_delay_ms(SPLODEY_TIME_PER_STEP/2); set_level(bright>>1); - delay_4ms(SPLODEY_TIME_PER_STEP/2/4); + nice_delay_ms(SPLODEY_TIME_PER_STEP/2); if (! button_is_pressed()) { reset = 0; break; @@ -2357,14 +2526,14 @@ void factory_reset() { bright = MAX_LEVEL; for (; bright > 0; bright--) { set_level(bright); - delay_4ms(SPLODEY_TIME_PER_STEP/6/4); + nice_delay_ms(SPLODEY_TIME_PER_STEP/8); } } // explosion cancelled, fade away else { for (; bright > 0; bright--) { set_level(bright); - delay_4ms(SPLODEY_TIME_PER_STEP/3/4); + nice_delay_ms(SPLODEY_TIME_PER_STEP/3); } } } @@ -2556,11 +2725,35 @@ void loop() { StatePtr state = current_state; + #ifdef USE_AUX_RGB_LEDS_WHILE_ON + if (! setting_rgb_mode_now) rgb_led_voltage_readout(1); + #endif + if (0) {} + #ifdef USE_VERSION_CHECK + else if (state == version_check_state) { + for (uint8_t i=0; i<sizeof(version_number)-1; i++) { + blink_digit(pgm_read_byte(version_number + i) - '0'); + nice_delay_ms(300); + } + // FIXME: when user interrupts with button, "off" takes an extra click + // before it'll turn back on, because the click to cancel gets sent + // to the "off" state instead of version_check_state + //while (button_is_pressed()) {} + //empty_event_sequence(); + + set_state(off_state, 0); + } + #endif // #ifdef USE_VERSION_CHECK + #ifdef USE_STROBE_STATE else if ((state == strobe_state) - || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes + #ifdef USE_MOMENTARY_MODE + // also handle momentary strobes + || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) + #endif + ) { uint8_t st = strobe_type; switch(st) { @@ -2600,7 +2793,7 @@ void loop() { break; #endif - #ifdef USE_SOS_MODE + #ifdef USE_SOS_MODE_IN_FF_GROUP default: // SOS sos_mode_iter(); break; @@ -2621,6 +2814,12 @@ void loop() { } #endif + #ifdef USE_SOS_MODE_IN_BLINKY_GROUP + else if (state == sos_state) { + sos_mode_iter(); + } + #endif + #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state? else if (state == tempcheck_state) { diff --git a/spaghetti-monster/anduril/anduril.svg b/spaghetti-monster/anduril/anduril.svg index 36840a4..d58e478 100644 --- a/spaghetti-monster/anduril/anduril.svg +++ b/spaghetti-monster/anduril/anduril.svg @@ -22,6 +22,49 @@ inkscape:export-ydpi="109.75774"> <defs id="defs4"> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0.0" + refX="0.0" + id="marker19840" + style="overflow:visible;" + inkscape:isstock="true"> + <path + id="path18419" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) rotate(180) translate(10,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Mstart" + orient="auto" + refY="0.0" + refX="0.0" + id="marker18673" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path18416" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1" + transform="scale(0.4) translate(10,0)" /> + </marker> + <marker + inkscape:stockid="Arrow1Mstart" + orient="auto" + refY="0" + refX="0" + id="marker17250" + style="overflow:visible" + inkscape:isstock="true"> + <path + id="path17248" + d="M 0,0 5,-5 -12.5,0 5,5 Z" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1" + transform="matrix(0.4,0,0,0.4,4,0)" + inkscape:connector-curvature="0" /> + </marker> <linearGradient inkscape:collect="always" id="linearGradient7690"> @@ -57,7 +100,7 @@ <path id="path61710" d="M 0,0 5,-5 -12.5,0 5,5 Z" - style="fill:#7777ff;fill-opacity:1;fill-rule:evenodd;stroke:#7777ff;stroke-width:1.00000003pt;stroke-opacity:1" + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1" transform="matrix(0.4,0,0,0.4,4,0)" inkscape:connector-curvature="0" /> </marker> @@ -1896,16 +1939,6 @@ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt" inkscape:connector-curvature="0" /> </marker> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient1694" - id="linearGradient1364-8" - x1="415.74805" - y1="495.00006" - x2="415.74805" - y2="215.43314" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(904.95044,-71.333651)" /> <marker inkscape:stockid="Arrow1Mend" orient="auto" @@ -2773,6 +2806,42 @@ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt" inkscape:connector-curvature="0" /> </marker> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1694" + id="linearGradient1364-8-7" + x1="415.74805" + y1="495.00006" + x2="415.74805" + y2="215.43314" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(904.95036,-71.333648)" /> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mend-597-9-5-1" + style="overflow:visible"> + <path + id="path3856-76-6-3-3" + d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt" + inkscape:connector-curvature="0" /> + </marker> + <marker + inkscape:stockid="Arrow1Mend" + orient="auto" + refY="0" + refX="0" + id="Arrow1Mend-88-34" + style="overflow:visible"> + <path + id="path3856-868-0" + d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt" + inkscape:connector-curvature="0" /> + </marker> </defs> <sodipodi:namedview id="base" @@ -2786,10 +2855,10 @@ inkscape:cy="568.52482" inkscape:document-units="px" inkscape:current-layer="layer1" - showgrid="false" + showgrid="true" showguides="true" gridtolerance="1" - inkscape:window-width="2389" + inkscape:window-width="2548" inkscape:window-height="1415" inkscape:window-x="0" inkscape:window-y="0" @@ -2799,12 +2868,16 @@ fit-margin-right="0.5" fit-margin-bottom="0.5" units="px" - inkscape:snap-global="false" + inkscape:snap-global="true" inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" + inkscape:bbox-paths="false" + inkscape:bbox-nodes="false" inkscape:snap-bbox-edge-midpoints="true" - inkscape:snap-bbox-midpoints="true"> + inkscape:snap-bbox-midpoints="true" + inkscape:snap-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true"> <inkscape:grid type="xygrid" id="grid3065" @@ -2876,6 +2949,49 @@ inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /> <rect + style="opacity:1;fill:url(#linearGradient1364-8-7);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="rect1356-0-3" + width="37.795277" + height="264.56693" + x="1320.6984" + y="144.09947" /> + <image + y="145.69688" + x="1339.1499" + id="image21186" + xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAMgCAYAAADVw8ezAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI +WXMAAC4mAAAuJgFVH1qAAAAAB3RJTUUH4wgWFywjmzkvJwAABGZJREFUeNrt08ENg0AQBEFjbf6R +9vP+kMUKoeoQpjTXOef+6TX9TQBEQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQARE +QIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIg +QAREQIAICBABASIgQAREQIAICBABASIgQAQEiIAICBABASIgX28qK3iIgAARECACAkRAgAiIgAAR +ECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiI +gAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAgIEAERECACAkRAgAgIEAERECACAkRA +gGivqazgIQICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAIC +RECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEi +IEAERECACAgQAQEiIEAERECACAgQAQGivaaygocICBABASIgQAQEiIAICBABASIgQAQEiIAICBAB +ASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAI +CBABASIgQAQEiIAICBABASIgQAQEiIAAERABASIgQAQEiIAAERABASIgQAQEiPaaygoeIiBABASI +gAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBA +BASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACAkRABASIgAARECAC +AkRABASIgAARECDaayoreIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQ +IAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiA +ABEQIAICRECACAgQAREQIAICRECACAgQAREQIAICRECAaK+prOAhAgJEQIAICBABASIgAgJEQIAI +CBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJE +QIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABAaK9 +prKChwgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEB +IiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAAR +EAEBIiBABASIgAAREAEBIiBABASI9noABIsdhY+pbIsAAAAASUVORK5CYII= +" + preserveAspectRatio="none" + height="261.36438" + width="17.741749" /> + <path + style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:0.30000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.80000007,1.80000007;stroke-dashoffset:2.19000029;stroke-opacity:1" + d="m 1339.1499,145.69688 0,261.36438" + id="path15687-2-6-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <rect style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect5159-4" width="416.73956" @@ -3113,13 +3229,6 @@ id="path5213-6-8-4-9-9-8-4-4" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> - <rect - style="opacity:1;fill:url(#linearGradient1364-8);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" - id="rect1356-0" - width="37.795277" - height="264.56693" - x="1320.6984" - y="144.09947" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" @@ -3136,35 +3245,35 @@ xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" x="1268.1858" - y="203.77095" + y="201.6805" id="text3021-8-4-1-8-6-14-2-4-7-7-9" transform="scale(1.0975814,0.91109416)"><tspan sodipodi:role="line" x="1268.1858" - y="203.77095" + y="201.6805" id="tspan3613-9-7-5-6" style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Ceil</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" x="1263.0579" - y="426.17856" + y="417.30948" id="text3021-8-4-1-8-6-14-2-4-7-7-3-1" transform="scale(1.0975814,0.91109416)"><tspan sodipodi:role="line" x="1263.0579" - y="426.17856" + y="417.30948" id="tspan3613-9-7-5-5-0" style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Floor</tspan></text> <path style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-70)" - d="M 1419.75,188.60753 V 372.1669" + d="m 1419.75,188.60753 0,174.38457" id="path5213-6-8-2-4" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path - style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-7-5)" - d="M 1400.1843,366.71374 V 191.75683" + style="fill:none;stroke:#000000;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:12.79799938,2.1329999;stroke-dashoffset:7.46549964;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-7-5)" + d="M 1400.1843,359.40373 V 191.75683" id="path5213-6-8-2-9-2" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -3245,22 +3354,16 @@ </g> <path style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:7.30000019;stroke-opacity:1" - d="m 1319.7047,180.764 h 69.7795" + d="m 1319.7047,178.2642 h 69.7795" id="path15687-2-6" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0)" - d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 133.489,-146.16407 131.2384,-34.5044" + d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 130.8373,-147.401507 128.5867,-35.74184" id="path5213-6-8-4-9-9-9-29-5-2" inkscape:connector-curvature="0" sodipodi:nodetypes="csc" /> - <path - style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1" - d="m 1319.7047,381.98134 h 64.5803" - id="path15687-9-5" - inkscape:connector-curvature="0" - sodipodi:nodetypes="cc" /> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672" @@ -3842,19 +3945,19 @@ id="tspan7287-5-5-6-6-1"> 6 Clicks</tspan></textPath></text> <path style="fill:none;stroke:none;stroke-width:1.32938921;stroke-miterlimit:4;stroke-dasharray:1.32938933, 1.32938933;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0-4-9)" - d="m 1400.1241,365.79536 c 0,0 0.043,-10.40514 -0.024,5.00754" + d="m 1400.1843,364.54546 c 0,0 -0.017,-16.32727 -0.084,-0.91459" id="path5213-6-8-4-9-9-1-3-0-0-6-4-9" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <path style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:4.05333376;stroke-opacity:1;marker-start:url(#marker14034-2-9);marker-end:url(#Arrow1Mend-597-9-5)" - d="m 1159.7225,198.62656 c 67.9862,-0.44112 68.743,-36.42258 0.1793,-35.70399" + d="m 954.34165,198.62656 c 67.98625,-0.44112 68.74305,-36.42258 0.1793,-35.70399" id="path5213-6-8-4-9-9-8-7-3-8" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> <g id="g18567" - transform="translate(13.62988,7.3367765)"> + transform="translate(-191.75097,7.3367765)"> <text id="text56273" y="160.56241" @@ -3898,7 +4001,7 @@ </g> <g id="g18572" - transform="translate(-76.57207,43.516866)"> + transform="translate(-281.95292,43.516866)"> <rect ry="3" rx="3" @@ -4249,7 +4352,7 @@ sodipodi:nodetypes="cc" /> <path style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0)" - d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 133.489,-146.164071 131.2384,-34.5044" + d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 130.8373,-147.401511 128.5867,-35.74184" id="path5213-6-8-4-9-9-9-29-5-2-7" inkscape:connector-curvature="0" sodipodi:nodetypes="csc" /> @@ -4289,7 +4392,7 @@ sodipodi:nodetypes="cc" /> <path style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-70-9)" - d="M 1419.75,188.60753 V 372.1669" + d="M 1419.75,188.60753 V 362.9921" id="path5213-6-8-2-4-1" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> @@ -4639,11 +4742,11 @@ <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - x="1185.0386" + x="979.65771" y="185.57895" id="text39623-4-9-0-0"><tspan sodipodi:role="line" - x="1185.0386" + x="979.65771" y="185.57895" id="tspan39625-9-1-3-5">3C</tspan></text> <text @@ -4666,8 +4769,8 @@ y="1179.0468" id="tspan39625-9-1-3-9">3C</tspan></text> <path - style="fill:none;stroke:#040404;stroke-width:2.18333006;stroke-miterlimit:4;stroke-dasharray:13.09998035, 2.18333006;stroke-dashoffset:1.30999804;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0-2)" - d="m 1130.3238,305.57915 c 48.1809,3.99296 64.2076,2.68523 79.9712,42.47521 42.8072,108.05255 133.489,146.16407 131.2384,34.5044" + style="fill:none;stroke:#040404;stroke-width:2.18300009;stroke-miterlimit:4;stroke-dasharray:13.09800053,2.18300009;stroke-dashoffset:6.76730027;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0-2)" + d="m 1130.3238,305.57915 c 48.1809,3.99296 64.2076,2.68523 79.9712,42.47521 42.8072,108.05255 131.144,142.50981 129.1171,41.94961" id="path5213-6-8-4-9-9-9-29-5-2-7-4" inkscape:connector-curvature="0" sodipodi:nodetypes="csc" /> @@ -4799,5 +4902,42 @@ x="1091.5458" y="1212.6874" id="tspan43995-1">Factory Reset: Loosen tailcap, Hold button, Tighten tailcap, Hold 3s (or 13H from Off)</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.29780102px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70212275px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="1341.7825" + y="220.06078" + id="text39623-4-9-0-0-1" + transform="scale(0.99329477,1.0067505)"><tspan + sodipodi:role="line" + x="1341.7825" + y="220.06078" + id="tspan39625-9-1-3-5-0" + style="stroke-width:0.70212275px">3C</tspan></text> + <g + id="g21114" + transform="matrix(0.5165354,0,0,0.5165354,676.70758,110.93007)"> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.13333344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:4.26666689;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 1263.3125,222.54688 h 4.2676 v -2.13282 h -4.2676 z m 8.5332,0 h 4.2676 v -2.13282 h -4.2676 z m 8.5332,0 h 4.2676 v -2.13282 h -4.2676 z m 8.5352,0 h 4.2656 v -2.13282 h -4.2656 z m 8.5332,0 h 4.2656 v -2.13282 h -4.2656 z" + id="path5213-6-8-4-9-9-8-3-3" + inkscape:connector-curvature="0" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.85333344pt;stroke-opacity:1" + d="m 1267.5798,221.48082 4.2667,-4.26667 -14.9333,4.26667 14.9333,4.26667 z" + id="path21120" + inkscape:connector-curvature="0" /> + <path + style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.85333344pt;stroke-opacity:1" + d="m 1297.3647,221.48082 -4.2667,4.26667 14.9333,-4.26667 -14.9333,-4.26667 z" + id="path21122" + inkscape:connector-curvature="0" /> + </g> + <path + style="opacity:1;fill:none;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4,4;stroke-dashoffset:3.4;stroke-opacity:1" + d="m 1319.7047,390.82553 h 19.4452 l 0,-16.40309 h 28.99 16.1451" + id="path15687-9-5-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="ccccc" /> </g> </svg> diff --git a/spaghetti-monster/anduril/anduril.txt b/spaghetti-monster/anduril/anduril.txt index db73cdb..0a3dc4c 100644 --- a/spaghetti-monster/anduril/anduril.txt +++ b/spaghetti-monster/anduril/anduril.txt @@ -162,6 +162,12 @@ Indicator LED / aux LED support: TODO: + - change 6C to 6H for exiting muggle mode? + - move muggle mode from 6C to ... 8C or something? + - add 4H from lockout to turn light on and start ramping up? + - move config modes to 5C instead of 4C, and move manual memory to 4C? + - remove beacon config mode, and use a hold to set timing instead? + - rewrite muggle mode to split it into on and off states * save settings in eeprom * decide on "hold until hot" or "click N times" for thermal config mode * test thermal regulation on an actual light diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh index 56a88bf..42a36fd 100755 --- a/spaghetti-monster/anduril/build-all.sh +++ b/spaghetti-monster/anduril/build-all.sh @@ -2,6 +2,8 @@ UI=anduril +date '+#define VERSION_NUMBER "%Y%m%d"' > version.h + for TARGET in cfg-*.h ; do NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') echo "===== $NAME =====" diff --git a/spaghetti-monster/anduril/cfg-blf-lantern.h b/spaghetti-monster/anduril/cfg-blf-lantern.h index 9467397..e12a453 100644 --- a/spaghetti-monster/anduril/cfg-blf-lantern.h +++ b/spaghetti-monster/anduril/cfg-blf-lantern.h @@ -27,6 +27,10 @@ // (one channel for warm emitters, one channel for cold) // so enable a special ramping mode which changes tint instead of brightness #define USE_TINT_RAMPING +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +//#define TINT_RAMPING_CORRECTION 26 // prototype, 140% +#define TINT_RAMPING_CORRECTION 10 // production model, 115% #ifdef RAMP_LENGTH #undef RAMP_LENGTH @@ -51,6 +55,12 @@ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 5 +#define MUGGLE_FLOOR 15 // about 20 lm +#define MUGGLE_CEILING 115 // about 350 lm + +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + // the sensor (attiny85) is nowhere near the emitters // so thermal regulation can't work #ifdef USE_THERMAL_REGULATION diff --git a/spaghetti-monster/anduril/cfg-emisar-d1.h b/spaghetti-monster/anduril/cfg-emisar-d1.h index 35e91db..9276ba3 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d1.h +++ b/spaghetti-monster/anduril/cfg-emisar-d1.h @@ -8,8 +8,3 @@ #undef THERM_FASTER_LEVEL #endif #define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high - -// no need to be extra-careful on this light -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h index 16fbacd..155a747 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d18.h +++ b/spaghetti-monster/anduril/cfg-emisar-d18.h @@ -42,5 +42,3 @@ // stop panicking at about ~40% power or ~5000 lm #define THERM_FASTER_LEVEL 125 -// optional, makes initial turbo step-down faster so first peak isn't as hot -//#define THERM_HARD_TURBO_DROP diff --git a/spaghetti-monster/anduril/cfg-emisar-d1s.h b/spaghetti-monster/anduril/cfg-emisar-d1s.h index 56200a2..c01c37d 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d1s.h +++ b/spaghetti-monster/anduril/cfg-emisar-d1s.h @@ -8,8 +8,3 @@ #undef THERM_FASTER_LEVEL #endif #define THERM_FASTER_LEVEL 144 // throttle back faster when high - -// no need to be extra-careful on this light -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2.h b/spaghetti-monster/anduril/cfg-emisar-d1v2.h new file mode 100644 index 0000000..91be3f3 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-emisar-d1v2.h @@ -0,0 +1,17 @@ +// Emisar D1v2 config options for Anduril (D1 w/ D4v2 driver) +// ATTINY: 1634 +// same as Emisar D4v2, mostly +#include "cfg-emisar-d4v2.h" + +// there are no aux LEDs on a D1 +#undef USE_AUX_RGB_LEDS +// ... and no button LED +#undef USE_BUTTON_LED +// no aux LEDs means no need for sleep ticks +#undef TICK_DURING_STANDBY + +// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h index c86a534..7700d88 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4.h @@ -18,14 +18,6 @@ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 7 -// optional, makes initial turbo step-down faster so first peak isn't as hot -// the D4 runs very very hot, so be extra careful -//#define THERM_HARD_TURBO_DROP - // stop panicking at ~30% power or ~1200 lm #define THERM_FASTER_LEVEL 105 -// respond to thermal changes faster -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 1 -#define THERM_PREDICTION_STRENGTH 4 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h index 230ac7c..f5addb2 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4s.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h @@ -37,12 +37,3 @@ #endif #define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to #define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high - -// no need to be extra-careful on this light -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif - -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 2 -#define THERM_PREDICTION_STRENGTH 4 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h new file mode 100644 index 0000000..9898246 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h @@ -0,0 +1,10 @@ +// Emisar D4Sv2-219 config options for Anduril +#include "cfg-emisar-d4sv2.h" +// ATTINY: 1634 + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,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,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,255,255,255 +#define PWM2_LEVELS 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,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,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM3_LEVELS 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,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,3,4,6,7,9,10,12,13,15,17,18,20,22,24,26,28,30,33,35,37,40,43,45,48,50,53,56,59,62,65,69,72,76,80,83,87,91,95,99,104,108,113,117,123,127,132,138,143,148,154,160,166,172,178,185,192 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h new file mode 100644 index 0000000..c578c4a --- /dev/null +++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h @@ -0,0 +1,53 @@ +// Emisar D4S V2 config options for Anduril +#include "hwdef-Emisar_D4Sv2.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// the aux LEDs are front-facing, so turn them off while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif +// enable blinking aux LEDs +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s +//#define STANDBY_TICK_SPEED 4 // every 0.256 s +//#define STANDBY_TICK_SPEED 5 // every 0.512 s + + +// 1x7135 + 3x7135 + FET +// ../../../bin/level_calc.py seventh 3 150 7135 1 2.3 130 7135 11 5 400.1 FET 2 10 4000 +// (and some manual edits to make the clock speed changes smoother) +#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,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,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,255,255,0 +#define PWM2_LEVELS 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,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,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM3_LEVELS 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,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,3,5,6,8,10,12,14,16,18,20,23,25,27,30,32,35,38,41,44,47,50,53,57,60,64,67,71,75,79,83,87,92,96,101,106,111,116,121,127,132,138,144,150,156,163,169,176,183,190,197,205,213,221,229,237,246,255 +#define MAX_1x7135 62 +#define MAX_Nx7135 93 +#define HALFSPEED_LEVEL 18 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 20, 38, 56, 75, [93], 111, 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define DEFAULT_LEVEL MAX_Nx7135 +#define STROBE_BRIGHTNESS MAX_LEVEL + +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING MAX_Nx7135 + +// stop panicking at ~50% power or ~2000 lm +#define THERM_FASTER_LEVEL 130 + +// easier access to thermal config mode, for Emisar +#define USE_TENCLICK_THERMAL_CONFIG + +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// seems relevant on attiny1634 +#define THERM_CAL_OFFSET 5 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h new file mode 100644 index 0000000..cbb5891 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h @@ -0,0 +1,34 @@ +// Emisar D4v2-noFET config options for Anduril +#include "cfg-emisar-d4v2.h" +// ATTINY: 1634 + +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#define PWM1_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255 +#undef MAX_1x7135 +#define MAX_1x7135 150 +#undef QUARTERSPEED_LEVEL +#undef HALFSPEED_LEVEL +#define QUARTERSPEED_LEVEL 8 +#define HALFSPEED_LEVEL 16 + +#define DEFAULT_LEVEL 80 + +#undef RAMP_SMOOTH_CEIL +#define RAMP_SMOOTH_CEIL 150 +// 10, 45, 80, 115, 150 +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 5 + +#define CANDLE_AMPLITUDE 60 + +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 150 + +#undef USE_THERMAL_REGULATION diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h index b83c65c..241ca7e 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h @@ -4,7 +4,11 @@ // this light has three aux LED channels: R, G, B #define USE_AUX_RGB_LEDS +// it also has an independent LED in the button +#define USE_BUTTON_LED // the aux LEDs are front-facing, so turn them off while main LEDs are on +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently #ifdef USE_INDICATOR_LED_WHILE_RAMPING #undef USE_INDICATOR_LED_WHILE_RAMPING #endif @@ -33,17 +37,8 @@ #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL #define RAMP_DISCRETE_STEPS 7 -// optional, makes initial turbo step-down faster so first peak isn't as hot -// the D4 runs very very hot, so be extra careful -//#define THERM_HARD_TURBO_DROP - // stop panicking at ~30% power or ~1200 lm #define THERM_FASTER_LEVEL 105 -// respond to thermal changes faster -#define THERMAL_WARNING_SECONDS 3 -#define THERMAL_UPDATE_SPEED 1 -#define THERM_PREDICTION_STRENGTH 4 -//#define THERM_RESPONSE_MAGNITUDE 128 // easier access to thermal config mode, for Emisar #define USE_TENCLICK_THERMAL_CONFIG diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h index 7a81c25..e6907c1 100644 --- a/spaghetti-monster/anduril/cfg-ff-pl47.h +++ b/spaghetti-monster/anduril/cfg-ff-pl47.h @@ -61,11 +61,6 @@ // regulate down faster when the FET is active, slower otherwise #define THERM_FASTER_LEVEL 135 // throttle back faster when high -// play it safe, don't try to regulate above the recommended safe level -#ifdef THERM_HARD_TURBO_DROP -#undef THERM_HARD_TURBO_DROP -#endif - // don't do this #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_CEILING diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h new file mode 100644 index 0000000..cab008c --- /dev/null +++ b/spaghetti-monster/anduril/cfg-ff-pl47g2.h @@ -0,0 +1,55 @@ +// Fireflies PL47 G2 config options for Anduril +#include "hwdef-FF_PL47.h" + +// the button lights up +#define USE_INDICATOR_LED +// the aux LEDs are in the same place as the main LEDs +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif +// enable blinking indicator LED while off? +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s +#define USE_FANCIER_BLINKING_INDICATOR + +// If TICK_DURING_STANDBY is enabled... +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + +#define RAMP_LENGTH 150 + +// driver is a FET + 3x7135, ~400 lm at highest regulated level +// ramp copied from Emisar D4S ramp +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,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,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 PWM2_LEVELS 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,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,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 +#define MAX_1x7135 83 +#define HALFSPEED_LEVEL 13 +#define QUARTERSPEED_LEVEL 6 + +// thermal regulation parameters +#ifdef MIN_THERM_STEPDOWN +#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 +#endif +#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to + +// ceiling is level 120/150 +#define RAMP_SMOOTH_CEIL 120 + +// 10, 28, 46, 65, [83], 101, 120 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 120 +#define RAMP_DISCRETE_STEPS 7 + +// ~25 lm to ~300 lm +#define MUGGLE_FLOOR 30 +#define MUGGLE_CEILING MAX_1x7135 + +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 135 // throttle back faster when high + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_CEILING + diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h index 2a90343..a87b66d 100644 --- a/spaghetti-monster/anduril/cfg-ff-rot66.h +++ b/spaghetti-monster/anduril/cfg-ff-rot66.h @@ -38,9 +38,6 @@ // regulate down faster when the FET is active, slower otherwise #define THERM_FASTER_LEVEL 130 // throttle back faster when high -// play it safe, don't try to regulate above the recommended safe level -//#define THERM_HARD_TURBO_DROP - // don't do this #undef BLINK_AT_RAMP_MIDDLE #undef BLINK_AT_RAMP_CEILING diff --git a/spaghetti-monster/anduril/cfg-ff-rot66g2.h b/spaghetti-monster/anduril/cfg-ff-rot66g2.h new file mode 100644 index 0000000..4e353a8 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-ff-rot66g2.h @@ -0,0 +1,49 @@ +// Fireflies ROT66 G2 config options for Anduril +#include "cfg-ff-rot66.h" + +// if the "low" mode was disabled, turn it back on +#ifdef INDICATOR_LED_SKIP_LOW +#undef INDICATOR_LED_SKIP_LOW +#endif +// enable blinking indicator LED while off +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s +#define USE_FANCIER_BLINKING_INDICATOR + +// lockout: blinking (3), off: low (1) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#endif +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// ramp shape is different than original ROT66 +// 1x7135: 150 lm +// Nx7135: 1200 lm +// FET: 4500 lm +// ../../../bin/level_calc.py 7.0 3 150 7135 1 4 180.16 7135 8 1 1374.48 FET 1 10 4000 +// (plus some manual tweaks) +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +#define PWM1_LEVELS 1,1,2,3,3,4,4,5,6,7,7,8,9,10,11,12,16,17,18,19,20,21,22,24,26,28,30,32,35,37,40,42,45,48,51,55,58,62,65,69,74,78,83,87,92,98,103,109,115,121,128,134,142,149,157,165,173,182,191,201,211,221,232,243,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,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 PWM2_LEVELS 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,11,13,14,16,18,20,22,24,26,28,30,33,35,38,41,43,46,49,52,55,59,62,66,69,73,77,81,85,89,94,98,103,108,113,118,124,129,135,141,147,153,160,166,173,180,188,195,203,211,219,228,237,246,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 PWM3_LEVELS 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,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,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,6,11,17,23,29,36,42,49,56,63,71,78,86,94,102,111,119,128,137,147,156,166,176,187,197,208,219,231,243,255 + +#undef MAX_1x7135 +#undef MAX_Nx7135 +#define MAX_1x7135 65 // ~150 lm +#define MAX_Nx7135 120 // ~1200 m +#undef HALFSPEED_LEVEL +#undef QUARTERSPEED_LEVEL +#define HALFSPEED_LEVEL 17 +#define QUARTERSPEED_LEVEL 6 + +// higher floor than default, and stop at highest regulated level +#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm +#define RAMP_SMOOTH_CEIL MAX_Nx7135 // ~1200 lm +// 10, 28, 46, [65], 83, 101, [120] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL MAX_Nx7135 +#define RAMP_DISCRETE_STEPS 7 + + diff --git a/spaghetti-monster/anduril/cfg-fw3a-nofet.h b/spaghetti-monster/anduril/cfg-fw3a-nofet.h new file mode 100644 index 0000000..a6be10d --- /dev/null +++ b/spaghetti-monster/anduril/cfg-fw3a-nofet.h @@ -0,0 +1,36 @@ +// FW3A with the FET disabled +#include "cfg-fw3a.h" + +// don't use channel 3 (FET) +#undef PWM_CHANNELS +#undef PWM3_PIN +#undef PWM3_LVL +#define PWM_CHANNELS 2 + +// reconfigure the ramp +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +// copied from Emisar D4, mostly +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 +#define PWM2_LEVELS 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 +#undef MAX_1x7135 +#define MAX_1x7135 65 +#undef HALFSPEED_LEVEL +#define HALFSPEED_LEVEL 14 +#undef QUARTERSPEED_LEVEL +#define QUARTERSPEED_LEVEL 6 + +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 10, 33, 56, 80, 103, 126, 150 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h new file mode 100644 index 0000000..28c77c2 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h @@ -0,0 +1,50 @@ +// Mateminco/Astrolux MF01-Mini options for Anduril +#include "hwdef-Mateminco_MF01-Mini.h" + +// the button lights up +#define USE_INDICATOR_LED +//#define INDICATOR_LED_SKIP_LOW // low mode doesn't work on this driver +// the button is visible while main LEDs are on +//#define USE_INDICATOR_LED_WHILE_RAMPING +// enable blinking indicator LED while off +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s +#define USE_FANCIER_BLINKING_INDICATOR +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + +// don't blink during ramp, it's irrelevant and annoying on this light +#define BLINK_AT_RAMP_CEILING +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// measured brightness with Sofirn 5500mAh cell at 3.97V: +// moon: 0.3 lm +// channel 1: 113 lm +// channel 2: 718 lm +// channel 3: 3500 lm +// ../../../bin/level_calc.py ninth 3 150 7135 1 2.5 115.65 7135 11 5 708.65 FET 1 10 3500 +// (plus some manual tweaks for a smoother ramp) +#define RAMP_LENGTH 150 +#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,9,10,14,15,16,17,18,19,20,21,22,24,26,28,30,32,34,37,39,42,45,48,51,54,58,62,65,69,74,78,83,88,93,98,104,110,116,123,130,137,145,153,161,170,179,188,198,208,219,231,243,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,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 PWM2_LEVELS 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,13,16,18,20,23,25,28,31,34,37,40,43,47,50,54,58,62,66,70,75,80,85,90,95,100,106,112,118,125,131,138,145,153,161,169,177,185,194,204,213,223,233,244,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 PWM3_LEVELS 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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,10,13,17,21,24,28,33,37,41,46,50,55,60,66,71,76,82,88,94,101,107,114,121,128,135,143,151,159,167,176,185,194,203,213,223,233,244,255 +#define MAX_1x7135 65 // ~113 lm +#define MAX_Nx7135 110 +#define HALFSPEED_LEVEL 16 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm +#define RAMP_SMOOTH_CEIL 130 // ~??? lm +// 14/135/6 = 14, 38, 62, 86, [110], 135 +// 20/110/7 = 20, 35, 50, [65], 80, 95, [110] +// 15/130/7 = 15, 34, 53, 72, 91, [110], 130 <-- +#define RAMP_DISCRETE_FLOOR 15 // ~?? lm +#define RAMP_DISCRETE_CEIL 130 // ~??? lm +#define RAMP_DISCRETE_STEPS 7 // ??, ??, ... lm + + +#define USE_TENCLICK_THERMAL_CONFIG // by request +#define THERM_FASTER_LEVEL 130 // throttle back faster when high diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h b/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h new file mode 100644 index 0000000..617801f --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h @@ -0,0 +1,65 @@ +// Noctigon K1 12V config options for Anduril +#include "hwdef-Noctigon_K1-12V.h" +// ATTINY: 1634 + +// this light can safely run a bit hotter than most +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 55 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON +#define USE_INDICATOR_LED_WHILE_RAMPING +#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage +#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow + +// enable blinking aux LEDs +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s + + +// level_calc.py cube 1 150 7135 0 4 1300 +// (with max_pwm set to 1023) +// (level 0 is usable on this light) +#define RAMP_LENGTH 150 +#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,42,44,46,48,51,53,56,59,61,64,67,70,73,76,80,83,86,90,94,97,101,105,109,113,117,122,126,130,135,140,144,149,154,159,165,170,175,181,187,193,198,204,211,217,223,230,236,243,250,257,264,271,279,286,294,302,310,318,326,334,343,351,360,369,378,387,397,406,416,426,436,446,456,466,477,488,499,510,521,532,544,555,567,579,591,604,616,629,642,655,668,682,695,709,723,737,751,766,780,795,810,825,841,856,872,888,904,921,937,954,971,988,1005,1023 +#define MAX_1x7135 50 + +// the entire ramp is regulated; don't blink halfway up +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// don't slow down at low levels; this isn't that sort of light +// (it needs to stay at full speed for the 10-bit PWM to work) +#ifdef USE_DYNAMIC_UNDERCLOCKING +#undef USE_DYNAMIC_UNDERCLOCKING +#endif + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, [50], 70, 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING 70 + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 32 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly +#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting + +// easier access to thermal config mode, for Noctigon +#define USE_TENCLICK_THERMAL_CONFIG + +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 4 + +#define THERM_CAL_OFFSET 5 + diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h new file mode 100644 index 0000000..fd98979 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h @@ -0,0 +1,61 @@ +// Noctigon K1-SBT90.2 config options for Anduril +// (is a K1 host with a KR4-like driver and a really high-powered LED) +#include "hwdef-Noctigon_K1-SBT90.h" +// ATTINY: 1634 + +// this light can safely run a bit hotter than most +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 55 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON +#define USE_INDICATOR_LED_WHILE_RAMPING +#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage +#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow +#define RGB_RAINBOW_SPEED 0x03 // half a second per color + +// enable blinking aux LEDs +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s + + +// brightness: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// FET: ~3700 lm +// maxreg at 130: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 2565 +// maxreg at 120: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 3190 +#define RAMP_LENGTH 150 +#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,0 +#define PWM2_LEVELS 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,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,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,22,51,79,109,138,168,198,229,260,292,324,357,390,423,457,492,527,562,598,634,671,708,746,784,822,861,901,941,982,1023 +#define DEFAULT_LEVEL 46 +#define MAX_1x7135 120 +#define HALFSPEED_LEVEL 10 +#define QUARTERSPEED_LEVEL 2 + +#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable +#define RAMP_SMOOTH_CEIL 120 +// 10, 28, [46], 65, 83, 101, [120] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING 65 + +// stop panicking at ~25% power or ~1000 lm +#define THERM_FASTER_LEVEL 120 +#define MIN_THERM_STEPDOWN DEFAULT_LEVEL +//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting +//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments + +// easier access to thermal config mode, for Noctigon +#define USE_TENCLICK_THERMAL_CONFIG + +// slow down party strobe; this driver can't pulse for 1ms or less +//#define PARTY_STROBE_ONTIME 2 + +#define THERM_CAL_OFFSET 5 + diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1.h b/spaghetti-monster/anduril/cfg-noctigon-k1.h new file mode 100644 index 0000000..4f2c2cc --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-k1.h @@ -0,0 +1,66 @@ +// Noctigon K1 config options for Anduril +// (originally known as Emisar D1S v2) +#include "hwdef-Noctigon_K1.h" +// ATTINY: 1634 + +// this light can safely run a bit hotter than most +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 55 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON +#define USE_INDICATOR_LED_WHILE_RAMPING +#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage +#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow + +// enable blinking aux LEDs +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s + + +// ../../bin/level_calc.py cube 1 150 7135 1 4 1300 +// (with max_pwm set to 1023) +// (level 0 flickers and isn't relevant on a thrower, so it's omitted) +#define RAMP_LENGTH 150 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,32,34,36,38,40,43,45,47,49,52,54,57,60,62,65,68,71,74,77,81,84,87,91,95,98,102,106,110,114,118,122,127,131,136,141,145,150,155,160,166,171,176,182,188,193,199,205,211,218,224,231,237,244,251,258,265,272,280,287,295,303,310,319,327,335,344,352,361,370,379,388,397,407,416,426,436,446,457,467,477,488,499,510,521,533,544,556,568,580,592,604,617,629,642,655,668,682,695,709,723,737,751,766,781,795,810,826,841,857,872,888,904,921,937,954,971,988,1005,1023 +#define MAX_1x7135 50 + +// the entire ramp is regulated; don't blink halfway up +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// don't slow down at low levels; this isn't that sort of light +// (it needs to stay at full speed for the 10-bit PWM to work) +#ifdef USE_DYNAMIC_UNDERCLOCKING +#undef USE_DYNAMIC_UNDERCLOCKING +#endif + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, [50], 70, 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING 70 + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 32 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly +#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting + +// easier access to thermal config mode, for Noctigon +#define USE_TENCLICK_THERMAL_CONFIG + +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 + +#define THERM_CAL_OFFSET 5 + diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h new file mode 100644 index 0000000..0cfccf2 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h @@ -0,0 +1,11 @@ +// Noctigon KR4 (75% FET) config options for Anduril +#include "cfg-noctigon-kr4.h" +// ATTINY: 1634 + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023 +// 75% FET power +#undef PWM2_LEVELS +#define PWM2_LEVELS 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,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,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,17,39,60,82,104,126,149,172,195,219,243,268,293,318,343,369,396,422,449,476,504,531,560,588,617,646,676,706,737,768 + diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h new file mode 100644 index 0000000..19cbc23 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h @@ -0,0 +1,47 @@ +// Noctigon KR4 (fetless) config options for Anduril +#include "cfg-noctigon-kr4.h" +// ATTINY: 1634 + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// level_calc.py 3.0 1 150 7135 0 5 1740 +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 +#define RAMP_LENGTH 150 +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,55,58,61,63,66,69,72,75,79,82,85,89,92,96,100,104,108,112,116,120,125,129,134,138,143,148,153,158,163,169,174,180,185,191,197,203,209,215,222,228,235,242,248,255,263,270,277,285,292,300,308,316,324,333,341,350,359,368,377,386,395,405,414,424,434,444,454,465,475,486,497,508,519,531,542,554,566,578,590,603,615,628,641,654,667,680,694,708,722,736,750,765,779,794,809,825,840,856,872,888,904,920,937,954,971,988,1005,1023 +#undef PWM2_LEVELS +#undef DEFAULT_LEVEL +#define DEFAULT_LEVEL 50 +#undef MAX_1x7135 +#define MAX_1x7135 150 + +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS + +#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, [50], 70, 90, 110, 130 (plus [150] on turbo) +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#undef MUGGLE_FLOOR +#undef MUGGLE_CEILING +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING 70 + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 32 + +// stop panicking at ~90% power or ~1600 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 143 +#undef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN DEFAULT_LEVEL + diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4.h b/spaghetti-monster/anduril/cfg-noctigon-kr4.h new file mode 100644 index 0000000..dc71655 --- /dev/null +++ b/spaghetti-monster/anduril/cfg-noctigon-kr4.h @@ -0,0 +1,58 @@ +// Noctigon KR4 config options for Anduril +#include "hwdef-Noctigon_KR4.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +//#define USE_AUX_RGB_LEDS_WHILE_ON +//#define USE_INDICATOR_LED_WHILE_RAMPING +#define RGB_LED_OFF_DEFAULT 0x17 // low, rainbow +#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow +#define RGB_RAINBOW_SPEED 0x03 // half a second per color + +// enable blinking aux LEDs +#define TICK_DURING_STANDBY +#define STANDBY_TICK_SPEED 3 // every 0.128 s + + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// FET: ~3700 lm +// maxreg at 130: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 2565 +// maxreg at 120: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 3190 +#define RAMP_LENGTH 150 +#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,0 +#define PWM2_LEVELS 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,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,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,22,51,79,109,138,168,198,229,260,292,324,357,390,423,457,492,527,562,598,634,671,708,746,784,822,861,901,941,982,1023 +#define DEFAULT_LEVEL 46 +#define MAX_1x7135 120 +#define HALFSPEED_LEVEL 10 +#define QUARTERSPEED_LEVEL 2 + +#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable +#define RAMP_SMOOTH_CEIL 120 +// 10, 28, [46], 65, 83, 101, [120] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR +#define MUGGLE_CEILING 65 + +// stop panicking at ~25% power or ~1000 lm +#define THERM_FASTER_LEVEL 100 +#define MIN_THERM_STEPDOWN DEFAULT_LEVEL +#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting +#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments + +// easier access to thermal config mode, for Noctigon +#define USE_TENCLICK_THERMAL_CONFIG + +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 + +#define THERM_CAL_OFFSET 5 + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h index 494a263..d808e2a 100644 --- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h +++ b/spaghetti-monster/anduril/cfg-sofirn-sp36.h @@ -28,10 +28,3 @@ #undef THERM_FASTER_LEVEL #endif #define THERM_FASTER_LEVEL 130 - -// be extra-careful at high levels -// (or not... this host seems to heat up pretty slowly) -//#ifndef THERM_HARD_TURBO_DROP -//#define THERM_HARD_TURBO_DROP -//#endif - diff --git a/spaghetti-monster/anduril/version.h b/spaghetti-monster/anduril/version.h new file mode 100644 index 0000000..8cf3c90 --- /dev/null +++ b/spaghetti-monster/anduril/version.h @@ -0,0 +1,4 @@ +// this file is replaced automatically by the build script +// set your own date here if you're not using the build script +// otherwise, default to first human contact with the moon +#define VERSION_NUMBER "19690720" diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c index e613f55..4058c2f 100644 --- a/spaghetti-monster/darkhorse/darkhorse.c +++ b/spaghetti-monster/darkhorse/darkhorse.c @@ -21,7 +21,6 @@ #define USE_LVP #define USE_THERMAL_REGULATION #define DEFAULT_THERM_CEIL 45 -#define USE_DELAY_4MS #define USE_RAMPING #define RAMP_LENGTH 150 #define USE_BATTCHECK diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c index 6832e32..45a4297 100644 --- a/spaghetti-monster/fsm-adc.c +++ b/spaghetti-monster/fsm-adc.c @@ -21,37 +21,49 @@ #define FSM_ADC_C -inline void set_admux_therm() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) +static inline void set_admux_therm() { + #if (ATTINY == 1634) ADMUX = ADMUX_THERM; - #elif (ATTINY == 841) + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + ADMUX = ADMUX_THERM | (1 << ADLAR); + #elif (ATTINY == 841) // FIXME: not tested ADMUXA = ADMUXA_THERM; ADMUXB = ADMUXB_THERM; #else #error Unrecognized MCU type #endif + adc_channel = 1; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); } inline void set_admux_voltage() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) - #ifdef USE_VOLTAGE_DIVIDER - // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER; - #else - // VCC / 1.1V reference - ADMUX = ADMUX_VCC; + #if (ATTINY == 1634) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC; #endif - #elif (ATTINY == 841) - #ifdef USE_VOLTAGE_DIVIDER - ADMUXA = ADMUXA_VOLTAGE_DIVIDER; - ADMUXB = ADMUXB_VOLTAGE_DIVIDER; - #else - ADMUXA = ADMUXA_VCC; - ADMUXB = ADMUXB_VCC; + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC | (1 << ADLAR); + #endif + #elif (ATTINY == 841) // FIXME: not tested + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUXA = ADMUXA_VOLTAGE_DIVIDER; + ADMUXB = ADMUXB_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUXA = ADMUXA_VCC; + ADMUXB = ADMUXB_VCC; #endif #else #error Unrecognized MCU type #endif + adc_channel = 0; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); } inline void ADC_start_measurement() { @@ -68,24 +80,25 @@ inline void ADC_on() #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) set_admux_voltage(); #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - DIDR0 |= (1 << VOLTAGE_ADC_DIDR); + // disable digital input on divider pin to reduce power consumption + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); #else - // disable digital input on VCC pin to reduce power consumption - //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin + // disable digital input on VCC pin to reduce power consumption + //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin #endif #if (ATTINY == 1634) - ACSRA |= (1 << ACD); // turn off analog comparator to save power + //ACSRA |= (1 << ACD); // turn off analog comparator to save power + ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX #endif - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; // end tiny25/45/85 - #elif (ATTINY == 841) + #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust ADCSRB = 0; // Right adjusted, auto trigger bits cleared. //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. set_admux_voltage(); - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; //ADCSRA |= (1 << ADSC); // start measuring #else #error Unrecognized MCU type @@ -99,288 +112,320 @@ inline void ADC_off() { #ifdef USE_VOLTAGE_DIVIDER static inline uint8_t calc_voltage_divider(uint16_t value) { // use 9.7 fixed-point to get sufficient precision - uint16_t adc_per_volt = ((ADC_44<<7) - (ADC_22<<7)) / (44-22); - // incoming value is 8.2 fixed-point, so shift it 2 bits less - uint8_t result = ((value<<5) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; + uint16_t adc_per_volt = ((ADC_44<<5) - (ADC_22<<5)) / (44-22); + // shift incoming value into a matching position + uint8_t result = ((value>>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR; return result; } #endif -// Each full cycle runs 7.8X per second with just voltage enabled, -// or 3.9X per second with voltage and temperature. +// Each full cycle runs ~2X per second with just voltage enabled, +// or ~1X per second with voltage and temperature. #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) -#define ADC_CYCLES_PER_SECOND 4 +#define ADC_CYCLES_PER_SECOND 1 #else -#define ADC_CYCLES_PER_SECOND 8 +#define ADC_CYCLES_PER_SECOND 2 #endif -// TODO: is this better done in main() or WDT()? + +// happens every time the ADC sampler finishes a measurement ISR(ADC_vect) { - // For some reason, the ADC interrupt is getting called a *lot* - // more often than it should be, like it's auto-triggering after each - // measurement, but I don't know why, or how to turn that off... - // So, skip every call except when explicitly requested. - if (! adcint_enable) return; - adcint_enable = 0; - static uint8_t adc_step = 0; + if (adc_sample_count) { - // LVP declarations - #ifdef USE_LVP - #ifdef USE_LVP_AVG - #define NUM_VOLTAGE_VALUES 4 - static int16_t voltage_values[NUM_VOLTAGE_VALUES]; - #endif - static uint8_t lvp_timer = 0; - static uint8_t lvp_lowpass = 0; - #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings - #define LVP_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second - #endif + uint16_t m; // latest measurement + uint16_t s; // smoothed measurement + uint8_t channel = adc_channel; - // thermal declarations - #ifdef USE_THERMAL_REGULATION - #ifndef THERMAL_UPDATE_SPEED - #define THERMAL_UPDATE_SPEED 2 - #endif - #define NUM_THERMAL_VALUES_HISTORY 8 - #define ADC_STEPS 4 - static uint8_t history_step = 0; // don't update history as often - static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY]; - static uint8_t temperature_timer = 0; - static uint8_t overheat_lowpass = 0; - static uint8_t underheat_lowpass = 0; - #define TEMPERATURE_TIMER_START ((THERMAL_WARNING_SECONDS-2)*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events - #define OVERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds - #define UNDERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds - #else - #define ADC_STEPS 2 - #endif + // update the latest value + m = ADC; + adc_raw[channel] = m; + + // lowpass the value + //s = adc_smooth[channel]; // easier to read + uint16_t *v = adc_smooth + channel; // compiles smaller + s = *v; + if (m > s) { s++; } + if (m < s) { s--; } + //adc_smooth[channel] = s; + *v = s; + + // track what woke us up, and enable deferred logic + irq_adc = 1; + + } + + // the next measurement isn't the first + adc_sample_count = 1; + // rollover doesn't really matter + //adc_sample_count ++; - uint16_t measurement = ADC; // latest 10-bit ADC reading +} + +void adc_deferred() { + irq_adc = 0; // event handled #ifdef USE_PSEUDO_RAND // real-world entropy makes this a true random, not pseudo - pseudo_rand_seed += measurement; + // Why here instead of the ISR? Because it makes the time-critical ISR + // code a few cycles faster and we don't need crypto-grade randomness. + pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2); + #endif + + // the ADC triggers repeatedly when it's on, but we only need to run the + // voltage and temperature regulation stuff once in a while...so disable + // this after each activation, until it's manually enabled again + if (! adc_deferred_enable) return; + + // disable after one iteration + adc_deferred_enable = 0; + + // what is being measured? 0 = battery voltage, 1 = temperature + uint8_t adc_step; + + #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) + // do whichever one is currently active + adc_step = adc_channel; + #else + // unless there's no temperature sensor... then just do voltage + adc_step = 0; #endif #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP) - // only measure battery voltage while asleep - if (go_to_standby) adc_step = 1; - else + // in sleep mode, turn off after just one measurement + // (having the ADC on raises standby power by about 250 uA) + // (and the usual standby level is only ~20 uA) + if (go_to_standby) { + ADC_off(); + // also, only check the battery while asleep, not the temperature + adc_channel = 0; + } #endif - adc_step = (adc_step + 1) & (ADC_STEPS-1); + if (0) {} // placeholder for easier syntax #ifdef USE_LVP - // voltage - if (adc_step == 1) { - #ifdef USE_LVP_AVG - // prime on first execution - if (voltage == 0) { - for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++) - voltage_values[i] = measurement; - voltage = 42; // the answer to life, the universe, and the voltage of a full li-ion cell - } else { - uint16_t total = 0; - uint8_t i; - for(i=0; i<NUM_VOLTAGE_VALUES-1; i++) { - voltage_values[i] = voltage_values[i+1]; - total += voltage_values[i]; - } - voltage_values[i] = measurement; - total += measurement; - total = total >> 2; - - #ifdef USE_VOLTAGE_DIVIDER - voltage = calc_voltage_divider(total); - #else - voltage = (uint16_t)(1.1*1024*10)/total + VOLTAGE_FUDGE_FACTOR; - #endif - } - #else // no USE_LVP_AVG - #ifdef USE_VOLTAGE_DIVIDER - voltage = calc_voltage_divider(measurement); - #else - // calculate actual voltage: volts * 10 - // ADC = 1.1 * 1024 / volts - // volts = 1.1 * 1024 / ADC - //voltage = (uint16_t)(1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR; - voltage = ((uint16_t)(2*1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR) >> 1; - #endif + else if (0 == adc_step) { // voltage + ADC_voltage_handler(); + #ifdef USE_THERMAL_REGULATION + // set the correct type of measurement for next time + if (! go_to_standby) set_admux_therm(); #endif - // if low, callback EV_voltage_low / EV_voltage_critical - // (but only if it has been more than N ticks since last call) - if (lvp_timer) { - lvp_timer --; - } else { // it has been long enough since the last warning - if (voltage < VOLTAGE_LOW) { - if (lvp_lowpass < LVP_LOWPASS_STRENGTH) { - lvp_lowpass ++; - } else { - // try to send out a warning - //uint8_t err = emit(EV_voltage_low, 0); - //uint8_t err = emit_now(EV_voltage_low, 0); - emit(EV_voltage_low, 0); - //if (!err) { - // on successful warning, reset counters - lvp_timer = LVP_TIMER_START; - lvp_lowpass = 0; - //} - } - } else { - // voltage not low? reset count - lvp_lowpass = 0; - } - } } - #endif // ifdef USE_LVP - + #endif #ifdef USE_THERMAL_REGULATION - // temperature - else if (adc_step == 3) { - // Convert ADC units to Celsius (ish) - int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset; - - // prime on first execution - if (reset_thermal_history) { - reset_thermal_history = 0; - temperature = temp; - for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++) - temperature_history[i] = temp; - } else { // update our current temperature estimate - // crude lowpass filter - // (limit rate of change to 1 degree per measurement) - if (temp > temperature) { - temperature ++; - } else if (temp < temperature) { - temperature --; - } - } + else if (1 == adc_step) { // temperature + ADC_temperature_handler(); + #ifdef USE_LVP + // set the correct type of measurement for next time + set_admux_voltage(); + #endif + } + #endif +} - // guess what the temperature will be in a few seconds - int16_t pt; - { - int16_t diff; - int16_t t = temperature; - - // algorithm tweaking; not really intended to be modified - // how far ahead should we predict? - #ifndef THERM_PREDICTION_STRENGTH - #define THERM_PREDICTION_STRENGTH 4 - #endif - // how proportional should the adjustments be? (not used yet) - #ifndef THERM_RESPONSE_MAGNITUDE - #define THERM_RESPONSE_MAGNITUDE 128 - #endif - // acceptable temperature window size in C - #define THERM_WINDOW_SIZE 5 - // highest temperature allowed - #define THERM_CEIL ((int16_t)therm_ceil) - // bottom of target temperature window - #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE) - - // if it's time to rotate the thermal history, do it - history_step ++; - #if (THERMAL_UPDATE_SPEED == 4) // new value every 4s - #define THERM_HISTORY_STEP_MAX 15 - #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s - #define THERM_HISTORY_STEP_MAX 7 - #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s - #define THERM_HISTORY_STEP_MAX 3 - #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s - #define THERM_HISTORY_STEP_MAX 1 - #endif - if (0 == (history_step & THERM_HISTORY_STEP_MAX)) { - // rotate measurements and add a new one - for (uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) { - temperature_history[i] = temperature_history[i+1]; - } - temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t; - } - - // guess what the temp will be several seconds in the future - // diff = rate of temperature change - //diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0]; - diff = t - temperature_history[0]; - // slight bias toward zero; ignore very small changes (noise) - for (uint8_t z=0; z<3; z++) { - if (diff < 0) diff ++; - if (diff > 0) diff --; - } - // projected_temperature = current temp extended forward by amplified rate of change - //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH); - pt = projected_temperature = t + (diff<<THERM_PREDICTION_STRENGTH); - } - // cancel counters if appropriate - if (pt > THERM_FLOOR) { - underheat_lowpass = 0; // we're probably not too cold - } - if (pt < THERM_CEIL) { - overheat_lowpass = 0; // we're probably not too hot - } +#ifdef USE_LVP +static inline void ADC_voltage_handler() { + // rate-limit low-voltage warnings to a max of 1 per N seconds + static uint8_t lvp_timer = 0; + #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings - if (temperature_timer) { - temperature_timer --; - } else { // it has been long enough since the last warning - - // Too hot? - if (pt > THERM_CEIL) { - if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) { - overheat_lowpass ++; - } else { - // reset counters - overheat_lowpass = 0; - temperature_timer = TEMPERATURE_TIMER_START; - // how far above the ceiling? - //int16_t howmuch = (pt - THERM_CEIL) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = pt - THERM_CEIL; - // try to send out a warning - emit(EV_temperature_high, howmuch); - } - } - - // Too cold? - else if (pt < THERM_FLOOR) { - if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) { - underheat_lowpass ++; - } else { - // reset counters - underheat_lowpass = 0; - temperature_timer = TEMPERATURE_TIMER_START; - // how far below the floor? - //int16_t howmuch = (THERM_FLOOR - pt) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = THERM_FLOOR - pt; - // try to send out a warning (unless voltage is low) - // (LVP and underheat warnings fight each other) - if (voltage > VOLTAGE_LOW) - emit(EV_temperature_low, howmuch); - } - } - } - } - #endif // ifdef USE_THERMAL_REGULATION + #ifdef NO_LVP_WHILE_BUTTON_PRESSED + // don't run if button is currently being held + // (because the button causes a reading of zero volts) + if (button_last_state) return; + #endif + uint16_t measurement; - // set the correct type of measurement for next time - #ifdef USE_THERMAL_REGULATION - #ifdef USE_LVP - if (adc_step < 2) set_admux_voltage(); - else set_admux_therm(); - #else - set_admux_therm(); - #endif + // latest ADC value + if (go_to_standby || (adc_smooth[0] < 255)) { + measurement = adc_raw[0]; + adc_smooth[0] = measurement; // no lowpass while asleep + } + else measurement = adc_smooth[0]; + + // values stair-step between intervals of 64, with random variations + // of 1 or 2 in either direction, so if we chop off the last 6 bits + // it'll flap between N and N-1... but if we add half an interval, + // the values should be really stable after right-alignment + // (instead of 99.98, 100.00, and 100.02, it'll hit values like + // 100.48, 100.50, and 100.52... which are stable when truncated) + //measurement += 32; + //measurement = (measurement + 16) >> 5; + measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 + + #ifdef USE_VOLTAGE_DIVIDER + voltage = calc_voltage_divider(measurement); #else - #ifdef USE_LVP - set_admux_voltage(); - #endif + // calculate actual voltage: volts * 10 + // ADC = 1.1 * 1024 / volts + // volts = 1.1 * 1024 / ADC + voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) + VOLTAGE_FUDGE_FACTOR) >> 1; #endif - #ifdef TICK_DURING_STANDBY - // if we were asleep, go back to sleep - if (go_to_standby) ADC_off(); + // if low, callback EV_voltage_low / EV_voltage_critical + // (but only if it has been more than N seconds since last call) + if (lvp_timer) { + lvp_timer --; + } else { // it has been long enough since the last warning + if (voltage < VOLTAGE_LOW) { + // send out a warning + emit(EV_voltage_low, 0); + // reset rate-limit counter + lvp_timer = LVP_TIMER_START; + } + } +} +#endif + + +#ifdef USE_THERMAL_REGULATION +// generally happens once per second while awake +static inline void ADC_temperature_handler() { + // coarse adjustment + #ifndef THERM_LOOKAHEAD + #define THERM_LOOKAHEAD 4 + #endif + // reduce frequency of minor warnings + #ifndef THERM_NEXT_WARNING_THRESHOLD + #define THERM_NEXT_WARNING_THRESHOLD 24 #endif + // fine-grained adjustment + // how proportional should the adjustments be? + #ifndef THERM_RESPONSE_MAGNITUDE + #define THERM_RESPONSE_MAGNITUDE 64 + #endif + // acceptable temperature window size in C + #define THERM_WINDOW_SIZE 2 + + // TODO? make this configurable per build target? + // (shorter time for hosts with a lower power-to-mass ratio) + // (because then it'll have smaller responses) + #define NUM_TEMP_HISTORY_STEPS 8 // don't change; it'll break stuff + static uint8_t history_step = 0; + static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS]; + static int8_t warning_threshold = 0; + + if (reset_thermal_history) { // wipe out old data + // don't keep resetting + reset_thermal_history = 0; + + // ignore average, use latest sample + uint16_t foo = adc_raw[1]; + adc_smooth[1] = foo; + + // forget any past measurements + for(uint8_t i=0; i<NUM_TEMP_HISTORY_STEPS; i++) + temperature_history[i] = (foo + 16) >> 5; + } + + // latest 16-bit ADC reading + uint16_t measurement = adc_smooth[1]; + + // values stair-step between intervals of 64, with random variations + // of 1 or 2 in either direction, so if we chop off the last 6 bits + // it'll flap between N and N-1... but if we add half an interval, + // the values should be really stable after right-alignment + // (instead of 99.98, 100.00, and 100.02, it'll hit values like + // 100.48, 100.50, and 100.52... which are stable when truncated) + //measurement += 32; + measurement = (measurement + 16) >> 5; + //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 + + // let the UI see the current temperature in C + // Convert ADC units to Celsius (ish) + temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset - 275; + + // how much has the temperature changed between now and a few seconds ago? + int16_t diff; + diff = measurement - temperature_history[history_step]; + + // update / rotate the temperature history + temperature_history[history_step] = measurement; + history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1); + + // PI[D]: guess what the temperature will be in a few seconds + uint16_t pt; // predicted temperature + pt = measurement + (diff * THERM_LOOKAHEAD); + + // convert temperature limit from C to raw 16-bit ADC units + // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + therm_cal_offset; + // ... so ... + // (C + 275 - THERM_CAL_OFFSET - therm_cal_offset) << 6 = ADC; + uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 1; + int16_t offset = pt - ceil; + + // bias small errors toward zero, while leaving large errors mostly unaffected + // (a diff of 1 C is 2 ADC units, * 4 for therm lookahead, so it becomes 8) + // (but a diff of 1 C should only send a warning of magnitude 1) + // (this also makes it only respond to small errors at the time the error + // happened, not after the temperature has stabilized) + for(uint8_t foo=0; foo<3; foo++) { + if (offset > 0) { + offset --; + } else if (offset < 0) { + offset ++; + } + } + + // Too hot? + // (if it's too hot and not getting cooler...) + if ((offset > 0) && (diff > -1)) { + // accumulated error isn't big enough yet to send a warning + if (warning_threshold > 0) { + warning_threshold -= offset; + } else { // error is big enough; send a warning + // how far above the ceiling? + // original method works, but is too slow on some small hosts: + // (and typically has a minimum response magnitude of 2 instead of 1) + // int16_t howmuch = offset; + // ... so increase the amount, except for small values + // (for example, 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77) + // ... and let us tune the response per build target if desired + int16_t howmuch = (offset + offset - 3) * THERM_RESPONSE_MAGNITUDE / 128; + if (howmuch < 1) howmuch = 1; + warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch; + + // send a warning + emit(EV_temperature_high, howmuch); + } + } + + // Too cold? + // (if it's too cold and still getting colder...) + // the temperature is this far below the floor: + #define BELOW (offset + (THERM_WINDOW_SIZE<<1)) + else if ((BELOW < 0) && (diff < 0)) { + // accumulated error isn't big enough yet to send a warning + if (warning_threshold < 0) { + warning_threshold -= BELOW; + } else { // error is big enough; send a warning + warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW; + + // how far below the floor? + // int16_t howmuch = ((-BELOW) >> 1) * THERM_RESPONSE_MAGNITUDE / 128; + int16_t howmuch = (-BELOW) >> 1; + // send a notification (unless voltage is low) + // (LVP and underheat warnings fight each other) + if (voltage > (VOLTAGE_LOW + 1)) + emit(EV_temperature_low, howmuch); + } + } + #undef BELOW + + // Goldilocks? + // (temperature is within target window, or at least heading toward it) + else { + // send a notification (unless voltage is low) + // (LVP and temp-okay events fight each other) + if (voltage > VOLTAGE_LOW) + emit(EV_temperature_okay, 0); + } } +#endif + #ifdef USE_BATTCHECK #ifdef BATTCHECK_4bars diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h index 274fb4d..241dee4 100644 --- a/spaghetti-monster/fsm-adc.h +++ b/spaghetti-monster/fsm-adc.h @@ -38,9 +38,24 @@ #define VOLTAGE_FUDGE_FACTOR 5 #endif #endif + +volatile uint8_t irq_adc = 0; // ADC interrupt happened? +uint8_t adc_sample_count = 0; // skip the first sample; it's junk +uint8_t adc_channel = 0; // 0=voltage, 1=temperature +uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature) +uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature) +// ADC code is split into two parts: +// - ISR: runs immediately at each interrupt, does the bare minimum because time is critical here +// - deferred: the bulk of the logic runs later when time isn't so critical +uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code +void adc_deferred(); // do the actual ADC-related calculations + +static inline void ADC_voltage_handler(); volatile uint8_t voltage = 0; -volatile uint8_t adcint_enable; // kludge, because adc auto-retrigger won't turn off +#ifdef USE_LVP void low_voltage(); +#endif + #ifdef USE_BATTCHECK void battcheck(); #ifdef BATTCHECK_VpT @@ -50,14 +65,10 @@ void battcheck(); #define USE_BLINK_DIGIT #endif #endif -#endif +#endif // ifdef USE_LVP #ifdef USE_THERMAL_REGULATION -// default 5 seconds between thermal regulation events -#ifndef THERMAL_WARNING_SECONDS -#define THERMAL_WARNING_SECONDS 5 -#endif // try to keep temperature below 45 C #ifndef DEFAULT_THERM_CEIL #define DEFAULT_THERM_CEIL 45 @@ -70,16 +81,13 @@ void battcheck(); #ifndef THERM_CAL_OFFSET #define THERM_CAL_OFFSET 0 #endif -// temperature now, in C (ish) * 2 (14.1 fixed-point) +// temperature now, in C (ish) volatile int16_t temperature; -// temperature in a few seconds, in C (ish) * 2 (14.1 fixed-point) -volatile int16_t projected_temperature; // Fight the future! uint8_t therm_ceil = DEFAULT_THERM_CEIL; int8_t therm_cal_offset = 0; -//void low_temperature(); -//void high_temperature(); volatile uint8_t reset_thermal_history = 1; -#endif +static inline void ADC_temperature_handler(); +#endif // ifdef USE_THERMAL_REGULATION inline void ADC_on(); diff --git a/spaghetti-monster/fsm-eeprom.c b/spaghetti-monster/fsm-eeprom.c index 277e2b2..0de7e05 100644 --- a/spaghetti-monster/fsm-eeprom.c +++ b/spaghetti-monster/fsm-eeprom.c @@ -30,6 +30,10 @@ uint8_t eeprom[EEPROM_BYTES]; #endif uint8_t load_eeprom() { + #ifdef LED_ENABLE_PIN + delay_4ms(2); // wait for power to stabilize + #endif + cli(); // check if eeprom has been initialized; abort if it hasn't uint8_t marker = eeprom_read_byte((uint8_t *)EEP_START); @@ -44,6 +48,10 @@ uint8_t load_eeprom() { } void save_eeprom() { + #ifdef LED_ENABLE_PIN + delay_4ms(2); // wait for power to stabilize + #endif + cli(); // save the actual data @@ -59,17 +67,21 @@ void save_eeprom() { #ifdef USE_EEPROM_WL uint8_t eeprom_wl[EEPROM_WL_BYTES]; -EEP_OFFSET_T eep_wl_prev_offset; +uint8_t * eep_wl_prev_offset; uint8_t load_eeprom_wl() { + #ifdef LED_ENABLE_PIN + delay_4ms(2); // wait for power to stabilize + #endif + cli(); // check if eeprom has been initialized; abort if it hasn't uint8_t found = 0; - EEP_OFFSET_T offset; + uint8_t * offset; for(offset = 0; - offset < EEP_WL_SIZE - EEPROM_WL_BYTES - 1; + offset < (uint8_t *)(EEP_WL_SIZE - EEPROM_WL_BYTES - 1); offset += (EEPROM_WL_BYTES + 1)) { - if (eeprom_read_byte((uint8_t *)offset) == EEP_MARKER) { + if (eeprom_read_byte(offset) == EEP_MARKER) { found = 1; eep_wl_prev_offset = offset; break; @@ -79,7 +91,7 @@ uint8_t load_eeprom_wl() { if (found) { // load the actual data for(uint8_t i=0; i<EEPROM_WL_BYTES; i++) { - eeprom_wl[i] = eeprom_read_byte((uint8_t *)(offset+1+i)); + eeprom_wl[i] = eeprom_read_byte(offset+1+i); } } sei(); @@ -87,24 +99,28 @@ uint8_t load_eeprom_wl() { } void save_eeprom_wl() { + #ifdef LED_ENABLE_PIN + delay_4ms(2); // wait for power to stabilize + #endif + cli(); // erase old state - EEP_OFFSET_T offset = eep_wl_prev_offset; + uint8_t * offset = eep_wl_prev_offset; for (uint8_t i = 0; i < EEPROM_WL_BYTES+1; i ++) { - eeprom_update_byte((uint8_t *)offset+i, 0xFF); + eeprom_update_byte(offset+i, 0xFF); } // save new state offset += EEPROM_WL_BYTES+1; - if (offset > EEP_WL_SIZE-EEPROM_WL_BYTES-1) offset = 0; + if (offset > (uint8_t *)(EEP_WL_SIZE-EEPROM_WL_BYTES-1)) offset = 0; eep_wl_prev_offset = offset; // marker byte // FIXME: write the marker last, to signal completed transaction - eeprom_update_byte((uint8_t *)offset, EEP_MARKER); + eeprom_update_byte(offset, EEP_MARKER); offset ++; // user data for(uint8_t i=0; i<EEPROM_WL_BYTES; i++, offset++) { - eeprom_update_byte((uint8_t *)(offset), eeprom_wl[i]); + eeprom_update_byte(offset, eeprom_wl[i]); } sei(); } diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c index 362a5cc..ad869a6 100644 --- a/spaghetti-monster/fsm-events.c +++ b/spaghetti-monster/fsm-events.c @@ -20,6 +20,8 @@ #ifndef FSM_EVENTS_C #define FSM_EVENTS_C +#include <util/delay_basic.h> + void empty_event_sequence() { current_event = EV_none; @@ -42,7 +44,7 @@ uint8_t push_event(uint8_t ev_type) { // set press flag current_event |= B_PRESS; // increase click counter - if ((current_event & B_COUNT) < (B_COUNT-1)) { + if ((current_event & B_COUNT) < (B_COUNT)) { current_event ++; } return 1; // event pushed, even if max clicks already reached @@ -108,7 +110,7 @@ uint8_t nice_delay_ms(uint16_t ms) { /* // delay_zero() implementation if (ms == 0) { CLKPR = 1<<CLKPCE; CLKPR = 0; // full speed - _delay_loop_2(BOGOMIPS*98/100/3); + _delay_loop_2(BOGOMIPS*95/100/3); return 1; } */ @@ -118,15 +120,15 @@ uint8_t nice_delay_ms(uint16_t ms) { uint8_t level = actual_level; // volatile, avoid repeat access if (level < QUARTERSPEED_LEVEL) { clock_prescale_set(clock_div_4); - _delay_loop_2(BOGOMIPS*98/100/4); + _delay_loop_2(BOGOMIPS*90/100/4); } //else if (level < HALFSPEED_LEVEL) { // clock_prescale_set(clock_div_2); - // _delay_loop_2(BOGOMIPS*98/100/2); + // _delay_loop_2(BOGOMIPS*95/100/2); //} else { clock_prescale_set(clock_div_1); - _delay_loop_2(BOGOMIPS*98/100); + _delay_loop_2(BOGOMIPS*90/100); } // restore regular clock speed clock_prescale_set(clock_div_1); @@ -134,15 +136,18 @@ uint8_t nice_delay_ms(uint16_t ms) { // underclock MCU to save power clock_prescale_set(clock_div_4); // wait - _delay_loop_2(BOGOMIPS*98/100/4); + _delay_loop_2(BOGOMIPS*90/100/4); // restore regular clock speed clock_prescale_set(clock_div_1); #endif // ifdef USE_RAMPING #else // wait - _delay_loop_2(BOGOMIPS*98/100); + _delay_loop_2(BOGOMIPS*90/100); #endif // ifdef USE_DYNAMIC_UNDERCLOCKING + // run pending system processes while we wait + handle_deferred_interrupts(); + if ((nice_delay_interrupt) || (old_state != current_state)) { return 0; // state changed; abort } diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h index 39ad3aa..a132d5a 100644 --- a/spaghetti-monster/fsm-events.h +++ b/spaghetti-monster/fsm-events.h @@ -85,6 +85,7 @@ static volatile uint16_t ticks_since_last_event = 0; #ifdef USE_THERMAL_REGULATION #define EV_temperature_high (B_SYSTEM|0b00000101) #define EV_temperature_low (B_SYSTEM|0b00000110) +#define EV_temperature_okay (B_SYSTEM|0b00000111) #endif // Button press events @@ -217,8 +218,8 @@ void emit(Event event, uint16_t arg); void emit_current_event(uint16_t arg); uint8_t nice_delay_ms(uint16_t ms); -//uint8_t nice_delay_4ms(uint8_t ms); //uint8_t nice_delay_s(); inline void interrupt_nice_delays(); +void delay_4ms(uint8_t ms); #endif diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c index e537a9e..790cc68 100644 --- a/spaghetti-monster/fsm-main.c +++ b/spaghetti-monster/fsm-main.c @@ -37,7 +37,7 @@ ISR(TIMER1_COMPA_vect) { #endif #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) -inline void hw_setup() { +static inline void hw_setup() { // configure PWM channels #if PWM_CHANNELS >= 1 DDRB |= (1 << PWM1_PIN); @@ -69,7 +69,7 @@ inline void hw_setup() { PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin } #elif (ATTINY == 1634) -inline void hw_setup() { +static inline void hw_setup() { // this gets tricky with so many pins... // ... so punt it to the hwdef file hwdef_setup(); @@ -79,22 +79,24 @@ inline void hw_setup() { #endif -#ifdef USE_REBOOT -void prevent_reboot_loop() { +//#ifdef USE_REBOOT +static inline void prevent_reboot_loop() { // prevent WDT from rebooting MCU again MCUSR &= ~(1<<WDRF); // reset status flag wdt_disable(); } -#endif +//#endif int main() { // Don't allow interrupts while booting cli(); - #ifdef USE_REBOOT + //#ifdef USE_REBOOT + // prevents cycling after a crash, + // whether intentional (like factory reset) or not (bugs) prevent_reboot_loop(); - #endif + //#endif hw_setup(); @@ -123,7 +125,6 @@ int main() { #else delay_4ms(1); #endif - empty_event_sequence(); // fallback for handling a few things #ifndef DONT_USE_DEFAULT_STATE @@ -160,6 +161,9 @@ int main() { standby_mode(); } + // catch up on interrupts + handle_deferred_interrupts(); + // give the recipe some time slices loop(); @@ -168,4 +172,22 @@ int main() { } } + +void handle_deferred_interrupts() { + /* + if (irq_pcint) { // button pressed or released + // nothing to do here + // (PCINT only matters during standby) + } + */ + if (irq_adc) { // ADC done measuring + adc_deferred(); + // irq_adc = 0; // takes care of itself + } + if (irq_wdt) { // the clock ticked + WDT_inner(); + // irq_wdt = 0; // takes care of itself + } +} + #endif diff --git a/spaghetti-monster/fsm-main.h b/spaghetti-monster/fsm-main.h index cc469d7..55ae2ff 100644 --- a/spaghetti-monster/fsm-main.h +++ b/spaghetti-monster/fsm-main.h @@ -21,5 +21,7 @@ #define FSM_MAIN_H int main(); +// needs to run frequently to execute the logic for WDT and ADC and stuff +void handle_deferred_interrupts(); #endif diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c index 8e88cbd..82be745 100644 --- a/spaghetti-monster/fsm-misc.c +++ b/spaghetti-monster/fsm-misc.c @@ -41,23 +41,46 @@ void auto_clock_speed() { #endif #if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT) +#define BLINK_SPEED 1000 uint8_t blink_digit(uint8_t num) { //StatePtr old_state = current_state; // "zero" digit gets a single short blink - uint8_t ontime = 200; + uint8_t ontime = BLINK_SPEED * 2 / 12; if (!num) { ontime = 8; num ++; } for (; num>0; num--) { set_level(BLINK_BRIGHTNESS); nice_delay_ms(ontime); set_level(0); - nice_delay_ms(400); + nice_delay_ms(BLINK_SPEED * 3 / 12); } - return nice_delay_ms(600); + return nice_delay_ms(BLINK_SPEED * 8 / 12); } #endif +#ifdef USE_BLINK_BIG_NUM +uint8_t blink_big_num(uint16_t num) { + uint16_t digits[] = { 10000, 1000, 100, 10, 1 }; + uint8_t started = 0; + for (uint8_t digit=0; digit<sizeof(digits)/sizeof(uint16_t); digit++) { + uint16_t scale = digits[digit]; + if (num >= scale) { + started = 1; + } + if (started) { + uint8_t digit = 0; + while (num >= scale) { + num -= scale; + digit ++; + } + if (! blink_digit(digit)) return 0; + } + } + + return nice_delay_ms(1000); +} +#endif #ifdef USE_BLINK_NUM uint8_t blink_num(uint8_t num) { //StatePtr old_state = current_state; @@ -147,6 +170,29 @@ void indicator_led_auto() { */ #endif // USE_INDICATOR_LED +#ifdef USE_BUTTON_LED +// TODO: Refactor this and RGB LED function to merge code and save space +void button_led_set(uint8_t lvl) { + switch (lvl) { + case 0: // LED off + BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT &= 0xff ^ (1 << BUTTON_LED_PIN); + break; + case 1: // LED low + BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); + break; + default: // LED high + BUTTON_LED_DDR |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); + break; + } +} +#endif + #ifdef USE_AUX_RGB_LEDS void rgb_led_set(uint8_t value) { // value: 0b00BBGGRR @@ -185,18 +231,20 @@ uint8_t triangle_wave(uint8_t phase) { #ifdef USE_REBOOT void reboot() { - #if 1 // WDT method, safer but larger + // put the WDT in hard reset mode, then trigger it cli(); - WDTCR = 0xD8 | WDTO_15MS; + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + WDTCR = 0xD8 | WDTO_15MS; + #elif (ATTINY == 1634) + // allow protected configuration changes for next 4 clock cycles + CCP = 0xD8; // magic number + // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000) + // (DS section 8.5.2 and table 8-4) + WDTCSR = 0b10001000; + #endif sei(); wdt_reset(); while (1) {} - #else // raw assembly method, doesn't reset registers or anything - __asm__ __volatile__ ( - "cli" "\n\t" - "rjmp 0x00" "\n\t" - ); - #endif } #endif diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h index a39d31a..66d31ba 100644 --- a/spaghetti-monster/fsm-misc.h +++ b/spaghetti-monster/fsm-misc.h @@ -43,10 +43,16 @@ uint8_t blink(uint8_t num, uint8_t speed); */ #ifdef USE_INDICATOR_LED +// FIXME: Remove this, replace with button_led() // lvl: 0=off, 1=low, 2=high void indicator_led(uint8_t lvl); #endif +#ifdef USE_BUTTON_LED +// lvl: 0=off, 1=low, 2=high +void button_led_set(uint8_t lvl); +#endif + #ifdef USE_AUX_RGB_LEDS // value: 0b00BBGGRR // each pair of bits: 0=off, 1=low, 2=high diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c index 4928980..d362633 100644 --- a/spaghetti-monster/fsm-pcint.c +++ b/spaghetti-monster/fsm-pcint.c @@ -24,19 +24,9 @@ #include <util/delay_basic.h> uint8_t button_is_pressed() { - // remember the past 32 measurements - static uint32_t readings = 0; - // take at least one new measurement, - // and wait for measurements to settle to all zeroes or all ones - do { - // shift past readings and add current value - readings = (readings << 1) | ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0); - // wait a moment - _delay_loop_2(BOGOMIPS/16); // up to 2ms to stabilize - } - while ((readings != 0) && (readings != 0xFFFFFFFF)); - button_last_state = readings; - return readings; + uint8_t value = ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0); + button_last_state = value; + return value; } inline void PCINT_on() { @@ -75,7 +65,14 @@ inline void PCINT_off() { //void button_change_interrupt() { #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) -EMPTY_INTERRUPT(PCINT0_vect); +//EMPTY_INTERRUPT(PCINT0_vect); +#ifdef PCINT_vect +ISR(PCINT_vect) { +#else +ISR(PCINT0_vect) { +#endif + irq_pcint = 1; +} #else #error Unrecognized MCU type #endif @@ -104,8 +101,7 @@ void PCINT_inner(uint8_t pressed) { pushed = push_event(B_RELEASE); } - // check if sequence matches any defined sequences - // if so, send event to current state callback + // send event to the current state callback if (pushed) { button_last_state = pressed; emit_current_event(0); diff --git a/spaghetti-monster/fsm-pcint.h b/spaghetti-monster/fsm-pcint.h index ec3ae4b..a7f3733 100644 --- a/spaghetti-monster/fsm-pcint.h +++ b/spaghetti-monster/fsm-pcint.h @@ -20,6 +20,7 @@ #ifndef FSM_PCINT_H #define FSM_PCINT_H +volatile uint8_t irq_pcint = 0; // pin change interrupt happened? //static volatile uint8_t button_was_pressed; #define BP_SAMPLES 32 volatile uint8_t button_last_state; diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c index efa07e4..bae601e 100644 --- a/spaghetti-monster/fsm-ramping.c +++ b/spaghetti-monster/fsm-ramping.c @@ -46,6 +46,9 @@ void set_level(uint8_t level) { #endif #ifdef USE_AUX_RGB_LEDS rgb_led_set(0); + #ifdef USE_BUTTON_LED + button_led_set((level > 0) + (level > MAX_1x7135)); + #endif #endif } #endif @@ -65,13 +68,31 @@ void set_level(uint8_t level) { #if PWM_CHANNELS >= 4 PWM4_LVL = 0; #endif + // disable the power channel, if relevant + #ifdef LED_ENABLE_PIN + LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); + #endif + #ifdef LED_ENABLE2_PIN + LED_ENABLE2_PORT &= ~(1 << LED_ENABLE2_PIN); + #endif } else { level --; + // enable the power channel, if relevant + #ifdef LED_ENABLE_PIN + LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); + #endif + #ifdef LED_ENABLE2_PIN + LED_ENABLE2_PORT |= (1 << LED_ENABLE2_PIN); + #endif + #ifdef USE_TINT_RAMPING + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif // calculate actual PWM levels based on a single-channel ramp // and a global tint value - uint8_t brightness = pgm_read_byte(pwm1_levels + level); + uint8_t brightness = PWM_GET(pwm1_levels, level); uint8_t warm_PWM, cool_PWM; // auto-tint modes @@ -93,7 +114,7 @@ void set_level(uint8_t level) { // correction is only necessary when PWM is fast if (level > HALFSPEED_LEVEL) { base_PWM = brightness - + ((((uint16_t)brightness) * 26 / 64) * triangle_wave(mytint) / 255); + + ((((uint16_t)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255); } cool_PWM = (((uint16_t)mytint * (uint16_t)base_PWM) + 127) / 255; @@ -104,16 +125,16 @@ void set_level(uint8_t level) { #else #if PWM_CHANNELS >= 1 - PWM1_LVL = pgm_read_byte(pwm1_levels + level); + PWM1_LVL = PWM_GET(pwm1_levels, level); #endif #if PWM_CHANNELS >= 2 - PWM2_LVL = pgm_read_byte(pwm2_levels + level); + PWM2_LVL = PWM_GET(pwm2_levels, level); #endif #if PWM_CHANNELS >= 3 - PWM3_LVL = pgm_read_byte(pwm3_levels + level); + PWM3_LVL = PWM_GET(pwm3_levels, level); #endif #if PWM_CHANNELS >= 4 - PWM4_LVL = pgm_read_byte(pwm4_levels + level); + PWM4_LVL = PWM_GET(pwm4_levels, level); #endif #endif // ifdef USE_TINT_RAMPING @@ -137,43 +158,43 @@ void gradual_tick() { gt --; // convert 1-based number to 0-based - uint8_t target; + PWM_DATATYPE target; #if PWM_CHANNELS >= 1 - target = pgm_read_byte(pwm1_levels + gt); + target = PWM_GET(pwm1_levels, gt); if ((gt < actual_level) // special case for FET-only turbo && (PWM1_LVL == 0) // (bypass adjustment period for first step) - && (target == 255)) PWM1_LVL = 255; + && (target == PWM_TOP)) PWM1_LVL = PWM_TOP; else if (PWM1_LVL < target) PWM1_LVL ++; else if (PWM1_LVL > target) PWM1_LVL --; #endif #if PWM_CHANNELS >= 2 - target = pgm_read_byte(pwm2_levels + gt); + target = PWM_GET(pwm2_levels, gt); if (PWM2_LVL < target) PWM2_LVL ++; else if (PWM2_LVL > target) PWM2_LVL --; #endif #if PWM_CHANNELS >= 3 - target = pgm_read_byte(pwm3_levels + gt); + target = PWM_GET(pwm3_levels, gt); if (PWM3_LVL < target) PWM3_LVL ++; else if (PWM3_LVL > target) PWM3_LVL --; #endif #if PWM_CHANNELS >= 4 - target = pgm_read_byte(pwm4_levels + gt); + target = PWM_GET(pwm4_levels, gt); if (PWM4_LVL < target) PWM4_LVL ++; else if (PWM4_LVL > target) PWM4_LVL --; #endif // did we go far enough to hit the next defined ramp level? // if so, update the main ramp level tracking var - if ((PWM1_LVL == pgm_read_byte(pwm1_levels + gt)) + if ((PWM1_LVL == PWM_GET(pwm1_levels, gt)) #if PWM_CHANNELS >= 2 - && (PWM2_LVL == pgm_read_byte(pwm2_levels + gt)) + && (PWM2_LVL == PWM_GET(pwm2_levels, gt)) #endif #if PWM_CHANNELS >= 3 - && (PWM3_LVL == pgm_read_byte(pwm3_levels + gt)) + && (PWM3_LVL == PWM_GET(pwm3_levels, gt)) #endif #if PWM_CHANNELS >= 4 - && (PWM4_LVL == pgm_read_byte(pwm4_levels + gt)) + && (PWM4_LVL == PWM_GET(pwm4_levels, gt)) #endif ) { diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h index dcc3b74..c650e21 100644 --- a/spaghetti-monster/fsm-ramping.h +++ b/spaghetti-monster/fsm-ramping.h @@ -38,18 +38,36 @@ inline void set_level_gradually(uint8_t lvl); void gradual_tick(); #endif +// auto-detect the data type for PWM tables +#ifndef PWM_BITS +#define PWM_BITS 8 +#define PWM_TOP 255 +#endif +#if PWM_BITS <= 8 +#define PWM_DATATYPE uint8_t +#define PWM_TOP 255 +#define PWM_GET(x,y) pgm_read_byte(x+y) +#else +#define PWM_DATATYPE uint16_t +#define PWM_TOP 1023 // 10 bits by default +// pointer plus 2*y bytes +//#define PWM_GET(x,y) pgm_read_word(x+(2*y)) +// nope, the compiler was already doing the math correctly +#define PWM_GET(x,y) pgm_read_word(x+y) +#endif + // use UI-defined ramp tables if they exist #ifdef PWM1_LEVELS -PROGMEM const uint8_t pwm1_levels[] = { PWM1_LEVELS }; +PROGMEM const PWM_DATATYPE pwm1_levels[] = { PWM1_LEVELS }; #endif #ifdef PWM2_LEVELS -PROGMEM const uint8_t pwm2_levels[] = { PWM2_LEVELS }; +PROGMEM const PWM_DATATYPE pwm2_levels[] = { PWM2_LEVELS }; #endif #ifdef PWM3_LEVELS -PROGMEM const uint8_t pwm3_levels[] = { PWM3_LEVELS }; +PROGMEM const PWM_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; #endif #ifdef PWM4_LEVELS -PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS }; +PROGMEM const PWM_DATATYPE pwm4_levels[] = { PWM4_LEVELS }; #endif // default / example ramps @@ -57,31 +75,31 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS }; #if PWM_CHANNELS == 1 #if RAMP_LENGTH == 50 // ../../bin/level_calc.py 1 50 7135 3 0.25 980 - PROGMEM const uint8_t pwm1_levels[] = { 3,3,3,3,4,4,4,5,5,6,7,8,9,11,12,14,16,18,20,23,25,28,32,35,39,43,47,52,57,62,68,74,80,87,94,102,110,118,127,136,146,156,167,178,189,201,214,227,241,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,4,4,4,5,5,6,7,8,9,11,12,14,16,18,20,23,25,28,32,35,39,43,47,52,57,62,68,74,80,87,94,102,110,118,127,136,146,156,167,178,189,201,214,227,241,255 }; #elif RAMP_LENGTH == 75 // ../../bin/level_calc.py 1 75 7135 3 0.25 980 - PROGMEM const uint8_t pwm1_levels[] = { 3,3,3,3,3,3,4,4,4,4,5,5,5,6,6,7,8,8,9,10,11,12,13,14,15,17,18,20,21,23,25,27,29,31,33,36,38,41,44,47,50,53,56,59,63,67,71,75,79,83,88,93,98,103,108,113,119,125,131,137,143,150,157,164,171,178,186,194,202,210,219,227,236,246,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,4,4,4,4,5,5,5,6,6,7,8,8,9,10,11,12,13,14,15,17,18,20,21,23,25,27,29,31,33,36,38,41,44,47,50,53,56,59,63,67,71,75,79,83,88,93,98,103,108,113,119,125,131,137,143,150,157,164,171,178,186,194,202,210,219,227,236,246,255 }; #elif RAMP_LENGTH == 150 // ../../bin/level_calc.py 1 150 7135 3 0.25 980 - PROGMEM const uint8_t pwm1_levels[] = { 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,12,12,13,13,14,15,15,16,17,17,18,19,19,20,21,22,23,24,24,25,26,27,28,29,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,57,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,89,91,93,96,98,101,103,106,109,111,114,117,120,123,125,128,131,134,138,141,144,147,151,154,157,161,164,168,171,175,179,183,186,190,194,198,202,206,210,215,219,223,228,232,236,241,246,250,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,12,12,13,13,14,15,15,16,17,17,18,19,19,20,21,22,23,24,24,25,26,27,28,29,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,57,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,89,91,93,96,98,101,103,106,109,111,114,117,120,123,125,128,131,134,138,141,144,147,151,154,157,161,164,168,171,175,179,183,186,190,194,198,202,206,210,215,219,223,228,232,236,241,246,250,255 }; #endif #elif PWM_CHANNELS == 2 #if RAMP_LENGTH == 50 // ../../bin/level_calc.py 2 50 7135 4 0.33 150 FET 1 10 1500 - PROGMEM const uint8_t pwm1_levels[] = { 4,5,6,8,10,13,17,22,28,35,44,54,65,78,93,109,128,149,171,197,224,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 }; - PROGMEM const uint8_t pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,20,26,31,37,44,51,58,65,73,82,91,100,110,121,132,143,155,168,181,194,209,224,239,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,10,13,17,22,28,35,44,54,65,78,93,109,128,149,171,197,224,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 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,20,26,31,37,44,51,58,65,73,82,91,100,110,121,132,143,155,168,181,194,209,224,239,255 }; #define MAX_1x7135 22 #elif RAMP_LENGTH == 75 // ../../bin/level_calc.py 2 75 7135 4 0.33 150 FET 1 10 1500 - PROGMEM const uint8_t pwm1_levels[] = { 4,4,5,6,7,8,10,12,14,17,20,24,28,32,37,43,49,56,64,72,82,91,102,114,126,139,153,168,184,202,220,239,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 }; - PROGMEM const uint8_t pwm2_levels[] = { 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,5,7,10,13,16,19,23,26,30,34,38,42,47,51,56,61,66,72,77,83,89,95,101,108,115,122,129,136,144,152,160,168,177,186,195,204,214,224,234,244,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,8,10,12,14,17,20,24,28,32,37,43,49,56,64,72,82,91,102,114,126,139,153,168,184,202,220,239,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 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 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,5,7,10,13,16,19,23,26,30,34,38,42,47,51,56,61,66,72,77,83,89,95,101,108,115,122,129,136,144,152,160,168,177,186,195,204,214,224,234,244,255 }; #define MAX_1x7135 33 #elif RAMP_LENGTH == 150 // ../../bin/level_calc.py 1 65 7135 1 0.8 150 // ... mixed with this: // ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500 - PROGMEM const uint8_t pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 }; - PROGMEM const uint8_t pwm2_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 }; #define MAX_1x7135 65 #define HALFSPEED_LEVEL 14 #define QUARTERSPEED_LEVEL 5 @@ -89,25 +107,25 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS }; #elif PWM_CHANNELS == 3 #if RAMP_LENGTH == 50 // ../../bin/level_calc.py 3 50 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000 - PROGMEM const uint8_t pwm1_levels[] = { 4,5,6,8,11,15,20,26,34,43,54,67,82,99,118,140,165,192,221,254,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 }; - PROGMEM const uint8_t pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,25,33,42,52,62,73,85,97,111,125,140,157,174,192,210,230,251,255,255,255,255,255,255,255,255,255,255,0 }; - PROGMEM const uint8_t pwm3_levels[] = { 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,14,34,54,76,98,122,146,172,198,226,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,11,15,20,26,34,43,54,67,82,99,118,140,165,192,221,254,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 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,25,33,42,52,62,73,85,97,111,125,140,157,174,192,210,230,251,255,255,255,255,255,255,255,255,255,255,0 }; + PROGMEM const PWM_DATATYPE pwm3_levels[] = { 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,14,34,54,76,98,122,146,172,198,226,255 }; #define MAX_1x7135 20 #define MAX_Nx7135 39 #elif RAMP_LENGTH == 75 // ../../bin/level_calc.py 3 75 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000 - PROGMEM const uint8_t pwm1_levels[] = { 4,4,5,6,7,9,11,14,16,20,24,28,34,40,46,54,62,71,81,92,104,117,130,146,162,179,198,218,239,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,255,255,0 }; - PROGMEM const uint8_t pwm2_levels[] = { 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,5,9,14,18,23,29,34,40,47,53,60,67,75,83,91,99,108,117,127,137,148,158,170,181,193,206,219,232,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 }; - PROGMEM const uint8_t pwm3_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,3,15,28,42,55,70,84,99,115,131,147,164,181,199,217,236,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,9,11,14,16,20,24,28,34,40,46,54,62,71,81,92,104,117,130,146,162,179,198,218,239,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,255,255,0 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 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,5,9,14,18,23,29,34,40,47,53,60,67,75,83,91,99,108,117,127,137,148,158,170,181,193,206,219,232,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 }; + PROGMEM const PWM_DATATYPE pwm3_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,3,15,28,42,55,70,84,99,115,131,147,164,181,199,217,236,255 }; #define MAX_1x7135 30 #define MAX_Nx7135 59 #elif RAMP_LENGTH == 150 // ../../bin/level_calc.py 1 65 7135 1 0.8 150 // ... mixed with this: // ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500 - PROGMEM const uint8_t pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 }; - PROGMEM const uint8_t pwm2_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 }; - PROGMEM const uint8_t pwm3_levels[] = { 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,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,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,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 }; + PROGMEM const PWM_DATATYPE pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,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,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 }; + PROGMEM const PWM_DATATYPE pwm2_levels[] = { 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 }; + PROGMEM const PWM_DATATYPE pwm3_levels[] = { 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,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,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,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 }; #define MAX_1x7135 65 #define MAX_Nx7135 130 #define HALFSPEED_LEVEL 14 @@ -119,7 +137,7 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS }; #endif // RAMP_SIZE / MAX_LVL -#define RAMP_SIZE sizeof(pwm1_levels) +#define RAMP_SIZE (sizeof(pwm1_levels)/sizeof(PWM_DATATYPE)) #define MAX_LEVEL RAMP_SIZE void set_level(uint8_t level); diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c index 44b047a..b002b91 100644 --- a/spaghetti-monster/fsm-standby.c +++ b/spaghetti-monster/fsm-standby.c @@ -42,16 +42,19 @@ void sleep_until_eswitch_pressed() // make sure switch isn't currently pressed while (button_is_pressed()) {} empty_event_sequence(); // cancel pending input on suspend - //PCINT_since_WDT = 0; // ensure PCINT won't ignore itself PCINT_on(); // wake on e-switch event #ifdef TICK_DURING_STANDBY + // detect which type of event caused a wake-up + irq_adc = 0; + irq_wdt = 0; + irq_pcint = 0; while (go_to_standby) { - f_wdt = 0; // detect if WDT was what caused a wake-up #else go_to_standby = 0; #endif + // configure sleep mode set_sleep_mode(SLEEP_MODE_PWR_DOWN); @@ -65,10 +68,19 @@ void sleep_until_eswitch_pressed() sleep_disable(); #ifdef TICK_DURING_STANDBY - // determine what woke us up... WDT or PCINT - if (! f_wdt) { // PCINT went off; wake up + // determine what woke us up... + if (irq_pcint) { // button pressed; wake up go_to_standby = 0; } + if (irq_adc) { // ADC done measuring + adc_deferred_enable = 1; + adc_deferred(); + //ADC_off(); // takes care of itself + //irq_adc = 0; // takes care of itself + } + if (irq_wdt) { // generate a sleep tick + WDT_inner(); + } } #endif @@ -78,7 +90,10 @@ void sleep_until_eswitch_pressed() #endif // go back to normal running mode - //PCINT_on(); // should be on already + // PCINT not needed any more, and can cause problems if on + // (occasional reboots on wakeup-by-button-press) + PCINT_off(); + // restore normal awake-mode interrupts ADC_on(); WDT_on(); } diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c index 6e61e87..94266c1 100644 --- a/spaghetti-monster/fsm-wdt.c +++ b/spaghetti-monster/fsm-wdt.c @@ -82,46 +82,52 @@ inline void WDT_off() // clock tick -- this runs every 16ms (62.5 fps) ISR(WDT_vect) { + irq_wdt = 1; // WDT event happened +} + +void WDT_inner() { + irq_wdt = 0; // WDT event handled; reset flag + static uint8_t adc_trigger = 0; - #ifdef TICK_DURING_STANDBY - f_wdt = 1; // WDT event happened + // cache this here to reduce ROM size, because it's volatile + uint16_t ticks_since_last = ticks_since_last_event; + // increment, but loop from max back to half + ticks_since_last = (ticks_since_last + 1) \ + | (ticks_since_last & 0x8000); + // copy back to the original + ticks_since_last_event = ticks_since_last; + + // detect and emit button change events (even during standby) + uint8_t was_pressed = button_last_state; + uint8_t pressed = button_is_pressed(); + if (was_pressed != pressed) { + go_to_standby = 0; + PCINT_inner(pressed); + } + // cache again, in case the value changed + ticks_since_last = ticks_since_last_event; - static uint16_t sleep_counter = 0; + #ifdef TICK_DURING_STANDBY // handle standby mode specially if (go_to_standby) { - // emit a halfsleep tick, and process it - emit(EV_sleep_tick, sleep_counter); - // wrap around from 65535 to 32768, not 0 - sleep_counter = (sleep_counter + 1) | (sleep_counter & 0x8000); + // emit a sleep tick, and process it + emit(EV_sleep_tick, ticks_since_last); process_emissions(); - #if defined(USE_SLEEP_LVP) - // stop here, usually... but proceed often enough for sleep LVP to work - if (0 != (sleep_counter & 0x7f)) return; - adc_trigger = 255; // make sure a measurement will happen - #else + #ifndef USE_SLEEP_LVP return; // no sleep LVP needed if nothing drains power while off + #else + // stop here, usually... but proceed often enough for sleep LVP to work + if (0 != (ticks_since_last & 0x3f)) return; + + adc_trigger = 0; // make sure a measurement will happen + ADC_on(); // enable ADC voltage measurement functions temporarily #endif } - else { sleep_counter = 0; } + else { // button handling should only happen while awake #endif - // detect and emit button change events - uint8_t was_pressed = button_last_state; - uint8_t pressed = button_is_pressed(); - if (was_pressed != pressed) PCINT_inner(pressed); - - // cache this here to reduce ROM size, because it's volatile - uint16_t ticks_since_last = ticks_since_last_event; - - // increment, but loop from max back to half - //if (ticks_since_last < 0xff) ticks_since_last ++; - ticks_since_last = (ticks_since_last + 1) \ - | (ticks_since_last & 0x8000); - // copy back to the original - ticks_since_last_event = ticks_since_last; - // if time since last event exceeds timeout, // append timeout to current event sequence, then // send event to current state callback @@ -141,9 +147,9 @@ ISR(WDT_vect) { emit_current_event(ticks_since_last); } // has button been down long enough to become a "hold"? + // (first frame of a "hold" event) else { if (ticks_since_last >= HOLD_TIMEOUT) { - //ticks_since_last_event = 0; current_event |= B_HOLD; emit_current_event(0); } @@ -156,30 +162,29 @@ ISR(WDT_vect) { // no timeout required when releasing a long-press // TODO? move this logic to PCINT() and simplify things here? if (current_event & B_HOLD) { - //emit_current_event(0); // should have been emitted by PCINT + //emit_current_event(0); // should have been emitted by PCINT_inner() empty_event_sequence(); } // end and clear event after release timeout else if (ticks_since_last >= RELEASE_TIMEOUT) { current_event |= B_TIMEOUT; - //ticks_since_last_event = 0; emit_current_event(0); empty_event_sequence(); } } + #ifdef TICK_DURING_STANDBY + } + #endif + #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // start a new ADC measurement every 4 ticks - adc_trigger ++; - if (0 == (adc_trigger & 3)) { - #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP) - // we shouldn't be here unless it woke up for a LVP check... - // so enable ADC voltage measurement functions temporarily - if (go_to_standby) ADC_on(); - #endif + // enable the deferred ADC handler once in a while + if (! adc_trigger) { ADC_start_measurement(); - adcint_enable = 1; + adc_deferred_enable = 1; } + // timing for the ADC handler is every 32 ticks (~2Hz) + adc_trigger = (adc_trigger + 1) & 31; #endif } diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h index 78fe791..d127551 100644 --- a/spaghetti-monster/fsm-wdt.h +++ b/spaghetti-monster/fsm-wdt.h @@ -25,9 +25,9 @@ void WDT_on(); inline void WDT_off(); -#ifdef TICK_DURING_STANDBY -volatile uint8_t f_wdt = 0; +volatile uint8_t irq_wdt = 0; // WDT interrupt happened? +#ifdef TICK_DURING_STANDBY #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) // measure battery charge while asleep #define USE_SLEEP_LVP diff --git a/spaghetti-monster/meteor/meteor.c b/spaghetti-monster/meteor/meteor.c index 5e925e2..7d854a1 100644 --- a/spaghetti-monster/meteor/meteor.c +++ b/spaghetti-monster/meteor/meteor.c @@ -22,7 +22,6 @@ #define USE_LVP #define USE_THERMAL_REGULATION #define DEFAULT_THERM_CEIL 45 -#define USE_DELAY_4MS #define USE_RAMPING #define RAMP_LENGTH 150 #define USE_BATTCHECK diff --git a/spaghetti-monster/momentary/momentary.c b/spaghetti-monster/momentary/momentary.c index 26acd44..c7a8cf1 100644 --- a/spaghetti-monster/momentary/momentary.c +++ b/spaghetti-monster/momentary/momentary.c @@ -22,7 +22,6 @@ #include "hwdef-Emisar_D4.h" #define USE_LVP #define USE_DEBUG_BLINK -#define USE_DELAY_4MS #include "spaghetti-monster.h" volatile uint8_t brightness; diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c index 18f488d..5eb7d8f 100644 --- a/spaghetti-monster/ramping-ui/ramping-ui.c +++ b/spaghetti-monster/ramping-ui/ramping-ui.c @@ -22,7 +22,6 @@ #define USE_THERMAL_REGULATION #define DEFAULT_THERM_CEIL 32 #define USE_DELAY_MS -#define USE_DELAY_4MS #define USE_DELAY_ZERO #define USE_RAMPING #define USE_BATTCHECK @@ -353,7 +352,7 @@ void loop() { battcheck(); } else if (current_state == tempcheck_state) { - blink_num(projected_temperature>>2); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c index d72e971..7f03e77 100644 --- a/spaghetti-monster/rampingios/rampingiosv3.c +++ b/spaghetti-monster/rampingios/rampingiosv3.c @@ -123,6 +123,7 @@ uint8_t ramp_config_state(Event event, uint16_t arg); uint8_t battcheck_state(Event event, uint16_t arg); #endif #ifdef USE_THERMAL_REGULATION +#define USE_BLINK_NUM uint8_t tempcheck_state(Event event, uint16_t arg); uint8_t thermal_config_state(Event event, uint16_t arg); #endif @@ -930,14 +931,15 @@ void thermal_config_save() { // calibrate room temperature val = config_state_values[0]; if (val) { - int8_t rawtemp = (temperature >> 1) - therm_cal_offset; + int8_t rawtemp = temperature - therm_cal_offset; therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data } val = config_state_values[1]; if (val) { // set maximum heat limit - therm_ceil = 30 + val; + therm_ceil = 30 + val - 1; } if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } @@ -966,9 +968,9 @@ uint8_t beacon_config_state(Event event, uint16_t arg) { inline void beacon_mode_iter() { // one iteration of main loop() set_level(memorized_level); - nice_delay_ms(500); + nice_delay_ms(100); set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 500); + nice_delay_ms(((beacon_seconds) * 1000) - 100); } #endif // #ifdef USE_BEACON_MODE @@ -1235,7 +1237,7 @@ void loop() { #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state? else if (state == tempcheck_state) { - blink_num(temperature>>1); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt index 9e051f1..434e1bc 100644 --- a/spaghetti-monster/spaghetti-monster.txt +++ b/spaghetti-monster/spaghetti-monster.txt @@ -124,14 +124,13 @@ Event types: between events. - EV_temperature_high: Sent whenever the MCU's projected temperature - is higher than therm_ceil. Minimum of THERMAL_WARNING_SECONDS - between events. The 'arg' indicates how far the temperature - exceeds the limit. + is higher than therm_ceil. Minimum of one second between events. + The 'arg' indicates how far the temperature exceeds the limit. - EV_temperature_low: Sent whenever the MCU's projected temperature is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of - THERMAL_WARNING_SECONDS between events. The 'arg' indicates how - far the temperature exceeds the limit. + one second between events. The 'arg' indicates how far the + temperature exceeds the limit. Button presses: @@ -297,9 +296,6 @@ Useful #defines: - DEFAULT_THERM_CEIL: Set the temperature limit to use by default when the user hasn't configured anything. - - THERMAL_WARNING_SECONDS: How long to wait between temperature - events. - - USE_RAMPING: Enable smooth ramping helpers. - RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes diff --git a/spaghetti-monster/werner/Makefile b/spaghetti-monster/werner/Makefile new file mode 100644 index 0000000..21d85f7 --- /dev/null +++ b/spaghetti-monster/werner/Makefile @@ -0,0 +1,7 @@ +all: + ./build-all.sh + +clean: + rm -f cfg-*.h *.hex *~ *.elf *.o + +.phony: clean diff --git a/spaghetti-monster/werner/build-all.sh b/spaghetti-monster/werner/build-all.sh index 87d4f0d..b114101 100755 --- a/spaghetti-monster/werner/build-all.sh +++ b/spaghetti-monster/werner/build-all.sh @@ -7,7 +7,9 @@ UI=werner for TARGET in cfg-*.h ; do NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') echo "===== $NAME =====" - echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" - ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" + ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }') + if [ -z "$ATTINY" ]; then ATTINY=85 ; fi + echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" + ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" mv -f "$UI".hex "$UI".$NAME.hex done diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c index 7c47cd7..1c3d4ad 100644 --- a/spaghetti-monster/werner/werner.c +++ b/spaghetti-monster/werner/werner.c @@ -33,6 +33,10 @@ //#define BATTCHECK_8bars // FIXME: breaks build //#define BATTCHECK_4bars // FIXME: breaks build +// cut clock speed at very low modes for better efficiency +// (defined here so config files can override it) +#define USE_DYNAMIC_UNDERCLOCKING + /***** specific settings for known driver types *****/ #ifdef CONFIGFILE #include "tk.h" @@ -63,7 +67,6 @@ #define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file #define USE_BATTCHECK #define USE_IDLE_MODE // reduce power use while awake and no tasks are pending -#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency // auto-detect how many eeprom bytes #define USE_EEPROM @@ -369,12 +372,14 @@ uint8_t battcheck_state(Event event, uint16_t arg) { set_state(off_state, 0); return MISCHIEF_MANAGED; } + #ifdef USE_THERMAL_REGULATION // 2 clicks: tempcheck mode else if (event == EV_2clicks) { blink_confirm(2); set_state(tempcheck_state, 0); return MISCHIEF_MANAGED; } + #endif return EVENT_NOT_HANDLED; } #endif @@ -467,14 +472,15 @@ void thermal_config_save() { // calibrate room temperature val = config_state_values[0]; if (val) { - int8_t rawtemp = (temperature >> 1) - therm_cal_offset; + int8_t rawtemp = temperature - therm_cal_offset; therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data } val = config_state_values[1]; if (val) { // set maximum heat limit - therm_ceil = 30 + val; + therm_ceil = 30 + val - 1; } if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; } @@ -589,7 +595,7 @@ uint8_t nearest_level(int16_t target) { for(uint8_t i=0; i<ramp_discrete_steps; i++) { this_level = ramp_discrete_floor + (i * (uint16_t)ramp_range / (ramp_discrete_steps-1)); - int8_t diff = target - this_level; + int16_t diff = target - this_level; if (diff < 0) diff = -diff; if (diff <= (ramp_discrete_step_size>>1)) return this_level; @@ -684,9 +690,6 @@ void loop() { StatePtr state = current_state; - #ifdef USE_DYNAMIC_UNDERCLOCKING - auto_clock_speed(); - #endif if (0) {} #ifdef USE_BATTCHECK @@ -697,7 +700,7 @@ void loop() { #ifdef USE_THERMAL_REGULATION // TODO: blink out therm_ceil during thermal_config_state else if (state == tempcheck_state) { - blink_num(temperature>>1); + blink_num(temperature); nice_delay_ms(1000); } #endif diff --git a/tk-attiny.h b/tk-attiny.h index 597af4d..49f1195 100644 --- a/tk-attiny.h +++ b/tk-attiny.h @@ -34,6 +34,7 @@ #define ADMUX_VCC 0b00001100 #define DELAY_ZERO_TIME 252 #define SWITCH_PORT PINB // PINA or PINB or PINC + #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR #elif (ATTINY == 25) // TODO: Use 6.4 MHz instead of 8 MHz? #define F_CPU 8000000UL @@ -44,6 +45,7 @@ #define ADMUX_THERM 0b10001111 #define DELAY_ZERO_TIME 1020 #define SWITCH_PORT PINB // PINA or PINB or PINC + #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR #elif (ATTINY == 85) // TODO: Use 6.4 MHz instead of 8 MHz? #define F_CPU 8000000UL @@ -56,16 +58,19 @@ #define ADMUX_THERM 0b10001111 #define DELAY_ZERO_TIME 1020 #define SWITCH_PORT PINB // PINA or PINB or PINC + #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR #elif (ATTINY == 1634) #define F_CPU 8000000UL #define V_REF REFS1 #define BOGOMIPS (F_CPU/4000) - // (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL) + // DS table 19-3, 19-4, 1.1V ref / VCC #define ADMUX_VCC 0b00001101 - // (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL) + // (1 << V_REF) | (THERM_CHANNEL) + // DS table 19-3, 19-4, internal sensor / 1.1V ref #define ADMUX_THERM 0b10001110 #define DELAY_ZERO_TIME 1020 //#define SWITCH_PORT PINA // set this in hwdef + //#define VOLTAGE_ADC_DIDR DIDR0 // set this in hwdef #else #error Hey, you need to define ATTINY. #endif @@ -48,12 +48,14 @@ void _delay_zero() { } #endif #ifdef USE_DELAY_4MS +#ifndef delay_4ms #define delay_4ms _delay_4ms void _delay_4ms(uint8_t n) // because it saves a bit of ROM space to do it this way { while(n-- > 0) _delay_loop_2(BOGOMIPS*4); } #endif +#endif #ifdef USE_DELAY_S #define delay_s _delay_s void _delay_s() // because it saves a bit of ROM space to do it this way |
