483.12.14
by Selene ToyKeeper
switched the rest of FSM + Anduril to use SPDX license headers |
1 |
// fsm-ramping.c: Ramping functions for SpaghettiMonster.
|
2 |
// Copyright (C) 2017-2023 Selene ToyKeeper
|
|
3 |
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
205
by Selene Scriven
Added a ramping UI example. |
4 |
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
5 |
#pragma once
|
205
by Selene Scriven
Added a ramping UI example. |
6 |
|
7 |
#ifdef USE_RAMPING
|
|
8 |
||
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
9 |
#ifdef HAS_AUX_LEDS
|
10 |
inline void set_level_aux_leds(uint8_t level) { |
|
336
by Selene Scriven
Started adding support for aux LEDs which go out the front instead of lighting up the button. |
11 |
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
|
483.1.169
by Selene ToyKeeper
allow supporting single-color and RGB side button in the same build |
12 |
// use side-facing aux LEDs while main LEDs are on
|
13 |
if (! go_to_standby) { |
|
433.1.1
by Selene Scriven
merged a sanitized copy of the Emisar D4v2 branch; history summarized below: |
14 |
#ifdef USE_INDICATOR_LED
|
481.1.4
by Selene Scriven
made lighted button go low/high based on DEFAULT_LEVEL instead of MAX_1x7135, |
15 |
indicator_led((level > 0) + (level > DEFAULT_LEVEL)); |
433.1.1
by Selene Scriven
merged a sanitized copy of the Emisar D4v2 branch; history summarized below: |
16 |
#endif
|
483.1.169
by Selene ToyKeeper
allow supporting single-color and RGB side button in the same build |
17 |
#ifdef USE_BUTTON_LED
|
18 |
button_led_set((level > 0) + (level > DEFAULT_LEVEL)); |
|
19 |
#endif
|
|
20 |
}
|
|
21 |
#else // turn off front-facing aux LEDs while main LEDs are on |
|
433.1.1
by Selene Scriven
merged a sanitized copy of the Emisar D4v2 branch; history summarized below: |
22 |
#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
|
23 |
if (! go_to_standby) { |
|
24 |
#ifdef USE_INDICATOR_LED
|
|
25 |
indicator_led(0); |
|
26 |
#endif
|
|
27 |
#ifdef USE_AUX_RGB_LEDS
|
|
28 |
rgb_led_set(0); |
|
439.1.1
by Selene Scriven
added button LED support to D4v2 |
29 |
#ifdef USE_BUTTON_LED
|
481.1.4
by Selene Scriven
made lighted button go low/high based on DEFAULT_LEVEL instead of MAX_1x7135, |
30 |
button_led_set((level > 0) + (level > DEFAULT_LEVEL)); |
439.1.1
by Selene Scriven
added button LED support to D4v2 |
31 |
#endif
|
433.1.1
by Selene Scriven
merged a sanitized copy of the Emisar D4v2 branch; history summarized below: |
32 |
#endif
|
33 |
}
|
|
34 |
#endif
|
|
303
by Selene Scriven
Moved indicator_led() from Anduril into FSM. Made it auto-set indicator level based on ramp level. |
35 |
#endif
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
36 |
}
|
37 |
#endif // ifdef HAS_AUX_LEDS |
|
38 |
||
39 |
||
40 |
void set_level(uint8_t level) { |
|
41 |
#ifdef USE_JUMP_START
|
|
42 |
// maybe "jump start" the engine, if it's prone to slow starts
|
|
43 |
// (pulse the output high for a moment to wake up the power regulator)
|
|
44 |
// (only do this when starting from off and going to a low level)
|
|
45 |
// TODO: allow different jump start behavior per channel mode
|
|
483.12.71
by Selene ToyKeeper
refactored how channel modes are defined, and converted emisar-2ch build |
46 |
// FIXME: don't jump-start during factory reset
|
47 |
// (it seems to cause some eeprom issues on KR4
|
|
48 |
// when doing a click with a loose tailcap)
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
49 |
if ((! actual_level) |
50 |
&& level |
|
483.12.13
by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct |
51 |
&& (level < JUMP_START_LEVEL)) { |
52 |
set_level(JUMP_START_LEVEL); |
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
53 |
delay_4ms(JUMP_START_TIME/4); |
54 |
}
|
|
55 |
#endif
|
|
56 |
||
57 |
#ifdef HAS_AUX_LEDS
|
|
58 |
set_level_aux_leds(level); |
|
59 |
#endif
|
|
60 |
||
61 |
// call the relevant hardware-specific set_level_*()
|
|
483.12.71
by Selene ToyKeeper
refactored how channel modes are defined, and converted emisar-2ch build |
62 |
SetLevelFuncPtr set_level_func = channels[CH_MODE].set_level; |
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
63 |
set_level_func(level); |
64 |
||
483.12.53
by Selene ToyKeeper
post-off voltage display: use low brightness when torch was at moon level before, |
65 |
if (actual_level != level) prev_level = actual_level; |
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
66 |
actual_level = level; |
67 |
||
68 |
#ifdef USE_SET_LEVEL_GRADUALLY
|
|
69 |
gradual_target = level; |
|
70 |
#endif
|
|
71 |
||
72 |
#ifdef USE_DYNAMIC_UNDERCLOCKING
|
|
73 |
auto_clock_speed(); |
|
74 |
#endif
|
|
75 |
}
|
|
76 |
||
77 |
#ifdef USE_LEGACY_SET_LEVEL
|
|
78 |
// (this is mostly just here for reference, temporarily)
|
|
79 |
// single set of LEDs with 1 to 3 stacked power channels,
|
|
80 |
// like linear, FET+1, and FET+N+1
|
|
81 |
// (default set_level_*() function for most lights)
|
|
82 |
void set_level_legacy(uint8_t level) { |
|
205
by Selene Scriven
Added a ramping UI example. |
83 |
if (level == 0) { |
84 |
#if PWM_CHANNELS >= 1
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
85 |
PWM1_LVL = 0; |
205
by Selene Scriven
Added a ramping UI example. |
86 |
#endif
|
87 |
#if PWM_CHANNELS >= 2
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
88 |
PWM2_LVL = 0; |
205
by Selene Scriven
Added a ramping UI example. |
89 |
#endif
|
90 |
#if PWM_CHANNELS >= 3
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
91 |
PWM3_LVL = 0; |
483.8.4
by Selene Scriven
got D4Sv2 tint ramping to work, with dynamic PWM (PFM) |
92 |
#endif
|
483.11.4
by Selene ToyKeeper
sp10-pro: hard reset phase while turning off, fixes shutoff bug without a delay |
93 |
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF)
|
94 |
PWM1_CNT = 0; |
|
95 |
#endif
|
|
96 |
#if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_OFF)
|
|
97 |
PWM2_CNT = 0; |
|
98 |
#endif
|
|
99 |
#if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_OFF)
|
|
100 |
PWM3_CNT = 0; |
|
101 |
#endif
|
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
102 |
#ifdef LED_OFF_DELAY
|
103 |
// for drivers with a slow regulator chip (eg, boost converter),
|
|
104 |
// delay before turning off to prevent flashes
|
|
105 |
delay_4ms(LED_OFF_DELAY/4); |
|
483.3.34
by Gabriel Hart
Created LED_DISABLE_DELAY option and enabled it for SP10 Pro to hopefully eliminate flashes when turning off at certain levels |
106 |
#endif
|
433.1.6
by Selene Scriven
fsm-ramping: turn opamp chip on and off automatically based on ramp level |
107 |
// disable the power channel, if relevant
|
108 |
#ifdef LED_ENABLE_PIN
|
|
109 |
LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); |
|
110 |
#endif
|
|
483.2.2
by Selene Scriven
renamed LED_ENABLE2_* to LED2_ENABLE_* |
111 |
#ifdef LED2_ENABLE_PIN
|
112 |
LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); |
|
433.2.2
by Selene Scriven
added support for boost PMIC enable pin (K1 12V has 2 pins which need to turn on/off for main LED output) |
113 |
#endif
|
205
by Selene Scriven
Added a ramping UI example. |
114 |
} else { |
433.1.6
by Selene Scriven
fsm-ramping: turn opamp chip on and off automatically based on ramp level |
115 |
// enable the power channel, if relevant
|
116 |
#ifdef LED_ENABLE_PIN
|
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
117 |
#ifdef LED_ON_DELAY
|
483.3.29
by Gabriel Hart
Experimental: add optional delay when using LED_ENABLE_PIN to avoid flashes of light |
118 |
uint8_t led_enable_port_save = LED_ENABLE_PORT; |
119 |
#endif
|
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
120 |
|
487.1.3
by Selene Scriven
added support for LED_ENABLE_PIN having a min/max ramp level where it turns on |
121 |
#ifndef LED_ENABLE_PIN_LEVEL_MIN
|
122 |
LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); |
|
123 |
#else
|
|
124 |
// only enable during part of the ramp
|
|
125 |
if ((level >= LED_ENABLE_PIN_LEVEL_MIN) |
|
126 |
&& (level <= LED_ENABLE_PIN_LEVEL_MAX)) |
|
127 |
LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); |
|
128 |
else // disable during other parts of the ramp |
|
129 |
LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); |
|
130 |
#endif
|
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
131 |
|
132 |
// for drivers with a slow regulator chip (eg, boost converter),
|
|
133 |
// delay before lighting up to prevent flashes
|
|
134 |
#ifdef LED_ON_DELAY
|
|
135 |
// only delay if the pin status changed
|
|
136 |
if (LED_ENABLE_PORT != led_enable_port_save) |
|
137 |
delay_4ms(LED_ON_DELAY/4); |
|
483.3.29
by Gabriel Hart
Experimental: add optional delay when using LED_ENABLE_PIN to avoid flashes of light |
138 |
#endif
|
433.1.6
by Selene Scriven
fsm-ramping: turn opamp chip on and off automatically based on ramp level |
139 |
#endif
|
483.2.2
by Selene Scriven
renamed LED_ENABLE2_* to LED2_ENABLE_* |
140 |
#ifdef LED2_ENABLE_PIN
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
141 |
#ifdef LED2_ON_DELAY
|
483.3.30
by Gabriel Hart
Experimental: add optional delay when using LED_ENABLE_PIN to avoid flashes of light (this time for LED2_ENABLE pin) |
142 |
uint8_t led2_enable_port_save = LED2_ENABLE_PORT; |
143 |
#endif
|
|
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
144 |
|
483.3.30
by Gabriel Hart
Experimental: add optional delay when using LED_ENABLE_PIN to avoid flashes of light (this time for LED2_ENABLE pin) |
145 |
LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); |
483.11.2
by Selene ToyKeeper
moved fuses from cfg to hwdef, renamed LED_EN/DISABLE_DELAY to LED_ON/OFF_DELAY |
146 |
|
147 |
// for drivers with a slow regulator chip (eg, boost converter),
|
|
148 |
// delay before lighting up to prevent flashes
|
|
149 |
#ifdef LED2_ON_DELAY
|
|
150 |
// only delay if the pin status changed
|
|
151 |
if (LED2_ENABLE_PORT != led2_enable_port_save) |
|
152 |
delay_4ms(LED2_ON_DELAY/4); |
|
483.3.30
by Gabriel Hart
Experimental: add optional delay when using LED_ENABLE_PIN to avoid flashes of light (this time for LED2_ENABLE pin) |
153 |
#endif
|
433.2.2
by Selene Scriven
added support for boost PMIC enable pin (K1 12V has 2 pins which need to turn on/off for main LED output) |
154 |
#endif
|
433.1.6
by Selene Scriven
fsm-ramping: turn opamp chip on and off automatically based on ramp level |
155 |
|
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
156 |
// PWM array index = level - 1
|
487.1.3
by Selene Scriven
added support for LED_ENABLE_PIN having a min/max ramp level where it turns on |
157 |
level --; |
158 |
||
205
by Selene Scriven
Added a ramping UI example. |
159 |
#if PWM_CHANNELS >= 1
|
433.1.3
by Selene Scriven
fsm-ramping: made it possible to change bit depth of PWM values (8-bit or 16-bit) |
160 |
PWM1_LVL = PWM_GET(pwm1_levels, level); |
205
by Selene Scriven
Added a ramping UI example. |
161 |
#endif
|
162 |
#if PWM_CHANNELS >= 2
|
|
433.1.3
by Selene Scriven
fsm-ramping: made it possible to change bit depth of PWM values (8-bit or 16-bit) |
163 |
PWM2_LVL = PWM_GET(pwm2_levels, level); |
205
by Selene Scriven
Added a ramping UI example. |
164 |
#endif
|
165 |
#if PWM_CHANNELS >= 3
|
|
433.1.3
by Selene Scriven
fsm-ramping: made it possible to change bit depth of PWM values (8-bit or 16-bit) |
166 |
PWM3_LVL = PWM_GET(pwm3_levels, level); |
205
by Selene Scriven
Added a ramping UI example. |
167 |
#endif
|
366.1.1
by Selene Scriven
Added tint ramping. Not tested yet. Also added BLF Lantern build target. |
168 |
|
483.1.121
by Selene Scriven
got PFM / dynamic PWM actually working on Noctigon KR4 |
169 |
#ifdef USE_DYN_PWM
|
483.1.126
by Selene Scriven
made dynamic PWM phase-correct; replaced brute-force phase reset to avoid flickering |
170 |
uint16_t top = PWM_GET(pwm_tops, level); |
483.11.4
by Selene ToyKeeper
sp10-pro: hard reset phase while turning off, fixes shutoff bug without a delay |
171 |
#if defined(PWM1_CNT) && defined(PWM1_PHASE_SYNC)
|
483.1.126
by Selene Scriven
made dynamic PWM phase-correct; replaced brute-force phase reset to avoid flickering |
172 |
// wait to ensure compare match won't be missed
|
173 |
// (causes visible flickering when missed, because the counter
|
|
174 |
// goes all the way to 65535 before returning)
|
|
175 |
// (see attiny1634 reference manual page 103 for a warning about
|
|
176 |
// the timing of changing the TOP value (section 12.8.4))
|
|
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
177 |
// (but don't wait when turning on from zero, because
|
178 |
// it'll reset the phase below anyway)
|
|
483.10.5
by Selene ToyKeeper
reduced SP10 downward ramp flicker even more |
179 |
// to be safe, allow at least 32 cycles to update TOP
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
180 |
while(actual_level && (PWM1_CNT > (top - 32))) {} |
483.1.126
by Selene Scriven
made dynamic PWM phase-correct; replaced brute-force phase reset to avoid flickering |
181 |
#endif
|
483.1.121
by Selene Scriven
got PFM / dynamic PWM actually working on Noctigon KR4 |
182 |
// pulse frequency modulation, a.k.a. dynamic PWM
|
483.1.126
by Selene Scriven
made dynamic PWM phase-correct; replaced brute-force phase reset to avoid flickering |
183 |
PWM1_TOP = top; |
184 |
#endif // ifdef USE_DYN_PWM |
|
483.11.4
by Selene ToyKeeper
sp10-pro: hard reset phase while turning off, fixes shutoff bug without a delay |
185 |
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON)
|
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
186 |
// force reset phase when turning on from zero
|
187 |
// (because otherwise the initial response is inconsistent)
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
188 |
if (! actual_level) { |
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
189 |
PWM1_CNT = 0; |
483.11.4
by Selene ToyKeeper
sp10-pro: hard reset phase while turning off, fixes shutoff bug without a delay |
190 |
#if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON)
|
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
191 |
PWM2_CNT = 0; |
192 |
#endif
|
|
483.11.4
by Selene ToyKeeper
sp10-pro: hard reset phase while turning off, fixes shutoff bug without a delay |
193 |
#if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_ON)
|
483.1.131
by Selene Scriven
force reset PWM phase when turning on from zero |
194 |
PWM3_CNT = 0; |
195 |
#endif
|
|
196 |
}
|
|
197 |
#endif
|
|
205
by Selene Scriven
Added a ramping UI example. |
198 |
}
|
284
by Selene Scriven
Added dynamic underclocking to FSM, instead of doing it manually in Anduril. |
199 |
#ifdef USE_DYNAMIC_UNDERCLOCKING
|
200 |
auto_clock_speed(); |
|
201 |
#endif
|
|
205
by Selene Scriven
Added a ramping UI example. |
202 |
}
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
203 |
#endif
|
204 |
||
205
by Selene Scriven
Added a ramping UI example. |
205 |
|
260
by Selene Scriven
Made thermal regulation adjust smoothly (1 PWM step at a time) to make adjustments less noticeable. |
206 |
#ifdef USE_SET_LEVEL_GRADUALLY
|
207 |
inline void set_level_gradually(uint8_t lvl) { |
|
208 |
gradual_target = lvl; |
|
209 |
}
|
|
210 |
||
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
211 |
|
262
by Selene Scriven
Rewrote gradual_tick(). Is smaller, better, and safer now. |
212 |
// call this every frame or every few frames to change brightness very smoothly
|
260
by Selene Scriven
Made thermal regulation adjust smoothly (1 PWM step at a time) to make adjustments less noticeable. |
213 |
void gradual_tick() { |
483.12.19
by Selene ToyKeeper
updated D4Sv2-tintramp -> Emisar 2-channel build target ... |
214 |
uint8_t gt = gradual_target; |
215 |
if (gt < actual_level) gt = actual_level - 1; |
|
216 |
else if (gt > actual_level) gt = actual_level + 1; |
|
217 |
gt --; |
|
218 |
||
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
219 |
// call the relevant hardware-specific function
|
483.12.71
by Selene ToyKeeper
refactored how channel modes are defined, and converted emisar-2ch build |
220 |
GradualTickFuncPtr gradual_tick_func = channels[CH_MODE].gradual_tick; |
483.12.19
by Selene ToyKeeper
updated D4Sv2-tintramp -> Emisar 2-channel build target ... |
221 |
bool done = gradual_tick_func(gt); |
222 |
||
223 |
if (done) { |
|
224 |
uint8_t orig = gradual_target; |
|
225 |
set_level(gt + 1); |
|
226 |
gradual_target = orig; |
|
227 |
}
|
|
483.12.2
by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working |
228 |
}
|
483.2.7
by Selene Scriven
made gradual_tick() work on K9.3 (via override), fixed strobe config, |
229 |
#endif // ifdef USE_SET_LEVEL_GRADUALLY |
260
by Selene Scriven
Made thermal regulation adjust smoothly (1 PWM step at a time) to make adjustments less noticeable. |
230 |
|
483.8.4
by Selene Scriven
got D4Sv2 tint ramping to work, with dynamic PWM (PFM) |
231 |
|
205
by Selene Scriven
Added a ramping UI example. |
232 |
#endif // ifdef USE_RAMPING |
483.12.14
by Selene ToyKeeper
switched the rest of FSM + Anduril to use SPDX license headers |
233 |