~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/hwdef-blf-lt1-t1616.c

  • Committer: Selene ToyKeeper
  • Date: 2023-11-04 15:09:10 UTC
  • mfrom: (483.1.175 anduril2)
  • Revision ID: bzr@toykeeper.net-20231104150910-ddd3afw4nhfvof2l
merged anduril2 branch -> fsm, with *years* of changes
(this also means this code is now Anduril 2 instead of Anduril 1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Sofirn LT1-t1616 PWM helpers
 
2
// Copyright (C) 2023 SiteRelEnby, Selene ToyKeeper
 
3
// SPDX-License-Identifier: GPL-3.0-or-later
 
4
#pragma once
 
5
 
 
6
#include "chan-aux.c"
 
7
 
 
8
 
 
9
void set_level_zero();
 
10
 
 
11
void set_level_ch1(uint8_t level);
 
12
void set_level_ch2(uint8_t level);
 
13
void set_level_both(uint8_t level);
 
14
void set_level_blend(uint8_t level);
 
15
void set_level_auto(uint8_t level);
 
16
 
 
17
bool gradual_tick_ch1(uint8_t gt);
 
18
bool gradual_tick_ch2(uint8_t gt);
 
19
bool gradual_tick_both(uint8_t gt);
 
20
bool gradual_tick_blend(uint8_t gt);
 
21
bool gradual_tick_auto(uint8_t gt);
 
22
 
 
23
 
 
24
Channel channels[] = {
 
25
    { // channel 1 only
 
26
        .set_level    = set_level_ch1,
 
27
        .gradual_tick = gradual_tick_ch1,
 
28
        .has_args     = 0
 
29
    },
 
30
    { // channel 2 only
 
31
        .set_level    = set_level_ch2,
 
32
        .gradual_tick = gradual_tick_ch2,
 
33
        .has_args     = 0
 
34
    },
 
35
    { // both channels, tied together (max "200%" power)
 
36
        .set_level    = set_level_both,
 
37
        .gradual_tick = gradual_tick_both,
 
38
        .has_args     = 0
 
39
    },
 
40
    { // both channels, manual blend (max "100%" power)
 
41
        .set_level    = set_level_blend,
 
42
        .gradual_tick = gradual_tick_blend,
 
43
        .has_args     = 1
 
44
    },
 
45
    { // both channels, auto blend
 
46
        .set_level    = set_level_auto,
 
47
        .gradual_tick = gradual_tick_auto,
 
48
        .has_args     = 1
 
49
    },
 
50
    AUX_CHANNELS
 
51
};
 
52
 
 
53
 
 
54
void set_level_zero() {
 
55
    // disable timer overflow interrupt
 
56
    // (helps improve button press handling from Off state)
 
57
    DSM_INTCTRL = 0;
 
58
 
 
59
    // turn off all LEDs
 
60
    ch1_dsm_lvl = 0;
 
61
    ch2_dsm_lvl = 0;
 
62
    CH1_PWM     = 0;
 
63
    CH2_PWM     = 0;
 
64
    PWM_CNT     = 0;
 
65
}
 
66
 
 
67
void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
 
68
 
 
69
    bool was_on = (CH1_PWM>0) || (CH2_PWM>0);
 
70
 
 
71
    // set delta-sigma soft levels
 
72
    ch1_dsm_lvl = ch1;
 
73
    ch2_dsm_lvl = ch2;
 
74
 
 
75
    // set hardware PWM levels and init dsm loop
 
76
    CH1_PWM = ch1_pwm = ch1 >> 7;
 
77
    CH2_PWM = ch2_pwm = ch2 >> 7;
 
78
 
 
79
    // enable timer overflow interrupt so DSM can work
 
80
    DSM_INTCTRL = DSM_OVF_bm;
 
81
 
 
82
    // reset phase when turning on
 
83
    if (! was_on) PWM_CNT = 0;
 
84
 
 
85
}
 
86
 
 
87
// delta-sigma modulation of PWM outputs
 
88
// happens on each Timer overflow (every 512 cpu clock cycles)
 
89
// uses 8-bit pwm w/ 7-bit dsm (0b 0PPP PPPP PDDD DDDD)
 
90
ISR(DSM_vect) {
 
91
    // set new hardware values first,
 
92
    // for best timing (reduce effect of interrupt jitter)
 
93
    CH1_PWM = ch1_pwm;
 
94
    CH2_PWM = ch2_pwm;
 
95
 
 
96
    // calculate next values, now that timing matters less
 
97
 
 
98
    // accumulate error
 
99
    ch1_dsm += (ch1_dsm_lvl & 0x007f);
 
100
    // next PWM = base PWM value + carry bit
 
101
    ch1_pwm  = (ch1_dsm_lvl >> 7) + (ch1_dsm > 0x7f);
 
102
    // clear carry bit
 
103
    ch1_dsm &= 0x7f;
 
104
 
 
105
    // repeat for other channels
 
106
 
 
107
    ch2_dsm += (ch2_dsm_lvl & 0x007f);
 
108
    ch2_pwm  = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f);
 
