1
// fsm-channels.c: Channel mode functions for SpaghettiMonster.
2
// Copyright (C) 2023 Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
7
#include "fsm-ramping.h"
10
void set_channel_mode(uint8_t mode) {
11
uint8_t cur_level = actual_level;
12
// turn off old LEDs before changing channel
23
#ifdef USE_CALC_2CH_BLEND
24
// calculate a "tint ramp" blend between 2 channels
25
// results are placed in *warm and *cool vars
26
// brightness : total amount of light units to distribute
27
// top : maximum allowed brightness per channel
28
// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool)
32
PWM_DATATYPE brightness,
36
#ifndef TINT_RAMPING_CORRECTION
37
#define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
40
// calculate actual PWM levels based on a single-channel ramp
42
PWM_DATATYPE warm_PWM, cool_PWM;
43
PWM_DATATYPE2 base_PWM = brightness;
45
#if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
46
uint8_t level = actual_level - 1;
48
// middle tints sag, so correct for that effect
49
// by adding extra power which peaks at the middle tint
50
// (correction is only necessary when PWM is fast)
51
if (level > HALFSPEED_LEVEL) {
53
+ ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
54
* triangle_wave(blend) / 255);
56
// fade the triangle wave out when above 100% power,
57
// so it won't go over 200%
58
if (brightness > top) {
60
((brightness - top) * TINT_RAMPING_CORRECTION / 64)
61
* triangle_wave(blend) / 255
64
// guarantee no more than 200% power
65
if (base_PWM > (top << 1)) { base_PWM = top << 1; }
68
cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255;
69
warm_PWM = base_PWM - cool_PWM;
70
// when running at > 100% power, spill extra over to other channel
72
warm_PWM += (cool_PWM - top);
74
} else if (warm_PWM > top) {
75
cool_PWM += (warm_PWM - top);
82
#endif // ifdef USE_CALC_2CH_BLEND
86
RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
89
uint16_t region, fpart, high, low, rising, falling;
92
color.r = color.g = color.b = v;
97
region = ((uint16_t)h * 6) >> 8;
98
// find remainder part, make it from 0-255
99
fpart = ((uint16_t)h * 6) - (region << 8);
101
// calculate graph segments, doing integer multiplication
103
low = (v * (255 - s)) >> 8;
104
falling = (v * (255 - ((s * fpart) >> 8))) >> 8;
105
rising = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
112
// assign graph shapes based on color cone region
148
#endif // ifdef USE_HSV2RGB
151
///// Common set_level_*() functions shared by multiple lights /////
152
// (unique lights should use their own,
153
// but these common versions cover most of the common hardware designs)
155
// TODO: upgrade some older lights to dynamic PWM
156
// TODO: 1ch w/ dynamic PWM
157
// TODO: 1ch w/ dynamic PWM and opamp enable pins?
158
// TODO: 2ch stacked w/ dynamic PWM
159
// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins?
162
#ifdef USE_SET_LEVEL_1CH
163
// single set of LEDs with 1 power channel
164
void set_level_1ch(uint8_t level) {
168
level --; // PWM array index = level - 1
169
LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
175
#ifdef USE_SET_LEVEL_2CH_STACKED
176
// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
177
void set_level_2ch_stacked(uint8_t level) {
182
level --; // PWM array index = level - 1
183
LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
184
HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
190
#ifdef USE_SET_LEVEL_3CH_STACKED
191
// single set of LEDs with 3 stacked power channels, like DDFET+N+1
192
void set_level_3ch_stacked(uint8_t level) {
198
level --; // PWM array index = level - 1
199
LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
200
MED_PWM_LVL = PWM_GET(med_pwm_levels, level);
201
HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
207
#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
208
void set_level_2ch_blend() {
209
#ifndef TINT_RAMPING_CORRECTION
210
#define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
213
// calculate actual PWM levels based on a single-channel ramp
214
// and a global tint value
215
//PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
216
uint16_t brightness = PWM1_LVL;
217
uint16_t warm_PWM, cool_PWM;
218
#ifdef USE_STACKED_DYN_PWM
219
uint16_t top = PWM1_TOP;
220
//uint16_t top = PWM_GET(pwm_tops, actual_level-1);
222
const uint16_t top = PWM_TOP;
227
uint8_t level = actual_level - 1;
229
// perceptual by ramp level
230
if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; }
231
else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); }
233
// linear with power level
234
//if (tint == 0) { mytint = brightness; }
235
//else if (tint == 255) { mytint = 255 - brightness; }
237
// stretch 1-254 to fit 0-255 range (hits every value except 98 and 198)
238
else { mytint = (tint * 100 / 99) - 1; }
240
PWM_DATATYPE2 base_PWM = brightness;
241
#if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
242
// middle tints sag, so correct for that effect
243
// by adding extra power which peaks at the middle tint
244
// (correction is only necessary when PWM is fast)
245
if (level > HALFSPEED_LEVEL) {
246
base_PWM = brightness
247
+ ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255);
249
// fade the triangle wave out when above 100% power,
250
// so it won't go over 200%
251
if (brightness > top) {
253
((brightness - top) * TINT_RAMPING_CORRECTION / 64)
254
* triangle_wave(mytint) / 255
257
// guarantee no more than 200% power
258
if (base_PWM > (top << 1)) { base_PWM = top << 1; }
261
cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
262
warm_PWM = base_PWM - cool_PWM;
263
// when running at > 100% power, spill extra over to other channel
264
if (cool_PWM > top) {
265
warm_PWM += (cool_PWM - top);
267
} else if (warm_PWM > top) {
268
cool_PWM += (warm_PWM - top);
272
TINT1_LVL = warm_PWM;
273
TINT2_LVL = cool_PWM;
275
// disable the power channel, if relevant
276
#ifdef LED_ENABLE_PIN
278
LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
280
LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
282
#ifdef LED2_ENABLE_PIN
284
LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN);
286
LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN);
289
#endif // ifdef USE_TINT_RAMPING
292
#ifdef USE_GRADUAL_TICK_1CH
293
void gradual_tick_1ch() {
294
GRADUAL_TICK_SETUP();
296
GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL);
298
// did we go far enough to hit the next defined ramp level?
299
// if so, update the main ramp level tracking var
300
if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)))
308
#ifdef USE_GRADUAL_TICK_2CH_STACKED
309
void gradual_tick_2ch_stacked() {
310
GRADUAL_TICK_SETUP();
312
GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
313
GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
315
// did we go far enough to hit the next defined ramp level?
316
// if so, update the main ramp level tracking var
317
if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
318
&& (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
327
#ifdef USE_GRADUAL_TICK_3CH_STACKED
328
void gradual_tick_3ch_stacked() {
329
GRADUAL_TICK_SETUP();
331
GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
332
GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP);
333
GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
335
// did we go far enough to hit the next defined ramp level?
336
// if so, update the main ramp level tracking var
337
if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
338
&& (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt))
339
&& (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))