~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/fsm-ramping.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:
6
6
 
7
7
#ifdef USE_RAMPING
8
8
 
9
 
void set_channel_mode(uint8_t mode) {
10
 
    uint8_t cur_level = actual_level;
11
 
    // turn off old LEDs before changing channel
12
 
    set_level(0);
13
 
 
14
 
    // change the channel
15
 
    CH_MODE = mode;
16
 
 
17
 
    // update the LEDs
18
 
    set_level(cur_level);
19
 
}
20
 
 
21
9
#ifdef HAS_AUX_LEDS
22
10
inline void set_level_aux_leds(uint8_t level) {
23
11
    #ifdef USE_INDICATOR_LED_WHILE_RAMPING
55
43
    // (pulse the output high for a moment to wake up the power regulator)
56
44
    // (only do this when starting from off and going to a low level)
57
45
    // TODO: allow different jump start behavior per channel mode
 
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)
58
49
    if ((! actual_level)
59
50
            && level
60
51
            && (level < JUMP_START_LEVEL)) {
68
59
    #endif
69
60
 
70
61
    // call the relevant hardware-specific set_level_*()
71
 
    SetLevelFuncPtr set_level_func = channel_modes[CH_MODE];
 
62
    SetLevelFuncPtr set_level_func = channels[CH_MODE].set_level;
72
63
    set_level_func(level);
73
64
 
74
65
    if (actual_level != level) prev_level = actual_level;
83
74
    #endif
84
75
}
85
76
 
86
 
///// Common set_level_*() functions shared by multiple lights /////
87
 
// (unique lights should use their own,
88
 
//  but these common versions cover most of the common hardware designs)
89
 
 
90
 
#ifdef USE_SET_LEVEL_1CH
91
 
// single set of LEDs with 1 power channel
92
 
void set_level_1ch(uint8_t level) {
93
 
    if (level == 0) {
94
 
        LOW_PWM_LVL  = 0;
95
 
    } else {
96
 
        level --;  // PWM array index = level - 1
97
 
        LOW_PWM_LVL  = PWM_GET(low_pwm_levels, level);
98
 
    }
99
 
}
100
 
#endif
101
 
 
102
 
#ifdef USE_SET_LEVEL_2CH_STACKED
103
 
// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
104
 
void set_level_2ch_stacked(uint8_t level) {
105
 
    if (level == 0) {
106
 
        LOW_PWM_LVL  = 0;
107
 
        HIGH_PWM_LVL = 0;
108
 
    } else {
109
 
        level --;  // PWM array index = level - 1
110
 
        LOW_PWM_LVL  = PWM_GET(low_pwm_levels,  level);
111
 
        HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
112
 
    }
113
 
}
114
 
#endif
115
 
 
116
 
#ifdef USE_SET_LEVEL_3CH_STACKED
117
 
// single set of LEDs with 3 stacked power channels, like DDFET+N+1
118
 
void set_level_3ch_stacked(uint8_t level) {
119
 
    if (level == 0) {
120
 
        LOW_PWM_LVL  = 0;
121
 
        MED_PWM_LVL  = 0;
122
 
        HIGH_PWM_LVL = 0;
123
 
    } else {
124
 
        level --;  // PWM array index = level - 1
125
 
        LOW_PWM_LVL  = PWM_GET(low_pwm_levels,  level);
126
 
        MED_PWM_LVL  = PWM_GET(med_pwm_levels,  level);
127
 
        HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
128
 
    }
129
 
}
130
 
#endif
131
 
 
132
 
// TODO: upgrade some older lights to dynamic PWM
133
 
// TODO: 1ch w/ dynamic PWM
134
 
// TODO: 1ch w/ dynamic PWM and opamp enable pins?
135
 
// TODO: 2ch stacked w/ dynamic PWM
136
 
// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins?
137
 
 
138
 
#ifdef USE_CALC_2CH_BLEND
139
 
// calculate a "tint ramp" blend between 2 channels
140
 
// results are placed in *warm and *cool vars
141
 
// brightness : total amount of light units to distribute
142
 
// top : maximum allowed brightness per channel
143
 
// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool)
144
 
void calc_2ch_blend(
145
 
    PWM_DATATYPE *warm,
146
 
    PWM_DATATYPE *cool,
147
 
    PWM_DATATYPE brightness,
148
 
    PWM_DATATYPE top,
149
 
    uint8_t blend) {
150
 
 
151
 
    #ifndef TINT_RAMPING_CORRECTION
152
 
    #define TINT_RAMPING_CORRECTION 26  // 140% brightness at middle tint
153
 
    #endif
154
 
 
155
 
    // calculate actual PWM levels based on a single-channel ramp
156
 
    // and a blend value
157
 
    PWM_DATATYPE warm_PWM, cool_PWM;
158
 
    PWM_DATATYPE2 base_PWM = brightness;
159
 
 
160
 
    #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
161
 
        uint8_t level = actual_level - 1;
162
 
 
163
 
        // middle tints sag, so correct for that effect
164
 
        // by adding extra power which peaks at the middle tint
165
 
        // (correction is only necessary when PWM is fast)
166
 
        if (level > HALFSPEED_LEVEL) {
167
 
            base_PWM = brightness
168
 
                     + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
169
 
                        * triangle_wave(blend) / 255);
170
 
        }
