From d351ec3fc21ccfe34eb631d6519b42995b3bc807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E7=90=AA?= Date: Mon, 27 Jul 2026 17:03:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=80=E5=85=B3=E6=9C=BAGa?= =?UTF-8?q?mma=E6=B8=90=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ble_peripheral/app/src/light_transition.c | 128 +++++++++++++++++- 1 file changed, 121 insertions(+), 7 deletions(-) diff --git a/project/example/ble/ble_peripheral/app/src/light_transition.c b/project/example/ble/ble_peripheral/app/src/light_transition.c index 88d7e76..0181415 100644 --- a/project/example/ble/ble_peripheral/app/src/light_transition.c +++ b/project/example/ble/ble_peripheral/app/src/light_transition.c @@ -7,6 +7,13 @@ volatile uint8_t Light_Transition_FadeActive; #define LIGHT_FADE_STEP 1U #define BRIGHTNESS_SMOOTH_SHIFT 4U #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 @@ -26,7 +33,30 @@ static const uint16_t brightness_gamma_q12[100] = { 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 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) { @@ -71,32 +101,115 @@ static uint16_t approach_target(uint16_t current, uint16_t target) 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) { 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) { - uint8_t target_brightness = 0U; + uint8_t powered_on = deviceStatus != POWEROFF; uint16_t target_w; uint16_t target_c; uint16_t target_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 = approach_brightness_scale(current_brightness_q12, 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 = (uint16_t)(((uint32_t)W_PWM * brightness_scale + 2048U) >> 12); target_c = (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) target_w = BREATH_MIN_OUTPUT; @@ -104,7 +217,7 @@ void Light_Transition_Task(void) target_c = BREATH_MIN_OUTPUT; } - if(Mode == mode2 && deviceStatus != POWEROFF) + if((Mode == mode2 && powered_on) || power_transitioning) { W_PWM_duty = target_w; C_PWM_duty = target_c; @@ -116,5 +229,6 @@ void Light_Transition_Task(void) } 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); }