~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/fsm-channels.c

  • Committer: Selene ToyKeeper
  • Date: 2023-07-10 17:56:04 UTC
  • mto: (483.1.175 anduril2)
  • mto: This revision was merged to the branch mainline in revision 491.
  • Revision ID: bzr@toykeeper.net-20230710175604-sjm29fwj8k5bj8m9
refactored how channel modes are defined, and converted emisar-2ch build

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// fsm-channels.c: Channel mode functions for SpaghettiMonster.
 
2
// Copyright (C) 2023 Selene ToyKeeper
 
3
// SPDX-License-Identifier: GPL-3.0-or-later
 
4
 
 
5
#pragma once
 
6
 
 
7
#include "fsm-ramping.h"
 
8
 
 
9
 
 
10
void set_channel_mode(uint8_t mode) {
 
11
    uint8_t cur_level = actual_level;
 
12
    // turn off old LEDs before changing channel
 
13
    set_level(0);
 
14
 
 
15
    // change the channel
 
16
    CH_MODE = mode;
 
17
 
 
18
    // update the LEDs
 
19
    set_level(cur_level);
 
20
}
 
21
 
 
22
 
 
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)
 
29
void calc_2ch_blend(
 
30
    PWM_DATATYPE *warm,
 
31
    PWM_DATATYPE *cool,
 
32
    PWM_DATATYPE brightness,
 
33
    PWM_DATATYPE top,
 
34
    uint8_t blend) {
 
35
 
 
36
    #ifndef TINT_RAMPING_CORRECTION
 
37
    #define TINT_RAMPING_CORRECTION 26  // 140% brightness at middle tint
 
38
    #endif
 
39
 
 
40
    // calculate actual PWM levels based on a single-channel ramp
 
41
    // and a blend value
 
42
    PWM_DATATYPE warm_PWM, cool_PWM;
 
43
    PWM_DATATYPE2 base_PWM = brightness;
 
44
 
 
45
    #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
 
46
        uint8_t level = actual_level - 1;
 
47
 
 
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) {
 
52
            base_PWM = brightness
 
53
                     + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
 
54
                        * triangle_wave(blend) / 255);
 
55
        }
 
56
        // fade the triangle wave out when above 100% power,
 
57
        // so it won't go over 200%
 
58
        if (brightness > top) {
 
59
            base_PWM -= 2 * (
 
60
                             ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
 
61
                             * triangle_wave(blend) / 255
 
62
                        );
 
63
        }
 
64
        // guarantee no more than 200% power
 
65
        if (base_PWM > (top << 1)) { base_PWM = top << 1; }
 
66
    #endif
 
67
 
 
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
 
71
    if (cool_PWM > top) {
 
72
        warm_PWM += (cool_PWM - top);
 
73
        cool_PWM = top;
 
74
    } else if (warm_PWM > top) {
 
75
        cool_PWM += (warm_PWM - top);
 
76
        warm_PWM = top;
 
77
    }
 
78
 
 
79
    *warm = warm_PWM;
 
80
    *cool = cool_PWM;
 
81
}
 
82
#endif  // ifdef USE_CALC_2CH_BLEND
 
83
 
 
84
 
 
85
#ifdef USE_HSV2RGB
 
86
RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
 
87
    RGB_t color;
 
88
 
 
89
    uint16_t region, fpart, high, low, rising, falling;
 
90
 
 
91
    if (s == 0) {  // grey
 
92
        color.r = color.g = color.b = v;
 
93
        return color;
 
94
    }
 
95
 
 
96
    // make hue 0-5
 
97
    region = ((uint16_t)h * 6) >> 8;
 
98
    // find remainder part, make it from 0-255
 
99
    fpart = ((uint16_t)h * 6) - (region << 8);
 
100
 
 
101
    // calculate graph segments, doing integer multiplication
 
102
    high    = v;
 
103
    low     = (v * (255 - s)) >> 8;
 
104
    falling = (v * (255 - ((s * fpart) >> 8))) >> 8;
 
105
    rising  = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
 
106
 
 
107
    // default floor
 
108
    color.r = low;
 
109
    color.g = low;
 
110
    color.b = low;
 
111
 
 
112
    // assign graph shapes based on color cone region
 
113
    switch (region) {
 
114
        case 0:
 
115
            color.r = high;
 
116
            color.g = rising;
 
117
            //color.b = low;
 
118
            break;
 
119
        case 1:
 
120
            color.r = falling;
 
121
            color.g = high;
 
122
            //color.b = low;
 
123
            break;
 
124
        case 2:
 
125
            //color.r = low;
 
126
            color.g = high;
 
127
            color.b = rising;
 
128
            break;
 
129
        case 3:
 
130
            //color.r = low;
 
131
            color.g = falling;
 
132
            color.b = high;
 
133
            break;
 
134
        case 4:
 
135
            color.r = rising;
 
136
            //color.g = low;
 
137
            color.b = high;
 
138
            break;
 
139
        default:
 
140
            color.r = high;
 
141
            //color.g = low;
 
142
            color.b = falling;
 
143
            break;
 
144
    }
 
