diff options
| author | Selene ToyKeeper | 2023-04-13 20:38:25 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-04-13 20:38:25 -0600 |
| commit | 55541be4a505da3df7d1a2b8bf3b5295b0af58f7 (patch) | |
| tree | fc1e7f22ffe1c51087117b28766ff266895228e3 | |
| parent | merging gchart's changes, part 1... (diff) | |
| download | anduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.tar.gz anduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.tar.bz2 anduril-55541be4a505da3df7d1a2b8bf3b5295b0af58f7.zip | |
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
with the new channel mode system ... but there's a lot more left to do
Diffstat (limited to '')
29 files changed, 1019 insertions, 508 deletions
diff --git a/hwdef-Emisar_D4v2.h b/hwdef-Emisar_D4v2.h index e118ed5..51e30f8 100644 --- a/hwdef-Emisar_D4v2.h +++ b/hwdef-Emisar_D4v2.h @@ -1,7 +1,8 @@ -#ifndef HWDEF_EMISAR_D4V2_H -#define HWDEF_EMISAR_D4V2_H +#pragma once -/* Emisar D4v2 driver layout (attiny1634) +/* hwdef for Emisar D4v2 (attiny1634) + * Copyright (C) 2019 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later * * Pin / Name / Function * 1 PA6 FET PWM (PWM1B) @@ -33,6 +34,16 @@ #define ATTINY 1634 #include <avr/io.h> +// TODO: add aux red and aux blue as disabled channel modes, +// so they can be used for the police strobe? +#define NUM_CHANNEL_MODES 1 +#define DEFAULT_CHANNEL_MODE 0 +#define SET_LEVEL_MODES set_level_2ch_stacked +#define GRADUAL_TICK_MODES gradual_tick_2ch_stacked +// no special handlers needed, can use common handlers +#define USE_SET_LEVEL_2CH_STACKED +#define USE_GRADUAL_TICK_2CH_STACKED + #define PWM_CHANNELS 2 #define SWITCH_PIN PA2 // pin 5 @@ -41,12 +52,17 @@ #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, 1x7135 PWM -#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3 +#define LOW_PWM_PIN PB3 // pin 16, 1x7135 PWM +#define LOW_PWM_LVL OCR1A // OCR1A is the output compare register for PB3 +#define PWM1_BITS 8 -#define PWM2_PIN PA6 // pin 1, FET PWM -#define PWM2_LVL OCR1B // OCR1B is the output compare register for PB1 +#define HIGH_PWM_PIN PA6 // pin 1, FET PWM +#define HIGH_PWM_LVL OCR1B // OCR1B is the output compare register for PB1 +#define PWM2_BITS 8 +// only using 8-bit on this light +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t #define ADC_PRSCL 0x07 // clk/128 @@ -68,37 +84,46 @@ #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 +// 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 + inline void hwdef_setup() { - // enable output ports - // 7135 - DDRB = (1 << PWM1_PIN); - // 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 - // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter - // pre-scale for timer: N = 1 - 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) - ; - - // 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 + // enable output ports + // 7135 + DDRB = (1 << LOW_PWM_PIN); + // FET, aux R/G/B, button LED + DDRA = (1 << HIGH_PWM_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + | (1 << BUTTON_LED_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 + 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) + ; + + // 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-Sofirn_LT1S-Pro.c b/hwdef-Sofirn_LT1S-Pro.c new file mode 100644 index 0000000..3c31c96 --- /dev/null +++ b/hwdef-Sofirn_LT1S-Pro.c @@ -0,0 +1,67 @@ +// BLF LT1S Pro hwdef functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + + +// "auto tint" channel mode +void set_level_auto_3ch_blend(uint8_t level) { + BLEND_PWM_DATATYPE vpwm; + + if (level == 0) { + vpwm = 0; + } else { + level --; // PWM array index = level - 1 + vpwm = PWM_GET(blend_pwm_levels, level); + } + + // tint goes from 0 (red) to 127 (warm white) to 255 (cool white) + uint8_t mytint; + mytint = 255 * (uint16_t)level / RAMP_SIZE; + + BLEND_PWM_DATATYPE a, b, c; + + // red is high at 0, low at 255 + a = (((PWM_DATATYPE2)(255 - mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // warm white is low at 0 and 255, high at 127 + b = (((PWM_DATATYPE2)triangle_wave(mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // cool white is low at 0, high at 255 + c = (((PWM_DATATYPE2)mytint + * (PWM_DATATYPE2)vpwm) + 127) / 255; + + RED_PWM_LVL = a; + WARM_PWM_LVL = b; + COOL_PWM_LVL = c; +} + + +// "white + red" channel mode +void set_level_red_white_blend(uint8_t level) { + // set the warm+cool white LEDs first + channel_mode = CM_WHITE; + set_level_2ch_blend(level); + channel_mode = CM_WHITE_RED; + + BLEND_PWM_DATATYPE vpwm; + + // set the red LED as a ratio of the white output level + if (level == 0) { + vpwm = 0; + } else { + level --; // PWM array index = level - 1 + vpwm = PWM_GET(blend_pwm_levels, level); + } + + // 0 = no red + // 255 = red at 100% of white channel PWM + uint8_t ratio = channel_mode_args[channel_mode]; + + PWM_DATATYPE red_pwm; + red_pwm = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255; + + RED_PWM_LVL = red_pwm; +} + diff --git a/hwdef-Sofirn_LT1S-Pro.h b/hwdef-Sofirn_LT1S-Pro.h index ce6ee3a..c52364f 100644 --- a/hwdef-Sofirn_LT1S-Pro.h +++ b/hwdef-Sofirn_LT1S-Pro.h @@ -1,6 +1,3 @@ -#ifndef HWDEF_SOFIRN_LT1S_PRO_H -#define HWDEF_SOFIRN_LT1S_PRO_H - /* BLF LT1S Pro driver layout using the Attiny1616 Driver pinout: @@ -13,8 +10,10 @@ Driver pinout: */ +#pragma once -#define LAYOUT_DEFINED + +#define HWDEF_C_FILE hwdef-Sofirn_LT1S-Pro.c #ifdef ATTINY #undef ATTINY @@ -22,40 +21,85 @@ Driver pinout: #define ATTINY 1616 #include <avr/io.h> +// channel modes: +// * 0. warm/cool white blend +// * 1. auto 3ch blend (red -> warm -> cool by ramp level) +// * 2. red only +// * 3. red + white blend +#define NUM_CHANNEL_MODES 4 +//#define CHANNEL_MODES_ENABLED 1,1,1,1 +#define CHANNEL_MODES_ENABLED 0b00001111 +#define CM_WHITE 0 +#define CM_AUTO 1 +#define CM_RED 2 +#define CM_WHITE_RED 3 + +// TODO: blend mode should enable this automatically? +#define USE_CHANNEL_MODES +// TODO: blend mode should enable this automatically? +#define USE_CHANNEL_MODE_ARGS +// TODO: or maybe if args are defined, the USE_ should be auto-set? +#define CHANNEL_MODE_ARGS 128,0,0,255 +#define SET_LEVEL_MODES set_level_2ch_blend, \ + set_level_auto_3ch_blend, \ + set_level_1ch, \ + set_level_red_white_blend +// TODO: gradual ticking for thermal regulation +#define GRADUAL_TICK_MODES gradual_tick_2ch_blend, \ + gradual_tick_auto_3ch_blend, \ + gradual_tick_1ch, \ + gradual_tick_red_white_blend +// can use some of the common handlers +#define USE_SET_LEVEL_2CH_BLEND +//#define USE_SET_LEVEL_AUTO_3CH_BLEND +#define USE_SET_LEVEL_1CH +//#define USE_SET_LEVEL_RED_WHITE_BLEND +// TODO: +//#define USE_GRADUAL_TICK_2CH_BLEND +//#define USE_GRADUAL_TICK_AUTO_3CH_BLEND +//#define USE_GRADUAL_TICK_1CH +//#define USE_GRADUAL_TICK_RED_WHITE_BLEND + +#define DEFAULT_CHANNEL_MODE CM_AUTO + +#define FACTORY_RESET_WARN_CHANNEL CM_RED +#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE + +#define POLICE_COLOR_STROBE_CH1 CM_RED +#define POLICE_COLOR_STROBE_CH2 CM_WHITE + +// TODO: remove this as soon as it's not needed #define PWM_CHANNELS 1 -#ifndef SWITCH_PIN #define SWITCH_PIN PIN5_bp #define SWITCH_PORT VPORTA.IN #define SWITCH_ISC_REG PORTA.PIN2CTRL #define SWITCH_VECT PORTA_PORT_vect #define SWITCH_INTFLG VPORTA.INTFLAGS -#endif - -// usually PWM1_LVL would be a hardware register, but we need to abstract -// it out to a soft brightness value, in order to handle tint ramping -// (this allows smooth thermal regulation to work, and makes things -// otherwise simpler and easier) -uint8_t PWM1_LVL; // warm tint channel -#ifndef PWM1_PIN -#define PWM1_PIN PB0 // -#define TINT1_LVL TCA0.SINGLE.CMP0 // CMP1 is the output compare register for PB0 -#endif +#define WARM_PWM_PIN PB0 +#define WARM_PWM_LVL TCA0.SINGLE.CMP0 // CMP1 is the output compare register for PB0 // cold tint channel -#ifndef PWM2_PIN -#define PWM2_PIN PB1 // -#define TINT2_LVL TCA0.SINGLE.CMP1 // CMP0 is the output compare register for PB1 -#endif +#define COOL_PWM_PIN PB1 +#define COOL_PWM_LVL TCA0.SINGLE.CMP1 // CMP0 is the output compare register for PB1 // red channel -#ifndef PWM3_PIN -#define PWM3_PIN PB0 // -#define PWM3_LVL TCA0.SINGLE.CMP2 // CMP2 is the output compare register for PB2 -#endif +#define RED_PWM_PIN PB0 // +#define RED_PWM_LVL TCA0.SINGLE.CMP2 // CMP2 is the output compare register for PB2 + +// translate cfg names to FSM names +#define LOW_PWM_LEVELS RED_PWM_LEVELS +#define LOW_PWM_LVL RED_PWM_LVL +#define LOW_PWM_PIN RED_PWM_PIN + +// only using 8-bit on this light +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define BLEND_PWM_DATATYPE uint8_t + // average drop across diode on this hardware #ifndef VOLTAGE_FUDGE_FACTOR @@ -64,14 +108,20 @@ uint8_t PWM1_LVL; // lighted button -#ifndef AUXLED_PIN #define AUXLED_PIN PIN5_bp #define AUXLED_PORT PORTB -#endif + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + + +// custom channel modes +void set_level_auto_3ch_blend(uint8_t level); +void set_level_red_white_blend(uint8_t level); -// with so many pins, doing this all with #ifdefs gets awkward... -// ... so just hardcode it in each hwdef file instead inline void hwdef_setup() { // set up the system clock to run at 10 MHz instead of the default 3.33 MHz @@ -111,4 +161,5 @@ inline void hwdef_setup() { } -#endif +#define LAYOUT_DEFINED + diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c index 47cd00f..07560bc 100644 --- a/spaghetti-monster/anduril/anduril.c +++ b/spaghetti-monster/anduril/anduril.c @@ -54,10 +54,10 @@ /********* specific settings for known driver types *********/ // Anduril config file name (set it here or define it at the gcc command line) -//#define CONFIGFILE cfg-blf-q8.h +//#define CFG_H cfg-blf-q8.h #include "tk.h" -#include incfile(CONFIGFILE) +#include incfile(CFG_H) /********* Include headers which need to be before FSM *********/ @@ -92,8 +92,11 @@ #include "spaghetti-monster.h" /********* does this build target have special code to include? *********/ -#ifdef OVERRIDES_FILE -#include incfile(OVERRIDES_FILE) +#ifdef HWDEF_C_FILE +#include incfile(HWDEF_C_FILE) +#endif +#ifdef CFG_C_FILE +#include incfile(CFG_C_FILE) #endif @@ -138,8 +141,10 @@ #include "tactical-mode.h" #endif -#ifdef USE_TINT_RAMPING -#include "tint-ramping.h" +// allow the channel mode handler even when only 1 mode +// (so a tint ramp light could still use 3H even if there's no other mode) +#if defined(USE_CHANNEL_MODES) +#include "channel-modes.h" #endif #ifdef USE_FACTORY_RESET @@ -197,8 +202,8 @@ #include "tactical-mode.c" #endif -#ifdef USE_TINT_RAMPING -#include "tint-ramping.c" +#if defined(USE_CHANNEL_MODES) +#include "channel-modes.c" #endif #ifdef USE_FACTORY_RESET @@ -234,13 +239,13 @@ void setup() { #if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER) // without this, initial boot-up brightness is wrong // when manual mem is enabled with a non-zero timer - if (manual_memory) memorized_level = manual_memory; + if (manual_memory) manual_memory_restore(); #endif - #ifdef USE_TINT_RAMPING - // add tint ramping underneath every other state - push_state(tint_ramping_state, 0); - #endif // ifdef USE_TINT_RAMPING + #if defined(USE_CHANNEL_MODES) + // add channel mode functions underneath every other state + push_state(channel_mode_state, 0); + #endif push_state(off_state, 1); @@ -250,10 +255,10 @@ void setup() { // power clicky acts as a momentary mode load_config(); - #ifdef USE_TINT_RAMPING - // add tint ramping underneath every other state - push_state(tint_ramping_state, 0); - #endif // ifdef USE_TINT_RAMPING + #if defined(USE_CHANNEL_MODES) + // add channel mode functions underneath every other state + push_state(channel_mode_state, 0); + #endif if (button_is_pressed()) // hold button to go to moon diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh index b9f6d15..768559c 100755 --- a/spaghetti-monster/anduril/build-all.sh +++ b/spaghetti-monster/anduril/build-all.sh @@ -33,8 +33,8 @@ for TARGET in cfg-*.h ; do if [ -z "$ATTINY" ]; then ATTINY=85 ; fi # try to compile - echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" - ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" + echo ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" + ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" # track result, and rename compiled files if [ 0 = $? ] ; then diff --git a/spaghetti-monster/anduril/candle-mode.c b/spaghetti-monster/anduril/candle-mode.c index d15195e..12ffa84 100644 --- a/spaghetti-monster/anduril/candle-mode.c +++ b/spaghetti-monster/anduril/candle-mode.c @@ -28,7 +28,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) { static int8_t ramp_direction = 1; - #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) + #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15) static uint8_t candle_wave1 = 0; static uint8_t candle_wave2 = 0; static uint8_t candle_wave3 = 0; diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h index 414971a..dad84f0 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h @@ -4,9 +4,10 @@ #define MODEL_NUMBER "0114" // ATTINY: 1634 -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#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 -// 65% 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,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166 +// don't turn off the low channel at turbo level +#undef LOW_PWM_LEVELS +#define LOW_PWM_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 +// 65% DDFET power +#undef HIGH_PWM_LEVELS +#define HIGH_PWM_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,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166 + diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h index 717afcf..5e33a05 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h @@ -4,11 +4,23 @@ #define MODEL_NUMBER "0115" // ATTINY: 1634 +// switch to 1-channel support functions +#undef USE_SET_LEVEL_2CH_STACKED +#undef USE_GRADUAL_TICK_2CH_STACKED +#define USE_SET_LEVEL_1CH +#define USE_GRADUAL_TICK_1CH +#undef SET_LEVEL_MODES +#undef GRADUAL_TICK_MODES +#define SET_LEVEL_MODES set_level_1ch +#define GRADUAL_TICK_MODES gradual_tick_1ch + #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 LOW_PWM_LEVELS +#undef HIGH_PWM_LEVELS +#define LOW_PWM_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 @@ -39,4 +51,7 @@ #undef THERM_FASTER_LEVEL #define THERM_FASTER_LEVEL 150 -#undef USE_THERMAL_REGULATION +// maybe keep this, in case someone uses a higher power channel? +//#undef USE_THERMAL_REGULATION +//#undef USE_SET_LEVEL_GRADUALLY + diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h index 4121465..54de297 100644 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h +++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h @@ -1,28 +1,19 @@ // Emisar D4 config options for Anduril +// Copyright (C) 2019 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + #define MODEL_NUMBER "0113" #include "hwdef-Emisar_D4v2.h" #include "hank-cfg.h" // ATTINY: 1634 -// 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 - - // copied from original D4, since it's also a FET+1 and has the same host // ../../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 -#define RAMP_LENGTH 150 -#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,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,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 RAMP_SIZE 150 +#define LOW_PWM_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 +#define HIGH_PWM_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 6 diff --git a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h index 4e5993b..e009e02 100644 --- a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h +++ b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h @@ -1,19 +1,9 @@ // Sofirn LT1S Pro + #define MODEL_NUMBER "0623" #include "hwdef-Sofirn_LT1S-Pro.h" // ATTINY: 1616 -// this model requires some special code -#define OVERRIDES_FILE cfg-sofirn-lt1s-pro.c -#define OVERRIDE_SET_LEVEL -inline void set_level_override(uint8_t level); - -// uses 4C action while On to switch between white and red channels (overrides lockout action) -#define USE_OUTPUT_MUX -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING // off mode: high (1) // lockout: blinking (3) #define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) @@ -21,19 +11,23 @@ inline void set_level_override(uint8_t level); // the lantern has two PWM channels, but they drive different sets of emitters // (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 +//#define USE_TINT_RAMPING // how much to increase total brightness at middle tint // (0 = 100% brightness, 64 = 200% brightness) #define TINT_RAMPING_CORRECTION 10 // 115% -#ifdef RAMP_LENGTH -#undef RAMP_LENGTH -#endif - // level_calc.py 1 150 7135 1 30 800 -#define RAMP_LENGTH 150 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255 +#define RAMP_SIZE 150 +// TODO: use dynamic PWM instead of plain 8-bit +// (so we can get lower lows and a smoother ramp) +// TODO: 200% power at top of ramp on white blend mode +#define PWM_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255 +#define BLEND_PWM_LEVELS PWM_LEVELS +#define RED_PWM_LEVELS PWM_LEVELS #define MAX_1x7135 65 +// FIXME: clock at 5 MHz w/ full+half+quarter speeds, +// instead of 10 MHz with w/ only half+quarter +// (10 MHz is just wasting power) #define HALFSPEED_LEVEL 256 // red LEDs use a QX7138 chip which has max PWM speed of 10 kHz, so never run faster than halfspeed #define QUARTERSPEED_LEVEL 5 @@ -68,8 +62,11 @@ inline void set_level_override(uint8_t level); #define USE_SOS_MODE #define USE_SOS_MODE_IN_BLINKY_GROUP -// the sensor (attiny) is nowhere near the emitters -// so thermal regulation can't work +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// FIXME: thermal regulation should actually work fine on this light #ifdef USE_THERMAL_REGULATION #undef USE_THERMAL_REGULATION #endif @@ -84,7 +81,7 @@ inline void set_level_override(uint8_t level); #ifdef BLINK_AT_RAMP_CEIL #undef BLINK_AT_RAMP_CEIL #endif +// without this, it's really hard to tell when ramping up stops +#define BLINK_AT_RAMP_CEIL -#ifndef USE_SOFT_FACTORY_RESET #define USE_SOFT_FACTORY_RESET -#endif
\ No newline at end of file diff --git a/spaghetti-monster/anduril/channel-modes.c b/spaghetti-monster/anduril/channel-modes.c new file mode 100644 index 0000000..958b375 --- /dev/null +++ b/spaghetti-monster/anduril/channel-modes.c @@ -0,0 +1,146 @@ +/* + * channel-modes.c: Multi-channel functions for Anduril. + * Copyright (C) 2017-2023 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "channel-modes.h" + + +uint8_t channel_mode_state(Event event, uint16_t arg) { + #ifdef USE_CHANNEL_MODE_ARGS + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + uint8_t tint = channel_mode_args[channel_mode]; + #endif + + // it's possible that a light may need 3H but not 3C, + // so try to detect if 3C is needed + #if NUM_CHANNEL_MODES > 1 + // 3 clicks: next channel mode + if (event == EV_3clicks) { + uint8_t next = channel_mode; + // go to next channel mode until we find one which is enabled + // (and don't do any infinite loops if the user disabled them all) + uint8_t count = 0; + do { + count ++; + next = (next + 1) % NUM_CHANNEL_MODES; + } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES); + //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES); + + // undo change if infinite loop detected (redundant?) + //if (NUM_CHANNEL_MODES == count) next = channel_mode; + + // if mode hasn't changed, abort + if (channel_mode == next) + return EVENT_NOT_HANDLED; + + set_channel_mode(next); + + // remember after battery changes + save_config(); + return EVENT_HANDLED; + } else + #endif // if NUM_CHANNEL_MODES > 1 + + #ifdef USE_CUSTOM_CHANNEL_3H_MODES + // defer to mode-specific function if defined + if (tint_ramp_modes[channel_mode]) { + StatePtr tint_func = channel_3H_modes[channel_mode]; + return tint_func(channel_mode); + } else + #endif + #ifdef USE_CHANNEL_MODE_ARGS + #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE + // click, click, hold: change the current channel's arg (like tint) + if (event == EV_click3_hold) { + ///// adjust value from 0 to 255 + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_NOT_HANDLED; + + // change normal tints + if ((tint_ramp_direction > 0) && (tint < 255)) { + tint += 1; + } + else if ((tint_ramp_direction < 0) && (tint > 0)) { + tint -= 1; + } + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + past_edge_counter = 1; + } + prev_tint = tint; + channel_mode_args[channel_mode] = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (0 == tint) tint_ramp_direction = 1; + else if (255 == tint) tint_ramp_direction = -1; + // remember tint after battery change + channel_mode_args[channel_mode] = tint; + save_config(); + // bug?: for some reason, brightness can seemingly change + // from 1/150 to 2/150 without this next line... not sure why + set_level(actual_level); + return EVENT_HANDLED; + } + #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE + #endif // ifdef USE_CHANNEL_MODE_ARGS + + #if defined(USE_SIMPLE_UI) + // remaining mappings aren't "simple", so stop here + if (simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + #if NUM_CHANNEL_MODES > 1 + // channel toggle menu on ... 9H? + else if (event == EV_click9_hold) { + push_state(channel_mode_config_state, 0); + return MISCHIEF_MANAGED; + } + #endif + + return EVENT_NOT_HANDLED; +} + +#if NUM_CHANNEL_MODES > 1 +void channel_mode_config_save(uint8_t step, uint8_t value) { + // 1 menu item per channel mode, to enable or disable that mode + step --; // step is 1-based, channel modes are 0-based + if (value) channel_mode_enable(step); + else channel_mode_disable(step); +} + +uint8_t channel_mode_config_state(Event event, uint16_t arg) { + // 1 menu item per channel mode, to enable or disable that mode + return config_state_base( + event, arg, + NUM_CHANNEL_MODES, + channel_mode_config_save + ); +} +#endif diff --git a/spaghetti-monster/anduril/channel-modes.h b/spaghetti-monster/anduril/channel-modes.h new file mode 100644 index 0000000..f536d58 --- /dev/null +++ b/spaghetti-monster/anduril/channel-modes.h @@ -0,0 +1,20 @@ +/* + * channel-modes.h: Multi-channel functions for Anduril. + * Copyright (C) 2017-2023 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#if defined(USE_MANUAL_MEMORY) && defined(USE_CHANNEL_MODE_ARGS) +// TODO: save to eeprom +// remember and reset 1 extra parameter per channel mode (like tint) +uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; +#endif + +// not actually a mode, more of a fallback under other modes +uint8_t channel_mode_state(Event event, uint16_t arg); + +#if NUM_CHANNEL_MODES > 1 +uint8_t channel_mode_config_state(Event event, uint16_t arg); +#endif diff --git a/spaghetti-monster/anduril/factory-reset.c b/spaghetti-monster/anduril/factory-reset.c index ecb4cc2..a14b5b9 100644 --- a/spaghetti-monster/anduril/factory-reset.c +++ b/spaghetti-monster/anduril/factory-reset.c @@ -1,27 +1,15 @@ -/* - * factory-reset.c: Factory reset functions for Anduril. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// factory-reset.c: Factory reset functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later -#ifndef FACTORY_RESET_C -#define FACTORY_RESET_C +#pragma once #include "factory-reset.h" +// allows setting channel mode per animation stage, +// so it can ramp up in red then explode in white (as one example) +// TODO: maybe also do the same in menus? + 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 @@ -31,6 +19,9 @@ void factory_reset() { uint8_t bright; uint8_t reset = 1; // wind up to an explosion + #ifdef FACTORY_RESET_WARN_CHANNEL + set_channel_mode(FACTORY_RESET_WARN_CHANNEL); + #endif for (bright=0; bright<SPLODEY_STEPS; bright++) { set_level(bright); nice_delay_ms(SPLODEY_TIME_PER_STEP/2); @@ -43,17 +34,29 @@ void factory_reset() { } // explode, if button pressed long enough if (reset) { + // auto-calibrate temperature // AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset #if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3) - thermal_config_save(1,temperature - therm_cal_offset); // this will cancel out the offset + // this will cancel out the offset + thermal_config_save(1, temperature - therm_cal_offset); #elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE) - // auto-calibrate temperature... assume current temperature is 21 C + // assume current temperature is 21 C thermal_config_save(1, 21); #endif + + #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE) && (FACTORY_RESET_WARN_CHANNEL != DEFAULT_CHANNEL_MODE) + // return to default channel before saving + set_channel_mode(DEFAULT_CHANNEL_MODE); + #endif + // save all settings to eeprom // (assuming they're all at default because we haven't loaded them yet) save_config(); + // explosion animation + #ifdef FACTORY_RESET_SUCCESS_CHANNEL + set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL); + #endif bright = MAX_LEVEL; for (; bright > 0; bright--) { set_level(bright); @@ -69,6 +72,3 @@ void factory_reset() { } } - -#endif - diff --git a/spaghetti-monster/anduril/factory-reset.h b/spaghetti-monster/anduril/factory-reset.h index 9f0af38..63c25cd 100644 --- a/spaghetti-monster/anduril/factory-reset.h +++ b/spaghetti-monster/anduril/factory-reset.h @@ -1,26 +1,8 @@ -/* - * factory-reset.h: Factory reset functions for Anduril. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ +// factory-reset.h: Factory reset functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later -#ifndef FACTORY_RESET_H -#define FACTORY_RESET_H +#pragma once void factory_reset(); - -#endif diff --git a/spaghetti-monster/anduril/hank-cfg.h b/spaghetti-monster/anduril/hank-cfg.h index 11eb0a1..f24ea67 100644 --- a/spaghetti-monster/anduril/hank-cfg.h +++ b/spaghetti-monster/anduril/hank-cfg.h @@ -1,5 +1,7 @@ -#ifndef HANK_CFG -#define HANK_CFG +// Intl-Outdoor (Hank)'s config options for Anduril +// Copyright (C) 2021 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once // config preferences for Hank Wang of Intl-Outdoor (Emisar, Noctigon) @@ -18,4 +20,3 @@ // double click while on goes to full-power turbo, not ramp ceiling #define DEFAULT_2C_STYLE 1 -#endif // ifndef HANK_CFG diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h index f0de161..9004f1f 100644 --- a/spaghetti-monster/anduril/load-save-config-fsm.h +++ b/spaghetti-monster/anduril/load-save-config-fsm.h @@ -54,13 +54,6 @@ typedef enum { #ifdef USE_MANUAL_MEMORY_TIMER manual_memory_timer_e, #endif - #ifdef USE_TINT_RAMPING - manual_memory_tint_e, - #endif - #endif - #ifdef USE_TINT_RAMPING - tint_e, - tint_style_e, #endif #ifdef USE_JUMP_START jump_start_level_e, @@ -100,7 +93,28 @@ typedef enum { tactical_lvl_2_e, tactical_lvl_3_e, #endif - eeprom_indexes_e_END + #if NUM_CHANNEL_MODES > 1 + channel_mode_e, + channel_modes_enabled_e, + #if defined(USE_MANUAL_MEMORY) + manual_memory_channel_mode_e, + #endif + #endif + #ifdef USE_CHANNEL_MODE_ARGS + // this is an array, needs a few bytes + channel_mode_args_e, + #if defined(USE_MANUAL_MEMORY) + // this is an array, needs a few bytes + manual_memory_channel_args_e = channel_mode_args_e + NUM_CHANNEL_MODES, + // and this is an ugly ugly kludge + // FIXME: use a struct for eeprom, not an array + eeprom_indexes_e_END = manual_memory_channel_args_e + NUM_CHANNEL_MODES + #else + eeprom_indexes_e_END = channel_mode_args_e + NUM_CHANNEL_MODES + #endif + #else + eeprom_indexes_e_END + #endif } eeprom_indexes_e; #define EEPROM_BYTES eeprom_indexes_e_END diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c index 4101e7a..ad84450 100644 --- a/spaghetti-monster/anduril/load-save-config.c +++ b/spaghetti-monster/anduril/load-save-config.c @@ -56,13 +56,6 @@ void load_config() { #ifdef USE_MANUAL_MEMORY_TIMER manual_memory_timer = eeprom[manual_memory_timer_e]; #endif - #ifdef USE_TINT_RAMPING - manual_memory_tint = eeprom[manual_memory_tint_e]; - #endif - #endif - #ifdef USE_TINT_RAMPING - tint = eeprom[tint_e]; - tint_style = eeprom[tint_style_e]; #endif #ifdef USE_JUMP_START jump_start_level = eeprom[jump_start_level_e], @@ -100,6 +93,21 @@ void load_config() { tactical_levels[1] = eeprom[tactical_lvl_2_e]; tactical_levels[2] = eeprom[tactical_lvl_3_e]; #endif + #if NUM_CHANNEL_MODES > 1 + channel_mode = eeprom[channel_mode_e]; + channel_modes_enabled = eeprom[channel_modes_enabled_e]; + #if defined(USE_MANUAL_MEMORY) + manual_memory_channel_mode = eeprom[manual_memory_channel_mode_e]; + #endif + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + channel_mode_args[i] = eeprom[i + channel_mode_args_e]; + #if defined(USE_MANUAL_MEMORY) + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + manual_memory_channel_args[i] = eeprom[i + manual_memory_channel_args_e]; + #endif + #endif } #ifdef START_AT_MEMORIZED_LEVEL if (load_eeprom_wl()) { @@ -140,13 +148,6 @@ void save_config() { #ifdef USE_MANUAL_MEMORY_TIMER eeprom[manual_memory_timer_e] = manual_memory_timer; #endif - #ifdef USE_TINT_RAMPING - eeprom[manual_memory_tint_e] = manual_memory_tint; - #endif - #endif - #ifdef USE_TINT_RAMPING - eeprom[tint_e] = tint; - eeprom[tint_style_e] = tint_style; #endif #ifdef USE_JUMP_START eeprom[jump_start_level_e] = jump_start_level, @@ -184,6 +185,21 @@ void save_config() { eeprom[tactical_lvl_2_e] = tactical_levels[1]; eeprom[tactical_lvl_3_e] = tactical_levels[2]; #endif + #if NUM_CHANNEL_MODES > 1 + eeprom[channel_mode_e] = channel_mode; + eeprom[channel_modes_enabled_e] = channel_modes_enabled; + #if defined(USE_MANUAL_MEMORY) + eeprom[manual_memory_channel_mode_e] = manual_memory_channel_mode; + #endif + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + eeprom[i + channel_mode_args_e] = channel_mode_args[i]; + #if defined(USE_MANUAL_MEMORY) + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + eeprom[i + manual_memory_channel_args_e] = manual_memory_channel_args[i]; + #endif + #endif save_eeprom(); } diff --git a/spaghetti-monster/anduril/off-mode.c b/spaghetti-monster/anduril/off-mode.c index 6781ade..68ab234 100644 --- a/spaghetti-monster/anduril/off-mode.c +++ b/spaghetti-monster/anduril/off-mode.c @@ -65,10 +65,7 @@ uint8_t off_state(Event event, uint16_t arg) { // reset to manual memory level when timer expires if (manual_memory && (arg >= (manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) { - memorized_level = manual_memory; - #ifdef USE_TINT_RAMPING - tint = manual_memory_tint; - #endif + manual_memory_restore(); } #endif #ifdef USE_INDICATOR_LED @@ -131,10 +128,7 @@ uint8_t off_state(Event event, uint16_t arg) { // this clause probably isn't used by any configs any more // but is included just in case someone configures it this way if (manual_memory) { - memorized_level = manual_memory; - #ifdef USE_TINT_RAMPING - tint = manual_memory_tint; - #endif + manual_memory_restore(); } #endif set_level(nearest_level(memorized_level)); diff --git a/spaghetti-monster/anduril/ramp-mode.c b/spaghetti-monster/anduril/ramp-mode.c index 6e8ba88..e57b68f 100644 --- a/spaghetti-monster/anduril/ramp-mode.c +++ b/spaghetti-monster/anduril/ramp-mode.c @@ -26,6 +26,7 @@ #include "sunset-timer.h" #endif + uint8_t steady_state(Event event, uint16_t arg) { static int8_t ramp_direction = 1; #if (B_TIMING_OFF == B_RELEASE_T) @@ -378,7 +379,26 @@ uint8_t steady_state(Event event, uint16_t arg) { #endif // 3 clicks: toggle smooth vs discrete ramping - else if (event == EV_3clicks) { + // (and/or 6 clicks when there are multiple channel modes) + // (handle 3C here anyway, when all but 1 mode is disabled) + else if ((event == EV_3clicks) + #if NUM_CHANNEL_MODES > 1 + || (event == EV_6clicks) + ) { + // detect if > 1 channel mode is enabled, + // and if so, fall through so channel mode code can handle it + // otherwise, change the ramp style + if (event == EV_3clicks) { + uint8_t enabled = 0; + for (uint8_t m=0; m<NUM_CHANNEL_MODES; m++) + enabled += channel_mode_enabled(m); + if (enabled > 1) + return EVENT_NOT_HANDLED; + } + #else + ) { + #endif + ramp_style = !ramp_style; save_config(); #ifdef START_AT_MEMORIZED_LEVEL @@ -400,9 +420,9 @@ uint8_t steady_state(Event event, uint16_t arg) { } #endif - #ifndef USE_TINT_RAMPING // 3H: momentary turbo (on lights with no tint ramping) - else if (event == EV_click3_hold) { + // (or 4H on lights with tint ramping) + else if (event == EV_MOMENTARY_TURBO) { if (! arg) { // first frame only, to allow thermal regulation to work #ifdef USE_2C_STYLE_CONFIG uint8_t tl = style_2c ? MAX_LEVEL : turbo_level; @@ -413,11 +433,10 @@ uint8_t steady_state(Event event, uint16_t arg) { } return MISCHIEF_MANAGED; } - else if (event == EV_click3_hold_release) { + else if (event == EV_MOMENTARY_TURBO_RELEASE) { set_level_and_therm_target(memorized_level); return MISCHIEF_MANAGED; } - #endif // ifndef USE_TINT_RAMPING #ifdef USE_MOMENTARY_MODE // 5 clicks: shortcut to momentary mode @@ -439,10 +458,7 @@ uint8_t steady_state(Event event, uint16_t arg) { #ifdef USE_MANUAL_MEMORY else if (event == EV_10clicks) { // turn on manual memory and save current brightness - manual_memory = actual_level; - #ifdef USE_TINT_RAMPING - manual_memory_tint = tint; // remember tint too - #endif + manual_memory_save(); save_config(); blink_once(); return MISCHIEF_MANAGED; @@ -643,6 +659,27 @@ void set_level_and_therm_target(uint8_t level) { #define set_level_and_therm_target(level) set_level(level) #endif +void manual_memory_restore() { + memorized_level = manual_memory; + #if NUM_CHANNEL_MODES > 1 + channel_mode = manual_memory_channel_mode; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + channel_mode_args[i] = manual_memory_channel_args[i]; + #endif +} + +void manual_memory_save() { + manual_memory = actual_level; + #if NUM_CHANNEL_MODES > 1 + manual_memory_channel_mode = channel_mode; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++) + manual_memory_channel_args[i] = channel_mode_args[i]; + #endif +} #endif diff --git a/spaghetti-monster/anduril/ramp-mode.h b/spaghetti-monster/anduril/ramp-mode.h index ed76ed5..969cb6b 100644 --- a/spaghetti-monster/anduril/ramp-mode.h +++ b/spaghetti-monster/anduril/ramp-mode.h @@ -72,6 +72,17 @@ #define B_TIMING_OFF B_TIMEOUT_T #endif + +// move a couple actions depending on whether there are channel modes +#ifdef USE_CHANNEL_MODE_ARGS + #define EV_MOMENTARY_TURBO EV_click4_hold + #define EV_MOMENTARY_TURBO_RELEASE EV_click4_hold_release +#else + #define EV_MOMENTARY_TURBO EV_click3_hold + #define EV_MOMENTARY_TURBO_RELEASE EV_click3_hold_release +#endif + + // default ramp options if not overridden earlier per-driver #ifndef RAMP_STYLE #define RAMP_STYLE 0 // smooth default @@ -102,6 +113,7 @@ // mile marker(s) partway up the ramp // default: blink only at border between regulated and FET #ifdef BLINK_AT_RAMP_MIDDLE + // FIXME: remove PWM_CHANNELS, use some other abstraction #if PWM_CHANNELS >= 3 #ifndef BLINK_AT_RAMP_MIDDLE_1 #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 @@ -166,6 +178,9 @@ uint8_t manual_memory = DEFAULT_MANUAL_MEMORY; uint8_t manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER; #endif #endif +void manual_memory_restore(); +void manual_memory_save(); + #ifdef USE_SIMPLE_UI // whether to enable the simplified interface or not uint8_t simple_ui_active = SIMPLE_UI_ACTIVE; diff --git a/spaghetti-monster/anduril/strobe-modes.c b/spaghetti-monster/anduril/strobe-modes.c index 78fe240..5ee2386 100644 --- a/spaghetti-monster/anduril/strobe-modes.c +++ b/spaghetti-monster/anduril/strobe-modes.c @@ -184,6 +184,12 @@ inline void strobe_state_iter() { break; #endif + #ifdef USE_POLICE_COLOR_STROBE_MODE + case police_color_strobe_e: + police_color_strobe_iter(); + break; + #endif + #ifdef USE_LIGHTNING_MODE case lightning_storm_e: lightning_storm_iter(); @@ -205,7 +211,7 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) { uint8_t del = strobe_delays[st]; // TODO: make tac strobe brightness configurable? set_level(STROBE_BRIGHTNESS); - if (0) {} // placeholde0 + if (0) {} // placeholder #ifdef USE_PARTY_STROBE_MODE else if (st == party_strobe_e) { // party strobe #ifdef PARTY_STROBE_ONTIME @@ -226,6 +232,28 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) { } #endif +#ifdef USE_POLICE_COLOR_STROBE_MODE +inline void police_color_strobe_iter() { + // one iteration of main loop() + uint8_t del = 66; + // TODO: make police strobe brightness configurable + uint8_t bright = memorized_level; + uint8_t channel = channel_mode; + + for (uint8_t i=0; i<10; i++) { + if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1); + else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2); + set_level(bright); + nice_delay_ms(del >> 1); + set_level(STROBE_OFF_LEVEL); + nice_delay_ms(del); + } + + // restore this when done + set_channel_mode(channel); +} +#endif + #ifdef USE_LIGHTNING_MODE inline void lightning_storm_iter() { // one iteration of main loop() diff --git a/spaghetti-monster/anduril/strobe-modes.h b/spaghetti-monster/anduril/strobe-modes.h index c6cfb53..685c249 100644 --- a/spaghetti-monster/anduril/strobe-modes.h +++ b/spaghetti-monster/anduril/strobe-modes.h @@ -29,6 +29,9 @@ typedef enum { #ifdef USE_TACTICAL_STROBE_MODE tactical_strobe_e, #endif + #ifdef USE_POLICE_COLOR_STROBE_MODE + police_color_strobe_e, + #endif #ifdef USE_LIGHTNING_MODE lightning_storm_e, #endif @@ -81,6 +84,10 @@ uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 inline void party_tactical_strobe_mode_iter(uint8_t st); #endif +#ifdef USE_POLICE_COLOR_STROBE_MODE +inline void police_color_strobe_iter(); +#endif + #ifdef USE_LIGHTNING_MODE inline void lightning_storm_iter(); #endif diff --git a/spaghetti-monster/anduril/tint-ramping.c b/spaghetti-monster/anduril/tint-ramping.c index d270d9d..13f5d29 100644 --- a/spaghetti-monster/anduril/tint-ramping.c +++ b/spaghetti-monster/anduril/tint-ramping.c @@ -1,24 +1,10 @@ /* * tint-ramping.c: Tint ramping functions for Anduril. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2017-2023 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later */ -#ifndef TINT_RAMPING_C -#define TINT_RAMPING_C +#pragma once #include "tint-ramping.h" @@ -100,6 +86,3 @@ uint8_t tint_ramping_state(Event event, uint16_t arg) { return EVENT_NOT_HANDLED; } - -#endif - diff --git a/spaghetti-monster/anduril/tint-ramping.h b/spaghetti-monster/anduril/tint-ramping.h index 1c5e22a..9b7f9a8 100644 --- a/spaghetti-monster/anduril/tint-ramping.h +++ b/spaghetti-monster/anduril/tint-ramping.h @@ -1,24 +1,10 @@ /* * tint-ramping.h: Tint ramping functions for Anduril. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2017-2023 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later */ -#ifndef TINT_RAMPING_H -#define TINT_RAMPING_H +#pragma once // 0: smooth tint ramp // 1: toggle tint only between two extremes @@ -35,5 +21,3 @@ uint8_t manual_memory_tint; // not actually a mode, more of a fallback under other modes uint8_t tint_ramping_state(Event event, uint16_t arg); - -#endif diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c index 30b8a67..fca1e83 100644 --- a/spaghetti-monster/fsm-main.c +++ b/spaghetti-monster/fsm-main.c @@ -39,6 +39,7 @@ ISR(TIMER1_COMPA_vect) { } #endif +// FIXME: hw_setup() shouldn't be here ... move it entirely to hwdef files #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) static inline void hw_setup() { // configure PWM channels diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h index 66d31ba..17ed66f 100644 --- a/spaghetti-monster/fsm-misc.h +++ b/spaghetti-monster/fsm-misc.h @@ -53,6 +53,11 @@ void indicator_led(uint8_t lvl); void button_led_set(uint8_t lvl); #endif +// if any type of aux LEDs exist, define a shorthand flag for it +#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) || defined(USE_BUTTON_LED) +#define HAS_AUX_LEDS +#endif + #ifdef USE_AUX_RGB_LEDS // value: 0b00BBGGRR // each pair of bits: 0=off, 1=low, 2=high diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c index 63692c8..5096dfd 100644 --- a/spaghetti-monster/fsm-ramping.c +++ b/spaghetti-monster/fsm-ramping.c @@ -2,46 +2,28 @@ * fsm-ramping.c: Ramping functions for SpaghettiMonster. * Handles 1- to 4-channel smooth ramping on a single LED. * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2017-2023 Selene ToyKeeper + * SPDX-License-Identifier: GPL-3.0-or-later */ -#ifndef FSM_RAMPING_C -#define FSM_RAMPING_C +#pragma once #ifdef USE_RAMPING -void set_level(uint8_t level) { - #ifdef USE_JUMP_START - // maybe "jump start" the engine, if it's prone to slow starts - // (pulse the output high for a moment to wake up the power regulator) - // (only do this when starting from off and going to a low level) - if ((! actual_level) - && level - && (level < jump_start_level)) { - set_level(jump_start_level); - delay_4ms(JUMP_START_TIME/4); - } - #endif // ifdef USE_JUMP_START +void set_channel_mode(uint8_t mode) { + uint8_t cur_level = actual_level; + // turn off old LEDs before changing channel + set_level(0); - actual_level = level; + // change the channel + channel_mode = mode; - #ifdef USE_SET_LEVEL_GRADUALLY - gradual_target = level; - #endif + // update the LEDs + set_level(cur_level); +} +#ifdef HAS_AUX_LEDS +inline void set_level_aux_leds(uint8_t level) { #ifdef USE_INDICATOR_LED_WHILE_RAMPING // use side-facing aux LEDs while main LEDs are on if (! go_to_standby) { @@ -52,9 +34,6 @@ void set_level(uint8_t level) { button_led_set((level > 0) + (level > DEFAULT_LEVEL)); #endif } - //if (level > MAX_1x7135) indicator_led(2); - //else if (level > 0) indicator_led(1); - //else if (! go_to_standby) indicator_led(0); #else // turn off front-facing aux LEDs while main LEDs are on #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) if (! go_to_standby) { @@ -70,33 +49,173 @@ void set_level(uint8_t level) { } #endif #endif +} +#endif // ifdef HAS_AUX_LEDS - #ifdef OVERRIDE_SET_LEVEL - set_level_override(level); - #else - #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC) - static uint8_t prev_level = 0; - uint8_t api_level = level; +void set_level(uint8_t level) { + #ifdef USE_JUMP_START + // maybe "jump start" the engine, if it's prone to slow starts + // (pulse the output high for a moment to wake up the power regulator) + // (only do this when starting from off and going to a low level) + // TODO: allow different jump start behavior per channel mode + if ((! actual_level) + && level + && (level < jump_start_level)) { + set_level(jump_start_level); + delay_4ms(JUMP_START_TIME/4); + } + #endif + + #ifdef HAS_AUX_LEDS + set_level_aux_leds(level); + #endif + + // call the relevant hardware-specific set_level_*() + SetLevelFuncPtr set_level_func = channel_modes[channel_mode]; + set_level_func(level); + + actual_level = level; + + #ifdef USE_SET_LEVEL_GRADUALLY + gradual_target = level; + #endif + + #ifdef USE_DYNAMIC_UNDERCLOCKING + auto_clock_speed(); + #endif +} + +///// Common set_level_*() functions shared by multiple lights ///// +// (unique lights should use their own, +// but these common versions cover most of the common hardware designs) + +#ifdef USE_SET_LEVEL_1CH +// single set of LEDs with 1 power channel +void set_level_1ch(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + } +} +#endif + +#ifdef USE_SET_LEVEL_2CH_STACKED +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_2ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + +#ifdef USE_SET_LEVEL_3CH_STACKED +// single set of LEDs with 3 stacked power channels, like DDFET+N+1 +void set_level_3ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + MED_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + MED_PWM_LVL = PWM_GET(med_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + +// TODO: upgrade some older lights to dynamic PWM +// TODO: 1ch w/ dynamic PWM +// TODO: 1ch w/ dynamic PWM and opamp enable pins? +// TODO: 2ch stacked w/ dynamic PWM +// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins? + +#ifdef USE_SET_LEVEL_2CH_BLEND +// warm + cool blend w/ middle sag correction +void set_level_2ch_blend(uint8_t level) { + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif + + BLEND_PWM_DATATYPE vpwm; + + if (level == 0) { + vpwm = 0; + } else { + level --; // PWM array index = level - 1 + vpwm = PWM_GET(blend_pwm_levels, level); + } + + // calculate actual PWM levels based on a single-channel ramp + // and a global tint value + uint16_t brightness = vpwm; + uint16_t warm_PWM, cool_PWM; + const uint16_t top = PWM_TOP; + + // auto-tint modes + uint8_t mytint = channel_mode_args[channel_mode]; + + PWM_DATATYPE2 base_PWM = brightness; + #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) + // middle tints sag, so correct for that effect + // by adding extra power which peaks at the middle tint + // (correction is only necessary when PWM is fast) + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(mytint) / 255); + } + // fade the triangle wave out when above 100% power, + // so it won't go over 200% + if (brightness > top) { + base_PWM -= 2 * ( + ((brightness - top) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(mytint) / 255 + ); + } + // guarantee no more than 200% power + if (base_PWM > (top << 1)) { base_PWM = top << 1; } #endif - //TCCR0A = PHASE; + cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; + // when running at > 100% power, spill extra over to other channel + if (cool_PWM > top) { + warm_PWM += (cool_PWM - top); + cool_PWM = top; + } else if (warm_PWM > top) { + cool_PWM += (warm_PWM - top); + warm_PWM = top; + } + + WARM_PWM_LVL = warm_PWM; + COOL_PWM_LVL = cool_PWM; +} +#endif // ifdef USE_TINT_RAMPING + +#ifdef USE_LEGACY_SET_LEVEL +// (this is mostly just here for reference, temporarily) +// single set of LEDs with 1 to 3 stacked power channels, +// like linear, FET+1, and FET+N+1 +// (default set_level_*() function for most lights) +void set_level_legacy(uint8_t level) { if (level == 0) { #if PWM_CHANNELS >= 1 - PWM1_LVL = 0; + PWM1_LVL = 0; #endif #if PWM_CHANNELS >= 2 - PWM2_LVL = 0; + PWM2_LVL = 0; #endif #if PWM_CHANNELS >= 3 - PWM3_LVL = 0; - #endif - #if PWM_CHANNELS >= 4 - PWM4_LVL = 0; - #endif - #ifdef USE_TINT_RAMPING - TINT1_LVL = 0; - TINT2_LVL = 0; + PWM3_LVL = 0; #endif #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF) PWM1_CNT = 0; @@ -121,7 +240,6 @@ void set_level(uint8_t level) { #endif } else { // enable the power channel, if relevant - #ifndef USE_TINT_RAMPING // update_tint handles this better #ifdef LED_ENABLE_PIN #ifdef LED_ON_DELAY uint8_t led_enable_port_save = LED_ENABLE_PORT; @@ -161,7 +279,6 @@ void set_level(uint8_t level) { delay_4ms(LED2_ON_DELAY/4); #endif #endif - #endif // ifndef USE_TINT_RAMPING // PWM array index = level - 1 level --; @@ -175,9 +292,6 @@ void set_level(uint8_t level) { #if PWM_CHANNELS >= 3 PWM3_LVL = PWM_GET(pwm3_levels, level); #endif - #if PWM_CHANNELS >= 4 - PWM4_LVL = PWM_GET(pwm4_levels, level); - #endif #ifdef USE_DYN_PWM uint16_t top = PWM_GET(pwm_tops, level); @@ -190,29 +304,15 @@ void set_level(uint8_t level) { // (but don't wait when turning on from zero, because // it'll reset the phase below anyway) // to be safe, allow at least 32 cycles to update TOP - while(prev_level && (PWM1_CNT > (top - 32))) {} + while(actual_level && (PWM1_CNT > (top - 32))) {} #endif // pulse frequency modulation, a.k.a. dynamic PWM PWM1_TOP = top; - - // repeat for other channels if necessary - #ifdef PMW2_TOP - #if defined(PWM2_CNT) && defined(PWM2_PHASE_SYNC) - while(prev_level && (PWM2_CNT > (top - 32))) {} - #endif - PWM2_TOP = top; - #endif - #ifdef PMW3_TOP - #if defined(PWM3_CNT) && defined(PWM3_PHASE_SYNC) - while(prev_level && (PWM3_CNT > (top - 32))) {} - #endif - PWM3_TOP = top; - #endif #endif // ifdef USE_DYN_PWM #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) // force reset phase when turning on from zero // (because otherwise the initial response is inconsistent) - if (! prev_level) { + if (! actual_level) { PWM1_CNT = 0; #if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON) PWM2_CNT = 0; @@ -223,114 +323,117 @@ void set_level(uint8_t level) { } #endif } - #ifdef USE_TINT_RAMPING - update_tint(); - #endif - - #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC) - prev_level = api_level; - #endif - #endif // ifdef OVERRIDE_SET_LEVEL #ifdef USE_DYNAMIC_UNDERCLOCKING auto_clock_speed(); #endif } +#endif + #ifdef USE_SET_LEVEL_GRADUALLY inline void set_level_gradually(uint8_t lvl) { gradual_target = lvl; } -#ifndef OVERRIDE_GRADUAL_TICK + // call this every frame or every few frames to change brightness very smoothly void gradual_tick() { - // go by only one ramp level at a time instead of directly to the target - uint8_t gt = gradual_target; - if (gt < actual_level) gt = actual_level - 1; - else if (gt > actual_level) gt = actual_level + 1; - - /* - #ifdef LED_ENABLE_PIN_LEVEL_MIN - // only enable during part of the ramp - if ((gt >= LED_ENABLE_PIN_LEVEL_MIN) - && (gt <= LED_ENABLE_PIN_LEVEL_MAX)) - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - else // disable during other parts of the ramp - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - #endif - */ + // call the relevant hardware-specific function + GradualTickFuncPtr gradual_tick_func = gradual_tick_modes[channel_mode]; + gradual_tick_func(); +} - gt --; // convert 1-based number to 0-based +// reduce repetition with macros +// common code at the beginning of every gradual tick handler +#define GRADUAL_TICK_SETUP() \ + uint8_t gt = gradual_target; \ + if (gt < actual_level) gt = actual_level - 1; \ + else if (gt > actual_level) gt = actual_level + 1; \ + gt --; \ PWM_DATATYPE target; - #if PWM_CHANNELS >= 1 - target = PWM_GET(pwm1_levels, gt); - #if PWM_CHANNELS > 1 - if ((gt < actual_level) // special case for FET-only turbo - && (PWM1_LVL == 0) // (bypass adjustment period for first step) - && (target == PWM_TOP)) PWM1_LVL = PWM_TOP; - else - #endif - if (PWM1_LVL < target) PWM1_LVL ++; - else if (PWM1_LVL > target) PWM1_LVL --; - #endif - #if PWM_CHANNELS >= 2 - target = PWM_GET(pwm2_levels, gt); - #if PWM_CHANNELS > 2 - if ((gt < actual_level) // special case for FET-only turbo - && (PWM2_LVL == 0) // (bypass adjustment period for first step) - && (target == PWM_TOP)) PWM2_LVL = PWM_TOP; - else - #endif - if (PWM2_LVL < target) PWM2_LVL ++; - else if (PWM2_LVL > target) PWM2_LVL --; - #endif - #if PWM_CHANNELS >= 3 - 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 = PWM_GET(pwm4_levels, gt); - if (PWM4_LVL < target) PWM4_LVL ++; - else if (PWM4_LVL > target) PWM4_LVL --; - #endif +// tick the top layer of the stack +#define GRADUAL_ADJUST_1CH(TABLE,PWM) \ + target = PWM_GET(TABLE, gt); \ + if (PWM < target) PWM ++; \ + else if (PWM > target) PWM --; + +// tick a base level of the stack +// (with support for special DD FET behavior +// like "low=0, high=255" --> "low=255, high=254") +#define GRADUAL_ADJUST(TABLE,PWM,TOP) \ + target = PWM_GET(TABLE, gt); \ + if ((gt < actual_level) \ + && (PWM == 0) \ + && (target == TOP)) PWM = TOP; \ + else \ + if (PWM < target) PWM ++; \ + else if (PWM > target) PWM --; + +// do this when output exactly matches a ramp level +#define GRADUAL_IS_ACTUAL() \ + uint8_t orig = gradual_target; \ + set_level(gt + 1); \ + gradual_target = orig; + +#ifdef USE_GRADUAL_TICK_1CH +void gradual_tick_1ch() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL); // did we go far enough to hit the next defined ramp level? // if so, update the main ramp level tracking var - if ((PWM1_LVL == PWM_GET(pwm1_levels, gt)) - #if PWM_CHANNELS >= 2 - && (PWM2_LVL == PWM_GET(pwm2_levels, gt)) - #endif - #if PWM_CHANNELS >= 3 - && (PWM3_LVL == PWM_GET(pwm3_levels, gt)) - #endif - #if PWM_CHANNELS >= 4 - && (PWM4_LVL == PWM_GET(pwm4_levels, gt)) - #endif + if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + +#ifdef USE_GRADUAL_TICK_2CH_STACKED +void gradual_tick_2ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) + ) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + +#ifdef USE_GRADUAL_TICK_3CH_STACKED +void gradual_tick_3ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) ) { - //actual_level = gt + 1; - uint8_t orig = gradual_target; - set_level(gt + 1); - gradual_target = orig; + GRADUAL_IS_ACTUAL(); } - // is handled in set_level() - //#ifdef USE_TINT_RAMPING - //update_tint(); - //#endif - // is handled in set_level() - //#ifdef USE_DYNAMIC_UNDERCLOCKING - //auto_clock_speed(); - //#endif } -#endif // ifdef OVERRIDE_GRADUAL_TICK +#endif #endif // ifdef USE_SET_LEVEL_GRADUALLY #if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) -void update_tint() { +void set_level_2ch_blend() { #ifndef TINT_RAMPING_CORRECTION #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint #endif @@ -340,7 +443,7 @@ void update_tint() { //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); uint16_t brightness = PWM1_LVL; uint16_t warm_PWM, cool_PWM; - #ifdef USE_DYN_PWM + #ifdef USE_STACKED_DYN_PWM uint16_t top = PWM1_TOP; //uint16_t top = PWM_GET(pwm_tops, actual_level-1); #else @@ -414,5 +517,12 @@ void update_tint() { #endif // ifdef USE_TINT_RAMPING -#endif // ifdef USE_RAMPING +// define the channel mode lists +// TODO: move to progmem +SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES] = { SET_LEVEL_MODES }; +#ifdef USE_SET_LEVEL_GRADUALLY +GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES] = { GRADUAL_TICK_MODES }; #endif + + +#endif // ifdef USE_RAMPING diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h index de090c2..3021ff2 100644 --- a/spaghetti-monster/fsm-ramping.h +++ b/spaghetti-monster/fsm-ramping.h @@ -18,23 +18,80 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef FSM_RAMPING_H -#define FSM_RAMPING_H +#pragma once #ifdef USE_RAMPING // actual_level: last ramp level set by set_level() uint8_t actual_level = 0; -#ifdef USE_TINT_RAMPING -#ifdef TINT_RAMP_TOGGLE_ONLY -uint8_t tint = 0; -#else -uint8_t tint = 128; +// TODO: size-optimize the case with only 1 channel mode +// (the arrays and stuff shouldn't be needed) + +// current multi-channel mode +uint8_t channel_mode = DEFAULT_CHANNEL_MODE; +#ifdef USE_MANUAL_MEMORY +// reset w/ manual memory +uint8_t manual_memory_channel_mode = DEFAULT_CHANNEL_MODE; #endif -#define USE_TRIANGLE_WAVE + +#if NUM_CHANNEL_MODES > 1 +#define USE_CHANNEL_MODES #endif +// one function per channel mode +typedef void SetLevelFunc(uint8_t level); +typedef SetLevelFunc * SetLevelFuncPtr; +// TODO: move to progmem +SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES]; + +#ifdef USE_SET_LEVEL_GRADUALLY +// the gradual tick mechanism may be different per channel +typedef void GradualTickFunc(); +typedef GradualTickFunc * GradualTickFuncPtr; +// TODO: move to progmem +GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES]; +#endif + +#ifdef USE_CUSTOM_CHANNEL_3H_MODES +// different 3H behavior per channel? +// TODO: move to progmem +StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; +#endif + +//#ifdef USE_CHANNEL_MODE_TOGGLES +#if NUM_CHANNEL_MODES > 1 +// user can take unwanted modes out of the rotation +// TODO: save to eeprom +// array +//uint8_t channel_modes_enabled[NUM_CHANNEL_MODES] = { CHANNEL_MODES_ENABLED }; +// bitmask +uint8_t channel_modes_enabled = CHANNEL_MODES_ENABLED; +#define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1) +#define channel_mode_enable(n) channel_modes_enabled |= (1 << n) +#define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff) +#endif + +#ifdef USE_CHANNEL_MODE_ARGS +// one byte of extra data per channel mode, like for tint value +uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; +#endif + +// TODO: remove this after implementing channel modes +//#ifdef USE_TINT_RAMPING +//#ifdef TINT_RAMP_TOGGLE_ONLY +//uint8_t tint = 0; +//#else +//uint8_t tint = 128; +//#endif +//#define USE_TRIANGLE_WAVE +//#endif + +void set_channel_mode(uint8_t mode); + +void set_level(uint8_t level); +//void set_level_smooth(uint8_t level); + #ifdef USE_SET_LEVEL_GRADUALLY // adjust brightness very smoothly uint8_t gradual_target; @@ -42,21 +99,24 @@ inline void set_level_gradually(uint8_t lvl); void gradual_tick(); #endif -#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) -void update_tint(); -#endif - // auto-detect the data type for PWM tables -#ifndef PWM_BITS - #define PWM_BITS 8 - #define PWM_TOP 255 +// FIXME: PWM bits and data type should be per PWM table +#ifndef PWM1_BITS + #define PWM1_BITS 8 + #define PWM1_TOP 255 + #define STACKED_PWM_TOP 255 #endif #if PWM_BITS <= 8 + #define STACKED_PWM_DATATYPE uint8_t #define PWM_DATATYPE uint8_t #define PWM_DATATYPE2 uint16_t #define PWM_TOP 255 + #define STACKED_PWM_TOP 255 + #ifndef PWM_GET #define PWM_GET(x,y) pgm_read_byte(x+y) + #endif #else + #define STACKED_PWM_DATATYPE uint16_t #define PWM_DATATYPE uint16_t #ifndef PWM_DATATYPE2 #define PWM_DATATYPE2 uint32_t @@ -64,32 +124,55 @@ void update_tint(); #ifndef PWM_TOP #define PWM_TOP 1023 // 10 bits by default #endif + #ifndef STACKED_PWM_TOP + #define STACKED_PWM_TOP 1023 + #endif // 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 + #ifndef PWM_GET #define PWM_GET(x,y) pgm_read_word(x+y) + #endif #endif +#define PWM_GET8(x,y) pgm_read_byte(x+y) +#define PWM_GET16(x,y) pgm_read_word(x+y) // use UI-defined ramp tables if they exist #ifdef PWM1_LEVELS -PROGMEM const PWM_DATATYPE pwm1_levels[] = { PWM1_LEVELS }; +PROGMEM const PWM1_DATATYPE pwm1_levels[] = { PWM1_LEVELS }; #endif #ifdef PWM2_LEVELS -PROGMEM const PWM_DATATYPE pwm2_levels[] = { PWM2_LEVELS }; +PROGMEM const PWM2_DATATYPE pwm2_levels[] = { PWM2_LEVELS }; #endif #ifdef PWM3_LEVELS -PROGMEM const PWM_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; +PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; +#endif + +// convenience defs for 1 LED with stacked channels +#ifdef LOW_PWM_LEVELS +PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS }; +#endif +#ifdef MED_PWM_LEVELS +PROGMEM const PWM_DATATYPE med_pwm_levels[] = { MED_PWM_LEVELS }; +#endif +#ifdef HIGH_PWM_LEVELS +PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS }; #endif -#ifdef PWM4_LEVELS -PROGMEM const PWM_DATATYPE pwm4_levels[] = { PWM4_LEVELS }; + +// 2 channel CCT blending ramp +#ifdef BLEND_PWM_LEVELS +PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS }; #endif + // pulse frequency modulation, a.k.a. dynamic PWM // (different ceiling / frequency at each ramp level) +// FIXME: dynamic PWM should be a per-channel option, not global #ifdef USE_DYN_PWM PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS }; #endif +// FIXME: jump start should be per channel / channel mode #ifdef USE_JUMP_START #ifndef JUMP_START_TIME #define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12 @@ -100,78 +183,11 @@ PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS }; uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL; #endif -// default / example ramps -#ifndef PWM1_LEVELS -#if PWM_CHANNELS == 1 - #if RAMP_LENGTH == 50 - // ../../bin/level_calc.py 1 50 7135 3 0.25 980 - 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 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 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 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 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 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 - #endif -#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 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 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 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 - #define QUARTERSPEED_LEVEL 5 - #endif -#elif PWM_CHANNELS == 4 - 4-channel PWM not really supported yet, sorry. -#endif -#endif - // RAMP_SIZE / MAX_LVL -#define RAMP_SIZE (sizeof(pwm1_levels)/sizeof(PWM_DATATYPE)) +// cfg-*.h should define RAMP_SIZE +//#define RAMP_SIZE (sizeof(stacked_pwm1_levels)/sizeof(STACKED_PWM_DATATYPE)) #define MAX_LEVEL RAMP_SIZE -void set_level(uint8_t level); -//void set_level_smooth(uint8_t level); #endif // ifdef USE_RAMPING -#endif + diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h index 9964bc1..2c51d0a 100644 --- a/spaghetti-monster/fsm-states.h +++ b/spaghetti-monster/fsm-states.h @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ - + #ifndef FSM_STATES_H #define FSM_STATES_H |