171
 
        // fade the triangle wave out when above 100% power,
172
 
        // so it won't go over 200%
173
 
        if (brightness > top) {
174
 
            base_PWM -= 2 * (
175
 
                             ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
176
 
                             * triangle_wave(blend) / 255
177
 
                        );
178
 
        }
179
 
        // guarantee no more than 200% power
180
 
        if (base_PWM > (top << 1)) { base_PWM = top << 1; }
181
 
    #endif
182
 
 
183
 
    cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255;
184
 
    warm_PWM = base_PWM - cool_PWM;
185
 
    // when running at > 100% power, spill extra over to other channel
186
 
    if (cool_PWM > top) {
187
 
        warm_PWM += (cool_PWM - top);
188
 
        cool_PWM = top;
189
 
    } else if (warm_PWM > top) {
190
 
        cool_PWM += (warm_PWM - top);
191
 
        warm_PWM = top;
192
 
    }
193
 
 
194
 
    *warm = warm_PWM;
195
 
    *cool = cool_PWM;
196
 
}
197
 
#endif  // ifdef USE_CALC_2CH_BLEND
198
 
 
199
 
#ifdef USE_HSV2RGB
200
 
RGB_t hsv2rgb(uint8_t h, uint8_t s, uint8_t v) {
201
 
    RGB_t color;
202
 
 
203
 
    uint16_t region, fpart, high, low, rising, falling;
204
 
 
205
 
    if (s == 0) {  // grey
206
 
        color.r = color.g = color.b = v;
207
 
        return color;
208
 
    }
209
 
 
210
 
    // make hue 0-5
211
 
    region = ((uint16_t)h * 6) >> 8;
212
 
    // find remainder part, make it from 0-255
213
 
    fpart = ((uint16_t)h * 6) - (region << 8);
214
 
 
215
 
    // calculate graph segments, doing integer multiplication
216
 
    high    = v;
217
 
    low     = (v * (255 - s)) >> 8;
218
 
    falling = (v * (255 - ((s * fpart) >> 8))) >> 8;
219
 
    rising  = (v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
220
 
 
221
 
    // default floor
222
 
    color.r = low;
223
 
    color.g = low;
224
 
    color.b = low;
225
 
 
226
 
    // assign graph shapes based on color cone region
227
 
    switch (region) {
228
 
        case 0:
229
 
            color.r = high;
230
 
            color.g = rising;
231
 
            //color.b = low;
232
 
            break;
233
 
        case 1:
234
 
            color.r = falling;
235
 
            color.g = high;
236
 
            //color.b = low;
237
 
            break;
238
 
        case 2:
239
 
            //color.r = low;
240
 
            color.g = high;
241
 
            color.b = rising;
242
 
            break;
243
 
        case 3:
244
 
            //color.r = low;
245
 
            color.g = falling;
246
 
            color.b = high;
247
 
            break;
248
 
        case 4:
249
 
            color.r = rising;
250
 
            //color.g = low;
251
 
            color.b = high;
252
 
            break;
253
 
        default:
254
 
            color.r = high;
255
 
            //color.g = low;
256
 
            color.b = falling;
257
 
            break;
258
 
    }
259
 
 
260
 
    return color;
261
 
}
262
 
#endif  // ifdef USE_HSV2RGB
263
 
 
264
 
 
265
77
#ifdef USE_LEGACY_SET_LEVEL
266
78
// (this is mostly just here for reference, temporarily)
267
79
// single set of LEDs with 1 to 3 stacked power channels,
405
217
    gt --;
406
218
 
407
219
    // call the relevant hardware-specific function
408
 
    GradualTickFuncPtr gradual_tick_func = gradual_tick_modes[CH_MODE];
 
220
    GradualTickFuncPtr gradual_tick_func = channels[CH_MODE].gradual_tick;
409
221
    bool done = gradual_tick_func(gt);
410
222
 
411
223
    if (done) {
414
226
        gradual_target = orig;
415
227
    }
416
228
}
417
 
 
418
 
 
419
 
#ifdef USE_GRADUAL_TICK_1CH
420
 
void gradual_tick_1ch() {
421
 
    GRADUAL_TICK_SETUP();
422
 
 
423
 
    GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL);
424
 
 
425
 
    // did we go far enough to hit the next defined ramp level?
426
 
    // if so, update the main ramp level tracking var
427
 
    if ((LOW_PWM_LVL  == PWM_GET(low_pwm_levels,  gt)))
428
 
    {
429
 
        GRADUAL_IS_ACTUAL();
430
 
    }
431
 
}
432
 
#endif
433
 
 
434
 
#ifdef USE_GRADUAL_TICK_2CH_STACKED
435
 
