diff options
| author | Selene ToyKeeper | 2023-10-25 10:26:56 -0600 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-10-25 10:26:56 -0600 |
| commit | 3a654aa5150a8943a787ecbfc65c2f9ff2bff75f (patch) | |
| tree | b77230ef73189b9f68a6630dfccff1c94d66d8d1 /hwdef-blf-lt1.c | |
| parent | fixed emisar-d1 + emisar-d1s (diff) | |
| download | anduril-3a654aa5150a8943a787ecbfc65c2f9ff2bff75f.tar.gz anduril-3a654aa5150a8943a787ecbfc65c2f9ff2bff75f.tar.bz2 anduril-3a654aa5150a8943a787ecbfc65c2f9ff2bff75f.zip | |
rewrote blf-lantern (blf-lt1) code to use multi-channel and PWM+DSM,
which required ... a few pretty significant changes:
- no dynamic underclocking (it isn't compatible with DSM yet)
- no tint ramping brightness correction (removed to save space)
- removed ramp blinks (to save space, and because they're annoying)
- removed momentary mode (to save space)
- removed SOS mode (to save space)
- removed (to save space) some other relatively recent features which
weren't present in the original production firmware
... but some other things improved:
+ added smooth steps
+ extended Simple UI
+ added stepped tint ramping
+ added 13H factory reset, to save wear on threads
+ lower lows
+ smoother ramp
+ much higher tint ramp resolution in low modes
I'm not entirely happy with this yet, so it probably needs additional work
later in order to adjust the weird ramp shape (these 7135 chips have a weird
response curve), add dynamic underclocking, cut down the ROM size if possible,
re-add tint ramping brightness correction, etc. Multi-channel stuff in
particular added a lot to the size.
This is a pretty big change from the previous working build, so some users
may want to stick with the last pre-multi-channel version. Non-trivial
sacrifices were made to bring in more recent features.
Diffstat (limited to 'hwdef-blf-lt1.c')
| -rw-r--r-- | hwdef-blf-lt1.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/hwdef-blf-lt1.c b/hwdef-blf-lt1.c new file mode 100644 index 0000000..df17612 --- /dev/null +++ b/hwdef-blf-lt1.c @@ -0,0 +1,197 @@ +// 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() { + // 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; +} + +// delta-sigma modulation of PWM outputs +// happens on each Timer0 overflow (every 512 cpu clock cycles) +// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD) +ISR(TIMER0_OVF_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 + |
