优化开关机Gamma渐变

This commit is contained in:
2026-07-27 17:03:20 +08:00
parent 18f0a2e2a4
commit d351ec3fc2
@@ -7,6 +7,13 @@ volatile uint8_t Light_Transition_FadeActive;
#define LIGHT_FADE_STEP 1U #define LIGHT_FADE_STEP 1U
#define BRIGHTNESS_SMOOTH_SHIFT 4U #define BRIGHTNESS_SMOOTH_SHIFT 4U
#define BREATH_MIN_OUTPUT 1U #define BREATH_MIN_OUTPUT 1U
#define POWER_FADE_Q12_MAX 4096U
#define POWER_FADE_ON_TICKS 140U
#define POWER_FADE_OFF_TICKS 160U
#define POWER_FADE_ON_STEP (POWER_FADE_Q12_MAX / POWER_FADE_ON_TICKS)
#define POWER_FADE_ON_REMAINDER (POWER_FADE_Q12_MAX % POWER_FADE_ON_TICKS)
#define POWER_FADE_OFF_STEP (POWER_FADE_Q12_MAX / POWER_FADE_OFF_TICKS)
#define POWER_FADE_OFF_REMAINDER (POWER_FADE_Q12_MAX % POWER_FADE_OFF_TICKS)
/* /*
* Gamma 1.8, mapping APP levels 1..100 to a stable physical range of * Gamma 1.8, mapping APP levels 1..100 to a stable physical range of
@@ -26,7 +33,30 @@ static const uint16_t brightness_gamma_q12[100] = {
3515U, 3577U, 3640U, 3704U, 3768U, 3832U, 3897U, 3963U, 4029U, 4096U 3515U, 3577U, 3640U, 3704U, 3768U, 3832U, 3897U, 3963U, 4029U, 4096U
}; };
/*
* Gamma 1.8 envelope for power-on/off. The transition progress is advanced
* independently of the requested brightness, so every turn-on takes about
* 700 ms and every turn-off takes about 800 ms. Linear interpolation between
* table entries prevents visible 1% steps.
*/
static const uint16_t power_fade_gamma_q12[101] = {
0U, 1U, 4U, 7U, 12U, 19U, 26U, 34U, 43U, 54U,
65U, 77U, 90U, 104U, 119U, 135U, 151U, 169U, 187U, 206U,
226U, 247U, 268U, 291U, 314U, 338U, 363U, 388U, 414U, 441U,
469U, 498U, 527U, 557U, 588U, 619U, 651U, 684U, 718U, 752U,
787U, 823U, 859U, 897U, 934U, 973U, 1012U, 1052U, 1093U, 1134U,
1176U, 1219U, 1262U, 1306U, 1351U, 1396U, 1442U, 1489U, 1536U, 1585U,
1633U, 1682U, 1732U, 1783U, 1834U, 1886U, 1939U, 1992U, 2046U, 2100U,
2155U, 2211U, 2268U, 2325U, 2382U, 2440U, 2499U, 2559U, 2619U, 2680U,
2741U, 2803U, 2866U, 2929U, 2993U, 3057U, 3122U, 3188U, 3254U, 3321U,
3388U, 3456U, 3525U, 3594U, 3664U, 3735U, 3806U, 3877U, 3950U, 4023U,
4096U
};
static uint16_t current_brightness_q12; static uint16_t current_brightness_q12;
static uint16_t power_fade_progress_q12;
static uint16_t power_fade_remainder;
static uint8_t power_fade_direction;
static uint16_t brightness_scale_for_level(uint8_t level) static uint16_t brightness_scale_for_level(uint8_t level)
{ {
@@ -71,32 +101,115 @@ static uint16_t approach_target(uint16_t current, uint16_t target)
return current; return current;
} }
static uint16_t power_fade_scale(uint16_t progress_q12)
{
uint32_t position;
uint16_t index;
uint16_t fraction;
uint16_t lower;
uint16_t upper;
if(progress_q12 >= POWER_FADE_Q12_MAX) return POWER_FADE_Q12_MAX;
position = (uint32_t)progress_q12 * 100U;
index = (uint16_t)(position >> 12);
fraction = (uint16_t)(position & 0x0FFFU);
lower = power_fade_gamma_q12[index];
upper = power_fade_gamma_q12[index + 1U];
return (uint16_t)(lower +
(((uint32_t)(upper - lower) * fraction + 2048U) >> 12));
}
static void update_power_fade(uint8_t powered_on)
{
uint16_t step;
if(powered_on)
{
if(power_fade_direction != 1U)
{
power_fade_direction = 1U;
power_fade_remainder = 0U;
}
if(power_fade_progress_q12 >= POWER_FADE_Q12_MAX) return;
step = POWER_FADE_ON_STEP;
power_fade_remainder += POWER_FADE_ON_REMAINDER;
if(power_fade_remainder >= POWER_FADE_ON_TICKS)
{
power_fade_remainder -= POWER_FADE_ON_TICKS;
step++;
}
if(step >= POWER_FADE_Q12_MAX - power_fade_progress_q12)
power_fade_progress_q12 = POWER_FADE_Q12_MAX;
else
power_fade_progress_q12 += step;
}
else
{
if(power_fade_direction != 2U)
{
power_fade_direction = 2U;
power_fade_remainder = 0U;
}
if(power_fade_progress_q12 == 0U) return;
step = POWER_FADE_OFF_STEP;
power_fade_remainder += POWER_FADE_OFF_REMAINDER;
if(power_fade_remainder >= POWER_FADE_OFF_TICKS)
{
power_fade_remainder -= POWER_FADE_OFF_TICKS;
step++;
}
if(step >= power_fade_progress_q12)
power_fade_progress_q12 = 0U;
else
power_fade_progress_q12 -= step;
}
}
void Light_Transition_SyncBrightness(uint8_t level) void Light_Transition_SyncBrightness(uint8_t level)
{ {
current_brightness_q12 = brightness_scale_for_level(level); current_brightness_q12 = brightness_scale_for_level(level);
power_fade_progress_q12 =
level == 0U ? 0U : POWER_FADE_Q12_MAX;
power_fade_remainder = 0U;
power_fade_direction = 0U;
} }
void Light_Transition_Task(void) void Light_Transition_Task(void)
{ {
uint8_t target_brightness = 0U; uint8_t powered_on = deviceStatus != POWEROFF;
uint16_t target_w; uint16_t target_w;
uint16_t target_c; uint16_t target_c;
uint16_t target_brightness_scale; uint16_t target_brightness_scale;
uint16_t brightness_scale; uint16_t brightness_scale;
uint16_t fade_scale;
uint8_t power_transitioning;
if(deviceStatus != POWEROFF) target_brightness = Brightness; /*
target_brightness_scale = brightness_scale_for_level(target_brightness); * Keep APP brightness tracking while off. Power fading is a separate
* envelope, so on/off duration does not change with the saved brightness.
*/
target_brightness_scale = brightness_scale_for_level(Brightness);
current_brightness_q12 = current_brightness_q12 =
approach_brightness_scale(current_brightness_q12, approach_brightness_scale(current_brightness_q12,
target_brightness_scale); target_brightness_scale);
brightness_scale = current_brightness_q12; update_power_fade(powered_on);
fade_scale = power_fade_scale(power_fade_progress_q12);
brightness_scale = (uint16_t)(
((uint32_t)current_brightness_q12 * fade_scale + 2048U) >> 12);
power_transitioning =
powered_on ? (power_fade_progress_q12 < POWER_FADE_Q12_MAX)
: (power_fade_progress_q12 != 0U);
target_w = target_w =
(uint16_t)(((uint32_t)W_PWM * brightness_scale + 2048U) >> 12); (uint16_t)(((uint32_t)W_PWM * brightness_scale + 2048U) >> 12);
target_c = target_c =
(uint16_t)(((uint32_t)C_PWM * brightness_scale + 2048U) >> 12); (uint16_t)(((uint32_t)C_PWM * brightness_scale + 2048U) >> 12);
if(Mode == mode1 && target_brightness != 0U) if(Mode == mode1 && powered_on && !power_transitioning)
{ {
if(W_PWM != 0U && target_w < BREATH_MIN_OUTPUT) if(W_PWM != 0U && target_w < BREATH_MIN_OUTPUT)
target_w = BREATH_MIN_OUTPUT; target_w = BREATH_MIN_OUTPUT;
@@ -104,7 +217,7 @@ void Light_Transition_Task(void)
target_c = BREATH_MIN_OUTPUT; target_c = BREATH_MIN_OUTPUT;
} }
if(Mode == mode2 && deviceStatus != POWEROFF) if((Mode == mode2 && powered_on) || power_transitioning)
{ {
W_PWM_duty = target_w; W_PWM_duty = target_w;
C_PWM_duty = target_c; C_PWM_duty = target_c;
@@ -116,5 +229,6 @@ void Light_Transition_Task(void)
} }
Light_Transition_FadeActive = Light_Transition_FadeActive =
deviceStatus == POWEROFF && (W_PWM_duty != 0U || C_PWM_duty != 0U); !powered_on && (power_fade_progress_q12 != 0U ||
W_PWM_duty != 0U || C_PWM_duty != 0U);
} }