diff options
Diffstat (limited to 'hw')
133 files changed, 11980 insertions, 0 deletions
diff --git a/hw/BRANDS b/hw/BRANDS new file mode 100644 index 0000000..059f311 --- /dev/null +++ b/hw/BRANDS @@ -0,0 +1,11 @@ +Vendor / Model IDs for version check function + +Undefined 0000 +Emisar 0100 - 0199 +Noctigon 0200 - 0199 +Lumintop 0300 - 0399 +Fireflies 0400 - 0499 +Mateminco 0500 - 0599 +Sofirn 0600 - 0699 +Wurkkos 0700 - 0799 +gChart 1600 - 1699 diff --git a/hw/fireflies/e01/cfg.h b/hw/fireflies/e01/cfg.h new file mode 100644 index 0000000..07bda87 --- /dev/null +++ b/hw/fireflies/e01/cfg.h @@ -0,0 +1,57 @@ +// Fireflies E01 SST-40 thrower config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0441" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// disable indicator LED; it's hardwired +#ifdef USE_INDICATOR_LED +#undef USE_INDICATOR_LED +#endif + + +#define RAMP_SIZE 150 + +// driver is a FET + 3x7135, ~413 lm at highest regulated level +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 +#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,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 +#define MAX_1x7135 93 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 7 + +// ceiling is level 130/150 (50% power) +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 + +// 20, 38, 56, 75, [93], 111, 130 (93 is highest regulated) +// (9 / 45 / 116 / 248 / 467 / 742 / 1280 + 2140 lm) +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~50% power +// 20 56 [93] 130 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 4 + +// thermal regulation parameters +#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to +#define THERM_FASTER_LEVEL 130 + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +//#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/pl47-219/cfg.h b/hw/fireflies/pl47-219/cfg.h new file mode 100644 index 0000000..11bd5b1 --- /dev/null +++ b/hw/fireflies/pl47-219/cfg.h @@ -0,0 +1,15 @@ +// Fireflies PL47-219B config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as PL47 but with FET modes limited to 67% power +// to avoid destroying the LEDs +#include "cfg-ff-pl47.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0422" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#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,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,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,15,16,17,19,20,22,23,25,27,28,30,31,33,35,37,39,41,43,45,47,50,52,55,57,60,63,65,68,71,74,77,80,83,87,90,93,97,101,105,108,112,116,121,125,129,134,139,143,148,153,159,164,169 diff --git a/hw/fireflies/pl47/cfg.h b/hw/fireflies/pl47/cfg.h new file mode 100644 index 0000000..6436477 --- /dev/null +++ b/hw/fireflies/pl47/cfg.h @@ -0,0 +1,79 @@ +// Fireflies PL47 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0421" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// 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 +//#define USE_INDICATOR_LED_WHILE_RAMPING + +// off mode: high (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +// the "low" mode doesn't work on this light's aux LEDs +// (but it does work on the switch LEDs) +// Fireflies wants to skip aux LED mode 1 (low) +#define INDICATOR_LED_SKIP_LOW + + +#define RAMP_SIZE 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 + +// ceiling is level 120/150 +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 + +// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 120 +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~25% power / ~1000 lm +// 10 34 59 [83] 108 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 108 +#define SIMPLE_UI_STEPS 5 + +// ~25 lm to ~300 lm +//#define SIMPLE_UI_FLOOR 30 +//#define SIMPLE_UI_CEIL MAX_1x7135 +// ~50 lm to ~500 lm +//#define SIMPLE_UI_FLOOR 40 +//#define SIMPLE_UI_CEIL 90 + +// 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 +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 135 // throttle back faster when high + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/pl47/hwdef.h b/hw/fireflies/pl47/hwdef.h new file mode 100644 index 0000000..7ce7070 --- /dev/null +++ b/hw/fireflies/pl47/hwdef.h @@ -0,0 +1,26 @@ +// Fireflies PL47 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as a D4S, basically, except ... + +// ... the PL47 has aux LEDs on pin 7 +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#endif + +// ... and switch LEDs on pin 3 +#ifndef AUXLED2_PIN +#define AUXLED2_PIN PB4 // pin 3 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#include "hwdef-emisar-d4s.h" +#undef FSM_EMISAR_D4S_DRIVER +#undef FSM_EMISAR_D4_DRIVER + diff --git a/hw/fireflies/pl47g2/cfg.h b/hw/fireflies/pl47g2/cfg.h new file mode 100644 index 0000000..830d7e8 --- /dev/null +++ b/hw/fireflies/pl47g2/cfg.h @@ -0,0 +1,67 @@ +// Fireflies PL47 G2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0423" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// 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 + +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + + +#define RAMP_SIZE 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 + +// ceiling is level 120/150 +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 + +// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 120 +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~25% power / ~1000 lm +// 10 34 59 [83] 108 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 108 +#define SIMPLE_UI_STEPS 5 + +// 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 +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 135 // throttle back faster when high + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/rot66-219/cfg.h b/hw/fireflies/rot66-219/cfg.h new file mode 100644 index 0000000..4dad4e2 --- /dev/null +++ b/hw/fireflies/rot66-219/cfg.h @@ -0,0 +1,26 @@ +// Fireflies ROT66-219 (7x7135) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as regular ROT66, but calibrated for Nichia 219B with 7x7135 chips +#include "cfg-ff-rot66.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0412" + +// driver is a FET+N+1, +// where N=6 for the 219b version +// calculated to get transition points at level 65 and 115 +// first 65 steps same as regular ROT66 +// remaining steps from this command: +// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 14 1 1058.5 FET 1 10 4500 +#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,15,17,18,20,21,23,25,27,29,31,33,36,38,41,43,46,49,52,55,58,62,65,69,73,77,81,86,90,95,100,105,111,116,122,128,135,141,148,155,163,170,178,187,195,204,214,224,234,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,0 +#undef PWM3_LEVELS +#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,4,8,12,16,21,25,30,35,40,45,50,56,62,68,74,81,87,94,101,109,116,124,133,141,150,159,168,178,188,198,209,220,231,243,255 +#undef MAX_Nx7135 +#define MAX_Nx7135 115 + +// regulate down faster when the FET is active, slower otherwise +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 // throttle back faster when high diff --git a/hw/fireflies/rot66/cfg.h b/hw/fireflies/rot66/cfg.h new file mode 100644 index 0000000..752ddbb --- /dev/null +++ b/hw/fireflies/rot66/cfg.h @@ -0,0 +1,65 @@ +// Fireflies ROT66 (14x7135) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0411" +#include "hwdef-ff-rot66.h" + +// 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 (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +#define RAMP_SIZE 150 + +// driver is a FET+N+1, +// where N=6 for the 219b version, +// or N=13 for the XP-L HI version (this version) +// calculated to get transition points at level 65 and 125 +// first 65 steps copied from FW3A +#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 +// remaining steps from this command: +// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 15 1 1644.16 FET 1 10 4500 +#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,16,17,18,19,20,21,22,23,25,26,27,29,30,32,34,35,37,39,41,43,46,48,50,53,55,58,61,64,67,70,73,77,81,84,88,92,97,101,106,110,115,120,126,131,137,143,149,156,162,169,177,184,192,200,208,217,226,235,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,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,0,0,0,0,0,7,13,21,28,35,43,51,60,68,77,87,96,106,117,127,138,150,161,173,186,199,212,226,240,255 +#define MAX_1x7135 65 +#define MAX_Nx7135 125 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL MAX_Nx7135 +// safe limit max regulated power +// 20 46 72 98 [125] +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL MAX_Nx7135 +#define SIMPLE_UI_STEPS 5 + +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_CEIL + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_MOMENTARY_MODE +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG +//#undef USE_TACTICAL_STROBE_MODE +//#ifdef USE_LOWPASS_WHILE_ASLEEP +//#undef USE_LOWPASS_WHILE_ASLEEP +//#endif + diff --git a/hw/fireflies/rot66/hwdef.h b/hw/fireflies/rot66/hwdef.h new file mode 100644 index 0000000..3fea9f1 --- /dev/null +++ b/hw/fireflies/rot66/hwdef.h @@ -0,0 +1,24 @@ +// Fireflies ROT66 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as a FW3A, basically, except ... + +// ... except the ROT66 has a lighted button +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#include "hwdef-fw3a.h" + +// ... and no optic nerve +#ifdef VISION_PIN +#undef VISION_PIN +#endif + diff --git a/hw/fireflies/rot66g2/cfg.h b/hw/fireflies/rot66g2/cfg.h new file mode 100644 index 0000000..04aac53 --- /dev/null +++ b/hw/fireflies/rot66g2/cfg.h @@ -0,0 +1,59 @@ +// Fireflies ROT66 G2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-ff-rot66.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0413" + +// if the "low" mode was disabled, turn it back on +#ifdef INDICATOR_LED_SKIP_LOW +#undef INDICATOR_LED_SKIP_LOW +#endif + +// 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) + +// the button is *not* visible while main LEDs are on +#undef USE_INDICATOR_LED_WHILE_RAMPING + +// 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 + +// safe limit max regulated power +// 10 37 65 92 [120] +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL MAX_Nx7135 +#define SIMPLE_UI_STEPS 5 + diff --git a/hw/gchart/fet1-t1616/cfg.h b/hw/gchart/fet1-t1616/cfg.h new file mode 100644 index 0000000..4082fca --- /dev/null +++ b/hw/gchart/fet1-t1616/cfg.h @@ -0,0 +1,63 @@ +// gChart's custom FET+1 driver config options for Anduril +// Copyright (C) 2020-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1618" // Golden Ratio... because I can +#include "hwdef-gchart-fet1-t1616.h" +// ATTINY: 1616 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +#define RAMP_SIZE 150 + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,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,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, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#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 130 +#define RAMP_DISCRETE_STEPS 7 + +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +//#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 40 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/gchart/fet1-t1616/hwdef.h b/hw/gchart/fet1-t1616/hwdef.h new file mode 100644 index 0000000..2d2b7a6 --- /dev/null +++ b/hw/gchart/fet1-t1616/hwdef.h @@ -0,0 +1,134 @@ +// gChart's custom FET+1 driver layout +// Copyright (C) 2020-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * PB0 - PWM for FET (TCA - WO0) + * PB1 - PWM for 7135 (TCA - WO1) + * PB2 - Switch pin, internal pullup + * PB3 - Aux LED with 4700 Ohm series resistor + * Read voltage from VCC pin, has diode with ~0.4v drop +*/ + +#define ATTINY 1616 +#include <avr/io.h> + +// nearly all t1616-based FET+1 drivers work pretty much the same +// (this one has single-color aux like the TS10) +#define HWDEF_C_FILE hwdef-wurkkos-ts10.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. FET+7135 stacked +// * 1. aux LEDs +#define NUM_CHANNEL_MODES 2 +enum CHANNEL_MODES { + CM_MAIN = 0, + CM_AUX +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +// not necessary when double-buffered "BUF" registers are used +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// 1x7135 channel +#define CH1_PIN PB1 +#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1 + +// DD FET channel +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// e-switch +#define SWITCH_PIN PIN2_bp +#define SWITCH_PORT VPORTB.IN +#define SWITCH_ISC_REG PORTB.PIN2CTRL +#define SWITCH_VECT PORTB_PORT_vect +#define SWITCH_INTFLG VPORTB.INTFLAGS + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 8 // 4 = add 0.20V +#endif + +// lighted button +#define AUXLED_PIN PIN3_bp +#define AUXLED_PORT PORTB + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = 0b00000010; + // Outputs + VPORTB.DIR = PIN0_bm // DD FET + | PIN1_bm // 7135 + | PIN3_bm; // Aux LED + //VPORTC.DIR = 0b00000000; + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm; + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // switch + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // Aux LED + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + PORTB.PIN5CTRL = PORT_PULLUPEN_bm; + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-2ch-fet/cfg.h b/hw/hank/emisar-2ch-fet/cfg.h new file mode 100644 index 0000000..4686483 --- /dev/null +++ b/hw/hank/emisar-2ch-fet/cfg.h @@ -0,0 +1,113 @@ +// Emisar 2-channel generic (plus FET) config options for Anduril (based on Noctigon K9.3) +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0136" +#include "hwdef-emisar-2ch-fet.h" +#include "hank-cfg.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 +// it also has an independent LED in the button +#define USE_BUTTON_LED +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently +// enabling this option breaks the button LED on D4v2.5 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +// enable max brightness out of the box +#define DEFAULT_CHANNEL_MODE CM_BOTH + +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + + +// channel 1 / main LEDs (w/ DD FET) +// output: unknown, 1750 lm? +// FET: unknown, 3000 lm? +// channel 2 +// output: unknown, 1750 lm? +// combined: 4000 to 5000 lm? +#define RAMP_SIZE 150 + +// linear+FET ramp: maxreg at 140/150 +// level_calc.py 5.01 2 150 7135 1 0.1 1924 FET 1 10 2600 --pwm dyn:68:4096:255:3 --clock 8:16:0 +// linear segment +#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,21,22,23,25,26,27,28,30,31,33,34,36,37,39,41,43,44,46,48,50,53,55,57,60,62,65,67,70,73,76,79,82,85,88,92,95,99,103,106,110,114,119,123,127,132,137,142,147,152,157,162,168,174,179,185,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255 +// DD FET segment +#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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,93,118,144,171,198,226,255 +// PWM TOPS values +#define PWM3_LEVELS 4096,3831,3570,3314,3063,2817,2577,2342,2115,1894,1682,1477,1281,2237,2005,1784,1576,1380,1196,1111,1026,1442,1348,1215,1091,976,871,774,1031,942,860,784,714,650,591,538,652,602,555,513,473,437,403,372,343,398,370,345,322,299,278,311,292,273,255,278,261,245,263,247,232,246,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,255 + +// linear-only ramp +// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0 +#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255 +// PWM_TOPS values for linear-only ramp +#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 DEFAULT_LEVEL 75 +#define MAX_1x7135 140 +#define MAX_Nx7135 MAX_1x7135 +#define HALFSPEED_LEVEL 16 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) +#define RAMP_SMOOTH_CEIL 140 +// 10, 31, 53, [75], 96, 118, [140] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit highest regulated power (no FET or turbo) +// 10, 42, [75], 107, 140 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1500 lm +#define THERM_FASTER_LEVEL 130 +#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 140,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 +// TODO: change speed per channel mode +// (the FET is really fast, but the regulator is not) +//#undef PARTY_STROBE_ONTIME + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 40 +#define BLINK_BRIGHTNESS 45 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-2ch-fet/hwdef.c b/hw/hank/emisar-2ch-fet/hwdef.c new file mode 100644 index 0000000..caf579d --- /dev/null +++ b/hw/hank/emisar-2ch-fet/hwdef.c @@ -0,0 +1,206 @@ +// Emisar generic 2-channel + DD FET w/ tint ramping +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + + +void set_level_zero(); + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +void set_level_auto(uint8_t level); + +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +bool gradual_tick_auto(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + .gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + .gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + .gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + .gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_auto, + .gradual_tick = gradual_tick_auto, + .has_args = 1 + }, + RGB_AUX_CHANNELS +}; + + +// set new values for both channels, +// handling any possible combination +// and any before/after state +void set_pwms(uint8_t ch1_pwm, uint8_t ch2_pwm, uint8_t ch3_pwm, uint16_t top) { + bool was_on = (CH1_PWM>0) | (CH2_PWM>0) | (CH3_PWM>0); + bool now_on = (ch1_pwm>0) | (ch2_pwm>0) | (ch3_pwm>0); + + if (! now_on) { + CH1_PWM = 0; // linear + CH2_PWM = 0; // linear + CH3_PWM = 0; // DD FET + PWM_TOP = PWM_TOP_INIT; + PWM_CNT = 0; + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp + return; + } + + if (ch1_pwm) + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + else + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + + if (ch2_pwm) + CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp + else + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + CH3_PWM = ch3_pwm; + + // manual phase sync when changing level while already on + if (was_on && now_on) while(PWM_CNT > (top - 32)) {} + + PWM_TOP = top; + + // reset phase when turning on or off + //if ((! was_on) | (! now_on)) PWM_CNT = 0; + if (! was_on) PWM_CNT = 0; +} + +void set_level_zero() { + return set_pwms(0, 0, 0, PWM_TOP_INIT); +} + +void set_level_ch1(uint8_t level) { + uint8_t pwm1 = PWM_GET8 (pwm1_levels, level); + uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); + uint16_t top = PWM_GET16(pwm3_levels, level); + set_pwms(pwm1, 0, pwm3, top); +} + +void set_level_ch2(uint8_t level) { + uint8_t pwm2 = PWM_GET8 (pwm4_levels, level); + uint16_t top = PWM_GET16(pwm5_levels, level); + set_pwms(0, pwm2, 0, top); +} + +void set_level_both(uint8_t level) { + uint8_t pwm1 = PWM_GET8 (pwm1_levels, level); + uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); + uint16_t top = PWM_GET16(pwm3_levels, level); + set_pwms(pwm1, pwm1, pwm3, top); +} + +void set_level_blend(uint8_t level) { + uint16_t pwm1, pwm2; + uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); // DD FET + //uint16_t brightness = PWM_GET8 (pwm1_levels, level) << 1; + uint16_t brightness = PWM_GET8 (pwm1_levels, level) + pwm3; + uint16_t top = PWM_GET16(pwm3_levels, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend); + + set_pwms(pwm1, pwm2, pwm3, top); +} + +void set_level_auto(uint8_t level) { + uint16_t pwm1, pwm2; + uint8_t brightness = PWM_GET8 (pwm4_levels, level); + uint16_t top = PWM_GET16(pwm5_levels, level); + uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + + calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend); + + set_pwms(pwm1, pwm2, 0, top); +} + + +///// bump each channel toward a target value ///// +bool gradual_adjust(uint8_t ch1_pwm, uint8_t ch2_pwm, uint8_t ch3_pwm) { + GRADUAL_ADJUST_STACKED(ch1_pwm, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_STACKED(ch2_pwm, CH2_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (ch3_pwm, CH3_PWM); + + // check for completion + if ((ch1_pwm == CH1_PWM) + && (ch2_pwm == CH2_PWM) + && (ch3_pwm == CH3_PWM)) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_ch1(uint8_t gt) { + uint8_t pwm1 = PWM_GET8(pwm1_levels, gt); + uint8_t pwm3 = PWM_GET8(pwm2_levels, gt); + return gradual_adjust(pwm1, 0, pwm3); +} + +bool gradual_tick_ch2(uint8_t gt) { + uint8_t pwm2 = PWM_GET8(pwm4_levels, gt); + return gradual_adjust(0, pwm2, 0); +} + +bool gradual_tick_both(uint8_t gt) { + uint8_t pwm1 = PWM_GET8(pwm1_levels, gt); + uint8_t pwm3 = PWM_GET8(pwm2_levels, gt); + return gradual_adjust(pwm1, pwm1, pwm3); +} + +bool gradual_tick_blend(uint8_t level) { + uint16_t pwm1, pwm2; + uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); // DD FET + //uint16_t brightness = PWM_GET8 (pwm1_levels, level) << 1; + uint16_t brightness = PWM_GET8 (pwm1_levels, level) + pwm3; + uint16_t top = PWM_GET16(pwm3_levels, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend); + + return gradual_adjust(pwm1, pwm2, pwm3); +} + +bool gradual_tick_auto(uint8_t level) { + uint16_t pwm1, pwm2; + uint8_t brightness = PWM_GET8 (pwm4_levels, level); + uint16_t top = PWM_GET16(pwm5_levels, level); + uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + + calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend); + + return gradual_adjust(pwm1, pwm2, 0); +} + + diff --git a/hw/hank/emisar-2ch-fet/hwdef.h b/hw/hank/emisar-2ch-fet/hwdef.h new file mode 100644 index 0000000..d756a0d --- /dev/null +++ b/hw/hank/emisar-2ch-fet/hwdef.h @@ -0,0 +1,209 @@ +// Emisar 2-channel generic w/ tint ramping + DD FET +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Pin / Name / Function + * 1 PA6 ch2 LED PWM (linear) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 button LED + * 6 PA1 Opamp 2 enable (channel 2 LEDs) + * 7 PA0 Opamp 1 enable (channel 1 LEDs) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 ch1 LED PWM (FET) (PWM0A, 8-bit) + * 16 PB3 ch1 LED PWM (linear) (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 (none) + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + * + * Both sets of LEDs use one pin to turn the Opamp on/off, + * and one pin to control the Opamp power level. + * The first channel also has a direct-drive FET for turbo. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-2ch-fet.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. channel 1 only (linear + DD FET) +// * 1. channel 2 only (linear) +// * 2. both channels, tied together, max "200%" power + DD FET at top of ramp +// * 3. both channels, manual blend, max "100%" power + "200%" and DD FET at top of ramp +// * 4. both channels, auto blend, reversible (linear only) +#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES) +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, + RGB_AUX_ENUMS +}; + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000011111 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, 128=middle CCT, 0=warm-to-cool +#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND + + +#define PWM_CHANNELS 3 // old, remove this + +#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // linear part of linear+FET ramp +#define PWM2_DATATYPE uint8_t // DD FET part of linear+FET ramp +#define PWM3_DATATYPE uint16_t // linear+FET ramp tops +#define PWM4_DATATYPE uint8_t // linear-only ramp +#define PWM5_DATATYPE uint16_t // linear-only ramp tops + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// main LEDs, linear +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PA0 // pin 7, Opamp power +#define CH1_ENABLE_PORT PORTA // control port for PA0 + +// 2nd LEDs, linear +#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 +#define CH2_ENABLE_PIN PA1 // pin 6, Opamp power +#define CH2_ENABLE_PORT PORTA // control port for PA1 + +// main LEDs, DD FET +#define CH3_PIN PC0 // pin 15, DD FET PWM +#define CH3_PWM OCR0A // OCR0A is the output compare register for PC0 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PA7 // pin 20 +#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC +#define SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#endif + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + + +inline void hwdef_setup() { + // enable output ports + DDRC = (1 << CH3_PIN); + DDRB = (1 << CH1_PIN); + DDRA = (1 << CH2_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + | (1 << BUTTON_LED_PIN) + | (1 << CH1_ENABLE_PIN) + | (1 << CH2_ENABLE_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 + // Linear opamp PWM for both main and 2nd LEDs (10-bit) + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // FET PWM (8-bit; this channel can't do 10-bit) + // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (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]: 1,0: PWM OC0B in the normal direction (DS table 11-7) + TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8) + | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4) + //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7) + ; + TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9) + | (0<<WGM02) // phase-correct PWM (DS table 11-8) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-2ch/cfg.h b/hw/hank/emisar-2ch/cfg.h new file mode 100644 index 0000000..4c3a329 --- /dev/null +++ b/hw/hank/emisar-2ch/cfg.h @@ -0,0 +1,103 @@ +// Emisar 2-channel generic config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0135" +#include "hwdef-emisar-2ch.h" +#include "hank-cfg.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 +// it also has an independent LED in the button +#define USE_BUTTON_LED +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently +// enabling this option breaks the button LED on D4v2.5 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +// enable max brightness out of the box +#define DEFAULT_CHANNEL_MODE CM_BLEND + +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BLEND + +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + + +// channel 1 +// output: unknown, 2000 lm? +// FET: absent / unused +// channel 2 +// output: unknown, 2000 lm? +#define RAMP_SIZE 150 + +// abstract ramp (power is split between both sets of LEDs) +// 1-130: 0 to 100% power +// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511 +// 131 to 150: 101% to 200% power +// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022 +#define PWM1_LEVELS 1,1,1,2,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,15,16,18,19,21,22,24,26,28,29,31,33,35,37,40,42,44,46,48,50,52,54,56,58,59,61,62,63,64,65,66,66,66,66,65,64,62,60,58,54,50,46,41,35,28,20,21,22,24,25,26,27,29,30,32,33,35,37,38,40,42,44,46,48,50,53,55,57,60,63,65,68,71,74,77,80,83,87,90,94,98,102,106,110,114,118,123,128,132,137,142,148,153,159,164,170,176,183,189,196,202,209,216,224,231,239,247,255,263,272,281,290,299,309,318,328,339,349,360,371,382,394,406,418,430,443,456,469,483,497,511 +#define PWM_TOPS 16383,13650,10715,15388,11902,8195,12771,9834,12258,13423,11192,11947,12284,12363,12271,12064,11775,11428,11039,11469,10973,11132,10595,10601,10054,9971,9833,9655,9122,8923,8704,8473,8232,8196,7932,7668,7408,7152,6901,6656,6417,6186,5961,5647,5444,5163,4899,4653,4422,4206,3941,3697,3470,3210,2971,2707,2466,2245,1968,1717,1489,1251,1005,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 +// max "200% power" ramp and tops +//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022 +//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +#define HALFSPEED_LEVEL 10 +#define QUARTERSPEED_LEVEL 2 + +#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) +#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 + +// safe limit highest regulated power (no FET or turbo) +// 10, 40, [70], 100, 130 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1500 lm +#define THERM_FASTER_LEVEL 130 +#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 130,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS 40 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-2ch/hwdef.c b/hw/hank/emisar-2ch/hwdef.c new file mode 100644 index 0000000..b09b681 --- /dev/null +++ b/hw/hank/emisar-2ch/hwdef.c @@ -0,0 +1,193 @@ +// Emisar 2-channel generic w/ tint ramping +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + + +void set_level_zero(); + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +void set_level_auto(uint8_t level); + +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +bool gradual_tick_auto(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + .gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + .gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + .gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + .gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_auto, + .gradual_tick = gradual_tick_auto, + .has_args = 1 + }, + RGB_AUX_CHANNELS +}; + + +// set new values for both channels, +// handling any possible combination +// and any before/after state +void set_pwms(uint16_t ch1_pwm, uint16_t ch2_pwm, uint16_t top) { + bool was_on = (CH1_PWM>0) | (CH2_PWM>0); + bool now_on = (ch1_pwm>0) | (ch2_pwm>0); + + if (! now_on) { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_TOP = PWM_TOP_INIT; + PWM_CNT = 0; + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp + return; + } + + if (ch1_pwm) + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + else + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + + if (ch2_pwm) + CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp + else + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + + // manual phase sync when changing level while already on + if (was_on && now_on) while(PWM_CNT > (top - 32)) {} + + PWM_TOP = top; + + // reset phase when turning on or off + //if ((! was_on) | (! now_on)) PWM_CNT = 0; + if (! was_on) PWM_CNT = 0; +} + +void set_level_zero() { + return set_pwms(0, 0, PWM_TOP_INIT); +} + +void set_level_ch1(uint8_t level) { + uint16_t pwm = PWM_GET(pwm1_levels, level); + uint16_t top = PWM_GET(pwm_tops, level); + set_pwms(pwm, 0, top); +} + +void set_level_ch2(uint8_t level) { + uint16_t pwm = PWM_GET(pwm1_levels, level); + uint16_t top = PWM_GET(pwm_tops, level); + set_pwms(0, pwm, top); +} + +void set_level_both(uint8_t level) { + uint16_t pwm = PWM_GET(pwm1_levels, level); + uint16_t top = PWM_GET(pwm_tops, level); + set_pwms(pwm, pwm, top); +} + +void set_level_blend(uint8_t level) { + PWM_DATATYPE ch1_pwm, ch2_pwm; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + PWM_DATATYPE top = PWM_GET(pwm_tops, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend); + + set_pwms(ch1_pwm, ch2_pwm, top); +} + +void set_level_auto(uint8_t level) { + PWM_DATATYPE ch1_pwm, ch2_pwm; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + PWM_DATATYPE top = PWM_GET(pwm_tops, level); + uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + + calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend); + + set_pwms(ch1_pwm, ch2_pwm, top); +} + + +///// bump each channel toward a target value ///// +bool gradual_adjust(uint16_t ch1_pwm, uint16_t ch2_pwm) { + GRADUAL_ADJUST_SIMPLE(ch1_pwm, CH1_PWM); + GRADUAL_ADJUST_SIMPLE(ch2_pwm, CH2_PWM); + + // check for completion + if ((ch1_pwm == CH1_PWM) + && (ch2_pwm == CH2_PWM)) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_ch1(uint8_t gt) { + uint16_t pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, 0); +} + +bool gradual_tick_ch2(uint8_t gt) { + uint16_t pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, pwm); +} + +bool gradual_tick_both(uint8_t gt) { + uint16_t pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, pwm); +} + +bool gradual_tick_blend(uint8_t gt) { + PWM_DATATYPE ch1_pwm, ch2_pwm; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE top = PWM_GET(pwm_tops, gt); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend); + + return gradual_adjust(ch1_pwm, ch2_pwm); +} + +bool gradual_tick_auto(uint8_t gt) { + PWM_DATATYPE ch1_pwm, ch2_pwm; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE top = PWM_GET(pwm_tops, gt); + uint8_t blend = 255 * (uint16_t)gt / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + + calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend); + + return gradual_adjust(ch1_pwm, ch2_pwm); +} + + diff --git a/hw/hank/emisar-2ch/hwdef.h b/hw/hank/emisar-2ch/hwdef.h new file mode 100644 index 0000000..99e4945 --- /dev/null +++ b/hw/hank/emisar-2ch/hwdef.h @@ -0,0 +1,210 @@ +// Emisar 2-channel generic w/ tint ramping +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Pin / Name / Function + * 1 PA6 ch2 LED PWM (linear) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 button LED + * 6 PA1 Opamp 2 enable (channel 2 LEDs) + * 7 PA0 Opamp 1 enable (channel 1 LEDs) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 [unused: ch1 LED PWM (FET) (PWM0A, 8-bit)] + * 16 PB3 ch1 LED PWM (linear) (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 (none) + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + * + * Both sets of LEDs use one pin to turn the Opamp on/off, + * and one pin to control the Opamp power level. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-2ch.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. channel 1 only +// * 1. channel 2 only +// * 2. both channels, tied together, max "200%" power +// * 3. both channels, manual blend, max "100%" power +// * 4. both channels, auto blend, reversible +#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES) +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, + RGB_AUX_ENUMS +}; + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000011111 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, 128=middle CCT, 0=warm-to-cool +#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // regular ramp table +#define PWM2_DATATYPE uint16_t // max "200% power" ramp table +//#define PWM3_DATATYPE uint8_t // DD FET ramp table (8-bit only) + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 511 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// main LEDs, linear +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PA0 // pin 7, Opamp power +#define CH1_ENABLE_PORT PORTA // control port for PA0 + +// 2nd LEDs, linear +#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 +#define CH2_ENABLE_PIN PA1 // pin 6, Opamp power +#define CH2_ENABLE_PORT PORTA // control port for PA1 + +// main LEDs, DD FET +//#define CH3_PIN PC0 // pin 15, DD FET PWM +//#define CH3_PWM OCR0A // OCR0A is the output compare register for PC0 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PA7 // pin 20 +#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC +#define SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#endif + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + + +inline void hwdef_setup() { + // enable output ports + //DDRC = (1 << CH3_PIN); + DDRB = (1 << CH1_PIN); + DDRA = (1 << CH2_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + | (1 << BUTTON_LED_PIN) + | (1 << CH1_ENABLE_PIN) + | (1 << CH2_ENABLE_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 + // Linear opamp PWM for both main and 2nd LEDs (10-bit) + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // unused on this driver + #if 0 + // FET PWM (8-bit; this channel can't do 10-bit) + // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (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]: 1,0: PWM OC0B in the normal direction (DS table 11-7) + TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8) + | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4) + //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7) + ; + TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9) + | (0<<WGM02) // phase-correct PWM (DS table 11-8) + ; + CH3_PWM = 0; // ensure this channel is off, if it exists + #endif + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d1/cfg.h b/hw/hank/emisar-d1/cfg.h new file mode 100644 index 0000000..c81171c --- /dev/null +++ b/hw/hank/emisar-d1/cfg.h @@ -0,0 +1,20 @@ +// Emisar D1 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as Emisar D4, mostly +#include "hwdef-emisar-d4.h" +#include "cfg-emisar-d4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0121" + +// safe limit ~50% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +// 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/hw/hank/emisar-d18-219/cfg.h b/hw/hank/emisar-d18-219/cfg.h new file mode 100644 index 0000000..126e9f4 --- /dev/null +++ b/hw/hank/emisar-d18-219/cfg.h @@ -0,0 +1,18 @@ +// Emisar D18 (FET+13+1) reduced-FET config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-emisar-d18.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0142" + +// don't turn off first channels at turbo level +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,8,9,10,11,15,16,18,20,22,24,26,28,30,33,36,39,43,47,51,56,61,66,72,78,85,92,99,107,116,125,135,145,156,168,180,194,208,222,238,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,7,8,10,11,13,14,16,18,19,21,23,26,28,30,33,35,38,41,44,47,51,54,58,62,66,70,75,79,84,90,95,101,106,112,119,126,133,140,147,155,164,172,181,190,200,210,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 +// 85% FET power +#undef PWM3_LEVELS +#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,5,6,8,10,12,13,16,17,20,22,24,27,29,32,34,38,40,44,46,50,53,57,60,64,68,72,76,80,85,89,94,98,103,108,114,119,125,131,137,143,150,156,163,170,177,185,193,200,209,217 + diff --git a/hw/hank/emisar-d18/cfg.h b/hw/hank/emisar-d18/cfg.h new file mode 100644 index 0000000..3e5d3ae --- /dev/null +++ b/hw/hank/emisar-d18/cfg.h @@ -0,0 +1,61 @@ +// Emisar D18 (FET+13+1) config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0141" +#include "hwdef-emisar-d18.h" +#include "hank-cfg.h" + +#define RAMP_SIZE 150 + +// level_calc.py seventh 3 150 7135 1 1.4 117.99 7135 6 1 1706.86 FET 3 10 13000 +// (designed to make 1x hit at level 50, and Nx hit at level 100) +#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,8,9,10,11,15,16,18,20,22,24,26,28,30,33,36,39,43,47,51,56,61,66,72,78,85,92,99,107,116,125,135,145,156,168,180,194,208,222,238,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,7,8,10,11,13,14,16,18,19,21,23,26,28,30,33,35,38,41,44,47,51,54,58,62,66,70,75,79,84,90,95,101,106,112,119,126,133,140,147,155,164,172,181,190,200,210,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,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,5,7,9,11,13,15,18,20,23,25,28,31,34,37,40,44,47,51,54,58,62,66,70,75,79,84,89,94,99,104,110,115,121,127,134,140,147,154,161,168,176,183,191,200,208,217,226,235,245,255 + +#define DEFAULT_LEVEL 50 +#define MAX_1x7135 50 +#define MAX_Nx7135 100 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 6 + +// start at ~2000 lm after battery change, not ~150 lm (at Emisar's request) +//#define DEFAULT_LEVEL MAX_Nx7135 + +// go up to ~4000 lm +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 117 +// 20 36 52 68 84 [100] 117 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~20% power / max regulated +// 20 40 60 80 100 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL MAX_Nx7135 +#define SIMPLE_UI_STEPS 5 + +// only blink at max regulated level and ceiling +//#define BLINK_AT_RAMP_MIDDLE +//#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 +#define BLINK_AT_RAMP_CEIL +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// stop panicking at about ~40% power or ~5000 lm +#define THERM_FASTER_LEVEL 125 + +// enable extra features +#define USE_SMOOTH_STEPS + +// for compatibility with other models +#define USE_SOFT_FACTORY_RESET + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/hank/emisar-d18/hwdef.h b/hw/hank/emisar-d18/hwdef.h new file mode 100644 index 0000000..df08221 --- /dev/null +++ b/hw/hank/emisar-d18/hwdef.h @@ -0,0 +1,101 @@ +// Emisar D18 (FET+13+1) driver layout +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- aux LED? + * FET -|3 6|- 13x7135 + * GND -|4 5|- 1x7135 + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-fw3a.c + +// channel modes +// * 0. FET+N+1 stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 3 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // 7x7135 ramp +#define PWM3_DATATYPE uint8_t // DD FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// 1x7135 channel +#define CH1_PIN PB0 // pin 5, 1x7135 PWM +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 + +// 7x7135 channel +#define CH2_PIN PB1 // pin 6, 7x7135 PWM +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 + +// DD FET channel +#define CH3_PIN PB4 // pin 3, FET PWM +#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#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 + + +inline void hwdef_setup() { + + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN) + | (1 << CH3_PIN); + + // configure PWM channels + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d1s/cfg.h b/hw/hank/emisar-d1s/cfg.h new file mode 100644 index 0000000..8b70a5d --- /dev/null +++ b/hw/hank/emisar-d1s/cfg.h @@ -0,0 +1,23 @@ +// Emisar D1S config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as Emisar D4, mostly +#include "hwdef-emisar-d4.h" +#include "cfg-emisar-d4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0122" + +// safe limit ~50% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +// stop panicking at ~90% power or ~1200 lm (D1S has a good power-to-thermal-mass ratio) +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 144 // throttle back faster when high + +// too big, turn off extra features +//#undef USE_TACTICAL_MODE diff --git a/hw/hank/emisar-d1v2-7135-fet/cfg.h b/hw/hank/emisar-d1v2-7135-fet/cfg.h new file mode 100644 index 0000000..4022ba6 --- /dev/null +++ b/hw/hank/emisar-d1v2-7135-fet/cfg.h @@ -0,0 +1,32 @@ +// Emisar D1v2 (7135+FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// same as Emisar D4v2, mostly +// (was only made for a short time, not many people have one) +#include "cfg-emisar-d4v2.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0123" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +// (early short run had no button LEDs at all, later run uses linear+FET instead, +// so it's unlikely that anyone needs this, but it doesn't hurt anything) +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit ~50% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +// 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/hw/hank/emisar-d1v2-linear-fet/cfg.h b/hw/hank/emisar-d1v2-linear-fet/cfg.h new file mode 100644 index 0000000..28c57f8 --- /dev/null +++ b/hw/hank/emisar-d1v2-linear-fet/cfg.h @@ -0,0 +1,30 @@ +// Emisar D1v2 (linear+FET) config options for Anduril +// (2022 re-issue / update of old D1) +// Copyright (C) 2022-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// similar to a Noctigon KR4, sort of +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0124" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit: max regulated power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL MAX_1x7135 + +// 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/hw/hank/emisar-d1v2-nofet/cfg.h b/hw/hank/emisar-d1v2-nofet/cfg.h new file mode 100644 index 0000000..7f5bcc4 --- /dev/null +++ b/hw/hank/emisar-d1v2-nofet/cfg.h @@ -0,0 +1,24 @@ +// Emisar D1v2 (linear only, no DDFET) config options for Anduril +// (2022 re-issue / update of old D1) +// Copyright (C) 2022-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// similar to a Noctigon KR4, sort of +#include "cfg-noctigon-kr4-nofet.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0125" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit: same as regular ramp +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL RAMP_SMOOTH_CEIL + diff --git a/hw/hank/emisar-d4-219/cfg.h b/hw/hank/emisar-d4-219/cfg.h new file mode 100644 index 0000000..65649e3 --- /dev/null +++ b/hw/hank/emisar-d4-219/cfg.h @@ -0,0 +1,17 @@ +// Emisar D4-219C config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as D4 but with FET modes limited to 80% power +// to avoid destroying the LEDs +#include "cfg-emisar-d4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0112" + +// 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 diff --git a/hw/hank/emisar-d4/cfg.h b/hw/hank/emisar-d4/cfg.h new file mode 100644 index 0000000..4b3ae5a --- /dev/null +++ b/hw/hank/emisar-d4/cfg.h @@ -0,0 +1,48 @@ +// Emisar D4 config options for Anduril +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0111" +#include "hwdef-emisar-d4.h" +#include "hank-cfg.h" +// ATTINY: 85 + +#define RAMP_SIZE 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 +#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 MAX_1x7135 65 +#define DEFAULT_LEVEL 65 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 +#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 + +// safe limit ~20% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~30% power or ~1200 lm +#define THERM_FASTER_LEVEL 105 + +// don't blink during ramp +#undef BLINK_AT_RAMP_MIDDLE + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +//#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/hank/emisar-d4/hwdef.c b/hw/hank/emisar-d4/hwdef.c new file mode 100644 index 0000000..972f682 --- /dev/null +++ b/hw/hank/emisar-d4/hwdef.c @@ -0,0 +1,59 @@ +// Emisar D4 PWM helper functions +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +//#ifdef AUXLED_PIN +#if 0 +#include "chan-aux.c" +#else +#define AUX_CHANNELS +#endif + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; +} + +// TODO: implement delta-sigma modulation for better low modes + +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/emisar-d4/hwdef.h b/hw/hank/emisar-d4/hwdef.h new file mode 100644 index 0000000..7be700a --- /dev/null +++ b/hw/hank/emisar-d4/hwdef.h @@ -0,0 +1,104 @@ +// Emisar D4 driver layout +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- + * AUX LED -|3 6|- PWM (FET) + * GND -|4 5|- PWM (1x7135) + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4.c + +// allow using aux LEDs as extra channel modes (when they exist) +//#ifdef AUXLED_PIN +#if 0 +#include "chan-aux.h" +#else +#define NUM_AUX_CHANNEL_MODES 0 +#endif + +// channel modes +// * 0. FET+7135 stacked +// * 1. button LED (only on some derivative models, like BLF Q8) +#define NUM_CHANNEL_MODES (1 + NUM_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + CM_AUX, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// 1x7135 channel +#ifndef CH1_PIN +#define CH1_PIN PB0 // pin 5, 1x7135 PWM +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 +#endif + +// DD FET channel +#ifndef CH2_PIN +#define CH2_PIN PB1 // pin 6, FET PWM +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 +#endif + +//#define AUXLED_PIN PB4 // pin 3 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +// (FIXME: remove? not used?) +//#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 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 + + +inline void hwdef_setup() { + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN); + + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d4k-3ch/cfg.h b/hw/hank/emisar-d4k-3ch/cfg.h new file mode 100644 index 0000000..c39ac01 --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/cfg.h @@ -0,0 +1,106 @@ +// Emisar D4K 3-channel config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0151" +#include "hwdef-emisar-d4k-3ch.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +// (in case there's a RGB button) +#define USE_AUX_RGB_LEDS_WHILE_ON 40 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// channel modes... +// CM_MAIN2, CM_LED3, CM_LED4, CM_ALL, +// CM_BLEND34A, CM_BLEND34B, CM_HSV, CM_AUTO3 +#define DEFAULT_CHANNEL_MODE CM_ALL + +#define FACTORY_RESET_WARN_CHANNEL CM_LED4 +#define FACTORY_RESET_SUCCESS_CHANNEL CM_MAIN2 + +#define CONFIG_WAITING_CHANNEL CM_LED3 +#define CONFIG_BLINK_CHANNEL CM_ALL + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN2 + +// LEDs 3 and 4 make a nice police strobe +#define POLICE_COLOR_STROBE_CH1 CM_LED3 +#define POLICE_COLOR_STROBE_CH2 CM_LED4 +// aux red + aux blue are the correct colors, but are dim +//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + +// main 2 LEDs +// output: unknown, 2000 lm? +// LED 3 / 4 +// output: unknown, 1000 lm each? +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 + +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 70 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#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 + +// 10 40 [70] 100 130 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1500 lm +#define THERM_FASTER_LEVEL 130 + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 +// #define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry +// bike strobe needs a longer pulse too? +//#define BIKE_STROBE_ONTIME 8 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// for consistency with KR4 (not otherwise necessary though) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/emisar-d4k-3ch/hwdef.c b/hw/hank/emisar-d4k-3ch/hwdef.c new file mode 100644 index 0000000..e35af08 --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/hwdef.c @@ -0,0 +1,362 @@ +// Emisar D4K 3-channel hwdef +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "spaghetti-monster/anduril/channel-modes.h" //for circular_tint_3h() +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main2(uint8_t level); +void set_level_led3(uint8_t level); +void set_level_led4(uint8_t level); +void set_level_all(uint8_t level); +void set_level_led34a_blend(uint8_t level); +void set_level_led34b_blend(uint8_t level); +void set_level_hsv(uint8_t level); +void set_level_auto3(uint8_t level); + +bool gradual_tick_main2(uint8_t gt); +bool gradual_tick_led3(uint8_t gt); +bool gradual_tick_led4(uint8_t gt); +bool gradual_tick_all(uint8_t gt); +bool gradual_tick_led34a_blend(uint8_t gt); +bool gradual_tick_led34b_blend(uint8_t gt); +bool gradual_tick_hsv(uint8_t gt); +bool gradual_tick_auto3(uint8_t gt); + + +Channel channels[] = { + { // main 2 LEDs only + .set_level = set_level_main2, + .gradual_tick = gradual_tick_main2, + .has_args = 0 + }, + { // 3rd LED only + .set_level = set_level_led3, + .gradual_tick = gradual_tick_led3, + .has_args = 0 + }, + { // 4th LED only + .set_level = set_level_led4, + .gradual_tick = gradual_tick_led4, + .has_args = 0 + }, + { // all channels, tied together (equal amounts, max power) + .set_level = set_level_all, + .gradual_tick = gradual_tick_all, + .has_args = 0 + }, + { // 3rd + 4th LEDs, manual blend (max "100%" power) (8/16/16) + .set_level = set_level_led34a_blend, + .gradual_tick = gradual_tick_led34a_blend, + .has_args = 1 + }, + { // 3rd + 4th LEDs, manual blend (max "100%" power) (16/16/8) + .set_level = set_level_led34b_blend, + .gradual_tick = gradual_tick_led34b_blend, + .has_args = 1 + }, + { // 3ch blend (HSV style) + .set_level = set_level_hsv, + .gradual_tick = gradual_tick_hsv, + .has_args = 1 + }, + { // 3ch auto blend (red-warm-cool style, led4-led3-main2) + .set_level = set_level_auto3, + .gradual_tick = gradual_tick_auto3, + .has_args = 0 + }, + RGB_AUX_CHANNELS +}; + +// HSV mode needs a different 3H handler +StatePtr channel_3H_modes[NUM_CHANNEL_MODES] = { + NULL, NULL, NULL, NULL, + NULL, NULL, circular_tint_3h, NULL, +}; + +void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + + // turn off all LEDs + MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); + LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN ); + LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN ); + main2_dsm_lvl = 0; + led3_dsm_lvl = 0; + led4_dsm_lvl = 0; + MAIN2_PWM_LVL = 0; + LED3_PWM_LVL = 0; + LED4_PWM_LVL = 0; + PWM_CNT = 0; + //PWM_TOP = PWM_TOP_INIT; +} + +// wrap setting the dsm vars, to get a faster response +// (just setting *_dsm_lvl doesn't work well for strobes) +void set_hw_levels(PWM_DATATYPE main2, // brightness, 0 to DSM_TOP + PWM_DATATYPE led3, + PWM_DATATYPE led4, + bool main2_en, // enable even at PWM=0? + bool led3_en, + bool led4_en + ) { + + // enable/disable LED power channels + if (main2 | main2_en) + MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN); + else MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN); + + if (led3 | led3_en ) + LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN); + else LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN); + + if (led4 | led4_en ) + LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN); + else LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN); + + // set delta-sigma soft levels + main2_dsm_lvl = main2; + led3_dsm_lvl = led3; + led4_dsm_lvl = led4; + // set hardware PWM levels and init dsm loop + MAIN2_PWM_LVL = main2_pwm = main2 >> 7; + LED3_PWM_LVL = led3_pwm = led3 >> 7; + LED4_PWM_LVL = led4_pwm = led4 >> 7; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; + + // force phase reset + PWM_CNT = PWM_CNT2 = 0; +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + MAIN2_PWM_LVL = main2_pwm; + LED3_PWM_LVL = led3_pwm; + LED4_PWM_LVL = led4_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + main2_dsm += (main2_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + main2_pwm = (main2_dsm_lvl >> 7) + (main2_dsm > 0x7f); + // clear carry bit + main2_dsm &= 0x7f; + + // repeat for other channels + + led3_dsm += (led3_dsm_lvl & 0x007f); + led3_pwm = (led3_dsm_lvl >> 7) + (led3_dsm > 0x7f); + led3_dsm &= 0x7f; + + led4_dsm += (led4_dsm_lvl & 0x007f); + led4_pwm = (led4_dsm_lvl >> 7) + (led4_dsm > 0x7f); + led4_dsm &= 0x7f; +} + +// LEDs 1+2 are 8-bit +// this 8-bit channel may be LEDs 1+2 or LED 4, depending on wiring +void set_level_main2(uint8_t level) { + set_hw_levels(PWM_GET(pwm1_levels, level), 0, 0, + 1, 0, 0); +} + +// LED 3 is 16-bit +void set_level_led3(uint8_t level) { + set_hw_levels(0, PWM_GET(pwm1_levels, level), 0, + 0, 1, 0); +} + +// this 16-bit channel may be LED 4 or LEDs 1+2, depending on wiring +void set_level_led4(uint8_t level) { + set_hw_levels(0, 0, PWM_GET(pwm1_levels, level), + 0, 0, 1); +} + +void set_level_all(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(pwm, pwm, pwm, 1, 1, 1); +} + +// 8/16/16 wiring, mix 16+16 +void set_level_led34a_blend(uint8_t level) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend); + + set_hw_levels(0, warm_PWM, cool_PWM, + 0, (blend<170), (blend>85)); +} + +// 16/16/8 wiring, mix 16+8 +void set_level_led34b_blend(uint8_t level) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend); + + set_hw_levels(cool_PWM, warm_PWM, 0, + (blend>85), (blend<170), 0); +} + +void set_level_hsv(uint8_t level) { + RGB_t color; + uint8_t h = cfg.channel_mode_args[channel_mode]; + uint8_t s = 255; // TODO: drop saturation at brightest levels + PWM_DATATYPE v = PWM_GET(pwm1_levels, level); + color = hsv2rgb(h, s, v); + + set_hw_levels(color.r, color.g, color.b, + 0, 0, 0); +} + +// calculate a 3-channel "auto tint" blend +// (like red -> warm white -> cool white) +// results are placed in *a, *b, and *c vars +// level : ramp level to convert into 3 channel levels +// (assumes ramp table is "pwm1_levels") +void calc_auto_3ch_blend( + PWM_DATATYPE *a, // red + PWM_DATATYPE *b, // warm + PWM_DATATYPE *c, // cool + uint8_t level) { + + PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level); + + // tint goes from 0 (red) to 127 (warm white) to 255 (cool white) + uint8_t mytint; + mytint = 255 * (uint16_t)(level+1) / RAMP_SIZE; + + uint8_t falling=0, rising=0; + if (level < (RAMP_SIZE/2)) + falling = 255 - triangle_wave(mytint); + else + rising = 255 - triangle_wave(mytint); + + // TODO: make "a" drop to zero sooner, and "c" start ramping up later + // red is high at 0, low at 255 (linear) + *a = (((PWM_DATATYPE2)falling + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // warm white is low at 0 and 255, high at 127 (linear triangle) + *b = (((PWM_DATATYPE2)triangle_wave(mytint) + * (PWM_DATATYPE2)vpwm) ) / 255; + // cool white is low at 0, high at 255 (linear) + *c = (((PWM_DATATYPE2)rising + * (PWM_DATATYPE2)vpwm) + 127) / 255; + +} + +// 3-channel "auto tint" channel mode +void set_level_auto3(uint8_t level) { + PWM_DATATYPE a, b, c; + calc_auto_3ch_blend(&a, &b, &c, level); + + set_hw_levels(c, b, a, + 0, 0, (0 == level)); +} + +///// "gradual tick" functions for smooth thermal regulation ///// +// (and other smooth adjustments) + +bool gradual_adjust(PWM_DATATYPE main2, PWM_DATATYPE led3, PWM_DATATYPE led4) { + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = main2_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(main2, main2_dsm_lvl); + + steps = led3_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(led3, led3_dsm_lvl ); + + steps = led4_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(led4, led4_dsm_lvl ); + + if ((main2 == main2_dsm_lvl) + && (led3 == led3_dsm_lvl ) + && (led4 == led4_dsm_lvl )) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_main2(uint8_t gt) { + PWM_DATATYPE main2 = PWM_GET(pwm1_levels, gt); + return gradual_adjust(main2, 0, 0); +} + +bool gradual_tick_led3(uint8_t gt) { + PWM_DATATYPE led3 = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, led3, 0); +} + +bool gradual_tick_led4(uint8_t gt) { + PWM_DATATYPE led4 = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, 0, led4); +} + +bool gradual_tick_all(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, pwm, pwm); +} + +// 8/16/16 wiring, mix 16+16 +bool gradual_tick_led34a_blend(uint8_t gt) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend); + + return gradual_adjust(0, warm_PWM, cool_PWM); +} + +// 16/16/8 wiring, mix 16+8 +bool gradual_tick_led34b_blend(uint8_t gt) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend); + + return gradual_adjust(cool_PWM, warm_PWM, 0); +} + +bool gradual_tick_hsv(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + RGB_t color; + uint8_t h = cfg.channel_mode_args[channel_mode]; + uint8_t s = 255; // TODO: drop saturation at brightest levels + PWM_DATATYPE v = PWM_GET(pwm1_levels, gt); + color = hsv2rgb(h, s, v); + + return gradual_adjust(color.r, color.g, color.b); +} + +bool gradual_tick_auto3(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + PWM_DATATYPE red, warm, cool; + calc_auto_3ch_blend(&red, &warm, &cool, gt); + return gradual_adjust(cool, warm, red); +} + diff --git a/hw/hank/emisar-d4k-3ch/hwdef.h b/hw/hank/emisar-d4k-3ch/hwdef.h new file mode 100644 index 0000000..2e83fbe --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/hwdef.h @@ -0,0 +1,248 @@ +// Emisar D4K 3-channel hwdef +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Pin / Name / Function + * 1 PA6 LED 4 PWM (linear) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 button LED + * 6 PA1 3rd LED opamp enable + * 7 PA0 4th LED opamp enable + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 main 2 LEDs PWM (linear) (PWM0A) (8-bit only) + * 16 PB3 3rd LED PWM (linear) (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 main 2 LEDs opamp enable + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + * + * PWM speed + resolution is dynamic on 2 channels, + * and 8-bit 16 kHz on 1 channel. + * + * Note: Some hardware might swap the wires at build time! + * It might be wired 8/16/16 (8-bit LEDs 1+2) or 16/16/8 (8-bit LED 4). + * So this code should support both wire layouts. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4k-3ch.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// - 1. main 2 LEDs only (8/16/16 wiring) or LED 4 only (16/16/8) +// - 2. 3rd LED only +// - 3. 4th LED only (8/16/16 wiring) or main 2 LEDs only (16/16/8) +// - 4. all 3 channels (equal amounts) +// - 5. 2ch blend (3rd + 4th LEDs, 8/16/16 wiring) +// - 6. 2ch blend (3rd + 4th LEDs, 16/16/8 wiring) +// - 7. 3ch blend (HSV style) +// - 8. 3ch auto blend (red-warm-cool style, led4-led3-main2) +// - 9+. RGB aux (hidden) +#define NUM_CHANNEL_MODES (8 + NUM_RGB_AUX_CHANNEL_MODES) +enum channel_modes_e { + CM_MAIN2 = 0, + CM_LED3, + CM_LED4, + CM_ALL, + CM_BLEND34A, // 8 / [16+16] + CM_BLEND34B, // 16 / [16+8] + CM_HSV, + CM_AUTO3, + RGB_AUX_ENUMS +}; + +#define CHANNEL_MODES_ENABLED 0b0000000000001111 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, _, 128=middle CCT, 128=middle CCT, 213=purple, _ +#define CHANNEL_MODE_ARGS 0,0,0,0,128,128,213,0,RGB_AUX_CM_ARGS +#define USE_CUSTOM_CHANNEL_3H_MODES +#define USE_CIRCULAR_TINT_3H + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND +#define USE_HSV2RGB + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp + +// dynamic PWM +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for checking / resetting phase +#define PWM_CNT2 TCNT0 // for checking / resetting phase +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER0_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE0) + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// main 2 LEDs / 1st channel (2 LEDs) +uint16_t main2_dsm_lvl; +uint8_t main2_pwm, main2_dsm; +#define MAIN2_PWM_PIN PC0 +#define MAIN2_PWM_LVL OCR0A // OCR0A is the output compare register for PC0 +#define MAIN2_ENABLE_PIN PB0 // Opamp power +#define MAIN2_ENABLE_PORT PORTB // control port for PB0 + +// LED 3 / 2nd channel (1 LED) +uint16_t led3_dsm_lvl; +uint8_t led3_pwm, led3_dsm; +#define LED3_PWM_PIN PB3 +#define LED3_PWM_LVL OCR1A // OCR1A is the output compare register for PB3 +#define LED3_ENABLE_PIN PA1 // Opamp power +#define LED3_ENABLE_PORT PORTA // control port for PA1 + +// LED 4 / 3rd channel (1 LED) +uint16_t led4_dsm_lvl; +uint8_t led4_pwm, led4_dsm; +#define LED4_PWM_PIN PA6 +#define LED4_PWM_LVL OCR1B // OCR1B is the output compare register for PA6 +#define LED4_ENABLE_PIN PA0 // Opamp power +#define LED4_ENABLE_PORT PORTA // control port for PA0 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PA7 // pin 20 +#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt +#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0] +#define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0] +#define SWITCH_PORT PINA // PINA or PINB or PINC +#define SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] +#endif + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + +// 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 +// it also has an independent LED in the button +#define USE_BUTTON_LED + + +inline void hwdef_setup() { + // enable output ports + DDRC = (1 << MAIN2_PWM_PIN); + DDRB = (1 << LED3_PWM_PIN) + | (1 << MAIN2_ENABLE_PIN) + ; + DDRA = (1 << LED4_PWM_PIN) + | (1 << LED4_ENABLE_PIN) + | (1 << LED3_ENABLE_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 + // Linear opamp PWM for both 16-bit channels + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (DS table 12-5) + // WGM1[3:0]: 1,1,1,0: PWM, Fast, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // 8-bit PWM channel (LEDs 1+2 or LED 4) + // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (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]: 1,0: PWM OC0B in the normal direction (DS table 11-7) + TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8) + | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4) + //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7) + ; + TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9) + | (0<<WGM02) // phase-correct PWM (DS table 11-8) + ; + + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d4s-219/cfg.h b/hw/hank/emisar-d4s-219/cfg.h new file mode 100644 index 0000000..f86c1b1 --- /dev/null +++ b/hw/hank/emisar-d4s-219/cfg.h @@ -0,0 +1,17 @@ +// Emisar D4S-219C config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as D4S but with FET modes limited to 80% power +// to avoid destroying the LEDs +#include "cfg-emisar-d4s.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0132" + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#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,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,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,14,15,17,18,19,21,23,24,25,27,28,30,32,34,36,38,39,41,43,45,47,50,52,54,57,60,62,65,67,70,73,76,79,82,86,89,92,95,99,103,106,110,114,119,123,127,132,136,141,145,151,156,161,166 diff --git a/hw/hank/emisar-d4s/cfg.h b/hw/hank/emisar-d4s/cfg.h new file mode 100644 index 0000000..e966132 --- /dev/null +++ b/hw/hank/emisar-d4s/cfg.h @@ -0,0 +1,49 @@ +// Emisar D4S config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0131" +#include "hwdef-emisar-d4s.h" +#include "hank-cfg.h" +// ATTINY: 85 + +// the button lights up (on some models) +#define USE_INDICATOR_LED +// the aux LEDs are behind the main LEDs +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +#define RAMP_SIZE 150 + +// 3x7135 + FET +// level_calc.py ninth 2 150 7135 1 11.2 450 FET 1 10 4000 +#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 + +#define RAMP_SMOOTH_FLOOR 1 +#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 + +// safe limit ~30% power +// 10 34 59 [83] 108 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 108 +#define SIMPLE_UI_STEPS 5 + +// 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 +#define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high + +// too big, turn off extra features +#undef USE_TACTICAL_MODE diff --git a/hw/hank/emisar-d4s/hwdef.h b/hw/hank/emisar-d4s/hwdef.h new file mode 100644 index 0000000..95789e6 --- /dev/null +++ b/hw/hank/emisar-d4s/hwdef.h @@ -0,0 +1,13 @@ +// Emisar D4S driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as a D4, basically +#include "hwdef-emisar-d4.h" + +// ... except the D4S has aux LEDs under the optic +#ifndef AUXLED_PIN +#define AUXLED_PIN PB4 // pin 3 +#endif + diff --git a/hw/hank/emisar-d4sv2-219/cfg.h b/hw/hank/emisar-d4sv2-219/cfg.h new file mode 100644 index 0000000..22775cc --- /dev/null +++ b/hw/hank/emisar-d4sv2-219/cfg.h @@ -0,0 +1,18 @@ +// Emisar D4Sv2-219 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-emisar-d4sv2.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0134" +// ATTINY: 1634 + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#undef PWM2_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 +// 65% FET power +#undef PWM3_LEVELS +#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,8,10,11,12,13,15,17,18,20,21,23,25,27,29,31,33,35,38,39,42,44,47,49,52,54,57,60,63,66,69,73,76,79,83,86,90,94,98,102,106,110,115,119,124,129,134,139,144,149,155,160,166 diff --git a/hw/hank/emisar-d4sv2/cfg.h b/hw/hank/emisar-d4sv2/cfg.h new file mode 100644 index 0000000..666b394 --- /dev/null +++ b/hw/hank/emisar-d4sv2/cfg.h @@ -0,0 +1,81 @@ +// Emisar D4S V2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0133" +#include "hwdef-emisar-d4sv2.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +#define RAMP_SIZE 150 + +// 1x7135 + 3x7135 + FET + +// level_calc.py 5.7895 3 150 7135 1 0.1 130 7135 1 1 321.43 FET 2 10 3000 --pwm dyn:74:4096:255:3 +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,6,12,19,25,32,40,47,55,64,72,81,91,101,111,122,133,144,156,169,182,195,209,224,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,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,3,5,7,10,12,15,17,20,22,25,28,31,34,38,41,44,48,52,56,60,64,68,72,77,81,86,91,96,101,107,112,118,124,130,136,143,149,156,163,170,178,186,193,201,210,218,227,236,245,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level +#define MAX_1x7135 75 +#define MAX_Nx7135 100 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +// old +//// ../../../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 PWM_TOPS 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 62 +//#define MAX_Nx7135 93 +//#define HALFSPEED_LEVEL 18 +//#define QUARTERSPEED_LEVEL 8 + + +#define DEFAULT_LEVEL MAX_Nx7135 + +#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 + +// safe limit ~35% power, 150% of sustainable thermal power +// 25 50 [75] [100] 125 +#define SIMPLE_UI_FLOOR 25 +#define SIMPLE_UI_CEIL 125 +#define SIMPLE_UI_STEPS 5 + +#define STROBE_BRIGHTNESS MAX_LEVEL + +// stop panicking at ~50% power or ~2000 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-d4sv2/hwdef.c b/hw/hank/emisar-d4sv2/hwdef.c new file mode 100644 index 0000000..6399fb8 --- /dev/null +++ b/hw/hank/emisar-d4sv2/hwdef.c @@ -0,0 +1,67 @@ +// Emisar D4Sv2 PWM helper functions +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + CH3_PWM = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with 3 stacked power channels, DDFET+3+1 +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + PWM_DATATYPE ch3_pwm = PWM_GET(pwm3_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + CH3_PWM = ch3_pwm; + // wait to sync the counter and avoid flashes + while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + PWM_DATATYPE pwm3 = PWM_GET(pwm3_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_STACKED(pwm2, CH2_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm3, CH3_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + && (pwm3 == CH3_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/emisar-d4sv2/hwdef.h b/hw/hank/emisar-d4sv2/hwdef.h new file mode 100644 index 0000000..11d1abb --- /dev/null +++ b/hw/hank/emisar-d4sv2/hwdef.h @@ -0,0 +1,179 @@ +// Emisar D4Sv2 driver layout (attiny1634) +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (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 button LED? + * 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) (8-bit) + * 16 PB3 1x7135 PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI + * 19 PB0 (none) + * 20 PA7 (none) + * ADC12 thermal sensor + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4sv2.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. FET+3+1 stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 3 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp (16-bit) +#define PWM2_DATATYPE uint8_t // 3x7135 ramp (8-bit) +#define PWM3_DATATYPE uint8_t // DD FET ramp (16-bit) + +// PWM parameters of FET and 1x7135 channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// 1x7135 channel +#define CH1_PIN PB3 // pin 16, 1x7135 PWM +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 + +// 3x7135 channel +#define CH2_PIN PC0 // pin 15, 3x7135 PWM +#define CH2_PWM OCR0A // OCR0A is the output compare register for PC0 + +// DD FET channel +#define CH3_PIN PA6 // pin 1, DD FET PWM +#define CH3_PWM OCR1B // OCR1B is the output compare register for PB1 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + + +#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 + +#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 + +// 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 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +inline void hwdef_setup() { + // enable output ports + // 3x7135 + DDRC = (1 << CH2_PIN); + // 1x7135 + DDRB = (1 << CH1_PIN); + // FET, aux R/G/B + DDRA = (1 << CH3_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 + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable 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 PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/emisar-d4v2-219/cfg.h b/hw/hank/emisar-d4v2-219/cfg.h new file mode 100644 index 0000000..e9775ec --- /dev/null +++ b/hw/hank/emisar-d4v2-219/cfg.h @@ -0,0 +1,17 @@ +// Emisar D4v2-219 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-emisar-d4v2.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0114" +// ATTINY: 1634 + +// 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/hw/hank/emisar-d4v2-nofet/cfg.h b/hw/hank/emisar-d4v2-nofet/cfg.h new file mode 100644 index 0000000..6eddb40 --- /dev/null +++ b/hw/hank/emisar-d4v2-nofet/cfg.h @@ -0,0 +1,62 @@ +// Emisar D4v2-noFET config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// switch to 1-channel support functions +#define HWDEF_C_FILE hwdef-emisar-d4v2-nofet.c + +#include "cfg-emisar-d4v2.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0115" +// ATTINY: 1634 + +// the ramp uses only 1x7135 chip, max ~130 lm +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM_TOPS +//#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 +// level_calc.py 3.01 1 150 7135 -1 0.1 140 --pwm dyn:64:4096:255:3 --clock 11:21:8.0 +// (and some manual tweaks to make half/quarter speed levels less bumpy) +#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,9,10,11,11,12,13,13,14,15,15,18,18,17,18,18,19,19,19,19,19,20,20,20,20,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,21,21,22,23,24,24,26,27,28,29,30,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,56,58,60,62,63,65,67,69,71,73,75,77,79,81,83,85,88,90,92,95,97,100,102,105,107,110,112,115,118,121,124,127,129,132,135,139,142,145,148,151,155,158,162,165,169,172,176,179,183,187,191,195,199,203,207,211,215,219,223,228,232,237,241,246,250,255 +#define PWM_TOPS 4094,2719,3280,1954,2599,3032,2342,2548,2626,2635,2246,2261,2244,1964,1956,1929,1743,1733,1763,1697,1492,1362,1245,1231,1132,1118,1034,958,889,826,821,767,717,671,629,591,556,523,493,465,440,416,394,373,354,336,319,304,299,293,279,282,269,257,260,249,251,252,253,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,255,255,255,255,255 + +#undef MAX_1x7135 +#define MAX_1x7135 150 +#undef QUARTERSPEED_LEVEL +#undef HALFSPEED_LEVEL +#define QUARTERSPEED_LEVEL 11 +#define HALFSPEED_LEVEL 21 + +#undef DEFAULT_LEVEL +#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 + +// safe limit ~100% power because no FET +#undef SIMPLE_UI_FLOOR +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL + +#undef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 60 + +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 150 + +// maybe keep this, in case someone uses a higher power channel? +//#undef USE_THERMAL_REGULATION +//#undef USE_SET_LEVEL_GRADUALLY + diff --git a/hw/hank/emisar-d4v2-nofet/hwdef.c b/hw/hank/emisar-d4v2-nofet/hwdef.c new file mode 100644 index 0000000..24477a7 --- /dev/null +++ b/hw/hank/emisar-d4v2-nofet/hwdef.c @@ -0,0 +1,55 @@ +// Emisar D4v2 (no DD FET, 1x7135 only) PWM helper functions +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with just a 1x7135 chip, max 350 mA or ~130 lm +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + // wait to sync the counter and avoid flashes + while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + + GRADUAL_ADJUST_SIMPLE (pwm1, CH1_PWM); + + if ( (pwm1 == CH1_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/emisar-d4v2/cfg.h b/hw/hank/emisar-d4v2/cfg.h new file mode 100644 index 0000000..0f0bb17 --- /dev/null +++ b/hw/hank/emisar-d4v2/cfg.h @@ -0,0 +1,64 @@ +// Emisar D4v2 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0113" +#include "hwdef-emisar-d4v2.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +#define RAMP_SIZE 150 + +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 2 500 3000 +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,2,4,5,7,9,11,13,15,17,19,21,23,25,27,29,32,34,36,39,41,43,46,49,51,54,57,59,62,65,68,71,74,77,80,83,86,90,93,96,100,103,107,110,114,117,121,125,129,133,137,141,145,149,153,157,162,166,170,175,179,184,189,193,198,203,208,213,218,223,228,233,239,244,249,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#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 + +// 20, 40, 60, 80, 100 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~25% power or ~1000 lm +#define THERM_FASTER_LEVEL 110 + +#define THERM_CAL_OFFSET 5 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-d4v2/hwdef.c b/hw/hank/emisar-d4v2/hwdef.c new file mode 100644 index 0000000..026b30d --- /dev/null +++ b/hw/hank/emisar-d4v2/hwdef.c @@ -0,0 +1,61 @@ +// Emisar D4v2 PWM helper functions +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + // wait to sync the counter and avoid flashes + while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/emisar-d4v2/hwdef.h b/hw/hank/emisar-d4v2/hwdef.h new file mode 100644 index 0000000..96c57a9 --- /dev/null +++ b/hw/hank/emisar-d4v2/hwdef.h @@ -0,0 +1,171 @@ +// hwdef for Emisar D4v2 (attiny1634) +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * 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 e-switch + * 6 PA1 button LED + * 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 7135 PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI + * 19 PB0 (none) + * 20 PA7 (none) + * ADC12 thermal sensor + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#ifndef HWDEF_C_FILE +#define HWDEF_C_FILE hwdef-emisar-d4v2.c +#endif + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. FET+7135 stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// 1x7135 channel +#define CH1_PIN PB3 // pin 16, 1x7135 PWM +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 + +// DD FET channel +#define CH2_PIN PA6 // pin 1, DD FET PWM +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + + +#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 + +#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 + +// 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 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +inline void hwdef_setup() { + // enable output ports + // 7135 + DDRB = (1 << CH1_PIN); + // DD FET, aux R/G/B, button LED + DDRA = (1 << CH2_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 + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + #if 0 // old 8-bit PWM setup + // 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) + ; + #endif + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/hank-cfg.h b/hw/hank/hank-cfg.h new file mode 100644 index 0000000..86ac605 --- /dev/null +++ b/hw/hank/hank-cfg.h @@ -0,0 +1,30 @@ +// Intl-Outdoor (Hank)'s config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// config preferences for Hank Wang of Intl-Outdoor (Emisar, Noctigon) + +// RGB aux LEDs should use rainbow cycling mode +// to impress new customers +// (people usually change it to voltage mode later though) +#ifdef RGB_LED_OFF_DEFAULT +#undef RGB_LED_OFF_DEFAULT +#endif +#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow + +// half a second per color in rainbow mode +//#define RGB_RAINBOW_SPEED 0x03 + +// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// double click while on goes to full-power turbo, not ramp ceiling +#define DEFAULT_2C_STYLE 1 + +// for consistency with KR4 (not otherwise necessary though) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-dm11-boost/cfg.h b/hw/hank/noctigon-dm11-boost/cfg.h new file mode 100644 index 0000000..2174fdb --- /dev/null +++ b/hw/hank/noctigon-dm11-boost/cfg.h @@ -0,0 +1,91 @@ +// Noctigon DM11 (boost driver) config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0273" +#include "hwdef-noctigon-dm11-boost.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +#define RAMP_SIZE 150 + +// power channels: +// - boost: 8A? +// - DD FET: none (can't do DD on a boost driver) + +#if 0 // old, dynamic PWM method +// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 +// (plus a 0 at the beginning for moon) +#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 +#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 150 +#define DEFAULT_LEVEL 70 +#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 +#endif + +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#define RAMP_SMOOTH_FLOOR 1 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, 50, [70], 90, 110, 130 +// Nichia B35 model: (0.56), 1.4, 8.4, 34.5, [102], 250, 500, 860, (1300) lm +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~75% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + +// the power regulator seems to "jump start" the LEDs all on its own, +// so the firmware doesn't have to +// (and unfortunately the power regulator jumps it a bit too hard) +#define DEFAULT_JUMP_START_LEVEL 1 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 2ms or less +#define PARTY_STROBE_ONTIME 3 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-dm11-boost/hwdef.c b/hw/hank/noctigon-dm11-boost/hwdef.c new file mode 100644 index 0000000..932323a --- /dev/null +++ b/hw/hank/noctigon-dm11-boost/hwdef.c @@ -0,0 +1,97 @@ +// Noctigon DM11 (boost driver) PWM helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + + // turn off all LEDs + ch1_dsm_lvl = 0; + CH1_PWM = 0; + PWM_CNT = 0; // reset phase + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN ); // disable opamp + CH1_ENABLE_PORT2 &= ~(1 << CH1_ENABLE_PIN2); // disable PMIC +} + +// single set of LEDs with single power channel, boost +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1 = PWM_GET(pwm1_levels, level); + + // set delta-sigma soft levels + ch1_dsm_lvl = ch1; + + // set hardware PWM levels and init dsm loop + CH1_PWM = ch1_pwm = ch1 >> 7; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; + + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; + + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN ); // enable opamp + CH1_ENABLE_PORT2 |= (1 << CH1_ENABLE_PIN2); // enable PMIC +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + CH1_PWM = ch1_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + ch1_dsm += (ch1_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f); + // clear carry bit + ch1_dsm &= 0x7f; +} + + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE ch1 = PWM_GET(pwm1_levels, gt); + + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = ch1_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl); + + if ((ch1 == ch1_dsm_lvl) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/noctigon-dm11-boost/hwdef.h b/hw/hank/noctigon-dm11-boost/hwdef.h new file mode 100644 index 0000000..d56a5f5 --- /dev/null +++ b/hw/hank/noctigon-dm11-boost/hwdef.h @@ -0,0 +1,206 @@ +// Noctigon DM11 boost driver layout (attiny1634) +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (based on Noctigon K1) + * + * 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 L: button LED + * 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 (PCINT10) + * 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. + * Regulated brightness control uses the power level pin, with PWM+DSM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-noctigon-dm11-boost.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp + +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 255 +#define PWM_CNT TCNT1 // for checking / resetting phase +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER1_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE1) + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// regulated channel +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 + +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +#define CH1_ENABLE_PIN2 PC0 // pin 15, boost PMIC enable +#define CH1_ENABLE_PORT2 PORTC // control port for PC0 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + +// 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 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +inline void hwdef_setup() { + // enable output ports + // boost PMIC on/off + DDRC = (1 << CH1_ENABLE_PIN2); + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN); + // aux R/G/B, button LED + DDRA = (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 + // PWM for both channels + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (DS table 12-5) + // WGM1[3:0]: 1,1,1,0: PWM, Fast, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-dm11-nofet/cfg.h b/hw/hank/noctigon-dm11-nofet/cfg.h new file mode 100644 index 0000000..b2fdfdb --- /dev/null +++ b/hw/hank/noctigon-dm11-nofet/cfg.h @@ -0,0 +1,49 @@ +// Noctigon DM11 (no DD FET) config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same support functions as a KR4 +#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c +#include "cfg-noctigon-dm11.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0272" +// ATTINY: 1634 + +// turn off the DD FET +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 +#define RAMP_SIZE 150 + +// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 +#undef PWM2_LEVELS +#undef PWM_TOPS +#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#undef DEFAULT_LEVEL +#define DEFAULT_LEVEL 70 +#undef MAX_1x7135 +#define MAX_1x7135 150 + +// make candle mode wobble more +#ifdef CANDLE_AMPLITUDE +#undef CANDLE_AMPLITUDE +#endif +#define CANDLE_AMPLITUDE 33 + + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +#define PARTY_STROBE_ONTIME 2 + +// jump start a bit higher than base driver +#undef DEFAULT_JUMP_START_LEVEL +#define DEFAULT_JUMP_START_LEVEL 25 + +// stop panicking at ~70% power or ~600 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 +#undef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range + diff --git a/hw/hank/noctigon-dm11-sbt90/cfg.h b/hw/hank/noctigon-dm11-sbt90/cfg.h new file mode 100644 index 0000000..9fac446 --- /dev/null +++ b/hw/hank/noctigon-dm11-sbt90/cfg.h @@ -0,0 +1,46 @@ +// Noctigon DM11-SBT90.2 config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0274" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// ... and a single LED in the button +#define USE_BUTTON_LED +// don't use aux LEDs while main LED is on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// power channels: +// - linear: 5A? +// - FET: DD + +#undef DEFAULT_LEVEL +#define DEFAULT_LEVEL 70 + +#undef RAMP_SMOOTH_FLOOR +#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable +// 10, 30, 50, [70], 90, 110, 130 +#undef RAMP_DISCRETE_FLOOR +#define RAMP_DISCRETE_FLOOR 10 + +// safe limit ~75% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL + +// stop panicking at ~70% power or ~600 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 + +#undef BLINK_BRIGHTNESS +#define BLINK_BRIGHTNESS 50 + +#undef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 30 + diff --git a/hw/hank/noctigon-dm11/cfg.h b/hw/hank/noctigon-dm11/cfg.h new file mode 100644 index 0000000..cd6bc9d --- /dev/null +++ b/hw/hank/noctigon-dm11/cfg.h @@ -0,0 +1,88 @@ +// Noctigon DM11 config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0271" +#include "hwdef-noctigon-dm11.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// ... and a single LED in the button +#define USE_BUTTON_LED +// don't use aux LEDs while main LED is on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +#define RAMP_SIZE 150 + +// power channels: +// - linear: 5A? +// - FET: DD + +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 +// (plus one extra level at the beginning for moon) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level + +#define MAX_1x7135 130 +#define DEFAULT_LEVEL 70 +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 + +#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable +#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 + +// safe limit ~75% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// there is usually no lighted button, so +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +//#define PARTY_STROBE_ONTIME 2 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 30 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-dm11/hwdef.h b/hw/hank/noctigon-dm11/hwdef.h new file mode 100644 index 0000000..b35b27f --- /dev/null +++ b/hw/hank/noctigon-dm11/hwdef.h @@ -0,0 +1,183 @@ +// Noctigon DM11 driver layout (attiny1634) +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (based on Noctigon K1) + * + * Pin / Name / Function + * 1 PA6 FET PWM (direct drive) (PWM1B) (on some models) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 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 (linear) (PWM1A) + * 17 PB2 MISO / (none) (PCINT10) + * 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. + * Linear brightness control uses the power level pin, with dynamic 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. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#ifndef HWDEF_C_FILE +#define HWDEF_C_FILE hwdef-noctigon-kr4.c +#endif + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. linear + DD FET stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // linear ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// linear channel +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +// DD FET channel +#define CH2_PIN PA6 // pin 1, DD FET PWM +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN); + // DD FET PWM, aux R/G/B, button LED + DDRA = (1 << CH2_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 + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-k1-boost/cfg.h b/hw/hank/noctigon-k1-boost/cfg.h new file mode 100644 index 0000000..0684ce7 --- /dev/null +++ b/hw/hank/noctigon-k1-boost/cfg.h @@ -0,0 +1,96 @@ +// Noctigon K1 boost config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0253" +#include "hwdef-noctigon-k1-boost.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// Most K1 lights can run hotter than default, but the 12V model +// is known to have issues with the driver itself getting too hot. +// This then causes the main LEDs to turn off during use, because the +// boost chip's built-in protection activates. So try to keep the +// temperature relatively low. +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 45 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +#if 0 // old, 10-bit PWM method +// 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 +// 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 +#endif + +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#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 + +// safe limit ~50% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + +#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly +#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 4 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 33 + +// the entire ramp is regulated; don't blink halfway up +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-k1-boost/hwdef.h b/hw/hank/noctigon-k1-boost/hwdef.h new file mode 100644 index 0000000..a2e693a --- /dev/null +++ b/hw/hank/noctigon-k1-boost/hwdef.h @@ -0,0 +1,188 @@ +// Noctigon K1 12V driver layout (attiny1634) +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * 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. + * Regulated brightness control uses the power level pin, with PWM+DSM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-noctigon-dm11-boost.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp + +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 255 +#define PWM_CNT TCNT1 // for checking / resetting phase +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER1_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE1) + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// regulated channel +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 + +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +#define CH1_ENABLE_PIN2 PC0 // pin 15, boost PMIC enable +#define CH1_ENABLE_PORT2 PORTC // control port for PC0 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + +#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) * 1024 / 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 + +inline void hwdef_setup() { + // enable output ports + // boost PMIC on/off + DDRC = (1 << CH1_ENABLE_PIN2); + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_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 + // PWM for both channels + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (DS table 12-5) + // WGM1[3:0]: 1,1,1,0: PWM, Fast, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-k1-sbt90/cfg.h b/hw/hank/noctigon-k1-sbt90/cfg.h new file mode 100644 index 0000000..568ff95 --- /dev/null +++ b/hw/hank/noctigon-k1-sbt90/cfg.h @@ -0,0 +1,96 @@ +// Noctigon K1-SBT90.2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// (is a K1 host with a KR4-like driver and a really high-powered LED) +#define MODEL_NUMBER "0252" +#include "hwdef-noctigon-k1-sbt90.h" +#include "hank-cfg.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 10 +#define USE_INDICATOR_LED_WHILE_RAMPING + + +// brightness: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// FET: ~3700 lm +#define RAMP_SIZE 150 +// FIXME: it should probably have max_regulated at 120, not 130 +// ramp copied from noctigon-kr4 +// nice low lows, but might have visible ripple on some lights: +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 +// (plus one extra level at the beginning for moon) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level + +#define MAX_1x7135 130 +#define DEFAULT_LEVEL 50 +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 + +#if 0 // original 10-bit ramp +// 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 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 +#endif + +#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 11 30 [50] 70 90 110 [130] +#define RAMP_DISCRETE_FLOOR 11 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~33% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~40% power or ~1700 lm +#define THERM_FASTER_LEVEL 130 +//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting +//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments + +#define THERM_CAL_OFFSET 5 + +// the power regulator is a bit slow, so push it harder for a quick response from off +// (unsure if necessary, copied from noctigon-kr4) +//#define DEFAULT_JUMP_START_LEVEL 21 +//#define BLINK_BRIGHTNESS DEFAULT_LEVEL +//#define BLINK_ONCE_TIME 12 + +// there is usually no lighted button, so +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// normal party strobe speed; this driver can pulse very quickly due to its DD FET +//#define PARTY_STROBE_ONTIME 2 + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// for compatibility with other models +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-k1-sbt90/hwdef.h b/hw/hank/noctigon-k1-sbt90/hwdef.h new file mode 100644 index 0000000..c89c3fc --- /dev/null +++ b/hw/hank/noctigon-k1-sbt90/hwdef.h @@ -0,0 +1,177 @@ +// Noctigon K1-SBT90.2 driver layout (attiny1634) +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (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 dynamic 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. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-noctigon-kr4.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. linear + DD FET stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // linear ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// linear channel +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +// DD FET channel +#define CH2_PIN PA6 // pin 1, DD FET PWM +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 + +// e-switch +#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 SWITCH_PUE PUEB // pullup group B +#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] + +#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) * 1024 / 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 + +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN); + // DD FET PWM, aux R/G/B + DDRA = (1 << CH2_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]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-k1/cfg.h b/hw/hank/noctigon-k1/cfg.h new file mode 100644 index 0000000..b2d4697 --- /dev/null +++ b/hw/hank/noctigon-k1/cfg.h @@ -0,0 +1,83 @@ +// Noctigon K1 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0251" +// (originally known as Emisar D1S v2) +#include "hwdef-noctigon-k1.h" +#include "hank-cfg.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 5 +#define USE_INDICATOR_LED_WHILE_RAMPING + + +#define RAMP_SIZE 150 + +// ../../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 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 50 // should be above highest dyn_pwm level +//#define HALFSPEED_LEVEL 12 +//#define QUARTERSPEED_LEVEL 4 +// 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 + +// safe limit ~75% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + +#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly +#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 10 +#define BLINK_BRIGHTNESS DEFAULT_LEVEL +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// there is usually no lighted button, +// so blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 32 + +// don't blink while ramping; the entire ramp is regulated +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/noctigon-k1/hwdef.c b/hw/hank/noctigon-k1/hwdef.c new file mode 100644 index 0000000..5d61860 --- /dev/null +++ b/hw/hank/noctigon-k1/hwdef.c @@ -0,0 +1,65 @@ +// Noctigon K1 PWM helper functions +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + //CH2_PWM = 0; + //PWM_CNT = 0; // reset phase + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp +} + +// single LED with 1 power channels, linear +void set_level_main(uint8_t level) { + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + //PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + //uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + //CH2_PWM = ch2_pwm; + // wait to sync the counter and avoid flashes + //while(actual_level && (PWM_CNT > (top - 32))) {} + //PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + //if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + //PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_SIMPLE (pwm1, CH1_PWM); + //GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + //GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + // && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/noctigon-k1/hwdef.h b/hw/hank/noctigon-k1/hwdef.h new file mode 100644 index 0000000..6467567 --- /dev/null +++ b/hw/hank/noctigon-k1/hwdef.h @@ -0,0 +1,170 @@ +// Noctigon K1 driver layout (attiny1634) +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (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. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#ifndef HWDEF_C_FILE +#define HWDEF_C_FILE hwdef-noctigon-k1.c +#endif + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LED +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // linear ramp + +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 1023 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// linear channel +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + + +#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) * 1024 / 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 + + +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_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 PWM resolution + //PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-k9.3-219/cfg.h b/hw/hank/noctigon-k9.3-219/cfg.h new file mode 100644 index 0000000..88abf05 --- /dev/null +++ b/hw/hank/noctigon-k9.3-219/cfg.h @@ -0,0 +1,15 @@ +// Noctigon K9.3 (reduced FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-noctigon-k9.3.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0263" +// ATTINY: 1634 + +// main LEDs +#undef PWM2_LEVELS +// 65% FET power +#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,3,7,11,15,19,23,27,32,37,41,46,51,56,61,66,72,78,83,89,95,102,108,115,121,128,135,143,150,157,165 + diff --git a/hw/hank/noctigon-k9.3-nofet/cfg.h b/hw/hank/noctigon-k9.3-nofet/cfg.h new file mode 100644 index 0000000..16504fd --- /dev/null +++ b/hw/hank/noctigon-k9.3-nofet/cfg.h @@ -0,0 +1,12 @@ +// Noctigon K9.3 (noFET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// no functional differences from generic emisar-2ch build +// (just use that one instead, this is only here for legacy reasons) +#include "cfg-emisar-2ch.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0262" +// ATTINY: 1634 + diff --git a/hw/hank/noctigon-k9.3/cfg.h b/hw/hank/noctigon-k9.3/cfg.h new file mode 100644 index 0000000..ba24504 --- /dev/null +++ b/hw/hank/noctigon-k9.3/cfg.h @@ -0,0 +1,111 @@ +// Noctigon K9.3 config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0261" +#include "hwdef-emisar-2ch-fet.h" +#include "hank-cfg.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 +// it also has an independent LED in the button +#define USE_BUTTON_LED +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently +// enabling this option breaks the button LED on D4v2.5 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +// enable max brightness out of the box +#define DEFAULT_CHANNEL_MODE CM_BOTH + +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + + +// main LEDs +// max regulated: 1500 to 2000 lm? +// FET: 5000 to 8000 lm? +// 2nd LEDs +// max regulated: ~1500 lm +#define RAMP_SIZE 150 + +// linear+FET ramp: maxreg at 120/150 +// level_calc.py 5.01 2 150 7135 1 0.1 1872 FET 1 10 5000 --pwm dyn:63:4096:255:3 --clock 8:16:0 +// linear segment +#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,9,9,10,10,10,11,12,12,13,14,14,16,17,18,19,20,21,22,24,25,26,28,29,31,33,35,36,38,40,42,45,47,49,52,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,108,112,117,122,127,133,138,144,150,156,162,169,175,182,189,197,204,212,220,229,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,255 +// DD FET segment +#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,6,11,17,24,30,36,43,50,57,64,71,79,87,95,103,111,120,129,138,147,157,167,177,187,198,209,220,231,243,255 +// PWM TOPS values +#define PWM3_LEVELS 4096,3798,3504,3214,2926,2645,2368,2098,1834,1579,1333,2304,2017,1744,1486,1242,1174,1700,1505,1323,1155,1037,1299,1163,1038,923,819,967,874,789,712,641,577,655,598,545,497,453,413,456,419,386,355,326,352,326,301,278,296,275,287,267,249,258,265,248,253,256,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +// linear-only ramp +// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0 +#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255 +// PWM_TOPS values for linear-only ramp +#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 DEFAULT_LEVEL 65 +#define MAX_1x7135 120 +#define MAX_Nx7135 MAX_1x7135 +#define HALFSPEED_LEVEL 16 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 10 // 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 + +// 10, 37, [65], 92, 120 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~25% power or ~1000 lm +#define THERM_FASTER_LEVEL 100 +#define MIN_THERM_STEPDOWN 60 // should be above highest dyn_pwm level + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 +// TODO: change speed per channel mode +// (the FET is really fast, but the regulator is not) +//#undef PARTY_STROBE_ONTIME + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 35 +#define BLINK_BRIGHTNESS 30 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/noctigon-kr4-219/cfg.h b/hw/hank/noctigon-kr4-219/cfg.h new file mode 100644 index 0000000..5d106ef --- /dev/null +++ b/hw/hank/noctigon-kr4-219/cfg.h @@ -0,0 +1,17 @@ +// Noctigon KR4 (reduced FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0213" +// ATTINY: 1634 + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +// 60% 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,0,0,0,0,0,0,0,0,0,0,6,12,18,25,32,38,45,53,60,68,75,83,91,99,108,117,125,135,144,153 + diff --git a/hw/hank/noctigon-kr4-219b/cfg.h b/hw/hank/noctigon-kr4-219b/cfg.h new file mode 100644 index 0000000..b242048 --- /dev/null +++ b/hw/hank/noctigon-kr4-219b/cfg.h @@ -0,0 +1,17 @@ +// Noctigon KR4 (reduced FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0214" +// ATTINY: 1634 + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +// 50% 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,0,0,0,0,0,0,0,0,0,0,5,10,15,21,26,32,38,44,50,56,63,69,76,83,90,97,104,112,120,128 + diff --git a/hw/hank/noctigon-kr4-2ch/cfg.h b/hw/hank/noctigon-kr4-2ch/cfg.h new file mode 100644 index 0000000..09dd604 --- /dev/null +++ b/hw/hank/noctigon-kr4-2ch/cfg.h @@ -0,0 +1,16 @@ +// Noctigon KR4 2-channel config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// (basically the same as Emisar generic 2-channel build, +// but switch on a different pin, and no lighted button) +// ATTINY: 1634 +#include "hwdef-noctigon-kr4-2ch.h" +#include "cfg-emisar-2ch.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0215" + +// the button doesn't light up +#undef USE_BUTTON_LED + diff --git a/hw/hank/noctigon-kr4-2ch/hwdef.h b/hw/hank/noctigon-kr4-2ch/hwdef.h new file mode 100644 index 0000000..a450693 --- /dev/null +++ b/hw/hank/noctigon-kr4-2ch/hwdef.h @@ -0,0 +1,47 @@ +// Noctigon KR4 w/ tint ramping +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (same driver as emisar-2ch, but with the switch on a different pin) + * + * Pin / Name / Function + * 1 PA6 2nd LED PWM (linear) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 button LED + * 6 PA1 Opamp 2 enable (2nd LEDs) + * 7 PA0 Opamp 1 enable (main LEDs) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 main LED PWM (FET) (PWM0A) (unused on some models because tint ramping) + * 16 PB3 main LED PWM (linear) (PWM1A) + * 17 PB2 MISO / e-switch (PCINT10) + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 (none) + * 20 PA7 (none) + * ADC12 thermal sensor + */ + +#define ATTINY 1634 +#include <avr/io.h> + +// move the switch to a different pin +#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 SWITCH_PUE PUEB // pullup group B +#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] + +// the rest of the config is the same as the generic Emisar 2ch build +#include "hwdef-emisar-2ch.h" + diff --git a/hw/hank/noctigon-kr4-boost/cfg.h b/hw/hank/noctigon-kr4-boost/cfg.h new file mode 100644 index 0000000..1603acf --- /dev/null +++ b/hw/hank/noctigon-kr4-boost/cfg.h @@ -0,0 +1,16 @@ +// Noctigon KR4 (12V) config options for Anduril +// (and Noctigon KR1) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// the only things different from dm11-boost are: +// - e-switch is on a different pin (defined in hwdef) +// - different model number +#include "cfg-noctigon-dm11-boost.h" +#include "hwdef-noctigon-kr4-boost.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0216" +#include "hank-cfg.h" +// ATTINY: 1634 + diff --git a/hw/hank/noctigon-kr4-boost/hwdef.h b/hw/hank/noctigon-kr4-boost/hwdef.h new file mode 100644 index 0000000..07c4f4b --- /dev/null +++ b/hw/hank/noctigon-kr4-boost/hwdef.h @@ -0,0 +1,57 @@ +// Noctigon KR4 boost driver layout (attiny1634) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (based on Noctigon DM11-boost and KR4) + * (is basically the same except the switch is on a different pin) + * + * 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 L: button LED + * 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 / e-switch (PCINT10) + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp power + * 20 PA7 (none) (PCINT7) + * ADC12 thermal sensor + * + * Main LED power uses one pin to turn the Opamp on/off, + * and one pin to control Opamp power level. + * Linear brightness control uses the power level pin, with dynamic PWM. + * The on/off pin is only used to turn the main LED on and off, + * not to change brightness. + */ + +#include "hwdef-noctigon-dm11-boost.h" + +// e-switch is on a different pin +#undef SWITCH_PIN +#undef SWITCH_PCINT +#undef SWITCH_PCIE +#undef SWITCH_PCMSK +#undef SWITCH_PORT +#undef SWITCH_PUE +#undef PCINT_vect +#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 SWITCH_PUE PUEB // pullup group B +#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8] + diff --git a/hw/hank/noctigon-kr4-nofet/cfg.h b/hw/hank/noctigon-kr4-nofet/cfg.h new file mode 100644 index 0000000..6d49a8b --- /dev/null +++ b/hw/hank/noctigon-kr4-nofet/cfg.h @@ -0,0 +1,66 @@ +// Noctigon KR4 (no DD FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// (and Noctigon KR1) +// (and Emisar D4v2 E21A, a.k.a. "D4v2.5") +#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0212" +// ATTINY: 1634 + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 +#define RAMP_SIZE 150 +// prioritize low lows, at risk of visible ripple +// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 +#undef PWM2_LEVELS +#undef PWM_TOPS +#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#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 11 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 11, 30, [50], 70, 90, 110, 130 (plus [150] on turbo) +#define RAMP_DISCRETE_FLOOR 11 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~1000 lm (can sustain 900 lm) +#undef SIMPLE_UI_FLOOR +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL + + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +#define PARTY_STROBE_ONTIME 2 + +// jump start a bit higher than base driver +#undef DEFAULT_JUMP_START_LEVEL +#define DEFAULT_JUMP_START_LEVEL 25 + +// stop panicking at ~1300 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 140 +#undef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range + diff --git a/hw/hank/noctigon-kr4-nofet/hwdef.c b/hw/hank/noctigon-kr4-nofet/hwdef.c new file mode 100644 index 0000000..0492def --- /dev/null +++ b/hw/hank/noctigon-kr4-nofet/hwdef.c @@ -0,0 +1,60 @@ +// Noctigon KR4 (no DD FET) PWM helper functions +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp +} + +// single set of LEDs with linear power channel +void set_level_main(uint8_t level) { + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = 0; + // wait to sync the counter and avoid flashes + while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + + GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM); + + if ( (pwm1 == CH1_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/noctigon-kr4/cfg.h b/hw/hank/noctigon-kr4/cfg.h new file mode 100644 index 0000000..5b24ef5 --- /dev/null +++ b/hw/hank/noctigon-kr4/cfg.h @@ -0,0 +1,89 @@ +// Noctigon KR4 config options for Anduril +// (and Emisar D4v2.5, which uses KR4 driver plus a button LED) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0211" +#include "hwdef-noctigon-kr4.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// FET: ~3700 lm +#define RAMP_SIZE 150 + +// nice low lows, but might have visible ripple on some lights: +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 +// (plus one extra level at the beginning for moon) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level + +// less ripple, but lows are a bit higher than ideal: +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:4096:255 +// (plus one extra level at the beginning for moon) +//#define PWM1_LEVELS 0,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,33,34,34,34,34,34,34,33,32,31,30,28,26,24,21,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +//#define PWM_TOPS 4095,4095,3760,3403,3020,2611,2176,3582,3062,2515,1940,3221,2761,2283,2998,2584,3004,2631,2899,2555,2735,2836,2538,2606,2636,2638,2387,2382,2361,2328,2286,2238,2185,2129,2070,2010,1949,1887,1826,1766,1706,1648,1591,1536,1482,1429,1379,1329,1242,1199,1122,1084,1016,953,895,842,791,723,659,602,549,482,422,367,302,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 130 +#define DEFAULT_LEVEL 50 +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 + +#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 11 30 [50] 70 90 110 [130] +#define RAMP_DISCRETE_FLOOR 11 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~30% power / ~1300 lm (can sustain 900 lm) +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1300 lm +#define THERM_FASTER_LEVEL 120 + +#define THERM_CAL_OFFSET 5 + + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS DEFAULT_LEVEL +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// there is usually no lighted button, so +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +//#define PARTY_STROBE_ONTIME 2 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-kr4/hwdef.c b/hw/hank/noctigon-kr4/hwdef.c new file mode 100644 index 0000000..884151d --- /dev/null +++ b/hw/hank/noctigon-kr4/hwdef.c @@ -0,0 +1,63 @@ +// Noctigon KR4 PWM helper functions +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp +} + +// single set of LEDs with 2 stacked power channels, linear + DD FET +void set_level_main(uint8_t level) { + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + // wait to sync the counter and avoid flashes + while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/hank/noctigon-kr4/hwdef.h b/hw/hank/noctigon-kr4/hwdef.h new file mode 100644 index 0000000..5570fb7 --- /dev/null +++ b/hw/hank/noctigon-kr4/hwdef.h @@ -0,0 +1,194 @@ +// Noctigon KR4 / D4V2.5 driver layout (attiny1634) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Pin / Name / Function + * 1 PA6 FET PWM (direct drive) (PWM1B) (on some models) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 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 (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. + * Linear brightness control uses the power level pin, with dynamic 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. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#ifndef HWDEF_C_FILE +#define HWDEF_C_FILE hwdef-noctigon-kr4.c +#endif + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. linear + DD FET stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 +// no args +//#define USE_CHANNEL_MODE_ARGS +//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // linear ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// linear channel +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +// DD FET channel +#define CH2_PIN PA6 // pin 1, DD FET PWM +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 + +// e-switch +#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 SWITCH_PUE PUEB // pullup group B +#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 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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// some variants also have 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 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +inline void hwdef_setup() { + // enable output ports + // Opamp level and Opamp on/off + DDRB = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN); + // DD FET PWM, aux R/G/B, button LED + DDRA = (1 << CH2_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 + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/hank/noctigon-m44/cfg.h b/hw/hank/noctigon-m44/cfg.h new file mode 100644 index 0000000..88bf628 --- /dev/null +++ b/hw/hank/noctigon-m44/cfg.h @@ -0,0 +1,134 @@ +// Noctigon M44 config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0143" +#include "hwdef-noctigon-m44.h" +#include "hank-cfg.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 +// it also has an independent LED in the button +#define USE_BUTTON_LED +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently +// enabling this option breaks the button LED on D4v2.5 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +// enable max brightness out of the box +#define DEFAULT_CHANNEL_MODE CM_BOTH + +//#define FACTORY_RESET_WARN_CHANNEL CM_CH2 +//#define FACTORY_RESET_SUCCESS_CHANNEL CM_BOTH + +#define USE_CONFIG_COLORS +//#define CONFIG_WAITING_CHANNEL CM_CH2 +//#define CONFIG_BLINK_CHANNEL CM_BOTH + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 +// aux red + aux blue are the correct colors, but are dim +//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + +// channel 1 +// output: unknown, 6000 lm? +// channel 2 +// output: unknown, 6000 lm? +#define RAMP_SIZE 150 +#if 0 // optimized hand-tweaked ramp from before PWM+DSM update +// "100% power" ramp +// level_calc.py 5.01 1 150 7135 0 2.0 5000 --pwm dyn:64:16384:511:5 +// (with manual tweaks) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,6,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,11,11,12,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,31,32,34,35,37,39,40,42,44,46,48,50,52,54,57,59,62,64,67,69,72,75,78,81,84,87,91,94,98,101,105,109,113,117,121,126,130,135,140,145,150,155,160,165,171,177,183,189,195,201,208,214,221,228,236,243,251,258,266,274,283,291,300,309,318,328,337,347,357,368,378,389,400,411,423,435,447,459,472,484,498,511 +#define PWM_TOPS 16383,16383,10002,11695,8083,9374,9793,7993,8291,7017,7180,6235,5431,5556,4927,4385,3916,3511,3159,2852,2585,2349,2142,1958,1795,1648,1517,1398,1290,1193,1104,1023,950,883,822,765,714,667,623,582,632,610,588,566,544,512,535,505,523,494,509,480,492,502,509,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 +// max "200% power" ramp and tops +// 1-130: 0 to 100% power +// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511 +// 131 to 150: 101% to 200% power +// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022 +//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022 +//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 +#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +#define HALFSPEED_LEVEL 10 +#define QUARTERSPEED_LEVEL 2 +#endif // end old ramp config + +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 + +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#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 + +// 10 40 [70] 100 130 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~???? lm +#define THERM_FASTER_LEVEL 130 + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver takes a while to start making any light +#define PARTY_STROBE_ONTIME 12 +//#define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry +// bike strobe needs a longer pulse too? nope +#define BIKE_STROBE_ONTIME 8 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// party strobe, tac strobe, police, lightning, candle, bike +#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO + +// the power regulator is a bit slow, so push it harder for a quick response from off +//#define DEFAULT_JUMP_START_LEVEL 50 +//#define JUMP_START_TIME 50 +#define BLINK_BRIGHTNESS 30 +#define BLINK_ONCE_TIME 16 // longer blink, since the boost driver is slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// for consistency with KR4 (not otherwise necessary though) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-m44/hwdef.c b/hw/hank/noctigon-m44/hwdef.c new file mode 100644 index 0000000..395a7a2 --- /dev/null +++ b/hw/hank/noctigon-m44/hwdef.c @@ -0,0 +1,262 @@ +// hwdef for Noctigon M44 2-channel light +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + + +void set_level_zero(); + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +void set_level_auto(uint8_t level); + +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +bool gradual_tick_auto(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + .gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + .gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + .gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + .gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_auto, + .gradual_tick = gradual_tick_auto, + .has_args = 1 + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + + // turn off all LEDs + ch1_dsm_lvl = 0; + ch2_dsm_lvl = 0; + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp +} + + +// wrap setting the dsm vars, to get a faster response +// (just setting *_dsm_lvl doesn't work well for strobes) +// set new values for both channels, +// handling any possible combination +// and any before/after state +void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2, + bool ch1_on, bool ch2_on) { + + bool was_on = (CH1_PWM>0) || (CH2_PWM>0) + || ( CH1_ENABLE_PORT & (1 << CH1_ENABLE_PIN) ) + || ( CH2_ENABLE_PORT & (1 << CH2_ENABLE_PIN) ); + //bool now_on = (ch1>0) || (ch2>0) || ch1_on || ch2_on; + + #if 0 // not needed any more, after switching to PWM+DSM + // phase-correct PWM at zero (for flicker-free moon), + // fast PWM otherwise + if (ch1 || ch2) + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + //| (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + else + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + #endif + + if (ch1 || ch1_on) + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp + else + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp + + if (ch2 || ch2_on) + CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp + else + CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp + + // set delta-sigma soft levels + ch1_dsm_lvl = ch1; + ch2_dsm_lvl = ch2; + + // set hardware PWM levels and init dsm loop + CH1_PWM = ch1_pwm = ch1 >> 7; + CH2_PWM = ch2_pwm = ch2 >> 7; + + #if 0 // not needed any more, after switching to PWM+DSM + // manual phase sync when changing level while already on + if (was_on && now_on) while(PWM_CNT > (top - 32)) {} + + PWM_TOP = top; + #endif + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; + + // reset phase when turning on + //if ((! was_on) | (! now_on)) PWM_CNT = 0; + if (! was_on) PWM_CNT = 0; + +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + ch1_dsm += (ch1_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f); + // clear carry bit + ch1_dsm &= 0x7f; + + // repeat for other channels + + ch2_dsm += (ch2_dsm_lvl & 0x007f); + ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f); + ch2_dsm &= 0x7f; +} + + +void set_level_ch1(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(pwm, 0, + 1, 0); +} + +void set_level_ch2(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(0, pwm, + 0, 1); +} + +void set_level_both(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(pwm, pwm, + 1, 1); +} + +void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) { + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint8_t blend; + if (channel_mode == CM_AUTO) { + blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + } else { + blend = cfg.channel_mode_args[channel_mode]; + } + + calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend); +} + +void set_level_blend(uint8_t level) { + PWM_DATATYPE warm, cool; + uint8_t blend = cfg.channel_mode_args[channel_mode]; + blend_helper(&warm, &cool, level); + // don't turn off either emitter entirely while using middle blends + set_hw_levels(warm, cool, + blend < 255, blend > 0); +} + +void set_level_auto(uint8_t level) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, level); + // don't turn off either emitter entirely + // (it blinks pretty bright when the regulator turns on mid-ramp) + set_hw_levels(warm, cool, + 1, 1); +} + +///// "gradual tick" functions for smooth thermal regulation ///// +// (and other smooth adjustments) + +///// bump each channel toward a target value ///// +bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) { + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = ch1_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl); + + steps = ch2_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl); + + if ((ch1 == ch1_dsm_lvl) + && (ch2 == ch2_dsm_lvl )) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_ch1(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, 0); +} + +bool gradual_tick_ch2(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, pwm); +} + +bool gradual_tick_both(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, pwm); +} + +bool gradual_tick_blend(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} + +bool gradual_tick_auto(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} + + diff --git a/hw/hank/noctigon-m44/hwdef.h b/hw/hank/noctigon-m44/hwdef.h new file mode 100644 index 0000000..5658c9f --- /dev/null +++ b/hw/hank/noctigon-m44/hwdef.h @@ -0,0 +1,206 @@ +// hwdef for Noctigon M44 2-channel light +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Pin / Name / Function + * 1 PA6 ch2 LED PWM (boost) (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 button LED + * 6 PA1 (none) + * 7 PA0 Opamp 2 enable (channel 2 LEDs) + * 8 GND GND + * 9 VCC VCC + * 10 PC5 (none) + * 11 PC4 (none) + * 12 PC3 RESET + * 13 PC2 (none) + * 14 PC1 SCK + * 15 PC0 (none) + * 16 PB3 ch1 LED PWM (linear) (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI / battery voltage (ADC6) + * 19 PB0 Opamp 1 enable (channel 1 LEDs) + * 20 PA7 e-switch (PCINT7) + * ADC12 thermal sensor + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-noctigon-m44.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. channel 1 only +// * 1. channel 2 only +// * 2. both channels, tied together, max "200%" power +// * 3. both channels, manual blend, max "100%" power +// * 4? both channels, manual blend, max 200% power +// * 4. both channels, auto blend, reversible +#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES) +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, + RGB_AUX_ENUMS +}; + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000011111 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, 128=middle CCT, 0=warm-to-cool +#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp +//#define PWM2_DATATYPE uint16_t // max "200% power" ramp table + +// PWM parameters of both channels are tied together because they share a counter +// dynamic PWM +#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 +#define PWM_CNT TCNT1 // for checking / resetting phase +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER1_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE1) + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// 1st channel (8 LEDs) +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; +#define CH1_PIN PB3 // pin 16, Opamp reference +#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3 +#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power +#define CH1_ENABLE_PORT PORTB // control port for PB0 + +// 2nd channel (8 LEDs) +uint16_t ch2_dsm_lvl; +uint8_t ch2_pwm, ch2_dsm; +#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference +#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6 +#define CH2_ENABLE_PIN PA0 // pin 7, Opamp power +#define CH2_ENABLE_PORT PORTA // control port for PA0 + + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + +#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) * 1024 / 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 + +#define BUTTON_LED_PIN PA2 // pin 5 +#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 + + +inline void hwdef_setup() { + // enable output ports + //DDRC = (1 << CH3_PIN); + DDRB = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN) + ; + DDRA = (1 << CH2_PIN) + | (1 << CH2_ENABLE_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 + // PWM for both channels + // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, adjustable (DS table 12-5) + // WGM1[3:0]: 1,1,1,0: PWM, Fast, adjustable (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) | (0<<WGM10) // adjustable PWM (TOP=ICR1) (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) + //| (1<<WGM13) | (1<<WGM12) // fast adjustable PWM (DS table 12-5) + | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5) + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/lumintop/blf-gt-mini/cfg.h b/hw/lumintop/blf-gt-mini/cfg.h new file mode 100644 index 0000000..083f18e --- /dev/null +++ b/hw/lumintop/blf-gt-mini/cfg.h @@ -0,0 +1,25 @@ +// BLF/Lumintop GT Mini config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "hwdef-blf-gt-mini.h" +// Same as an Emisar D1S, except it has a lighted button +#include "cfg-emisar-d1s.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0322" + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +// too big, remove stuff to make room +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG +//#undef USE_TACTICAL_STROBE_MODE + diff --git a/hw/lumintop/blf-gt-mini/hwdef.h b/hw/lumintop/blf-gt-mini/hwdef.h new file mode 100644 index 0000000..f4a4d7b --- /dev/null +++ b/hw/lumintop/blf-gt-mini/hwdef.h @@ -0,0 +1,14 @@ +// BLF/Lumintop GT Mini driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// exactly the same as a D1S, but with a lighted button +// (which is the same hwdef as a D4) +#include "hwdef-emisar-d4.h" + +// lighted button +#ifndef AUXLED_PIN +#define AUXLED_PIN PB4 // pin 3 +#endif + diff --git a/hw/lumintop/blf-gt/cfg.h b/hw/lumintop/blf-gt/cfg.h new file mode 100644 index 0000000..580318a --- /dev/null +++ b/hw/lumintop/blf-gt/cfg.h @@ -0,0 +1,62 @@ +// BLF GT config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0321" +#include "hwdef-blf-gt.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +#define RAMP_SIZE 150 + +// First 60 values: level_calc.py 1 60 7135 4 5.0 255 +// Remainder: all 255 (buck driver at 100% duty cycle) +#define PWM1_LEVELS 4,5,6,6,7,8,9,11,12,13,15,16,18,19,21,23,25,27,30,32,34,37,40,43,46,49,52,55,59,63,66,70,75,79,83,88,93,98,103,108,114,119,125,131,137,144,150,157,164,171,179,186,194,202,210,219,228,236,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,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +// First 60 values: all 25 (buck driver at 10% power) +// Remainder: values 61-150 of level_calc.py 1 150 7135 1 3 3000 +#define PWM2_LEVELS 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, \ + 26,27,28,29,30,31,32,33,35,36,37,38,40,41,42,44,45,47,48,50,51,53,54,56,58,59,61,63,65,67,69,70,72,74,76,79,81,83,85,87,89,92,94,96,99,101,104,106,109,112,114,117,120,123,125,128,131,134,137,140,143,147,150,153,156,160,163,167,170,174,177,181,184,188,192,196,200,204,208,212,216,220,224,228,233,237,241,246,250,255 +#define POWER_80PX 138 // 2.0 Amps out of maximum 2.5 Amps +#define MAX_1x7135 60 // where it switches from PWM to current control +#define DEFAULT_LEVEL 69 // nice +#define HALFSPEED_LEVEL 17 +#define QUARTERSPEED_LEVEL 6 + +// use 2.0 A as the ceiling, 2.5 A only for turbo +// start both ramps at the bottom; even moon throws a long way on the GT +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL POWER_80PX +// 1 23 46 [69] 92 115 138 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL POWER_80PX +#define RAMP_DISCRETE_STEPS 7 + +// GT can handle heat well, so don't limit simple mode +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// smoother, more wobbly candle +#define CANDLE_AMPLITUDE 33 + +// turbo (i.e. "giggles" mode), low, tactical strobe +#define TACTICAL_LEVELS 150,30,(RAMP_SIZE+2) + +// stop panicking at 80% power, this light has plenty of thermal mass +#define THERM_FASTER_LEVEL POWER_80PX // throttle back faster when high + +// don't blink during ramp, it's irrelevant and annoying on this light +#undef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// too big, turn off extra features +//#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/lumintop/blf-gt/hwdef.h b/hw/lumintop/blf-gt/hwdef.h new file mode 100644 index 0000000..fdb0cb6 --- /dev/null +++ b/hw/lumintop/blf-gt/hwdef.h @@ -0,0 +1,102 @@ +// BLF GT driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC (unused) + * eswitch -|2 7|- Voltage divider + * AUX LED -|3 6|- Current control (buck level) + * GND -|4 5|- PWM (buck output on/off) + * ---- + * + * On high modes, the buck regulator's current level is adjusted by pin 6. + * On low modes, the buck is set to ~10% power + * and its output gets PWM'd by pin 5. + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4.c + +// channel modes +// * 0. main LEDs +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // low modes (PWM with buck at 10% power) +#define PWM2_DATATYPE uint8_t // high modes (adjustable constant current) + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// low modes (PWM turns regulator on/off) +#define CH1_PIN PB0 // pin 5 +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 + +// high modes (control voltage sets regulator's level) +#define CH2_PIN PB1 // pin 6 +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 + +#define AUXLED_PIN PB4 // pin 3 + +// e-switch +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt + +// VCC is regulated, so measure battery on pin 7 instead +#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC +#define VOLTAGE_PIN PB2 // pin 7, voltage ADC +#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds 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) +#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*4) +#endif +#ifndef ADC_22 +#define ADC_22 (92*4) +#endif + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + + +inline void hwdef_setup() { + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN); + + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/lumintop/fw3a-219/cfg.h b/hw/lumintop/fw3a-219/cfg.h new file mode 100644 index 0000000..0bd5250 --- /dev/null +++ b/hw/lumintop/fw3a-219/cfg.h @@ -0,0 +1,16 @@ +// FW3A-219 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-fw3a.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0312" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_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 +#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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,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,13,19,25,30,36,42,48,54,61,67,73,80,86,93,100,107,114,120,128 + diff --git a/hw/lumintop/fw3a-nofet/cfg.h b/hw/lumintop/fw3a-nofet/cfg.h new file mode 100644 index 0000000..b5d4166 --- /dev/null +++ b/hw/lumintop/fw3a-nofet/cfg.h @@ -0,0 +1,54 @@ +// FW3A with the FET disabled +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-fw3a.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0313" + +// don't use channel 3 (FET) +#undef PWM_CHANNELS +#undef CH3_PIN +#undef CH3_PWM +#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 MAX_Nx7135 +#undef HALFSPEED_LEVEL +#define HALFSPEED_LEVEL 15 +#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 + +// safe limit ~25% power +// 10 37 65 92 120 +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 + +// without the 3rd channel, extra features can fit +#define USE_TACTICAL_MODE + diff --git a/hw/lumintop/fw3a/cfg.h b/hw/lumintop/fw3a/cfg.h new file mode 100644 index 0000000..53c4135 --- /dev/null +++ b/hw/lumintop/fw3a/cfg.h @@ -0,0 +1,54 @@ +// FW3A config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0311" +#include "hwdef-fw3a.h" + +#define RAMP_SIZE 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 +#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,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 +#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,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 DEFAULT_LEVEL 50 +#define MAX_1x7135 65 +#define MAX_Nx7135 130 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL MAX_Nx7135 +// 10, 30, 50, [70], 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~20% power +// 20 40 60 80 100 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at about 3A or ~1100 lm, this light is a hotrod +#define THERM_FASTER_LEVEL MAX_Nx7135 + +// don't blink during ramp +#define BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// enable extra features +#define USE_SMOOTH_STEPS + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/lumintop/fw3a/hwdef.c b/hw/lumintop/fw3a/hwdef.c new file mode 100644 index 0000000..b20969d --- /dev/null +++ b/hw/lumintop/fw3a/hwdef.c @@ -0,0 +1,71 @@ +// BLF/TLF FW3A PWM helper functions +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + #ifdef CH3_PIN + CH3_PWM = 0; + #endif +} + +// TODO: implement delta-sigma modulation for better low modes + +// single set of LEDs with 3 stacked power channels, FET+N+1 +// (or just use N+1 on the no-FET model or FET+1 model) +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + #ifdef CH3_PIN + PWM_DATATYPE ch3_pwm = PWM_GET(pwm3_levels, level); + #endif + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + #ifdef CH3_PIN + CH3_PWM = ch3_pwm; + #endif +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + #ifdef CH3_PIN + PWM_DATATYPE pwm3 = PWM_GET(pwm3_levels, gt); + #endif + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + #ifdef CH3_PIN + GRADUAL_ADJUST_STACKED(pwm2, CH2_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm3, CH3_PWM); + #else + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + #endif + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + #ifdef CH3_PIN + && (pwm3 == CH3_PWM) + #endif + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/lumintop/fw3a/hwdef.h b/hw/lumintop/fw3a/hwdef.h new file mode 100644 index 0000000..519fb97 --- /dev/null +++ b/hw/lumintop/fw3a/hwdef.h @@ -0,0 +1,105 @@ +// BLF/TLF FW3A driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- optic nerve + * FET -|3 6|- 7x7135 + * GND -|4 5|- 1x7135 + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-fw3a.c + +// channel modes +// * 0. FET+7+1 stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 3 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // 7x7135 ramp +#define PWM3_DATATYPE uint8_t // DD FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// 1x7135 channel +#define CH1_PIN PB0 // pin 5, 1x7135 PWM +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 + +// 7x7135 channel +#define CH2_PIN PB1 // pin 6, 7x7135 PWM +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 + +// DD FET channel +#define CH3_PIN PB4 // pin 3, FET PWM +#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +#ifndef VISION_PIN +#define VISION_PIN PB2 // pin 7, optic nerve +//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 +//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 +#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 + + +inline void hwdef_setup() { + + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN) + | (1 << CH3_PIN); + + // configure PWM channels + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin + + // TODO: set up the vision pin + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/lumintop/fw3x-lume1/cfg.h b/hw/lumintop/fw3x-lume1/cfg.h new file mode 100644 index 0000000..184ab8e --- /dev/null +++ b/hw/lumintop/fw3x-lume1/cfg.h @@ -0,0 +1,81 @@ +// lume1 for FW3x config options for Anduril +// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Constant current Buck-Boost + FET driver + * For more information: www.loneoceans.com/labs/ + * Datasheets: + * - 1634: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8303-8-bit-AVR-Microcontroller-tinyAVR-ATtiny1634_Datasheet.pdf + * - 85: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf + */ + +#define MODEL_NUMBER "0314" +#include "hwdef-fw3x-lume1.h" +// ATTINY: 1634 + +// set this light for 50C thermal ceiling +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 50 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// it has no independent LED in the button unlike emisar d4 +//#define USE_BUTTON_LED + +// 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 + +// ../../bin/level_calc.py cube 1 149 7135 0 0.5 1000, with 0 appended to the end. +// for FET PWM (PWM2), all values are 0, except for last value of 1023 +// (with max_pwm set to 1023) +#define RAMP_SIZE 150 +//#define PWM1_LEVELS 0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,32,34,36,38,40,42,45,47,49,52,55,58,60,63,66,70,73,76,80,83,87,91,94,98,102,107,111,115,120,124,129,134,139,144,150,155,160,166,172,178,184,190,196,203,209,216,223,230,237,244,252,259,267,275,283,291,299,308,316,325,334,343,353,362,372,382,392,402,412,423,433,444,455,466,478,489,501,513,525,538,550,563,576,589,602,616,630,644,658,672,687,701,716,731,747,762,778,794,810,827,844,861,878,895,913,930,948,967,985,1004,1023,0 +#define PWM1_LEVELS 1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,56,58,61,64,67,70,74,77,80,84,88,91,95,99,103,108,112,116,121,125,130,135,140,145,150,156,161,167,173,178,184,191,197,203,210,217,223,230,238,245,252,260,268,275,283,292,300,308,317,326,335,344,353,363,372,382,392,402,413,423,434,445,456,467,478,490,502,514,526,538,551,563,576,589,603,616,630,644,658,672,687,702,717,732,747,763,778,794,811,827,844,861,878,895,913,931,949,967,985,1004,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1023 +#define DEFAULT_LEVEL 56 +#define MAX_1x7135 149 +// TODO: test if underclocking works on lume1 +//#define HALFSPEED_LEVEL 14 +//#define QUARTERSPEED_LEVEL 5 +// 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 + +// the entire ramp is regulated except turbo; don't blink halfway up +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 149 +// turn on BuckBoost from level 1 to 149, but not 150 +// Level 150 is when BuckBoost is off and FET is ON 100% +#define LED_ENABLE_PIN_LEVEL_MIN 1 +#define LED_ENABLE_PIN_LEVEL_MAX 149 +// 10 33 56 79 102 125 [149] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// slow down party strobe; this driver can't pulse for too short a time +#define PARTY_STROBE_ONTIME 4 + +// stop panicking at ~85% regulated power or ~750 lm +#define THERM_FASTER_LEVEL 140 + +#define THERM_CAL_OFFSET 0 // not needed due to external sensor + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/lumintop/fw3x-lume1/hwdef.c b/hw/lumintop/fw3x-lume1/hwdef.c new file mode 100644 index 0000000..2f31ed0 --- /dev/null +++ b/hw/lumintop/fw3x-lume1/hwdef.c @@ -0,0 +1,60 @@ +// FW3X Lume1 PWM helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase + CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable regulator +} + +// single set of LEDs with 2 stacked power channels, regulated + DD FET +void set_level_main(uint8_t level) { + CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable regulator + + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + + // force reset phase when turning on from zero + // (for faster, more consistent initial response) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + // 150/150 is full FET + zero regulated, + // 149/150 is zero FET + full regulated, + // so don't try to gradually adjust between + if ((RAMP_SIZE == actual_level) || (gt >= RAMP_SIZE-1)) { + set_level(gt + 1); + return true; + } + + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM); + + if (pwm1 == CH1_PWM) return true; // done + return false; // not done yet +} + diff --git a/hw/lumintop/fw3x-lume1/hwdef.h b/hw/lumintop/fw3x-lume1/hwdef.h new file mode 100644 index 0000000..c03248b --- /dev/null +++ b/hw/lumintop/fw3x-lume1/hwdef.h @@ -0,0 +1,202 @@ +// lume1 Driver Rev B for FW3x driver layout (attiny1634) +// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* lume1 Driver Rev B for FW3x driver layout (attiny1634) + * www.loneoceans.com/labs/ for more information + * + * Pin / Name / Function in Lume1 Rev B + * 1 PA6 Regulated PWM (PWM1B) + * 2 PA5 R: red aux LED (PWM0B) + * 3 PA4 G: green aux LED + * 4 PA3 B: blue aux LED + * 5 PA2 e-switch (PCINT2) + * 6 PA1 Jumper 1 + * 7 PA0 Jumper 2 + * 8 GND GND + * 9 VCC VCC + * 10 PC5 Jumper 3 + * 11 PC4 Jumper 4 + * 12 PC3 RESET + * 13 PC2 External Thermal Sensor (ADC11) + * 14 PC1 SCK + * 15 PC0 N/C + * 16 PB3 FET PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI + * 19 PB0 ADC5 Voltage Battery Sense (2:1 divider) + * 20 PA7 BB_Enable + * ADC12 internal thermal sensor (not used for lume1) + * + * Main LED power uses one pin as a global Buck Boost Enable, and + * one pin to control the power level via PWM. + * Another pin is used for DD FET control. + */ + +#define ATTINY 1634 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-fw3x-lume1.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +#define PWM_CHANNELS 2 // old, remove this + +// Added for Lume1 Buck Boost Driver +#define PWM_BITS 16 // 0 to 1023 at 3.9 kHz, not 0 to 255 at 15.6 kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // regulated ramp +#define PWM2_DATATYPE uint16_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM +#define PWM_TOP_INIT 1023 +#define PWM_CNT TCNT1 // for dynamic PWM, reset phase + +// regulated channel +#define CH1_PIN PA6 // pin 1, Buck Boost CTRL pin or 7135-eqv PWM +#define CH1_PWM OCR1B // OCR1B is the output compare register for PA6 +#define CH1_ENABLE_PIN PA7 // pin 20, BuckBoost Enable +#define CH1_ENABLE_PORT PORTA // control port for PA7 + +// DD FET channel +#define CH2_PIN PB3 // pin 16, FET PWM Pin, but only used as on (1023) or off (0) +#define CH2_PWM OCR1A // OCR1A is the output compare register for PB3 + +/* // For Jumpers X1 to X4, no SW support yet +#define JUMPER1_PIN PA1 +#define JUMPER2_PIN PA0 +#define JUMPER3_PIN PC5 +#define JUMPER4_PIN PC4 +*/ + +// e-switch +#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 SWITCH_PUE PUEA // pullup group A +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened +#define VOLTAGE_PIN PB0 // Pin 19 PB0 ADC5 +// pin to ADC mappings are in DS table 19-4 +#define VOLTAGE_ADC ADC5D // 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 ADC5D +// 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, 0, 1 for ADC5 / PB0 +// divided by ... +// REFS[1:0] = 0, 0 for VCC as analog reference at 2.5V +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b00000101 +#define ADC_PRSCL 0x06 // clk/64 + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// [(Vbatt)*(R2/(R2+R1)) / 2.5V] * 1023 +// R1 = R2 = 100kR +#ifndef ADC_44 +#define ADC_44 900 +#endif +#ifndef ADC_22 +#define ADC_22 450 +#endif + +// Default ADMUX_THERM for Temperature is: 0b10001110 in tk-attiny.h +// REFS[1:0] as 10 for analog reference at internal 1.1Vref +// MUX[3:0] as 1110 for ADC'12' - temperature sensor internal + +// Modified fsm-adc.c to use different ADMUX and ADC_temperature_handler() +// based on USE_EXTERNAL_TEMP_SENSOR +// See line 34 and line 209 +#define USE_EXTERNAL_TEMP_SENSOR +#define ADMUX_THERM_EXTERNAL_SENSOR 0b00001011 // VCC reference (2.5V), Channel PC2 +// Used for Lume1 Driver: MCP9700 - T_Celsius = 100*(VOUT - 0.5V) +// ADC is 2.5V reference, 0 to 1023 +// FIXME: due to floating point, this calculation takes 916 extra bytes +// (should use an integer equivalent instead) +#define EXTERN_TEMP_FORMULA(m) (((m)-205)/4.09) + +// this driver allows for 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 + +// For lume1 driver, no SW support for Auxillary Jumpers X1 to X4 yet! +inline void hwdef_setup() { + // enable output ports + // FET PWM Pin + DDRB = (1 << CH2_PIN); + // Main PWM, Buck Boost Enable Pin, aux R/G/B + DDRA = (1 << CH1_PIN) + | (1 << CH1_ENABLE_PIN) + | (1 << AUXLED_R_PIN) + | (1 << AUXLED_G_PIN) + | (1 << AUXLED_B_PIN) + ; + + //DDRB&=~(1<<VOLTAGE_PIN); // All pins are input by default + /* // For Jumpers X1 to X4, no SW support yet + DDRA &= (1<<JUMPER1_PIN); + DDRA &= (1<<JUMPER2_PIN); + DDRC &= (1<<JUMPER3_PIN); + DDRC &= (1<<JUMPER4_PIN); + PUEA = (1 << JUMPER1_PIN); + PUEA = (1 << JUMPER2_PIN); + PUEC = (1 << JUMPER3_PIN); + PUEC = (1 << JUMPER4_PIN); + */ + + // configure PWM for 10 bit at 3.9kHz + // 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 Clear OC1A on Compare Match + | (1<<COM1B1) | (0<<COM1B0) // PWM 1B Clear OC1B on Compare Match + ; + TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6) + | (0<<WGM13) | (0<<WGM12) // PWM, Phase Correct, 10-bit + ; + + // set PWM resolution + PWM_TOP = PWM_TOP_INIT; + + // set up e-switch + SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch + SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt +} + + +#define LAYOUT_DEFINED + diff --git a/hw/mateminco/mf01-mini/cfg.h b/hw/mateminco/mf01-mini/cfg.h new file mode 100644 index 0000000..fb62ac6 --- /dev/null +++ b/hw/mateminco/mf01-mini/cfg.h @@ -0,0 +1,74 @@ +// Mateminco/Astrolux MF01-Mini options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0521" +#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 +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + +#define RAMP_SIZE 150 + +// 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 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 + +// safe limit max regulated power +// 15 38 62 86 [110] +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 110 +#define SIMPLE_UI_STEPS 5 + + +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// don't blink during ramp +//#define BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#define USE_SOFT_FACTORY_RESET +//#undef USE_SIMPLE_UI +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +#undef USE_BEACON_MODE +#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG + diff --git a/hw/mateminco/mf01-mini/hwdef.h b/hw/mateminco/mf01-mini/hwdef.h new file mode 100644 index 0000000..35405db --- /dev/null +++ b/hw/mateminco/mf01-mini/hwdef.h @@ -0,0 +1,100 @@ +// Mateminco MF01-Mini driver layout +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- aux LEDs + * FET PWM -|3 6|- PWM (7x7135) + * GND -|4 5|- PWM (1x7135) + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-fw3a.c + +// channel modes +// * 0. FET+N+1 stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 3 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // 7x7135 ramp +#define PWM3_DATATYPE uint8_t // DD FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// 1x7135 channel +#define CH1_PIN PB0 // pin 5, 1x7135 PWM +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 + +// 7x7135 channel +#define CH2_PIN PB1 // pin 6, 7x7135 PWM +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 + +// DD FET channel +#define CH3_PIN PB4 // pin 3, FET PWM +#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4 + +// lighted button and 1-channel front aux +#define AUXLED_PIN PB2 // pin 7 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#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 + + +inline void hwdef_setup() { + + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN) + | (1 << CH3_PIN); + + // configure PWM channels + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/mateminco/mf01s/cfg.h b/hw/mateminco/mf01s/cfg.h new file mode 100644 index 0000000..de92693 --- /dev/null +++ b/hw/mateminco/mf01s/cfg.h @@ -0,0 +1,61 @@ +// Mateminco MT18S / Astrolux MF01S options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0511" +#include "hwdef-mateminco-mf01s.h" +// ATTINY: 85 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + +#define RAMP_SIZE 150 + +// measured brightness with 4x30Q cells at 4.11V: +// moon: 2.5 lm +// channel 1: 617 lm +// channel 2: 13500 lm +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 717 FET 1 10 13000 +// (with some manual tweaks afterward) +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,17,18,19,21,22,24,26,28,30,32,34,36,38,41,44,46,49,52,55,59,62,66,70,74,78,83,87,92,97,102,108,114,120,126,133,139,147,154,162,170,178,187,196,206,215,226,236,248,255,235,255,240,255,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,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,1,2,2,3,3,4,4,5,6,7,8,9,10,11,12,13,14,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,41,43,45,47,49,52,54,57,59,62,65,67,70,73,76,80,83,86,90,93,97,101,105,109,113,117,122,126,131,135,140,145,151,156,161,167,173,179,185,191,197,204,211,218,225,232,239,247,255 + +#define MAX_1x7135 70 // ~626 lm +#define HALFSPEED_LEVEL 23 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 // ~2.5 lm +#define RAMP_SMOOTH_CEIL 120 // ~5400 lm +// 20, 36, 53, [70], 86, 103, 120 +#define RAMP_DISCRETE_FLOOR 20 // 35 lm +#define RAMP_DISCRETE_CEIL 120 // ~5400 lm +#define RAMP_DISCRETE_STEPS 7 // 35, 108, 280, 626, 1500, 2930, 5400 lm + +// safe limit ~25% power +// 18 35 52 [70] 87 105 +#define SIMPLE_UI_FLOOR 18 +#define SIMPLE_UI_CEIL 105 +#define SIMPLE_UI_STEPS 6 + +#define THERM_FASTER_LEVEL 125 // throttle back faster when high (>6000 lm) +#define THERM_HARD_TURBO_DROP // this light is massively overpowered + +// don't blink during ramp, it's irrelevant and annoying on this light +#define BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/mateminco/mf01s/hwdef.h b/hw/mateminco/mf01s/hwdef.h new file mode 100644 index 0000000..af214b2 --- /dev/null +++ b/hw/mateminco/mf01s/hwdef.h @@ -0,0 +1,102 @@ +// Mateminco MT18S / Astrolux MF01S driver layout +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC (unused) + * eswitch -|2 7|- Voltage divider (2S) + * AUX LED -|3 6|- PWM (FET) + * GND -|4 5|- PWM (smaller FET) + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4.c + +// channel modes +// * 0. small FET + big FET stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // little FET ramp +#define PWM2_DATATYPE uint8_t // big FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// little FET channel +#define CH1_PIN PB0 // pin 5, 1x7135 PWM +#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0 + +// big FET channel +#define CH2_PIN PB1 // pin 6, FET PWM +#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1 + +// lighted button and 1-channel front aux +#define AUXLED_PIN PB4 // pin 3 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC +#ifndef VOLTAGE_PIN +#define VOLTAGE_PIN PB2 // pin 7, voltage ADC +#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds 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 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*4) +#endif +#ifndef ADC_22 +#define ADC_22 (117*4) +#endif + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + + +inline void hwdef_setup() { + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN); + + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/mateminco/mt35-mini/cfg.h b/hw/mateminco/mt35-mini/cfg.h new file mode 100644 index 0000000..1ca5008 --- /dev/null +++ b/hw/mateminco/mt35-mini/cfg.h @@ -0,0 +1,65 @@ +// Mateminco MT35 Mini / Astrolux FT03 +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0531" +#include "hwdef-mateminco-mt35-mini.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +#define RAMP_SIZE 150 + +// level_calc.py fifth 2 150 7135 1 1 120 FET 1 10 2000 +#define PWM1_LEVELS 1,2,2,3,4,5,6,7,8,9,11,13,14,16,18,21,23,26,28,31,35,38,41,45,49,53,58,63,67,73,78,84,90,96,102,109,116,124,131,139,147,156,165,174,184,194,204,215,226,237,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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,1,2,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,20,21,22,24,25,27,28,30,31,33,35,36,38,40,42,43,45,47,49,51,53,55,57,59,62,64,66,68,71,73,75,78,80,83,86,88,91,94,96,99,102,105,108,111,114,117,120,124,127,130,134,137,140,144,148,151,155,159,162,166,170,174,178,182,186,190,195,199,203,208,212,217,221,226,231,235,240,245,250,255 + +#define DEFAULT_LEVEL 46 +#define MAX_1x7135 52 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 10 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 112 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~33% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// this light should be fine running a bit hotter than most +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 55 + +// stop panicking at ~40% power +#define THERM_FASTER_LEVEL 120 +#define MIN_THERM_STEPDOWN 95 // ~600 lumens +#define THERM_RESPONSE_MAGNITUDE 16 // smaller adjustments, big body. default = 64 + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo (Anduril 1 style) + +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG + diff --git a/hw/mateminco/mt35-mini/hwdef.h b/hw/mateminco/mt35-mini/hwdef.h new file mode 100644 index 0000000..015c813 --- /dev/null +++ b/hw/mateminco/mt35-mini/hwdef.h @@ -0,0 +1,93 @@ +// Mateminco MT35-Mini / Astrolux FT03 +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- Aux LED + * 1x7135 -|3 6|- NC + * GND -|4 5|- FET + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-emisar-d4.c + +// channel modes +// * 0. FET+7135 stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 8 // attiny85 only supports up to 8 bits +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t +#define PWM1_DATATYPE uint8_t // little FET ramp +#define PWM2_DATATYPE uint8_t // big FET ramp + +#define PWM_TOP_INIT 255 // highest value used in top half of ramp + +// 1x7135 channel +#define CH1_PIN PB4 // pin 3, 1x7135 PWM +#define CH1_PWM OCR1B // OCR1B is the output compare register for PB4 + +// DD FET channel +#define CH2_PIN PB0 // pin 5, FET PWM +#define CH2_PWM OCR0A // OCR0A is the output compare register for PB0 + +// lighted button +#define AUXLED_PIN PB2 // pin 7 + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt +#endif + +#define ADC_PRSCL 0x07 // clk/128 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + + +inline void hwdef_setup() { + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN); + + // configure PWM channels + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/blf-lt1-t1616/cfg.h b/hw/sofirn/blf-lt1-t1616/cfg.h new file mode 100644 index 0000000..fc02619 --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/cfg.h @@ -0,0 +1,110 @@ +// BLF Lantern config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0622" +#include "hwdef-blf-lt1-t1616.h" +// ATTINY: 1616 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +#define DEFAULT_CHANNEL_MODE CM_AUTO +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define CONFIG_WAITING_CHANNEL CM_BOTH +#define CONFIG_BLINK_CHANNEL CM_BOTH + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +// 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% + +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 +#define PWM1_LEVELS 4,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 + +#define DEFAULT_LEVEL 75 +#define MAX_1x7135 75 +#define HALFSPEED_LEVEL 0 // always use tint ramping correction +#define QUARTERSPEED_LEVEL 2 // quarter speed at level 1, full speed at 2+ +//#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp + +#define USE_SET_LEVEL_GRADUALLY + + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1 can handle heat well, so don't limit simple mode +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 +// aux red + aux blue are the correct colors, but are dim +//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// party strobe, tac strobe, police, lightning, candle, bike +#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO + +// the sensor (attiny1616) is nowhere near the emitters +// so thermal regulation can't work +#ifdef USE_THERMAL_REGULATION +#undef USE_THERMAL_REGULATION +#endif + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +// except the top... blink at the top +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// for consistency with other models +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-lt1-t1616/hwdef.c b/hw/sofirn/blf-lt1-t1616/hwdef.c new file mode 100644 index 0000000..9d268a4 --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/hwdef.c @@ -0,0 +1,210 @@ +// Sofirn LT1-t1616 PWM helpers +// Copyright (C) 2023 SiteRelEnby, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-aux.c" + + +void set_level_zero(); + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +void set_level_auto(uint8_t level); + +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +bool gradual_tick_auto(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + .gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + .gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + .gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + .gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_auto, + .gradual_tick = gradual_tick_auto, + .has_args = 1 + }, + AUX_CHANNELS +}; + + +void set_level_zero() { + // disable timer overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL = 0; + + // turn off all LEDs + ch1_dsm_lvl = 0; + ch2_dsm_lvl = 0; + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; +} + +void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) { + + bool was_on = (CH1_PWM>0) || (CH2_PWM>0); + + // set delta-sigma soft levels + ch1_dsm_lvl = ch1; + ch2_dsm_lvl = ch2; + + // set hardware PWM levels and init dsm loop + CH1_PWM = ch1_pwm = ch1 >> 7; + CH2_PWM = ch2_pwm = ch2 >> 7; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL = DSM_OVF_bm; + + // reset phase when turning on + if (! was_on) PWM_CNT = 0; + +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + ch1_dsm += (ch1_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f); + // clear carry bit + ch1_dsm &= 0x7f; + + // repeat for other channels + + ch2_dsm += (ch2_dsm_lvl & 0x007f); + ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f); + ch2_dsm &= 0x7f; + + // clear the interrupt flag to indicate it was handled + // as per: https://onlinedocs.microchip.com/pr/GUID-C37FFBA8-82C6-4339-A2B1-ABD9A0F6C162-en-US-8/index.html?GUID-C2A2BEFD-158F-413D-B9D4-0F0556AA79BD + DSM_INTFLAGS = DSM_OVF_bm; +} + + +void set_level_ch1(uint8_t level) { + set_hw_levels(PWM_GET(pwm1_levels, level), 0); +} + +void set_level_ch2(uint8_t level) { + set_hw_levels(0, PWM_GET(pwm1_levels, level)); +} + +void set_level_both(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(pwm, pwm); +} + +void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) { + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint8_t blend; + if (channel_mode == CM_AUTO) { + blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + } else { + blend = cfg.channel_mode_args[channel_mode]; + } + + calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend); +} + +void set_level_blend(uint8_t level) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, level); + set_hw_levels(warm, cool); +} + +void set_level_auto(uint8_t level) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, level); + set_hw_levels(warm, cool); +} + +///// "gradual tick" functions for smooth thermal regulation ///// +// (and other smooth adjustments) + +///// bump each channel toward a target value ///// +bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) { + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = ch1_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl); + + steps = ch2_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl); + + if ((ch1 == ch1_dsm_lvl) + && (ch2 == ch2_dsm_lvl )) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_ch1(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, 0); +} + +bool gradual_tick_ch2(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, pwm); +} + +bool gradual_tick_both(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, pwm); +} + +bool gradual_tick_blend(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} + +bool gradual_tick_auto(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} + + diff --git a/hw/sofirn/blf-lt1-t1616/hwdef.h b/hw/sofirn/blf-lt1-t1616/hwdef.h new file mode 100644 index 0000000..7c1f10b --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/hwdef.h @@ -0,0 +1,161 @@ +// BLF LT1 driver layout using the Attiny1616 +// Copyright (C) 2021-2023 (gchart), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Driver pinout: + * eSwitch: PA5 + * Aux LED: PB5 + * PWM cool: PB0 (TCA0 WO0) + * PWM warm: PB1 (TCA0 WO1) + * Voltage: VCC + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-blf-lt1-t1616.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. warm only +// * 1. cool only +// * 2. both channels, tied together, max "200%" power +// * 3. both channels, manual blend, max "100%" power +// * 4. both channels, auto blend, reversible +#define NUM_CHANNEL_MODES 6 +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, + CM_AUX +}; + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00011000 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, 128=middle CCT, 0=warm-to-cool +#define CHANNEL_MODE_ARGS 0,0,0,128,0,0 + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp + +// PWM parameters of both channels are tied together because they share a counter +// dynamic PWM +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TCA0_OVF_vect +#define DSM_INTCTRL TCA0.SINGLE.INTCTRL +#define DSM_INTFLAGS TCA0.SINGLE.INTFLAGS +#define DSM_OVF_bm TCA_SINGLE_OVF_bm + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// warm LEDs +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; +#define CH1_PIN PB1 +#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1 + +// cold LEDs +uint16_t ch2_dsm_lvl; +uint8_t ch2_pwm, ch2_dsm; +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// lighted button +#define AUXLED_PIN PIN5_bp +#define AUXLED_PORT PORTB + +// e-switch +#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 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs + VPORTB.DIR = PIN0_bm // cool white + | PIN1_bm // warm white + // | PIN2_bm // for testing on LT1S Pro, disable red channel + | PIN5_bm; // aux LED + //VPORTC.DIR = ...; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // cold tint channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // warm tint channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // comment out for testing on LT1S Pro + PORTB.PIN3CTRL = PORT_PULLUPEN_bm; + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // TODO: add references to MCU documentation + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + + // set up interrupt for delta-sigma modulation + // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) + //DSM_INTCTRL |= DSM_OVF_bm; // interrupt once for each timer cycle + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/blf-lt1/cfg.h b/hw/sofirn/blf-lt1/cfg.h new file mode 100644 index 0000000..53c3203 --- /dev/null +++ b/hw/sofirn/blf-lt1/cfg.h @@ -0,0 +1,105 @@ +// BLF Lantern config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0621" +#include "hwdef-blf-lt1.h" +// ATTINY: 85 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +#define DEFAULT_CHANNEL_MODE CM_AUTO +//#define DEFAULT_BLINK_CHANNEL CM_BOTH // takes too much space + +// 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% +#define TINT_RAMPING_CORRECTION 0 // none + +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 +#define PWM1_LEVELS 32,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 + +#define DEFAULT_LEVEL 75 +#define MAX_1x7135 75 +#define HALFSPEED_LEVEL 44 +#define QUARTERSPEED_LEVEL 34 +#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp + +#define USE_SMOOTH_STEPS +//#define USE_SET_LEVEL_GRADUALLY + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1 can handle heat well, so don't limit simple mode +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE +#define USE_EXTENDED_SIMPLE_UI + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + + +// the sensor (attiny85) is nowhere near the emitters +// so thermal regulation can't work +#ifdef USE_THERMAL_REGULATION +#undef USE_THERMAL_REGULATION +#endif + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// too big, turn off extra features +//#undef USE_STEPPED_TINT_RAMPING +#undef USE_MOMENTARY_MODE +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_SIMPLE_UI +//#undef USE_BEACON_MODE +//#undef USE_RAMP_SPEED_CONFIG +#undef USE_RAMP_AFTER_MOON_CONFIG +#undef USE_2C_STYLE_CONFIG +#undef USE_VOLTAGE_CORRECTION +//#undef USE_CHANNEL_PER_STROBE +// party strobe, tac strobe, lightning, candle, bike +#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO + +// for consistency with other models +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-lt1/hwdef.c b/hw/sofirn/blf-lt1/hwdef.c new file mode 100644 index 0000000..8a4c0e1 --- /dev/null +++ b/hw/sofirn/blf-lt1/hwdef.c @@ -0,0 +1,204 @@ +// BLF LT1 PWM functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + + +void set_level_zero(); + +void set_level_ch1(uint8_t level); +void set_level_ch2(uint8_t level); +void set_level_both(uint8_t level); +void set_level_blend(uint8_t level); +//void set_level_auto(uint8_t level); // redundant + +#if 0 // gradual adjustments are disabled to save space +bool gradual_tick_ch1(uint8_t gt); +bool gradual_tick_ch2(uint8_t gt); +bool gradual_tick_both(uint8_t gt); +bool gradual_tick_blend(uint8_t gt); +//bool gradual_tick_auto(uint8_t gt); // redundant +#endif + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_ch1, + //.gradual_tick = gradual_tick_ch1, + .has_args = 0 + }, + { // channel 2 only + .set_level = set_level_ch2, + //.gradual_tick = gradual_tick_ch2, + .has_args = 0 + }, + { // both channels, tied together (max "200%" power) + .set_level = set_level_both, + //.gradual_tick = gradual_tick_both, + .has_args = 0 + }, + { // both channels, manual blend (max "100%" power) + .set_level = set_level_blend, + //.gradual_tick = gradual_tick_blend, + .has_args = 1 + }, + { // both channels, auto blend + .set_level = set_level_blend, + //.gradual_tick = gradual_tick_blend, + .has_args = 1 + }, +}; + +void set_level_zero() { + // disable timer 0 overflow interrupt + // (helps improve button press handling from Off state) + DSM_INTCTRL &= ~DSM_OVF_bm; + + // turn off all LEDs + ch1_dsm_lvl = 0; + ch2_dsm_lvl = 0; + CH1_PWM = 0; + CH2_PWM = 0; +} + +// wrap setting the dsm vars, to get a faster response +// (just setting *_dsm_lvl doesn't work well for strobes) +void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) { + // set delta-sigma soft levels + ch1_dsm_lvl = ch1; + ch2_dsm_lvl = ch2; + + // set hardware PWM levels and init dsm loop + CH1_PWM = ch1_pwm = ch1 >> 7; + CH2_PWM = ch2_pwm = ch2 >> 7; + + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(DSM_vect) { + // set new hardware values first, + // for best timing (reduce effect of interrupt jitter) + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + + // calculate next values, now that timing matters less + + // accumulate error + ch1_dsm += (ch1_dsm_lvl & 0x007f); + // next PWM = base PWM value + carry bit + ch1_pwm = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f); + // clear carry bit + ch1_dsm &= 0x7f; + + // repeat for other channels + + ch2_dsm += (ch2_dsm_lvl & 0x007f); + ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f); + ch2_dsm &= 0x7f; +} + + +void set_level_ch1(uint8_t level) { + set_hw_levels(PWM_GET(pwm1_levels, level), 0); +} + +void set_level_ch2(uint8_t level) { + set_hw_levels(0, PWM_GET(pwm1_levels, level)); +} + +void set_level_both(uint8_t level) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level); + set_hw_levels(pwm, pwm); +} + +void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) { + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint8_t blend; + if (channel_mode == CM_AUTO) { + blend = 255 * (uint16_t)level / RAMP_SIZE; + if (cfg.channel_mode_args[channel_mode] & 0b01000000) + blend = 255 - blend; + } else { + blend = cfg.channel_mode_args[channel_mode]; + } + + calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend); +} + +void set_level_blend(uint8_t level) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, level); + set_hw_levels(warm, cool); +} + +/* +void set_level_auto(uint8_t level) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, level); + set_hw_levels(warm, cool); +} +*/ + +///// "gradual tick" functions for smooth thermal regulation ///// +// (and other smooth adjustments) + +#if 0 // disabled to save space +///// bump each channel toward a target value ///// +bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) { + // adjust multiple times based on current brightness + // (so it adjusts faster/coarser when bright, slower/finer when dim) + + // higher shift = slower/finer adjustments + const uint8_t shift = 9; // ((255 << 7) >> 9) = 63 max + uint8_t steps; + + steps = ch1_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl); + + steps = ch2_dsm_lvl >> shift; + for (uint8_t i=0; i<=steps; i++) + GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl); + + if ((ch1 == ch1_dsm_lvl) + && (ch2 == ch2_dsm_lvl )) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_ch1(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, 0); +} + +bool gradual_tick_ch2(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(0, pwm); +} + +bool gradual_tick_both(uint8_t gt) { + PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt); + return gradual_adjust(pwm, pwm); +} + +bool gradual_tick_blend(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} + +/* +bool gradual_tick_auto(uint8_t gt) { + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); +} +*/ + +#endif // if 0 + diff --git a/hw/sofirn/blf-lt1/hwdef.h b/hw/sofirn/blf-lt1/hwdef.h new file mode 100644 index 0000000..b113fd4 --- /dev/null +++ b/hw/sofirn/blf-lt1/hwdef.h @@ -0,0 +1,114 @@ +// BLF LT1 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ---- + * Reset -|1 8|- VCC + * eswitch -|2 7|- (unused) + * aux LED -|3 6|- PWM (5000K) + * GND -|4 5|- PWM (3000K) + * ---- + */ + +#define ATTINY 85 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-blf-lt1.c + +// channel modes: +// * 0. channel 1 only +// * 1. channel 2 only +// * 2. both channels, tied together, max "200%" power +// * 3. both channels, manual blend, max "100%" power +// * 4. both channels, auto blend, reversible +#define NUM_CHANNEL_MODES 5 +enum channel_modes_e { + CM_CH1 = 0, + CM_CH2, + CM_BOTH, + CM_BLEND, + CM_AUTO, +}; + + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00011000 +#define USE_CHANNEL_MODE_ARGS +// _, _, _, 128=middle CCT, 0=warm-to-cool +#define CHANNEL_MODE_ARGS 0,0,0,128,0 + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 16 // 8-bit hardware PWM + 16-bit DSM + +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // 15-bit PWM+DSM ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP_INIT 255 +#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry + +// timer interrupt for DSM +#define DSM_vect TIMER0_OVF_vect +#define DSM_INTCTRL TIMSK +#define DSM_OVF_bm (1<<TOIE0) + +#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts + +// warm LEDs +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; +#define CH1_PIN PB1 // pin 6, warm tint PWM +#define CH1_PWM OCR0B // OCR0B is the output compare register for PB1 + +// cold LEDs +uint16_t ch2_dsm_lvl; +uint8_t ch2_pwm, ch2_dsm; +#define CH2_PIN PB0 // pin 5, cold tint PWM +#define CH2_PWM OCR0A // OCR0A is the output compare register for PB0 + +// lighted button +#define AUXLED_PIN PB4 // pin 3 + +// e-switch +#define SWITCH_PIN PB3 // pin 2 +#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt + +#define ADC_PRSCL 0x07 // clk/128 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#define FAST 0xA3 // fast PWM both channels +#define PHASE 0xA1 // phase-correct PWM both channels + + +inline void hwdef_setup() { + // configure PWM channels + DDRB = (1 << CH1_PIN) + | (1 << CH2_PIN); + + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + + // enable timer overflow interrupt for DSM purposes + //DSM_INTCTRL |= DSM_OVF_bm; // moved to hwdef.c functions instead + + // configure e-switch + PORTB = (1 << SWITCH_PIN); // e-switch is the only input + PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin +} + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/blf-q8-t1616/cfg.h b/hw/sofirn/blf-q8-t1616/cfg.h new file mode 100644 index 0000000..9f0402c --- /dev/null +++ b/hw/sofirn/blf-q8-t1616/cfg.h @@ -0,0 +1,82 @@ +// BLF Q8 config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0613" +#include "hwdef-blf-q8-t1616.h" +#include "wurkkos-cfg.h" // Sofirn lights are closely related to Wurkkos +// ATTINY: 1616 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// copied from Wurkkos TS25 ramp +#define RAMP_SIZE 150 +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 1 200 3000 +// (FIXME? there's a visible bump when the FET kicks in, even with just 1/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,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 20 38 56 [75] 93 111 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +#define SIMPLE_UI_FLOOR 1 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-q8-t1616/hwdef.h b/hw/sofirn/blf-q8-t1616/hwdef.h new file mode 100644 index 0000000..d9b981f --- /dev/null +++ b/hw/sofirn/blf-q8-t1616/hwdef.h @@ -0,0 +1,138 @@ +// BLF Q8 driver layout using the Attiny1616 +// Copyright (C) 2021-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (based on Wurkkos TS10 driver layout, + * which in turn was based on an older version of this BLF-Q8-t1616 driver) + * (should probably merge the two files at some point) + * Driver pinout: + * eSwitch: PA5 + * Aux LED: PB5 + * PWM FET: PB0 (TCA0 WO0) + * PWM 1x7135: PB1 (TCA0 WO1) + * Voltage: VCC + */ + +#define ATTINY 1616 +#include <avr/io.h> + +// nearly all t1616-based FET+1 drivers work pretty much the same +// (this one has single-color aux like the TS10) +#define HWDEF_C_FILE hwdef-wurkkos-ts10.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. FET+7135 stacked +// * 1. aux LEDs +#define NUM_CHANNEL_MODES 2 +enum CHANNEL_MODES { + CM_MAIN = 0, + CM_AUX +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +// not necessary when double-buffered "BUF" registers are used +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// 1x7135 channel +#define CH1_PIN PB1 +#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1 + +// DD FET channel +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// e-switch +#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 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +// lighted button +#define AUXLED_PIN PIN5_bp +#define AUXLED_PORT PORTB + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs + VPORTB.DIR = PIN0_bm // DD FET + | PIN1_bm // 7135 + | PIN5_bm; // Aux LED + //VPORTC.DIR = ...; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; + PORTB.PIN3CTRL = PORT_PULLUPEN_bm; + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/blf-q8/cfg.h b/hw/sofirn/blf-q8/cfg.h new file mode 100644 index 0000000..29dbcb7 --- /dev/null +++ b/hw/sofirn/blf-q8/cfg.h @@ -0,0 +1,71 @@ +// BLF Q8 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0611" +#include "hwdef-blf-q8.h" + +#define RAMP_SIZE 150 + +// 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 (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +// copied from Emisar D4 ramp +// ../../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 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 MAX_1x7135 65 +#define DEFAULT_LEVEL 65 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#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 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE +#define USE_EXTENDED_SIMPLE_UI + +// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +#define USE_SMOOTH_STEPS + +// too big, remove stuff to make room +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG +//#undef USE_TACTICAL_STROBE_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/sofirn/blf-q8/hwdef.h b/hw/sofirn/blf-q8/hwdef.h new file mode 100644 index 0000000..cdf311d --- /dev/null +++ b/hw/sofirn/blf-q8/hwdef.h @@ -0,0 +1,20 @@ +// BLF Q8 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// Q8 driver is the same as a D4, basically + +// ... except the Q8 has a lighted button +#ifndef AUXLED_PIN +#define AUXLED_PIN PB4 // pin 3 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +// Q8 driver is the same as a D4, basically +#include "hwdef-emisar-d4.h" + diff --git a/hw/sofirn/lt1s-pro/cfg.h b/hw/sofirn/lt1s-pro/cfg.h new file mode 100644 index 0000000..440a03d --- /dev/null +++ b/hw/sofirn/lt1s-pro/cfg.h @@ -0,0 +1,125 @@ +// Sofirn LT1S Pro config file for Anduril +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0623" +#include "hwdef-sofirn-lt1s-pro.h" +// ATTINY: 1616 + +// off mode: low (1) +// lockout: blinking (3) +// Standby power usage: +// - aux high: 6.9 mA (30 days) +// - aux low: 0.16 mA (3.5 years) +// - red moon: 2.17 mA (96 days) +// - white moon: 1.47 mA (141 days) +// Low mode isn't bright enough to be useful on this light, +// but at least it doesn't drain the battery 3X faster than moon mode. +// (it seriously would be more practical to just use moon instead) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_WHITE, CM_AUTO2, CM_AUTO3, CM_RED, CM_WHITE_RED +#define DEFAULT_CHANNEL_MODE CM_AUTO3 + +#define FACTORY_RESET_WARN_CHANNEL CM_RED +#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE + +#define CONFIG_WAITING_CHANNEL CM_RED +#define CONFIG_BLINK_CHANNEL CM_WHITE + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_RED + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +// seems unnecessary on this light +#define TINT_RAMPING_CORRECTION 0 + +#define RAMP_SIZE 150 +// use dynamic PWM instead of plain 8-bit +// (so we can get lower lows and a smoother ramp) +// (also, red LEDs use a QX7138 chip which has max PWM speed of 10 kHz, +// and it behaves erratically at full speed, +// so PWM here is 576 clock cycles long to keep the speed low enough) +// +// This first ramp seems a bit too low: 0.2 / 1.9 / 10 / 37 / 109 / 272 / 600 lm +// level_calc.py 5.99 1 150 7135 1 0.2 600 --pwm dyn:77:16383:575:3 +//#define PWM_LEVELS 1,1,2,2,3,4,4,5,6,6,7,8,9,9,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,24,24,24,25,26,26,27,28,29,30,32,33,34,36,38,40,42,44,46,48,50,52,55,57,59,62,65,68,70,73,77,80,83,86,90,94,97,101,105,110,114,118,123,128,133,138,143,148,154,160,166,172,178,185,191,198,205,213,220,228,236,244,252,261,270,279,289,298,308,319,329,340,351,363,374,386,399,411,424,438,452,466,480,495,510,526,542,558,575 +//#define PWM_TOPS 16383,10869,13246,8043,11458,12772,10093,11043,11450,9664,9991,10091,10048,8868,8838,8730,7814,7724,7589,6864,6748,6604,6024,5899,5398,5287,5159,4754,4638,4287,3963,3876,3594,3511,3265,3038,2829,2770,2586,2417,2260,2115,1981,1857,1742,1636,1537,1445,1360,1281,1207,1138,1073,1013,957,904,855,848,803,760,720,714,677,643,637,630,599,592,585,577,569,579,570,560,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 +// +// This ramp is a bit higher: 0.2 / 3 / 19 / 61 / 152 / 320 / 600 lm +// level_calc.py 4.001 1 150 7135 1 0.2 600 --pwm dyn:78:16383:575:3.333 +#define PWM1_LEVELS 1,1,2,4,5,6,7,8,9,10,12,13,14,15,17,18,19,21,22,23,24,25,26,27,28,29,30,31,32,33,33,34,34,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,38,38,39,40,40,41,43,44,45,47,48,50,52,54,56,59,62,64,67,69,72,75,78,81,84,87,90,94,97,101,104,108,112,116,120,124,128,133,137,142,147,151,156,161,167,172,177,183,189,194,200,206,213,219,226,232,239,246,253,260,268,275,283,291,299,307,316,324,333,342,351,361,370,380,390,400,410,420,431,442,453,464,476,487,499,511,523,536,549,562,575 +#define PWM_TOPS 16383,8174,7823,14429,13603,12806,12046,11328,10652,10017,10402,9742,9134,8575,8615,8089,7605,7536,7093,6684,6307,5959,5636,5337,5060,4802,4562,4337,4127,3929,3633,3468,3216,3077,2862,2744,2559,2390,2234,2091,1960,1838,1727,1623,1528,1439,1357,1280,1209,1143,1081,1024,970,919,872,828,787,770,732,716,682,668,654,624,611,613,600,587,587,574,573,571,569,566,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 +// TODO? 200% power at top of ramp on white blend mode +// 2nd table handles "200% power" turbo +//#define PWM2_LEVELS ... +// tops for PWM2 +//#define PWM3_LEVELS ... +#define MAX_1x7135 75 +#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 5 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +//#define RAMP_DISCRETE_FLOOR 17 // 17 50 83 116 150 +#define RAMP_DISCRETE_FLOOR 1 // 1 25 50 75 100 125 150 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1S can handle heat well, so don't limit simple mode +//#define SIMPLE_UI_FLOOR 10 // 10 45 80 115 150 +#define SIMPLE_UI_FLOOR 1 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 7 + +// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// turn on at med-low brightness by default (level 50/150, or ramp step 3/7) +// (also sets lockout mode 2H to a useful level) +#define DEFAULT_MANUAL_MEMORY 50 +// reset to default after being off for 10 minutes +#define DEFAULT_MANUAL_MEMORY_TIMER 10 + +// enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_COLOR_STROBE_CH1 CM_RED +#define POLICE_COLOR_STROBE_CH2 CM_WHITE + +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#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 + +#define USE_SOFT_FACTORY_RESET diff --git a/hw/sofirn/lt1s-pro/hwdef.c b/hw/sofirn/lt1s-pro/hwdef.c new file mode 100644 index 0000000..90c2c07 --- /dev/null +++ b/hw/sofirn/lt1s-pro/hwdef.c @@ -0,0 +1,237 @@ +// BLF LT1S Pro hwdef functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + + +void set_level_zero(); + +void set_level_red(uint8_t level); +void set_level_white_blend(uint8_t level); +void set_level_auto_2ch_blend(uint8_t level); +void set_level_auto_3ch_blend(uint8_t level); +void set_level_red_white_blend(uint8_t level); + +bool gradual_tick_red(uint8_t gt); +bool gradual_tick_white_blend(uint8_t gt); +bool gradual_tick_auto_2ch_blend(uint8_t gt); +bool gradual_tick_auto_3ch_blend(uint8_t gt); +bool gradual_tick_red_white_blend(uint8_t gt); + + +Channel channels[] = { + { // manual blend of warm and cool white + .set_level = set_level_white_blend, + .gradual_tick = gradual_tick_white_blend, + .has_args = 1 + }, + { // auto blend from warm white to cool white + .set_level = set_level_auto_2ch_blend, + .gradual_tick = gradual_tick_auto_2ch_blend, + .has_args = 0 + }, + { // auto blend from red to warm white to cool white + .set_level = set_level_auto_3ch_blend, + .gradual_tick = gradual_tick_auto_3ch_blend, + .has_args = 0 + }, + { // red only + .set_level = set_level_red, + .gradual_tick = gradual_tick_red, + .has_args = 0 + }, + { // manual white blend + adjustable red + .set_level = set_level_red_white_blend, + .gradual_tick = gradual_tick_red_white_blend, + .has_args = 1 + } +}; + + +// calculate a 3-channel "auto tint" blend +// (like red -> warm white -> cool white) +// results are placed in *a, *b, and *c vars +// level : ramp level to convert into 3 channel levels +// (assumes ramp table is "pwm1_levels") +void calc_auto_3ch_blend( + PWM_DATATYPE *a, + PWM_DATATYPE *b, + PWM_DATATYPE *c, + uint8_t level) { + + PWM_DATATYPE vpwm = PWM_GET(pwm1_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; + + // red is high at 0, low at 255 (linear) + *a = (((PWM_DATATYPE2)(255 - mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // warm white is low at 0 and 255, high at 127 (linear triangle) + *b = (((PWM_DATATYPE2)triangle_wave(mytint) + * (PWM_DATATYPE2)vpwm) + 127) / 255; + // cool white is low at 0, high at 255 (linear) + *c = (((PWM_DATATYPE2)mytint + * (PWM_DATATYPE2)vpwm) + 127) / 255; + +} + + +void set_level_zero() { + WARM_PWM_LVL = 0; + COOL_PWM_LVL = 0; + RED_PWM_LVL = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with 1 power channel and dynamic PWM +void set_level_red(uint8_t level) { + RED_PWM_LVL = PWM_GET(pwm1_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + PWM_TOP = PWM_GET(pwm_tops, level); + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + + +// warm + cool blend w/ dynamic PWM +void set_level_white_blend(uint8_t level) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + PWM_DATATYPE top = PWM_GET(pwm_tops, level); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend); + + WARM_PWM_LVL = warm_PWM; + COOL_PWM_LVL = cool_PWM; + PWM_TOP = top; + if (! actual_level) PWM_CNT = 0; // reset phase +} + + +// same as white blend, but tint is calculated from the ramp level +void set_level_auto_2ch_blend(uint8_t level) { + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + PWM_DATATYPE top = PWM_GET(pwm_tops, level); + uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend); + + WARM_PWM_LVL = warm_PWM; + COOL_PWM_LVL = cool_PWM; + PWM_TOP = top; + if (! actual_level) PWM_CNT = 0; // reset phase +} + + +// "auto tint" channel mode with dynamic PWM +void set_level_auto_3ch_blend(uint8_t level) { + PWM_DATATYPE a, b, c; + calc_auto_3ch_blend(&a, &b, &c, level); + + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET(pwm_tops, level); + + RED_PWM_LVL = a; + WARM_PWM_LVL = b; + COOL_PWM_LVL = c; + PWM_TOP = top; + if (! actual_level) PWM_CNT = 0; +} + + +// "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_white_blend(level); + channel_mode = CM_WHITE_RED; + + PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level); + + // set the red LED as a ratio of the white output level + // 0 = no red + // 255 = red at 100% of white channel PWM + uint8_t ratio = cfg.channel_mode_args[channel_mode]; + + RED_PWM_LVL = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255; + if (! actual_level) PWM_CNT = 0; // reset phase +} + + +///// "gradual tick" functions for smooth thermal regulation ///// + +///// bump each channel toward a target value ///// +bool gradual_adjust(uint16_t red, uint16_t warm, uint16_t cool) { + GRADUAL_ADJUST_SIMPLE(red, RED_PWM_LVL ); + GRADUAL_ADJUST_SIMPLE(warm, WARM_PWM_LVL); + GRADUAL_ADJUST_SIMPLE(cool, COOL_PWM_LVL); + + // check for completion + if ((red == RED_PWM_LVL ) + && (warm == WARM_PWM_LVL) + && (cool == COOL_PWM_LVL)) { + return true; // done + } + return false; // not done yet +} + +bool gradual_tick_red(uint8_t gt) { + uint16_t red = PWM_GET(pwm1_levels, gt); + return gradual_adjust(red, 0, 0); +} + + +bool gradual_tick_white_blend(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE top = PWM_GET(pwm_tops, gt); + uint8_t blend = cfg.channel_mode_args[channel_mode]; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend); + + return gradual_adjust(0, warm_PWM, cool_PWM); +} + + +// same as white blend, but tint is calculated from the ramp level +bool gradual_tick_auto_2ch_blend(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE top = PWM_GET(pwm_tops, gt); + uint8_t blend = 255 * (uint16_t)gt / RAMP_SIZE; + + calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend); + + return gradual_adjust(0, warm_PWM, cool_PWM); +} + + +bool gradual_tick_auto_3ch_blend(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + PWM_DATATYPE red, warm, cool; + calc_auto_3ch_blend(&red, &warm, &cool, gt); + return gradual_adjust(red, warm, cool); +} + + +bool gradual_tick_red_white_blend(uint8_t gt) { + // figure out what exact PWM levels we're aiming for + PWM_DATATYPE red, warm, cool; + PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE top = PWM_GET(pwm_tops, gt); + uint8_t blend = cfg.channel_mode_args[CM_WHITE]; + uint8_t ratio = cfg.channel_mode_args[channel_mode]; + + red = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)brightness) + 127) / 255; + calc_2ch_blend(&warm, &cool, brightness, top, blend); + + return gradual_adjust(red, warm, cool); +} + diff --git a/hw/sofirn/lt1s-pro/hwdef.h b/hw/sofirn/lt1s-pro/hwdef.h new file mode 100644 index 0000000..ae6b3bf --- /dev/null +++ b/hw/sofirn/lt1s-pro/hwdef.h @@ -0,0 +1,151 @@ +// BLF LT1S Pro driver layout using the Attiny1616 +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Driver pinout: + * eSwitch: PA5 + * Aux LED: PB5 + * WW PWM: PB0 (TCA0 WO0) + * CW PWM: PB1 (TCA0 WO1) + * Red PWM: PB2 (TCA0 WO2) + * Voltage: VCC + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-sofirn-lt1s-pro.c + +// channel modes: +// * 0. warm/cool white blend +// * 1. auto 2ch white blend (warm -> cool by ramp level) +// * 2. auto 3ch blend (red -> warm -> cool by ramp level) +// * 3. red only +// * 4. red + white blend +#define NUM_CHANNEL_MODES 5 +enum channel_modes_e { + CM_WHITE = 0, + CM_AUTO2, + CM_AUTO3, + CM_RED, + CM_WHITE_RED, +}; + +#define CHANNEL_MODES_ENABLED 0b00011111 +#define USE_CHANNEL_MODE_ARGS +// 128=middle CCT, _, _, _, 255=100% red +#define CHANNEL_MODE_ARGS 128,0,0,0,255 + +// can use some of the common handlers +#define USE_CALC_2CH_BLEND +//#define USE_CALC_AUTO_3CH_BLEND + + +#define PWM_CHANNELS 1 // old, remove this + +// only using 16-bit PWM on this light +#define PWM_BITS 16 + +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t +#define PWM1_DATATYPE uint16_t +#define PWM_DATATYPE2 uint32_t + +// dynamic PWM +// PWM parameters of all channels are tied together because they share a counter +#define PWM_TOP_INIT 511 // highest value used in the top half of the ramp +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// warm LEDs +//#define WARM_PWM_PIN PB0 +#define WARM_PWM_LVL TCA0.SINGLE.CMP0BUF // CMP1 is the output compare register for PB0 + +// cold LEDs +//#define COOL_PWM_PIN PB1 +#define COOL_PWM_LVL TCA0.SINGLE.CMP1BUF // CMP0 is the output compare register for PB1 + +// red LEDs +//#define RED_PWM_PIN PB2 +#define RED_PWM_LVL TCA0.SINGLE.CMP2BUF // CMP2 is the output compare register for PB2 + +// lighted button +#define AUXLED_PIN PIN5_bp +#define AUXLED_PORT PORTB + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +// e-switch +#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 + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs + VPORTB.DIR = PIN0_bm // warm white + | PIN1_bm // cool white + | PIN2_bm // red + | PIN5_bm; // aux LED + //VPORTC.DIR = ...; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // warm tint channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // cold tint channel + //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // red LEDs + PORTB.PIN3CTRL = PORT_PULLUPEN_bm; + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // TODO: add references to MCU documentation + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_CMP2EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + PWM_TOP = PWM_TOP_INIT; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; +} + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/sc21-pro/cfg.h b/hw/sofirn/sc21-pro/cfg.h new file mode 100644 index 0000000..8fd2dee --- /dev/null +++ b/hw/sofirn/sc21-pro/cfg.h @@ -0,0 +1,14 @@ +// Sofirn SC21 Pro - same setup as a Wurkkos TS10, but with the aux indicator on while ramping +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-wurkkos-ts10.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0632" +// ATTINY: 1616 + +// turn on the aux LED while main LED is on +#ifndef USE_INDICATOR_LED_WHILE_RAMPING +#define USE_INDICATOR_LED_WHILE_RAMPING +#endif diff --git a/hw/sofirn/sp10-pro/cfg.h b/hw/sofirn/sp10-pro/cfg.h new file mode 100644 index 0000000..0e2f28d --- /dev/null +++ b/hw/sofirn/sp10-pro/cfg.h @@ -0,0 +1,81 @@ +// Sofirn SP10 Pro config options for Anduril +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0631" +#include "hwdef-sofirn-sp10-pro.h" +// ATTINY: 1616 + +// 1....15: level_calc.py 3.01 1 15 7135 1 0.1 2 --pwm dyn:15:64:64 +// 16..150: level_calc.py 5.01 1 135 7135 1 2 800 --pwm dyn:49:3072:255:3.0 +#define RAMP_SIZE 150 +#define _PWM1_LEVELS_ 1, 2, 4, 6, 9,12,15,19,23,28,34,41,48,55,64 +#define _PWM1_TOPS_ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +#define _PWM2_LEVELS_ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 44, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 74, 77, 79, 82, 85, 88, 90, 93, 96, 99,103,106,109,113,116,120,123,127,131,135,139,143,147,151,156,160,165,170,175,180,185,190,195,201,206,212,218,223,230,236,242,248,255 +#define _PWM2_TOPS_ 3072,1960,2372,1476,2097,1572,1920,1570,1777,1524,1646,1454,1286,1369,1234,1115,1011,918,837,894,823,759,702,650,603,560,522,487,455,425,398,374,351,330,310,292,275,259,280,265,251,266,253,240,252,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 PWM1_LEVELS _PWM1_LEVELS_,_PWM2_TOPS_ +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_PWM2_LEVELS_ +#define PWM_TOPS _PWM1_TOPS_,_PWM2_TOPS_ + +#define MAX_1x7135 15 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 15 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 1 25 50 [75] 100 125 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +// 15 48 [82] 116 150 +#define SIMPLE_UI_FLOOR MAX_1x7135 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// turn on at ~6 lm by default (level 50/150, or ramp step 2/5 or 3/7) +// (also sets lockout mode 2H to a useful level) +#define DEFAULT_MANUAL_MEMORY 50 +// reset to default after being off for 10 minutes +#define DEFAULT_MANUAL_MEMORY_TIMER 10 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// and finally, set the default ramp style to Stepped +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped + +// stop panicking at ~30% power +#define THERM_FASTER_LEVEL 105 +#define MIN_THERM_STEPDOWN 65 // must be > end of dynamic PWM range + +// slow down party strobe; this driver can't pulse for too short a time +//#define PARTY_STROBE_ONTIME 8 +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 50 + +// enable 2 click turbo (replaces USE_2C_MAX_TURBO) +#define DEFAULT_2C_STYLE 1 + +// don't blink during the ramp or at the ceiling +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + + +// enable factory reset on 13H without loosening tailcap +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/sp10-pro/hwdef.c b/hw/sofirn/sp10-pro/hwdef.c new file mode 100644 index 0000000..42844a7 --- /dev/null +++ b/hw/sofirn/sp10-pro/hwdef.c @@ -0,0 +1,63 @@ +// Sofirn SP10 Pro PWM helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase + BST_ENABLE_PORT &= ~(1 << BST_ENABLE_PIN); // boost off +} + +// single set of LEDs with 2 stacked power channels +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); // boost on + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + PWM_TOP = top; + + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + // ch1 sometimes makes huge leaps; don't make it gradual + // if either current or new level is in the leap zone, just leap + if ((CH1_PWM + pwm1) > 128) CH1_PWM = pwm1; + else GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM); + + GRADUAL_ADJUST_SIMPLE(pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/sofirn/sp10-pro/hwdef.h b/hw/sofirn/sp10-pro/hwdef.h new file mode 100644 index 0000000..5cef6a7 --- /dev/null +++ b/hw/sofirn/sp10-pro/hwdef.h @@ -0,0 +1,157 @@ +// Sofirn SP10 Pro pinout +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * ATTINY1616 Mapping: + * PB5 : PWM small channel (TCA0 - WO2 Alternate MUX) + * PB3 : eSwitch + * PB0 : PWM big channel (TCA0 - WO0) + * PB4 : Voltage divider (ADC0 - AIN9) + * PA1 : Boost Enable + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-sofirn-sp10-pro.c + +// channel modes: +// * 0. low+high PWM stacked +#define NUM_CHANNEL_MODES 1 +enum CHANNEL_MODES { + CM_MAIN = 0, +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // data type needs 16 bits, not 8 +#define PWM_GET PWM_GET16 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint16_t // low PWM ramp +#define PWM2_DATATYPE uint16_t // high PWM ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment +#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) + +// Small channel +#define CH1_PIN PB5 +#define CH1_PWM TCA0.SINGLE.CMP2BUF // PB5 is Alternate MUX for TCA Compare 2 + +// Big channel +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // PB0 is TCA Compare 0 + +// boost enable +#define BST_ENABLE_PIN PIN1_bp +#define BST_ENABLE_PORT PORTA_OUT + +// e-switch +#define SWITCH_PIN 3 +#define SWITCH_PORT VPORTB.IN +#define SWITCH_ISC_REG PORTB.PIN3CTRL +#define SWITCH_VECT PORTB_PORT_vect +#define SWITCH_INTFLG VPORTB.INTFLAGS +#define SWITCH_PCINT PCINT0 +#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0] + +// Voltage divider battLVL +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated +#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW 7 // the lower voltage range's danger zone 0.7 volts (NiMH) +#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN9_gc // which ADC channel to read + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 1024 / 1.1) +// Resistors are 300,000 and 100,000 +#ifndef ADC_44 +#define ADC_44 1023 // raw value at 4.40V +#endif +#ifndef ADC_22 +#define ADC_22 512 // raw value at 2.20V +#endif + + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + VPORTA.DIR = PIN1_bm; // Boost enable pin + VPORTB.DIR = PIN0_bm // big PWM channel + | PIN5_bm; // small PWM channel + //VPORTC.DIR = ...; + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // Boost enable pin + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm; + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // Big PWM channel + PORTB.PIN1CTRL = PORT_PULLUPEN_bm; + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; + PORTB.PIN3CTRL = PORT_PULLUPEN_bm + | PORT_ISC_BOTHEDGES_gc; // e-switch + //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // Voltage divider + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Small PWM channel + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + PORTMUX.CTRLC = PORTMUX_TCA02_ALTERNATE_gc; // Use alternate pin for TCA0:WO2 + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP2EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + PWM_TOP = PWM_TOP_INIT; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; +} + + +// set fuses, these carry over to the ELF file +// we need this for enabling BOD in Active Mode from the factory. +// settings can be verified / dumped from the ELF file using this +// command: avr-objdump -d -S -j .fuse anduril.elf +FUSES = { + .WDTCFG = FUSE_WDTCFG_DEFAULT, // Watchdog Configuration + .BODCFG = FUSE_ACTIVE0, // BOD Configuration + .OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration + .TCD0CFG = FUSE_TCD0CFG_DEFAULT, // TCD0 Configuration + .SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0 + .SYSCFG1 = FUSE_SYSCFG1_DEFAULT, // System Configuration 1 + .APPEND = FUSE_APPEND_DEFAULT, // Application Code Section End + .BOOTEND = FUSE_BOOTEND_DEFAULT, // Boot Section End +}; + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/sp36-t1616/cfg.h b/hw/sofirn/sp36-t1616/cfg.h new file mode 100644 index 0000000..cb29e4f --- /dev/null +++ b/hw/sofirn/sp36-t1616/cfg.h @@ -0,0 +1,37 @@ +// Sofirn SP36 (small Q8) config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8, mostly +#include "cfg-blf-q8-t1616.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0614" +// ATTINY: 1616 + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +// the high button LED mode on this light uses too much power +// off mode: low (1) +// lockout: blinking (3) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) +#endif + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// stop panicking at ~60% power or ~3000 lm +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 130 diff --git a/hw/sofirn/sp36/cfg.h b/hw/sofirn/sp36/cfg.h new file mode 100644 index 0000000..3661686 --- /dev/null +++ b/hw/sofirn/sp36/cfg.h @@ -0,0 +1,36 @@ +// Sofirn SP36 (small Q8) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8, mostly +#include "cfg-blf-q8.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0612" + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +// the high button LED mode on this light uses too much power +// off mode: low (1) +// lockout: blinking (3) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) +#endif + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// stop panicking at ~60% power or ~3000 lm +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 130 diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h new file mode 100644 index 0000000..460346e --- /dev/null +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h @@ -0,0 +1,116 @@ +// thefreeman's BCK-FWAA-MP3432 (li-ion / AA) +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1632" +#include "hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h" +// ATTINY: 1616 + +// HPRsense : 4.2+0.3+20 = 24.5mR (DMN1004UFDF+trace resistance+20mR) +// R1=165k Vsense=49.02 Iout=2001mA +// LPRsense : 2R +// transition DAC lvl 14, ramp lvl 51 +// fifth power ramp 0.06mA to 2001mA + +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +#define PWM1_LEVELS 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \ + 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \ + 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \ + 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 50 +#define DEFAULT_LEVEL 44 +#define HDR_ENABLE_LEVEL_MIN 51 // when HDR FET turns ON + +// no PWM, so MCU clock speed can be slow +#define HALFSPEED_LEVEL 46 +#define QUARTERSPEED_LEVEL 45 // seems to run fine at 10kHz/4, try reducing more + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 // ~50% power, ~??? mA / ??? lm +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 20 [45] 70 95 120 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 120 // ~37% power, ~??? mA / ??? lm +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// thermal config + +// temperature limit +#define THERM_FASTER_LEVEL 130 +#define MIN_THERM_STEPDOWN MAX_1x7135 + +//#define THERM_LOOKAHEAD 2 // 4 by default -> decrease for longer turbo +//#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) +//#define THERM_RESPONSE_MAGNITUDE 16 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) +//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation + + +// UI + +//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo + + +// AUX + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// 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 + + +// Misc + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 40 + +// enable 13H factory reset so it can be used on tail e-switch lights +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h new file mode 100644 index 0000000..9126a1a --- /dev/null +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h @@ -0,0 +1,187 @@ +// hwdef for thefreeman's boost FWAA driver 1.1 w/ MP3432, HDR DAC, RGB aux +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* thefreeman’s FWAA AA/li-ion Boost driver based on MP3432 and attiny1616 + * with high dynamic range and DAC control, AUX : RGB + * hardware versions : 1.0, 1.1 + * + * Pin / Name / Function + * 1 PA2 BattLVL (ADC0 - AIN2) + * 2 PA3 + * 3 GND GND + * 4 VCC VCC + * 5 PA4 BST EN: boost enable + * 6 PA5 HDR + * 7 PA6 DAC + * 8 PA7 + * 9 PB5 B: blue aux LED + * 10 PB4 G: green aux LED + * 11 PB3 R: red aux LED + * 12 PB2 IN- NFET + * 13 PB1 + * 14 PB0 + * 15 PC0 + * 16 PC1 + * 17 PC2 + * 18 PC3 e-switch + * 19 PA0 UDPI + * 20 PA1 + * + * BST EN enable the boost regulator and Op-Amp + * DAC sets the current, max current depends on Vset voltage divider and Rsense + * HDR FET switches between high value Rsense (low current range, pin low), + * and low value Rsense (high current range, pin high) + * IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 8 // 8-bit DAC +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // main LED ramp + +// main LED outputs +#define DAC_LVL DAC0.DATA // 0 to 255, for 0V to Vref +#define DAC_VREF VREF.CTRLA // 0.55V or 2.5V +#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) +// Vref values +#define V055 16 +#define V11 17 +#define V25 18 +#define V43 19 +#define V15 20 + +// BST enable +#define BST_ENABLE_PIN PIN4_bp +#define BST_ENABLE_PORT PORTA_OUT + +// HDR +// turns on HDR FET for the high current range +#define HDR_ENABLE_PIN PIN5_bp +#define HDR_ENABLE_PORT PORTA_OUT + +// IN- NFET +// pull high to force output to zero to eliminate the startup flash +#define IN_NFET_DELAY_TIME 4 // (ms) +#define IN_NFET_ENABLE_PIN PIN2_bp +#define IN_NFET_ENABLE_PORT PORTB_OUT + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PIN3_bp +#define SWITCH_PORT VPORTC.IN +#define SWITCH_ISC_REG PORTC.PIN3CTRL +#define SWITCH_VECT PORTC_PORT_vect +#define SWITCH_INTFLG VPORTC.INTFLAGS +#define SWITCH_PCINT PCINT0 +#define PCINT_vect PCINT0_vect +#endif + +// Voltage divider battLVL +#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is regulated +#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level +#define DUAL_VOLTAGE_LOW_LOW 7 // the lower voltage range's danger zone 0.7 volts (NiMH) +#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN2_gc // which ADC channel to read + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 1024 / 1.1) +// Resistors are 330k and 100k +#ifndef ADC_44 +#define ADC_44 951 // raw value at 4.40V +#endif +#ifndef ADC_22 +#define ADC_22 476 // raw value at 2.20V +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN3_bp +#define AUXLED_G_PIN PIN4_bp +#define AUXLED_B_PIN PIN5_bp +#define AUXLED_RGB_PORT PORTB // PORTA or PORTB or PORTC + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + + +inline void hwdef_setup() { + + // TODO: for this DAC controlled-light, try to decrease the clock speed or use the ULP + // set up the system clock to run at 10 MHz to match other attiny1616 lights + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + VPORTA.DIR = PIN4_bm // BST EN + | PIN5_bm // HDR + | PIN6_bm; // DAC + VPORTB.DIR = PIN2_bm // IN- NFET + | PIN3_bm // R + | PIN4_bm // G + | PIN5_bm; // B + //VPORTC.DIR = PIN0_bm | PIN1_bm; + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // BattLVL + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN4CTRL = PORT_PULLUPEN_bm; // EN + //PORTA.PIN5CTRL = PORT_PULLUPEN_bm; // HDR + //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // DAC + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + PORTB.PIN0CTRL = PORT_PULLUPEN_bm; + PORTB.PIN1CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // IN- NFET + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // R + //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // G + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // B + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm + | PORT_ISC_BOTHEDGES_gc; // e-switch + + // set up the DAC + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // DAC ranges from 0V to (255 * Vref) / 256 + // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc + VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; + VREF.CTRLB |= VREF_DAC0REFEN_bm; + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; + DAC0.DATA = 255; // set the output voltage + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h new file mode 100644 index 0000000..66cc2d0 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h @@ -0,0 +1,119 @@ +// thefreeman's BST21 BST20-FWxA (no button LED) +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1631" +#include "hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h" +// ATTINY: 1616 + +// HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR) +// Vsense=42.46mV, R1= 191k +// LPRsense : 1R +// transition DAC level 8, ramp level 45 +// fifth power ramp 0.1mA to 6066mA + +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +#define PWM1_LEVELS 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \ + 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \ + 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \ + 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 44 +#define DEFAULT_LEVEL 44 +#define HDR_ENABLE_LEVEL_MIN 45 // when HDR FET turns ON + +// no PWM, so MCU clock speed can be slow +#define HALFSPEED_LEVEL 41 +#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 // 50% / 3A / 1000 lm +// 1 22 [44] 65 87 108 130 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 20 [45] 70 95 120 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 120 // ~2.25A / ~750 lm +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// thermal config + +// temperature limit +#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/3A +#define MIN_THERM_STEPDOWN MAX_1x7135 + +//#define THERM_LOOKAHEAD 4 // 4 by default -> decrease for longer turbo +#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) +#define THERM_RESPONSE_MAGNITUDE 32 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) +//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation + + +// UI + +//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo + + +// AUX + +//#define USE_BUTTON_LED + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// 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 + + +// Misc + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 40 + +// enable 13H factory reset so it can be used on tail e-switch lights +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c new file mode 100644 index 0000000..31feec9 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c @@ -0,0 +1,90 @@ +// thefreeman boost driver 2.1 output helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + DAC_LVL = 0; // DAC off + DAC_VREF = V055; // low Vref + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off + + // prevent post-off flash + IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + delay_4ms(IN_NFET_DELAY_TIME/4); + IN_NFET_ENABLE_PORT &= ~(1 << IN_NFET_ENABLE_PIN); + + // turn off boost last + BST_ENABLE_PORT &= ~(1 << BST_ENABLE_PIN); // BST off +} + +// single set of LEDs with 1 regulated power channel +// and low/high HDR plus low/high Vref as different "gears" +void set_level_main(uint8_t level) { + uint8_t noflash = 0; + + // when turning on from off, use IN_NFET to prevent a flash + if ((! actual_level) && (level < HDR_ENABLE_LEVEL_MIN)) { + noflash = 1; + IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + } + + // BST on first, to give it a few extra microseconds to spin up + BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); + + // pre-load ramp data so it can be assigned faster later + PWM_DATATYPE dac_lvl = PWM_GET(pwm1_levels, level); + PWM_DATATYPE dac_vref = PWM_GET(pwm_tops, level); + + // enable HDR on top half of ramp + if (level >= (HDR_ENABLE_LEVEL_MIN-1)) + HDR_ENABLE_PORT |= (1 << HDR_ENABLE_PIN); + else + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); + + // set these in successive clock cycles to avoid getting out of sync + // (minimizes ramp bumps when changing gears) + DAC_LVL = dac_lvl; + DAC_VREF = dac_vref; + + if (noflash) { + // wait for flash prevention to finish + delay_4ms(IN_NFET_DELAY_TIME/4); + IN_NFET_ENABLE_PORT &= ~(1 << IN_NFET_ENABLE_PIN); + } +} + +bool gradual_tick_main(uint8_t gt) { + // if HDR and Vref "engine gear" is the same, do a small adjustment... + // otherwise, simply jump to the next ramp level + // and let set_level() handle any gear changes + + PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt); + + // different gear = full adjustment + if (vref_next != DAC_VREF) return true; // let parent set_level() for us + + // same gear = small adjustment + GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL); + if (dac_next == DAC_LVL) return true; // done + + return false; // not done yet +} + diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h new file mode 100644 index 0000000..1c35014 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h @@ -0,0 +1,181 @@ +// hwdef for thefreeman's boost driver 2.1 w/ MP3431, DAC, ARGB +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* thefreeman’s Boost driver based on MP3431 and attiny1616 + * with high dynamic range and DAC control, AUX : RGB + button + * hardware version : 2.0+ + * compatible with BST20-FWxA v1.0 (no button LED A) + * + * Pin / Name / Function + * 1 PA2 + * 2 PA3 + * 3 GND GND + * 4 VCC VCC + * 5 PA4 + * 6 PA5 + * 7 PA6 DAC + * 8 PA7 + * 9 PB5 IN- NFET + * 10 PB4 HDR + * 11 PB3 B: blue aux LED + * 12 PB2 G: green aux LED + * 13 PB1 R: red aux LED + * 14 PB0 + * 15 PC0 boost enable + * 16 PC1 A: button LED + * 17 PC2 e-switch + * 18 PC3 + * 19 PA0 UDPI + * 20 PA1 + * + * BST EN enable the boost regulator and Op-Amp + * DAC sets the current, max current depends on Vset voltage divider and Rsense + * HDR FET switches between high value Rsense (low current range, pin low), + * and low value Rsense (high current range, pin high) + * IN- NFET : pull up after BST enable to eliminate startup flash, pull down otherwise + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 8 // 8-bit DAC +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // main LED ramp + +// main LED outputs +#define DAC_LVL DAC0.DATA // 0 to 255, for 0V to Vref +#define DAC_VREF VREF.CTRLA // 0.55V or 2.5V +#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) +// Vref values +#define V055 16 +#define V11 17 +#define V25 18 +#define V43 19 +#define V15 20 + +// BST enable +#define BST_ENABLE_PIN PIN0_bp +#define BST_ENABLE_PORT PORTC_OUT + +// HDR +// turns on HDR FET for the high current range +#define HDR_ENABLE_PIN PIN4_bp +#define HDR_ENABLE_PORT PORTB_OUT + +// IN- NFET +// pull high to force output to zero to eliminate the startup flash +#define IN_NFET_DELAY_TIME 8 // (ms) +#define IN_NFET_ENABLE_PIN PIN5_bp +#define IN_NFET_ENABLE_PORT PORTB_OUT + +// e-switch +#ifndef SWITCH_PIN +#define SWITCH_PIN PIN2_bp +#define SWITCH_PORT VPORTC.IN +#define SWITCH_ISC_REG PORTC.PIN2CTRL +#define SWITCH_VECT PORTC_PORT_vect +#define SWITCH_INTFLG VPORTC.INTFLAGS +#define SWITCH_PCINT PCINT0 +#define PCINT_vect PCINT0_vect +#endif + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN1_bp +#define AUXLED_G_PIN PIN2_bp +#define AUXLED_B_PIN PIN3_bp +#define AUXLED_RGB_PORT PORTB // PORTA or PORTB or PORTC + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// A: button LED +#ifndef BUTTON_LED_PIN +#define BUTTON_LED_PIN PIN1_bp +#define BUTTON_LED_PORT PORTC +#endif + + +inline void hwdef_setup() { + + // TODO: for this DAC controlled-light, try to decrease the clock speed or use the ULP + // set up the system clock to run at 10 MHz to match other attiny1616 lights + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + VPORTA.DIR = PIN6_bm; // DAC + VPORTB.DIR = PIN1_bm // R + | PIN2_bm // G + | PIN3_bm // B + | PIN4_bm // HDR + | PIN5_bm; // IN- NFET + VPORTC.DIR = PIN0_bm // BST EN + | PIN1_bm; // A + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // DAC + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + PORTB.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // R + //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // G + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // B + //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // HDR + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // IN- NFET + + //PORTC.PIN0CTRL = PORT_PULLUPEN_bm; // EN + //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // A + PORTC.PIN2CTRL = PORT_PULLUPEN_bm + | PORT_ISC_BOTHEDGES_gc; // e-switch + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the DAC + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // DAC ranges from 0V to (255 * Vref) / 256 + // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc + VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; + VREF.CTRLB |= VREF_DAC0REFEN_bm; + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; + DAC0.DATA = 255; // set the output voltage + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/thefreeman/lin16dac/cfg.h b/hw/thefreeman/lin16dac/cfg.h new file mode 100644 index 0000000..20d2bf5 --- /dev/null +++ b/hw/thefreeman/lin16dac/cfg.h @@ -0,0 +1,61 @@ +// thefreeman's Linear 16 driver using DAC control +// Copyright (C) 2021-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1630" +#include "hwdef-thefreeman-lin16dac.h" +// ATTINY: 1616 + +// 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: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// level_calc.py ninth 2 150 7135 1 0.03 6.4 7135 1 6.3 1600 +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +// FIXME: ramp stalls with 8 duplicate levels in a row +// (maybe use 1.1V Vref during that part of the ramp?) +#define PWM1_LEVELS 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \ + 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \ + 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 34 +#define HDR_ENABLE_LEVEL_MIN 35 // bottom level of top half of the ramp +#define HALFSPEED_LEVEL 255 // always run at 1/4th speed +#define QUARTERSPEED_LEVEL 255 // because DAC doesn't use PWM + +#define RAMP_SMOOTH_FLOOR 1 +#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 + +// stop panicking at ~30% power +#define THERM_FASTER_LEVEL 123 + +// enable 2 click turbo +#define DEFAULT_2C_STYLE 1 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/thefreeman/lin16dac/hwdef.c b/hw/thefreeman/lin16dac/hwdef.c new file mode 100644 index 0000000..07c7f9e --- /dev/null +++ b/hw/thefreeman/lin16dac/hwdef.c @@ -0,0 +1,92 @@ +// thefreeman linear t1616 DAC driver helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "chan-aux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + AUX_CHANNELS +}; + + +void set_level_zero() { + DAC_LVL = 0; // DAC off + DAC_VREF = V055; // low Vref + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); // HDR off + + // prevent post-off flash + //IN_NFET_ENABLE_PORT |= (1 << IN_NFET_ENABLE_PIN); + //delay_4ms(IN_NFET_DELAY_TIME/4); + //IN_NFET_ENABLE_PORT &= ~(1 << IN_NFET_ENABLE_PIN); + + // turn off opamp last + OPAMP_ENABLE_PORT &= ~(1 << OPAMP_ENABLE_PIN); // Opamp off +} + +// single set of LEDs with 1 regulated power channel +// and low/high HDR plus low/high Vref as different "gears" +void set_level_main(uint8_t level) { + #if 0 // unsure if this helps anything + uint8_t noflash = 0; + + // when turning on from off, try to prevent a flash + if ((! actual_level) && (level < HDR_ENABLE_LEVEL_MIN)) { + noflash = 1; + } + #endif + + // Opamp on first, to give it a few extra microseconds to spin up + OPAMP_ENABLE_PORT |= (1 << OPAMP_ENABLE_PIN); + + // pre-load ramp data so it can be assigned faster later + PWM_DATATYPE dac_lvl = PWM_GET(pwm1_levels, level); + PWM_DATATYPE dac_vref = PWM_GET(pwm_tops, level); + + // enable HDR on top half of ramp + if (level >= (HDR_ENABLE_LEVEL_MIN-1)) + HDR_ENABLE_PORT |= (1 << HDR_ENABLE_PIN); + else + HDR_ENABLE_PORT &= ~(1 << HDR_ENABLE_PIN); + + #if 0 + if (noflash) { + // wait for flash prevention to finish + delay_4ms(OPAMP_ON_DELAY/4); + } + #endif + + // set these in successive clock cycles to avoid getting out of sync + // (minimizes ramp bumps when changing gears) + DAC_LVL = dac_lvl; + DAC_VREF = dac_vref; +} + +bool gradual_tick_main(uint8_t gt) { + // if HDR and Vref "engine gear" is the same, do a small adjustment... + // otherwise, simply jump to the next ramp level + // and let set_level() handle any gear changes + + PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt); + + // different gear = full adjustment + if (vref_next != DAC_VREF) return true; // let parent set_level() for us + + // same gear = small adjustment + GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL); + if (dac_next == DAC_LVL) return true; // done + + return false; // not done yet +} + diff --git a/hw/thefreeman/lin16dac/hwdef.h b/hw/thefreeman/lin16dac/hwdef.h new file mode 100644 index 0000000..c0d0638 --- /dev/null +++ b/hw/thefreeman/lin16dac/hwdef.h @@ -0,0 +1,137 @@ +// thefreeman's Linear 16 driver using DAC control +// Copyright (C) 2021-2023 thefreeman, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * PA6 - DAC for LED brightness control + * PA7 - Op-amp enable pin + * PB5 - Aux LED + * PB4 - Switch pin, internal pullup + * PB3 - HDR control, set High to enable the high power channel, set Low for low power + * Read voltage from VCC pin, has PFET so no drop + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-thefreeman-lin16dac.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#define NUM_CHANNEL_MODES 2 +enum CHANNEL_MODES { + CM_MAIN = 0, + CM_AUX +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +#define PWM_CHANNELS 1 // old, remove this + +#define PWM_BITS 8 // 8-bit DAC +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint8_t +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // main LED ramp + +// main LED outputs +#define DAC_LVL DAC0.DATA // 0 to 255, for 0V to Vref +#define DAC_VREF VREF.CTRLA // 0.55V or 2.5V +#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?) +// Vref values +#define V055 16 +#define V11 17 +#define V25 18 +#define V43 19 +#define V15 20 + +// Opamp enable +// For turning on and off the op-amp +#define OPAMP_ENABLE_PIN PIN7_bp +#define OPAMP_ENABLE_PORT PORTA_OUT +// how many ms to delay turning on the lights after enabling the channel +// (FIXME: 80 is long enough it's likely to cause bugs elsewhere, +// as events stack up unhandled for 5 consecutive WDT ticks) +#define OPAMP_ON_DELAY 80 + +// HDR +// turns on HDR FET for the high current range +#define HDR_ENABLE_PIN PIN3_bp +#define HDR_ENABLE_PORT PORTB_OUT + +// e-switch +#define SWITCH_PIN PIN4_bp +#define SWITCH_PORT VPORTB.IN +#define SWITCH_ISC_REG PORTB.PIN2CTRL +#define SWITCH_VECT PORTB_PORT_vect +#define SWITCH_INTFLG VPORTB.INTFLAGS + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop +#endif + +// lighted button +#define AUXLED_PIN PIN5_bp +#define AUXLED_PORT PORTB + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + // (it'll get underclocked to 2.5 MHz later) + // TODO: maybe run even slower? + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + VPORTA.DIR = PIN6_bm // DAC + | PIN7_bm; // Opamp + VPORTB.DIR = PIN3_bm; // HDR + //VPORTC.DIR = 0b00000000; + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // DAC ouput + //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // Op-amp enable pin + + PORTB.PIN0CTRL = PORT_PULLUPEN_bm; + PORTB.PIN1CTRL = PORT_PULLUPEN_bm; + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // HDR channel selection + PORTB.PIN4CTRL = PORT_PULLUPEN_bm + | PORT_ISC_BOTHEDGES_gc; // e-switch + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the DAC + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // DAC ranges from 0V to (255 * Vref) / 256 + // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc + VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; + VREF.CTRLB |= VREF_DAC0REFEN_bm; + DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; + DAC0.DATA = 255; // set the output voltage + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/wurkkos/fc13/cfg.h b/hw/wurkkos/fc13/cfg.h new file mode 100644 index 0000000..56161b4 --- /dev/null +++ b/hw/wurkkos/fc13/cfg.h @@ -0,0 +1,17 @@ +// Wurkkos FC13, like a TS25 but with a RGB button and no front aux +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1616 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0716" + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +#define USE_AUX_RGB_LEDS_WHILE_ON 20 +#define USE_INDICATOR_LED_WHILE_RAMPING + diff --git a/hw/wurkkos/ts10-rgbaux/cfg.h b/hw/wurkkos/ts10-rgbaux/cfg.h new file mode 100644 index 0000000..6770c47 --- /dev/null +++ b/hw/wurkkos/ts10-rgbaux/cfg.h @@ -0,0 +1,85 @@ +// Wurkkos TS10 (RGB aux version) config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// most things are the same as TS25 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0713" +// ATTINY: 1616 + +///// apply some config from the TS10 ///// + +// use the TS10 ramp, not the TS25 ramp +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM_TOPS +#undef MAX_1x7135 +#undef MIN_THERM_STEPDOWN +#undef HALFSPEED_LEVEL +#undef QUARTERSPEED_LEVEL +#undef DEFAULT_LEVEL +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS +#undef SIMPLE_UI_FLOOR +#undef SIMPLE_UI_CEIL +#undef SIMPLE_UI_STEPS +#undef THERM_FASTER_LEVEL + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,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,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, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 20 38 56 [75] 93 111 130 +// 10 30 50 70 [90] 110 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) +// 20 47 [75] 102 130 +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#undef DEFAULT_BLINK_CHANNEL +#define DEFAULT_BLINK_CHANNEL CM_AUXWHT + +// the aux LEDs are pretty bright; set the high-mode threshold a bit higher +// (default is 15) +#define POST_OFF_VOLTAGE_BRIGHTNESS 25 + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/wurkkos/ts10/cfg.h b/hw/wurkkos/ts10/cfg.h new file mode 100644 index 0000000..90839e7 --- /dev/null +++ b/hw/wurkkos/ts10/cfg.h @@ -0,0 +1,85 @@ +// Wurkkos TS10 (originally used Sofirn SP36-t1616 firmware) config options for Anduril using the Attiny1616 +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8 T1616, mostly (added Dynamic PWM) +#define MODEL_NUMBER "0714" +#include "hwdef-wurkkos-ts10.h" +#include "wurkkos-cfg.h" +// ATTINY: 1616 + +// uses forward-facing aux LEDs +#define USE_INDICATOR_LED +// don't turn on the aux LEDs while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif +// the high button LED mode on this light uses too much power, default to low +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +#define RAMP_SIZE 150 + +#if 0 // 2022 version by SammysHP, smooth but only level 1/150 is underclocked +// level 1 by hand, for the rest +// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 +#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,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,255,255,255,255,255,255,255,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,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 +#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 90 +#define HALFSPEED_LEVEL 2 +#define QUARTERSPEED_LEVEL 2 +#endif + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,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,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, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#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 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/wurkkos/ts10/hwdef.c b/hw/wurkkos/ts10/hwdef.c new file mode 100644 index 0000000..06f5bac --- /dev/null +++ b/hw/wurkkos/ts10/hwdef.c @@ -0,0 +1,65 @@ +// Wurkkos TS10 PWM helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-aux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // main LEDs + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + { // aux LEDs + .set_level = set_level_aux, + .gradual_tick = gradual_tick_null + } +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + // wait to sync the counter and avoid flashes + // (unnecessary w/ buffered registers) + //while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/wurkkos/ts10/hwdef.h b/hw/wurkkos/ts10/hwdef.h new file mode 100644 index 0000000..e693c99 --- /dev/null +++ b/hw/wurkkos/ts10/hwdef.h @@ -0,0 +1,136 @@ +// Wurkkos TS10 driver layout +// Copyright (C) 2021-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * (based on BLF Q8-t1616 driver layout) + * Driver pinout: + * eSwitch: PA5 + * Aux LED: PB5 + * PWM FET: PB0 (TCA0 WO0) + * PWM 1x7135: PB1 (TCA0 WO1) + * Voltage: VCC + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-wurkkos-ts10.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. FET+7135 stacked +// * 1. aux LEDs +#define NUM_CHANNEL_MODES 2 +enum CHANNEL_MODES { + CM_MAIN = 0, + CM_AUX +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b00000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +// not necessary when double-buffered "BUF" registers are used +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// 1x7135 channel +#define CH1_PIN PB1 +#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1 + +// DD FET channel +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// e-switch +#define SWITCH_PIN PIN5_bp +//#define SWITCH_PCINT PCINT0 +#define SWITCH_PORT VPORTA.IN +#define SWITCH_ISC_REG PORTA.PIN2CTRL +#define SWITCH_VECT PORTA_PORT_vect +#define SWITCH_INTFLG VPORTA.INTFLAGS +//#define PCINT_vect PCINT0_vect + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +// front-facing aux LEDs +#define AUXLED_PIN PIN5_bp +#define AUXLED_PORT PORTB + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs + VPORTB.DIR = PIN0_bm // DD FET + | PIN1_bm // 7135 + | PIN5_bm; // Aux LED + //VPORTC.DIR = ...; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; + PORTB.PIN3CTRL = PORT_PULLUPEN_bm; + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + PORTC.PIN1CTRL = PORT_PULLUPEN_bm; + PORTC.PIN2CTRL = PORT_PULLUPEN_bm; + PORTC.PIN3CTRL = PORT_PULLUPEN_bm; + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/wurkkos/ts11/cfg.h b/hw/wurkkos/ts11/cfg.h new file mode 100644 index 0000000..8955731 --- /dev/null +++ b/hw/wurkkos/ts11/cfg.h @@ -0,0 +1,19 @@ +// Wurkkos TS11, like a TS25 but with a RGB button and RGB front aux +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1616 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0717" + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +// (but not until the main LEDs are bright enough to overpower the aux) +// (setting this lower makes an annoying effect on some levels) +#define USE_AUX_RGB_LEDS_WHILE_ON 50 +#define USE_INDICATOR_LED_WHILE_RAMPING + diff --git a/hw/wurkkos/ts25/cfg.h b/hw/wurkkos/ts25/cfg.h new file mode 100644 index 0000000..852e91b --- /dev/null +++ b/hw/wurkkos/ts25/cfg.h @@ -0,0 +1,87 @@ +// Wurkkos TS25, modelled after the TS10 but with RGB Aux +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0715" +#include "hwdef-wurkkos-ts25.h" +#include "wurkkos-cfg.h" +// ATTINY: 1616 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// don't turn on the aux LEDs while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +#define RAMP_SIZE 150 + +#if 0 // 2022 version +// level 1 by hand, for the rest +// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 +#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,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,255,255,255,255,255,255,255,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,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 +#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 90 +#define HALFSPEED_LEVEL 2 +#define QUARTERSPEED_LEVEL 2 +#endif + +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 1 200 3000 +// (FIXME? there's a visible bump when the FET kicks in, even with just 1/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,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,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 MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 20 38 56 [75] 93 111 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// 25 50 [75] 100 125 +#define SIMPLE_UI_FLOOR 25 +#define SIMPLE_UI_CEIL 125 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 120 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +// (so battcheck will be visible while charging) +#define DEFAULT_BLINK_CHANNEL CM_MAIN +// blink numbers on the aux LEDs by default +//#define DEFAULT_BLINK_CHANNEL CM_AUXWHT + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/wurkkos/ts25/hwdef.c b/hw/wurkkos/ts25/hwdef.c new file mode 100644 index 0000000..26c9b0d --- /dev/null +++ b/hw/wurkkos/ts25/hwdef.c @@ -0,0 +1,62 @@ +// Wurkkos TS25 PWM helper functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "chan-rgbaux.c" + +void set_level_zero(); + +void set_level_main(uint8_t level); +bool gradual_tick_main(uint8_t gt); + + +Channel channels[] = { + { // channel 1 only + .set_level = set_level_main, + .gradual_tick = gradual_tick_main + }, + RGB_AUX_CHANNELS +}; + + +void set_level_zero() { + CH1_PWM = 0; + CH2_PWM = 0; + PWM_CNT = 0; // reset phase +} + +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_main(uint8_t level) { + PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level); + PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level); + // pulse frequency modulation, a.k.a. dynamic PWM + uint16_t top = PWM_GET16(pwm_tops, level); + + CH1_PWM = ch1_pwm; + CH2_PWM = ch2_pwm; + // wait to sync the counter and avoid flashes + // (unnecessary w/ buffered registers) + //while(actual_level && (PWM_CNT > (top - 32))) {} + PWM_TOP = top; + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) PWM_CNT = 0; +} + +bool gradual_tick_main(uint8_t gt) { + PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt); + PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt); + + GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT); + GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM); + + if ( (pwm1 == CH1_PWM) + && (pwm2 == CH2_PWM) + ) { + return true; // done + } + return false; // not done yet +} + diff --git a/hw/wurkkos/ts25/hwdef.h b/hw/wurkkos/ts25/hwdef.h new file mode 100644 index 0000000..5eed219 --- /dev/null +++ b/hw/wurkkos/ts25/hwdef.h @@ -0,0 +1,144 @@ +// Wurkkos TS25 driver layout +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Driver pinout: + * eSwitch: PA5 + * PWM FET: PB0 (TCA0 WO0) + * PWM 1x7135: PB1 (TCA0 WO1) + * Voltage: VCC + * Aux Red: PC2 + * Aux Green: PC3 + * Aux Blue: PC1 + */ + +#define ATTINY 1616 +#include <avr/io.h> + +#define HWDEF_C_FILE hwdef-wurkkos-ts25.c + +// allow using aux LEDs as extra channel modes +#include "chan-rgbaux.h" + +// channel modes: +// * 0. FET+7135 stacked +// * 1+. aux RGB +#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES) +enum CHANNEL_MODES { + CM_MAIN = 0, + RGB_AUX_ENUMS +}; + +#define DEFAULT_CHANNEL_MODE CM_MAIN + +// right-most bit first, modes are in fedcba9876543210 order +#define CHANNEL_MODES_ENABLED 0b0000000000000001 + + +#define PWM_CHANNELS 2 // old, remove this + +#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255 +#define PWM_GET PWM_GET8 +#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255) +#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255 +#define PWM1_DATATYPE uint8_t // 1x7135 ramp +#define PWM2_DATATYPE uint8_t // DD FET ramp + +// PWM parameters of both channels are tied together because they share a counter +#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM +#define PWM_TOP_INIT 255 // highest value used in top half of ramp +// not necessary when double-buffered "BUF" registers are used +#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment + +// 1x7135 channel +#define CH1_PIN PB1 +#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1 + +// DD FET channel +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0 + +// e-switch +#define SWITCH_PIN PIN5_bp +//#define SWITCH_PCINT PCINT0 +#define SWITCH_PORT VPORTA.IN +#define SWITCH_ISC_REG PORTA.PIN2CTRL +#define SWITCH_VECT PORTA_PORT_vect +#define SWITCH_INTFLG VPORTA.INTFLAGS +//#define PCINT_vect PCINT0_vect + +// average drop across diode on this hardware +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PIN2_bp // pin 2 +#define AUXLED_G_PIN PIN3_bp // pin 3 +#define AUXLED_B_PIN PIN1_bp // pin 1 +#define AUXLED_RGB_PORT PORTC // PORTA or PORTB or PORTC + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + + +inline void hwdef_setup() { + + // set up the system clock to run at 10 MHz instead of the default 3.33 MHz + _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, + CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm ); + + //VPORTA.DIR = ...; + // Outputs: PWMs + VPORTB.DIR = PIN0_bm + | PIN1_bm; + // RGB aux LEDs + VPORTC.DIR = PIN1_bm + | PIN2_bm + | PIN3_bm; + + // enable pullups on the unused pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + PORTA.PIN1CTRL = PORT_PULLUPEN_bm; + PORTA.PIN2CTRL = PORT_PULLUPEN_bm; + PORTA.PIN3CTRL = PORT_PULLUPEN_bm; + PORTA.PIN4CTRL = PORT_PULLUPEN_bm; + PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm; + PORTB.PIN3CTRL = PORT_PULLUPEN_bm; + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + PORTB.PIN5CTRL = PORT_PULLUPEN_bm; + + PORTC.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // RGB Aux + //PORTC.PIN2CTRL = PORT_PULLUPEN_bm; // RGB Aux + //PORTC.PIN3CTRL = PORT_PULLUPEN_bm; // RGB Aux + + // set up the PWM + // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf + // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm + // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm + // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm + // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc + // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc + // See the manual for other pins, clocks, configs, portmux, etc + TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm + | TCA_SINGLE_CMP1EN_bm + | TCA_SINGLE_WGMODE_DSBOTTOM_gc; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; + + PWM_TOP = PWM_TOP_INIT; + +} + + +#define LAYOUT_DEFINED + diff --git a/hw/wurkkos/wurkkos-cfg.h b/hw/wurkkos/wurkkos-cfg.h new file mode 100644 index 0000000..b9992fb --- /dev/null +++ b/hw/wurkkos/wurkkos-cfg.h @@ -0,0 +1,26 @@ +// Wurkkos config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// enable factory reset on 13H without loosening tailcap +// only really needed on TS10, but doesn't hurt on other models +#define USE_SOFT_FACTORY_RESET + +// higher temperature limit because attiny1616 is more accurate +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 50 + |
