diff options
| author | Selene ToyKeeper | 2023-10-28 07:02:38 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-10-28 07:02:38 -0600 |
| commit | aaa760e5243cf87a43297945b20e41a448c906e4 (patch) | |
| tree | a29f30ceeeedf5573be58e43d486f8ec393f3657 | |
| parent | enabled smooth steps on blf-q8 and sofirn-sp36, instead of tactical mode (diff) | |
| download | anduril-aaa760e5243cf87a43297945b20e41a448c906e4.tar.gz anduril-aaa760e5243cf87a43297945b20e41a448c906e4.tar.bz2 anduril-aaa760e5243cf87a43297945b20e41a448c906e4.zip | |
switched blf-lt1-t1616 from plain PWM to PWM+DSM
(and made DSM interrupt definitions a bit cleaner)
| -rw-r--r-- | hwdef-blf-lt1-t1616.c | 143 | ||||
| -rw-r--r-- | hwdef-blf-lt1-t1616.h | 64 | ||||
| -rw-r--r-- | hwdef-noctigon-m44.c | 14 | ||||
| -rw-r--r-- | hwdef-noctigon-m44.h | 13 | ||||
| -rw-r--r-- | spaghetti-monster/anduril/cfg-blf-lantern-t1616.h | 41 |
5 files changed, 186 insertions, 89 deletions
diff --git a/hwdef-blf-lt1-t1616.c b/hwdef-blf-lt1-t1616.c index ea7e6b4..48a401d 100644 --- a/hwdef-blf-lt1-t1616.c +++ b/hwdef-blf-lt1-t1616.c @@ -1,6 +1,6 @@ // Sofirn LT1-t1616 PWM helpers // Copyright (C) 2023 SiteRelEnby, Selene ToyKeeper -// (adapted from emisar-2ch 15/10/2023) +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include "chan-aux.c" @@ -52,59 +52,125 @@ Channel channels[] = { void set_level_zero() { - CH1_PWM = 0; - CH2_PWM = 0; + // 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; } -void set_pwms(PWM_DATATYPE ch1_pwm, PWM_DATATYPE ch2_pwm) { +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; } + void set_level_ch1(uint8_t level) { - set_pwms(PWM_GET(pwm1_levels, level), 0); + set_hw_levels(PWM_GET(pwm1_levels, level), 0); } void set_level_ch2(uint8_t level) { - set_pwms(0, PWM_GET(pwm1_levels, 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_pwms(pwm, pwm); + set_hw_levels(pwm, pwm); } -void set_level_blend(uint8_t level) { - PWM_DATATYPE ch1_pwm, ch2_pwm; +void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) { PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); - uint8_t blend = cfg.channel_mode_args[channel_mode]; + 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(&ch1_pwm, &ch2_pwm, brightness, PWM_TOP_INIT, blend); + calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend); +} - set_pwms(ch1_pwm, ch2_pwm); +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 ch1_pwm, ch2_pwm; - PWM_DATATYPE brightness = PWM_GET(pwm1_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(&ch1_pwm, &ch2_pwm, brightness, PWM_TOP_INIT, blend); - - set_pwms(ch1_pwm, ch2_pwm); + 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(uint16_t ch1_pwm, uint16_t ch2_pwm) { - GRADUAL_ADJUST_SIMPLE(ch1_pwm, CH1_PWM); - GRADUAL_ADJUST_SIMPLE(ch2_pwm, CH2_PWM); +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; - // check for completion - if ((ch1_pwm == CH1_PWM) - && (ch2_pwm == CH2_PWM)) { + 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 @@ -126,24 +192,15 @@ bool gradual_tick_both(uint8_t gt) { } bool gradual_tick_blend(uint8_t gt) { - PWM_DATATYPE ch1_pwm, ch2_pwm; - PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt); - uint8_t blend = cfg.channel_mode_args[channel_mode]; - - calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, PWM_TOP_INIT, blend); - - return gradual_adjust(ch1_pwm, ch2_pwm); + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); } bool gradual_tick_auto(uint8_t gt) { - PWM_DATATYPE ch1_pwm, ch2_pwm; - PWM_DATATYPE brightness = PWM_GET(pwm1_levels, 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, PWM_TOP_INIT, blend); - - return gradual_adjust(ch1_pwm, ch2_pwm); + PWM_DATATYPE warm, cool; + blend_helper(&warm, &cool, gt); + return gradual_adjust(warm, cool); } + diff --git a/hwdef-blf-lt1-t1616.h b/hwdef-blf-lt1-t1616.h index 4a411d4..cc6d065 100644 --- a/hwdef-blf-lt1-t1616.h +++ b/hwdef-blf-lt1-t1616.h @@ -46,40 +46,49 @@ enum channel_modes_e { #define USE_CALC_2CH_BLEND -#define PWM_CHANNELS 1 // old, remove this +#define PWM_CHANNELS 1 // old, remove this -#define PWM_BITS 8 // - -#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 // +#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 -#define PWM_TOP_INIT 255 // 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 -// TODO: implement DSM +// 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_OVF_bm TCA_SINGLE_OVF_bm + // warm LEDs +uint16_t ch1_dsm_lvl; +uint8_t ch1_pwm, ch1_dsm; #define CH1_PIN PB1 -#define CH1_PWM TCA0.SINGLE.CMP1 // CMP1 is the output compare register for PB1 +#define CH1_PWM TCA0.SINGLE.CMP1 // CMP1 is the output compare register for PB1 // cold LEDs -#define CH2_PIN PB0 -#define CH2_PWM TCA0.SINGLE.CMP0 // CMP0 is the output compare register for PB0 +uint16_t ch2_dsm_lvl; +uint8_t ch2_pwm, ch2_dsm; +#define CH2_PIN PB0 +#define CH2_PWM TCA0.SINGLE.CMP0 // 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 +#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 @@ -89,11 +98,12 @@ enum channel_modes_e { inline void hwdef_setup() { - // set up the system clock to run at 5 MHz instead of the default 3.33 MHz - _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm ); + // 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: + // Outputs VPORTB.DIR = PIN0_bm // cool white | PIN1_bm // warm white | PIN5_bm; // aux LED @@ -130,10 +140,16 @@ inline void hwdef_setup() { // TODO: add references to MCU documentation TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_CMP1EN_bm - | TCA_SINGLE_WGMODE_SINGLESLOPE_gc; - PWM_TOP = PWM_TOP_INIT; + | 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 + } diff --git a/hwdef-noctigon-m44.c b/hwdef-noctigon-m44.c index 0b7f027..395a7a2 100644 --- a/hwdef-noctigon-m44.c +++ b/hwdef-noctigon-m44.c @@ -52,9 +52,9 @@ Channel channels[] = { void set_level_zero() { - // disable timer 1 overflow interrupt + // disable timer overflow interrupt // (helps improve button press handling from Off state) - TIMSK &= ~(1 << TOIE1); + DSM_INTCTRL &= ~DSM_OVF_bm; // turn off all LEDs ch1_dsm_lvl = 0; @@ -120,19 +120,19 @@ void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2, PWM_TOP = top; #endif - // enable timer 1 overflow interrupt so DSM can work - TIMSK |= (1 << TOIE1); + // enable timer overflow interrupt so DSM can work + DSM_INTCTRL |= DSM_OVF_bm; - // reset phase when turning on or off + // 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 Timer0 overflow (every 512 cpu clock cycles) +// happens on each Timer overflow (every 512 cpu clock cycles) // uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) -ISR(TIMER1_OVF_vect) { +ISR(DSM_vect) { // set new hardware values first, // for best timing (reduce effect of interrupt jitter) CH1_PWM = ch1_pwm; diff --git a/hwdef-noctigon-m44.h b/hwdef-noctigon-m44.h index 2a9198a..a41ea79 100644 --- a/hwdef-noctigon-m44.h +++ b/hwdef-noctigon-m44.h @@ -39,8 +39,8 @@ // channel modes: // * 0. channel 1 only // * 1. channel 2 only -// * 2. both channels, tied together -// * 3. both channels, manual blend +// * 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) @@ -65,7 +65,7 @@ enum channel_modes_e { #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_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 @@ -80,6 +80,11 @@ enum channel_modes_e { // (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) + // 1st channel (8 LEDs) uint16_t ch1_dsm_lvl; uint8_t ch1_pwm, ch1_dsm; @@ -187,7 +192,7 @@ inline void hwdef_setup() { // set up interrupt for delta-sigma modulation // (moved to hwdef.c functions so it can be enabled/disabled based on ramp level) - //TIMSK |= (1<<TOIE1); // interrupt once for each timer 1 cycle + //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 diff --git a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h b/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h index 85784bb..24a7c7c 100644 --- a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h +++ b/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h @@ -19,6 +19,7 @@ // CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO #define DEFAULT_CHANNEL_MODE CM_AUTO #define DEFAULT_BLINK_CHANNEL CM_BOTH +#define USE_CHANNEL_MODE_ARGS // how much to increase total brightness at middle tint // (0 = 100% brightness, 64 = 200% brightness) @@ -26,14 +27,19 @@ #define TINT_RAMPING_CORRECTION 10 // production model, 115% #define RAMP_SIZE 150 -// level_calc.py 1 150 7135 1 30 800 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255 -#define MAX_1x7135 65 -#define HALFSPEED_LEVEL 14 -#define QUARTERSPEED_LEVEL 5 +// 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 1 // lowest level for tint ramping correction +#define QUARTERSPEED_LEVEL 1 +#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp + +#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 @@ -42,7 +48,7 @@ // because this lantern isn't overpowered #define RAMP_SMOOTH_FLOOR 1 #define RAMP_SMOOTH_CEIL 150 -#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_FLOOR 1 #define RAMP_DISCRETE_CEIL 150 #define RAMP_DISCRETE_STEPS 7 @@ -58,6 +64,22 @@ #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 @@ -76,9 +98,6 @@ #define BLINK_AT_RAMP_CEIL #endif -#define USE_CHANNEL_MODE_ARGS -#define USE_SET_LEVEL_GRADUALLY - // for consistency with other models #define USE_SOFT_FACTORY_RESET |