145
 
 
146
    return color;
 
147
}
 
148
#endif  // ifdef USE_HSV2RGB
 
149
 
 
150
 
 
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)
 
154
 
 
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?
 
160
 
 
161
 
 
162
#ifdef USE_SET_LEVEL_1CH
 
163
// single set of LEDs with 1 power channel
 
164
void set_level_1ch(uint8_t level) {
 
165
    if (level == 0) {
 
166
        LOW_PWM_LVL  = 0;
 
167
    } else {
 
168
        level --;  // PWM array index = level - 1
 
169
        LOW_PWM_LVL  = PWM_GET(low_pwm_levels, level);
 
170
    }
 
171
}
 
172
#endif
 
173
 
 
174
 
 
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) {
 
178
    if (level == 0) {
 
179
        LOW_PWM_LVL  = 0;
 
180
        HIGH_PWM_LVL = 0;
 
181
    } else {
 
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);
 
185
    }
 
186
}
 
187
#endif
 
188
 
 
189
 
 
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) {
 
193
    if (level == 0) {
 
194
        LOW_PWM_LVL  = 0;
 
195
        MED_PWM_LVL  = 0;
 
196
        HIGH_PWM_LVL = 0;
 
197
    } else {
 
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);
 
202
    }
 
203
}
 
204
#endif
 
205
 
 
206
 
 
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
 
211
    #endif
 
212
 
 
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);
 
221
    #else
 
222
        const uint16_t top = PWM_TOP;
 
223
    #endif
 
224
 
 
225
    // auto-tint modes
 
226
    uint8_t mytint;
 
227
    uint8_t level = actual_level - 1;
 
228
    #if 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); }
 
232
    #else
 
233
    // linear with power level
 
234
    //if (tint == 0) { mytint = brightness; }
 
235
    //else if (tint == 255) { mytint = 255 - brightness; }
 
236
    #endif
 
237
    // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198)
 
238
    else { mytint = (tint * 100 / 99) - 1; }
 
239
 
 
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);
 
248
        }
 
249
        // fade the triangle wave out when above 100% power,
 
250
        // so it won't go over 200%
 
251
        if (brightness > top) {
 
252
            base_PWM -= 2 * (
 
253
                             ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
 
254
                             * triangle_wave(mytint) / 255
 
255
                        );
 
256
        }
 
257
        // guarantee no more than 200% power
 
258
        if (base_PWM > (top << 1)) { base_PWM = top << 1; }
 
259
    #endif
 
260
 
 
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);
 
266
        cool_PWM = top;
 
267
    } else if (warm_PWM > top) {
 
268
        cool_PWM += (warm_PWM - top);
 
269
        warm_PWM = top;
 
270
    }
 
271
 
 
272
    TINT1_LVL = warm_PWM;
 
273
    TINT2_LVL = cool_PWM;
 
274
 
 
275
    // disable the power channel, if relevant
 
276
    #ifdef LED_ENABLE_PIN
 
277
    if (warm_PWM)
 
278
        LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
 
279
    else
 
280
        LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
 
281
    #endif
 
282
    #ifdef LED2_ENABLE_PIN
 
283
    if (cool_PWM)
 
284
        LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN);
 
285
    else
 
286
        LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN);
 
287
    #endif
 
288
}
 
289
#endif  // ifdef USE_TINT_RAMPING
 
290
 
 
291
 
 
292
#ifdef USE_GRADUAL_TICK_1CH
 
293
void gradual_tick_1ch() {
 
294
    GRADUAL_TICK_SETUP();
 
295
 
 
296
    GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL);
 
297
 
 
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)))
 
301
    {
 
302
        GRADUAL_IS_ACTUAL();
 
303
    }
 
304
}
 
305
#endif
 
306
 
 
307
 
 
308
#ifdef USE_GRADUAL_TICK_2CH_STACKED
 
309
void gradual_tick_2ch_stacked() {
 
310
    GRADUAL_TICK_SETUP();
 
311
 
 
312
    GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
 
313
    GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
 
314
 
 
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))
 
319
       )
 
320
    {
 
321
        GRADUAL_IS_ACTUAL();
 
322
    }
 
323
}
 
324
#endif
 
325
 
 
326
 
 
327
#ifdef USE_GRADUAL_TICK_3CH_STACKED
 
328
void gradual_tick_3ch_stacked() {
 
329
    GRADUAL_TICK_SETUP();
 
330
 
 
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);
 
334
 
 
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))
 
340
        )
 
341
    {
 
342
        GRADUAL_IS_ACTUAL();
 
343
    }
 
344
}
 
345
#endif
 
346
 
 
347