109
    ch2_dsm &= 0x7f;
 
110
 
 
111
    // clear the interrupt flag to indicate it was handled
 
112
    // as per: https://onlinedocs.microchip.com/pr/GUID-C37FFBA8-82C6-4339-A2B1-ABD9A0F6C162-en-US-8/index.html?GUID-C2A2BEFD-158F-413D-B9D4-0F0556AA79BD
 
113
    DSM_INTFLAGS = DSM_OVF_bm;
 
114
}
 
115
 
 
116
 
 
117
void set_level_ch1(uint8_t level) {
 
118
    set_hw_levels(PWM_GET(pwm1_levels, level), 0);
 
119
}
 
120
 
 
121
void set_level_ch2(uint8_t level) {
 
122
    set_hw_levels(0, PWM_GET(pwm1_levels, level));
 
123
}
 
124
 
 
125
void set_level_both(uint8_t level) {
 
126
    PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
 
127
    set_hw_levels(pwm, pwm);
 
128
}
 
129
 
 
130
void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) {
 
131
    PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
 
132
    uint8_t blend;
 
133
    if (channel_mode == CM_AUTO) {
 
134
        blend = 255 * (uint16_t)level / RAMP_SIZE;
 
135
        if (cfg.channel_mode_args[channel_mode] & 0b01000000)
 
136
            blend = 255 - blend;
 
137
    } else {
 
138
        blend = cfg.channel_mode_args[channel_mode];
 
139
    }
 
140
 
 
141
    calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend);
 
142
}
 
143
 
 
144
void set_level_blend(uint8_t level) {
 
145
    PWM_DATATYPE warm, cool;
 
146
    blend_helper(&warm, &cool, level);
 
147
    set_hw_levels(warm, cool);
 
148
}
 
149
 
 
150
void set_level_auto(uint8_t level) {
 
151
    PWM_DATATYPE warm, cool;
 
152
    blend_helper(&warm, &cool, level);
 
153
    set_hw_levels(warm, cool);
 
154
}
 
155
 
 
156
///// "gradual tick" functions for smooth thermal regulation /////
 
157
// (and other smooth adjustments)
 
158
 
 
159
///// bump each channel toward a target value /////
 
160
bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
 
161
    // adjust multiple times based on current brightness
 
162
    // (so it adjusts faster/coarser when bright, slower/finer when dim)
 
163
 
 
164
    // higher shift = slower/finer adjustments
 
165
    const uint8_t shift = 9;  // ((255 << 7) >> 9) = 63 max
 
166
    uint8_t steps;
 
167
 
 
168
    steps = ch1_dsm_lvl >> shift;
 
169
    for (uint8_t i=0; i<=steps; i++)
 
170
        GRADUAL_ADJUST_SIMPLE(ch1, ch1_dsm_lvl);
 
171
 
 
172
    steps = ch2_dsm_lvl  >> shift;
 
173
    for (uint8_t i=0; i<=steps; i++)
 
174
        GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl);
 
175
 
 
176
    if ((ch1 == ch1_dsm_lvl)
 
177
     && (ch2 == ch2_dsm_lvl )) {
 
178
        return true;  // done
 
179
    }
 
180
    return false;  // not done yet
 
181
}
 
182
 
 
183
bool gradual_tick_ch1(uint8_t gt) {
 
184
    PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
 
185
    return gradual_adjust(pwm, 0);
 
186
}
 
187
 
 
188
bool gradual_tick_ch2(uint8_t gt) {
 
189
    PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
 
190
    return gradual_adjust(0, pwm);
 
191
}
 
192
 
 
193
bool gradual_tick_both(uint8_t gt) {
 
194
    PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
 
195
    return gradual_adjust(pwm, pwm);
 
196
}
 
197
 
 
198
bool gradual_tick_blend(uint8_t gt) {
 
199
    PWM_DATATYPE warm, cool;
 
200
    blend_helper(&warm, &cool, gt);
 
201
    return gradual_adjust(warm, cool);
 
202
}
 
203
 
 
204
bool gradual_tick_auto(uint8_t gt) {
 
205
    PWM_DATATYPE warm, cool;
 
206
    blend_helper(&warm, &cool, gt);
 
207
    return gradual_adjust(warm, cool);
 
208
}
 
209
 
 
210