void gradual_tick_2ch_stacked() {
436
 
    GRADUAL_TICK_SETUP();
437
 
 
438
 
    GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
439
 
    GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
440
 
 
441
 
    // did we go far enough to hit the next defined ramp level?
442
 
    // if so, update the main ramp level tracking var
443
 
    if (   (LOW_PWM_LVL  == PWM_GET(low_pwm_levels,  gt))
444
 
        && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
445
 
       )
446
 
    {
447
 
        GRADUAL_IS_ACTUAL();
448
 
    }
449
 
}
450
 
#endif
451
 
 
452
 
#ifdef USE_GRADUAL_TICK_3CH_STACKED
453
 
void gradual_tick_3ch_stacked() {
454
 
    GRADUAL_TICK_SETUP();
455
 
 
456
 
    GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
457
 
    GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP);
458
 
    GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
459
 
 
460
 
    // did we go far enough to hit the next defined ramp level?
461
 
    // if so, update the main ramp level tracking var
462
 
    if (   (LOW_PWM_LVL  == PWM_GET(low_pwm_levels,  gt))
463
 
        && (MED_PWM_LVL  == PWM_GET(med_pwm_levels,  gt))
464
 
        && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
465
 
        )
466
 
    {
467
 
        GRADUAL_IS_ACTUAL();
468
 
    }
469
 
}
470
 
#endif
471
229
#endif  // ifdef USE_SET_LEVEL_GRADUALLY
472
230
 
473
231
 
474
 
#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
475
 
void set_level_2ch_blend() {
476
 
    #ifndef TINT_RAMPING_CORRECTION
477
 
    #define TINT_RAMPING_CORRECTION 26  // 140% brightness at middle tint
478
 
    #endif
479
 
 
480
 
    // calculate actual PWM levels based on a single-channel ramp
481
 
    // and a global tint value
482
 
    //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
483
 
    uint16_t brightness = PWM1_LVL;
484
 
    uint16_t warm_PWM, cool_PWM;
485
 
    #ifdef USE_STACKED_DYN_PWM
486
 
        uint16_t top = PWM1_TOP;
487
 
        //uint16_t top = PWM_GET(pwm_tops, actual_level-1);
488
 
    #else
489
 
        const uint16_t top = PWM_TOP;
490
 
    #endif
491
 
 
492
 
    // auto-tint modes
493
 
    uint8_t mytint;
494
 
    uint8_t level = actual_level - 1;
495
 
    #if 1
496
 
    // perceptual by ramp level
497
 
    if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; }
498
 
    else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); }
499
 
    #else
500
 
    // linear with power level
501
 
    //if (tint == 0) { mytint = brightness; }
502
 
    //else if (tint == 255) { mytint = 255 - brightness; }
503
 
    #endif
504
 
    // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198)
505
 
    else { mytint = (tint * 100 / 99) - 1; }
506
 
 
507
 
    PWM_DATATYPE2 base_PWM = brightness;
508
 
    #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
509
 
        // middle tints sag, so correct for that effect
510
 
        // by adding extra power which peaks at the middle tint
511
 
        // (correction is only necessary when PWM is fast)
512
 
        if (level > HALFSPEED_LEVEL) {
513
 
            base_PWM = brightness
514
 
                     + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255);
515
 
        }
516
 
        // fade the triangle wave out when above 100% power,
517
 
        // so it won't go over 200%
518
 
        if (brightness > top) {
519
 
            base_PWM -= 2 * (
520
 
                             ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
521
 
                             * triangle_wave(mytint) / 255
522
 
                        );
523
 
        }
524
 
        // guarantee no more than 200% power
525
 
        if (base_PWM > (top << 1)) { base_PWM = top << 1; }
526
 
    #endif
527
 
 
528
 
    cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
529
 
    warm_PWM = base_PWM - cool_PWM;
530
 
    // when running at > 100% power, spill extra over to other channel
531
 
    if (cool_PWM > top) {
532
 
        warm_PWM += (cool_PWM - top);
533
 
        cool_PWM = top;
534
 
    } else if (warm_PWM > top) {
535
 
        cool_PWM += (warm_PWM - top);
536
 
        warm_PWM = top;
537
 
    }
538
 
 
539
 
    TINT1_LVL = warm_PWM;
540
 
    TINT2_LVL = cool_PWM;
541
 
 
542
 
    // disable the power channel, if relevant
543
 
    #ifdef LED_ENABLE_PIN
544
 
    if (warm_PWM)
545
 
        LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
546
 
    else
547
 
        LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
548
 
    #endif
549
 
    #ifdef LED2_ENABLE_PIN
550
 
    if (cool_PWM)
551
 
        LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN);
552
 
    else
553
 
        LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN);
554
 
    #endif
555
 
}
556
 
#endif  // ifdef USE_TINT_RAMPING
557
 
 
558
 
 
559
 
// define the channel mode lists
560
 
// TODO: move to progmem
561
 
SetLevelFuncPtr channel_modes[NUM_CHANNEL_MODES] = { SET_LEVEL_MODES };
562
 
#ifdef USE_SET_LEVEL_GRADUALLY
563
 
GradualTickFuncPtr gradual_tick_modes[NUM_CHANNEL_MODES] = { GRADUAL_TICK_MODES };
564
 
#endif
565
 
 
566
 
 
567
232
#endif  // ifdef USE_RAMPING
568
233