firmware/main: Add the new animation led code
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
parent
4da1f456ad
commit
69d8da7f2a
|
@ -18,6 +18,7 @@ HEADERS=\
|
|||
config.h \
|
||||
console.h \
|
||||
i2c.h \
|
||||
led_anim.h \
|
||||
led_ctrl.h \
|
||||
pmu.h \
|
||||
spi.h \
|
||||
|
@ -31,6 +32,7 @@ SOURCES=\
|
|||
start.S \
|
||||
console_dummy.c \
|
||||
i2c.c \
|
||||
led_anim.c \
|
||||
led_ctrl.c \
|
||||
main.c \
|
||||
pmu.c \
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* led_anim.c
|
||||
*
|
||||
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "led_anim.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define N_LEDS 42
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Helper: Candle effect */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Sequences from half life, designed to run at 10 fps */
|
||||
static const char * const candles[] = {
|
||||
"mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
|
||||
"mmmaaaabcdefgmmmmaaaammmaamm",
|
||||
"mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
|
||||
};
|
||||
|
||||
struct candle_state {
|
||||
uint8_t sel; /* Which candle ? */
|
||||
uint8_t len; /* Sequence lenght */
|
||||
uint8_t spd; /* Speed of sequence (64 = Nominal) */
|
||||
uint16_t pha; /* Current phase (10.6 format) */
|
||||
};
|
||||
|
||||
static void
|
||||
candle_init(struct candle_state *c)
|
||||
{
|
||||
c->sel = rand16() % 3;
|
||||
c->len = strlen(candles[c->sel]);
|
||||
c->spd = 48 + (rand16() % 40);
|
||||
c->pha = rand16() % (c->len << 6);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
candle_exec(struct candle_state *c, uint8_t spd)
|
||||
{
|
||||
int m;
|
||||
|
||||
/* Advance */
|
||||
/* We're called at 30 fps, but anim string nominal is 10 fps */
|
||||
/* Also we need to modulate both by our internal speed factor
|
||||
* and the one provided in argument */
|
||||
c->pha += (85 * spd * c->spd) >> 14;
|
||||
|
||||
/* Wrap */
|
||||
m = (int)c->len << 6;
|
||||
while (c->pha >= m)
|
||||
c->pha -= m;
|
||||
|
||||
/* Return current state */
|
||||
return candles[c->sel][c->pha >> 6] - 'a';
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Anim: Sparkle */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct la_sparkle_state {
|
||||
uint16_t time;
|
||||
bool candle;
|
||||
struct {
|
||||
uint16_t raw;
|
||||
struct candle_state cdl;
|
||||
} leds[N_LEDS];
|
||||
};
|
||||
|
||||
static void
|
||||
la_sparkle_init_common(void *state, bool candle)
|
||||
{
|
||||
struct la_sparkle_state *s = state;
|
||||
|
||||
s->time = 0;
|
||||
s->candle = candle;
|
||||
|
||||
for (int i=0; i<N_LEDS; i++) {
|
||||
s->leds[i].raw = 0;
|
||||
candle_init(&s->leds[i].cdl);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
la_sparkle1_init(void *state)
|
||||
{
|
||||
la_sparkle_init_common(state, true);
|
||||
}
|
||||
|
||||
static void
|
||||
la_sparkle2_init(void *state)
|
||||
{
|
||||
la_sparkle_init_common(state, false);
|
||||
}
|
||||
|
||||
static void
|
||||
la_sparkle_render(void *state, uint16_t *leds,
|
||||
struct led_anim_config *conf, uint32_t frame)
|
||||
{
|
||||
struct la_sparkle_state *s = state;
|
||||
bool has_ob = false;
|
||||
int i;
|
||||
|
||||
/* Decrease lightness of all leds */
|
||||
for (i=0; i<N_LEDS; i++)
|
||||
{
|
||||
uint16_t dec;
|
||||
|
||||
/* Base */
|
||||
dec = 2 * conf->speed;
|
||||
|
||||
/* Fast decrease for high intensity leds */
|
||||
if (s->leds[i].raw > 40000)
|
||||
dec += ((uint32_t)(s->leds[i].raw - 40000) * conf->speed) >> 10;
|
||||
|
||||
/* Decrement with low saturation to 0 */
|
||||
if ((s->leds[i].raw < dec) || (s->leds[i].raw < 500))
|
||||
s->leds[i].raw = 0;
|
||||
else
|
||||
s->leds[i].raw -= dec;
|
||||
|
||||
/* Check if any led is over bright */
|
||||
if (s->leds[i].raw > 45000)
|
||||
has_ob = true;
|
||||
}
|
||||
|
||||
/* Every ~0.25s we make a led brighter. If there is none already,
|
||||
* make it overbright, else just normal */
|
||||
s->time += conf->speed;
|
||||
if (s->time > 512) {
|
||||
s->time -= 512;
|
||||
while (s->leds[i = (rand16() % N_LEDS)].raw > 10000);
|
||||
s->leds[i].raw = has_ob ? 35000 : 65535;
|
||||
}
|
||||
|
||||
/* Final lightness is raw + candle flicker if it's < 6000 */
|
||||
for (i=0; i<N_LEDS; i++)
|
||||
{
|
||||
/* Base value */
|
||||
uint16_t r = s->leds[i].raw;
|
||||
|
||||
/* Add candle flicker */
|
||||
if (s->candle && (r < 6000))
|
||||
r += (550 * candle_exec(&s->leds[i].cdl, conf->speed));
|
||||
|
||||
/* Final assign */
|
||||
leds[i] = ((uint32_t)r * conf->dim) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct led_anim la_sparkle1 = {
|
||||
.init = la_sparkle1_init,
|
||||
.render = la_sparkle_render,
|
||||
};
|
||||
|
||||
static const struct led_anim la_sparkle2 = {
|
||||
.init = la_sparkle2_init,
|
||||
.render = la_sparkle_render,
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Anim: Candle */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
struct la_candle_state {
|
||||
struct candle_state cdl[N_LEDS];
|
||||
};
|
||||
|
||||
static void
|
||||
la_candle_init(void *state)
|
||||
{
|
||||
struct la_candle_state *s = state;
|
||||
|
||||
for (int i=0; i<N_LEDS; i++)
|
||||
candle_init(&s->cdl[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
la_candle_render(void *state, uint16_t *leds,
|
||||
struct led_anim_config *conf, uint32_t frame)
|
||||
{
|
||||
struct la_candle_state *s = state;
|
||||
|
||||
for (int i=0; i<N_LEDS; i++)
|
||||
leds[i] = (3750 * conf->dim * candle_exec(&s->cdl[i], conf->speed)) >> 8;
|
||||
}
|
||||
|
||||
static const struct led_anim la_candle = {
|
||||
.init = la_candle_init,
|
||||
.render = la_candle_render,
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Anim: Breathe */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static const uint8_t breathe[256] = {
|
||||
2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7,
|
||||
8, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23,
|
||||
25, 26, 28, 30, 32, 34, 36, 38, 41, 43, 46, 48, 51, 54, 57, 60,
|
||||
63, 67, 70, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 114, 118, 123,
|
||||
127, 132, 137, 141, 146, 151, 156, 160, 165, 170, 174, 179, 184, 188, 193, 197,
|
||||
201, 206, 210, 214, 217, 221, 225, 228, 231, 234, 237, 240, 242, 245, 247, 249,
|
||||
250, 252, 253, 254, 254, 255, 255, 255, 255, 255, 254, 254, 253, 253, 252, 251,
|
||||
250, 249, 248, 247, 246, 245, 243, 242, 240, 238, 237, 235, 233, 231, 229, 227,
|
||||
224, 222, 220, 217, 215, 213, 210, 207, 205, 202, 199, 196, 194, 191, 188, 185,
|
||||
182, 179, 176, 173, 170, 167, 164, 161, 158, 155, 152, 149, 146, 143, 140, 137,
|
||||
134, 131, 128, 125, 122, 119, 116, 113, 110, 108, 105, 102, 99, 97, 94, 91,
|
||||
89, 86, 84, 81, 79, 77, 74, 72, 70, 68, 65, 63, 61, 59, 57, 55,
|
||||
53, 52, 50, 48, 46, 45, 43, 41, 40, 38, 37, 36, 34, 33, 32, 30,
|
||||
29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 17, 16, 15,
|
||||
14, 14, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 8, 7, 7,
|
||||
6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
struct la_breathe_state {
|
||||
int n_ob;
|
||||
struct {
|
||||
uint8_t spd; /* Speed (64=Nominal) */
|
||||
uint16_t pha; /* Phase (8.8 FP) */
|
||||
uint8_t brg; /* Brightness (128=Nominal, 184=OverBright) */
|
||||
bool ob;
|
||||
} leds[N_LEDS];
|
||||
};
|
||||
|
||||
static void
|
||||
la_breathe_init(void *state)
|
||||
{
|
||||
struct la_breathe_state *s = state;
|
||||
|
||||
s->n_ob = 0;
|
||||
|
||||
for (int i=0; i<N_LEDS; i++) {
|
||||
s->leds[i].spd = 48 + (rand16() % 40);
|
||||
s->leds[i].pha = rand16();
|
||||
s->leds[i].brg = 64 + (rand16() & 63);
|
||||
s->leds[i].ob = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
la_breathe_render(void *state, uint16_t *leds,
|
||||
struct led_anim_config *conf, uint32_t frame)
|
||||
{
|
||||
struct la_breathe_state *s = state;
|
||||
|
||||
for (int i=0; i<N_LEDS; i++) {
|
||||
/* Advance */
|
||||
uint16_t pha = s->leds[i].pha + ((28 * s->leds[i].spd * conf->speed) >> 8);
|
||||
|
||||
/* Wrap around */
|
||||
if (pha < s->leds[i].pha) {
|
||||
/* Pick new speed */
|
||||
s->leds[i].spd = 48 + (rand16() % 40);
|
||||
|
||||
/* New brightness, possibly OverBright */
|
||||
if (!s->leds[i].ob && (s->n_ob < 2) && (rand16() & 1)) {
|
||||
s->leds[i].brg = 152 + (rand16() & 31);
|
||||
s->leds[i].ob = true;
|
||||
s->n_ob++;
|
||||
} else {
|
||||
s->leds[i].brg = 64 + (rand16() & 63);
|
||||
}
|
||||
}
|
||||
|
||||
s->leds[i].pha = pha;
|
||||
|
||||
/* Early OverBright release */
|
||||
/* After pha >= 40000, we're not overbright anyway, so
|
||||
* allow another led to use it early */
|
||||
if (s->leds[i].ob && (s->leds[i].pha > 40000)) {
|
||||
s->leds[i].ob = false;
|
||||
s->n_ob--;
|
||||
}
|
||||
|
||||
/* Render */
|
||||
leds[i] = (350UL * conf->dim * s->leds[i].brg * breathe[s->leds[i].pha >> 8]) >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct led_anim la_breathe = {
|
||||
.init = la_breathe_init,
|
||||
.render = la_breathe_render,
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Anim: Constant */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static void
|
||||
la_const_init(void *state)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static void
|
||||
la_const_render(void *state, uint16_t *leds,
|
||||
struct led_anim_config *conf, uint32_t frame)
|
||||
{
|
||||
for (int i=0; i<N_LEDS; i++)
|
||||
leds[i] = conf->dim * 192;
|
||||
}
|
||||
|
||||
static const struct led_anim la_const = {
|
||||
.init = la_const_init,
|
||||
.render = la_const_render,
|
||||
};
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Anim list */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
const struct led_anim * const all_anims[] = {
|
||||
&la_sparkle1,
|
||||
&la_sparkle2,
|
||||
&la_candle,
|
||||
&la_breathe,
|
||||
&la_const,
|
||||
NULL /* guard */
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* led_anim.h
|
||||
*
|
||||
* Copyright (C) 2023 Sylvain Munaut <tnt@246tNt.com>
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
struct led_anim_config {
|
||||
uint8_t dim; /* 255 = Normal */
|
||||
uint8_t speed; /* 64 = Normal */
|
||||
};
|
||||
|
||||
typedef void (*led_anim_init_fn) (void *state);
|
||||
typedef void (*led_anim_render_fn)(void *state, uint16_t *leds, struct led_anim_config *conf, uint32_t frame);
|
||||
|
||||
struct led_anim {
|
||||
led_anim_init_fn init;
|
||||
led_anim_render_fn render;
|
||||
};
|
||||
|
||||
extern const struct led_anim * const all_anims[];
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "config.h"
|
||||
#include "pmu.h"
|
||||
#include "led_anim.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
|
@ -135,38 +136,6 @@ cie_light2pwm(uint16_t lum)
|
|||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Animation */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
_led_render(uint32_t frame, uint16_t *leds)
|
||||
{
|
||||
/* Decrease luminosoty so they all fade out in 15 second */
|
||||
for (int i=0; i<42; i++)
|
||||
{
|
||||
if (leds[i] > 40000)
|
||||
leds[i] -= 150 + ((leds[i] - 40000) >> 4);
|
||||
if (leds[i] > 500)
|
||||
leds[i] -= 150;
|
||||
else
|
||||
leds[i] = 0;
|
||||
}
|
||||
|
||||
/* Every second, pick a led and make it bright */
|
||||
if ((frame & 15) == 0) {
|
||||
int i;
|
||||
while (leds[i = (rand16() % 42)] > 10000);
|
||||
leds[i] = 65535;
|
||||
}
|
||||
else if ((frame & 7) == 0) {
|
||||
int i;
|
||||
i = (rand16() % 42);
|
||||
leds[i] = 35000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Frame mapping */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
@ -302,16 +271,50 @@ _led_map(struct led_subframe *subframes, uint16_t *leds)
|
|||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Frame mapping */
|
||||
/* Animation Runner */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define N_DIM 2
|
||||
static const uint8_t k_dim[] = { 255, 144 };
|
||||
|
||||
#define N_SPEED 3
|
||||
static const uint8_t k_speed[] = { 64, 104, 36 };
|
||||
|
||||
static struct {
|
||||
/* UI options */
|
||||
uint8_t ui_dim;
|
||||
uint8_t ui_speed;
|
||||
uint8_t ui_anim;
|
||||
|
||||
/* HW control */
|
||||
uint8_t frame_nxt;
|
||||
uint32_t time;
|
||||
|
||||
/* Current time */
|
||||
uint32_t frame;
|
||||
|
||||
/* Temp buffers */
|
||||
uint16_t leds[14*3];
|
||||
struct led_subframe subframe[LED_N_SUBFRAMES];
|
||||
} g_led;
|
||||
|
||||
static uint8_t g_anim[1024] __attribute__((aligned(4)));
|
||||
|
||||
|
||||
static void
|
||||
_led_render(void)
|
||||
{
|
||||
struct led_anim_config cfg;
|
||||
|
||||
/* Increment time */
|
||||
g_led.frame++;
|
||||
|
||||
/* Get dim / speed */
|
||||
cfg.dim = k_dim [g_led.ui_dim];
|
||||
cfg.speed = k_speed [g_led.ui_speed];
|
||||
|
||||
/* Execute */
|
||||
all_anims[g_led.ui_anim]->render(g_anim, g_led.leds, &cfg, g_led.frame);
|
||||
}
|
||||
|
||||
static bool
|
||||
_led_fill(void)
|
||||
|
@ -333,7 +336,7 @@ _led_fill(void)
|
|||
while (g_led.frame_nxt != frame_limit)
|
||||
{
|
||||
/* Render frame */
|
||||
_led_render(g_led.time++, g_led.leds);
|
||||
_led_render();
|
||||
|
||||
/* Convert to subframe */
|
||||
_led_map(g_led.subframe, g_led.leds);
|
||||
|
@ -366,6 +369,9 @@ led_init(void)
|
|||
/* Clear internal state */
|
||||
memset(&g_led, 0x00, sizeof(g_led));
|
||||
|
||||
/* Init animation */
|
||||
all_anims[g_led.ui_anim]->init(g_anim);
|
||||
|
||||
/* Clear frame memory */
|
||||
for (int frame=0; frame<LED_N_FRAMES; frame++) {
|
||||
for (int subframe=0; subframe<LED_N_SUBFRAMES; subframe++) {
|
||||
|
@ -394,6 +400,29 @@ led_stop(void)
|
|||
led_regs->csr &= ~(LED_CSR_DRV_CURREN | LED_CSR_DRV_RGBLEDEN | LED_CSR_DRV_SCAN_ENA);
|
||||
}
|
||||
|
||||
void
|
||||
led_cycle_dim(void)
|
||||
{
|
||||
if (++g_led.ui_dim >= N_DIM)
|
||||
g_led.ui_dim = 0;
|
||||
}
|
||||
|
||||
void
|
||||
led_cycle_speed(void)
|
||||
{
|
||||
if (++g_led.ui_speed >= N_SPEED)
|
||||
g_led.ui_speed = 0;
|
||||
}
|
||||
|
||||
void
|
||||
led_cycle_anim(void)
|
||||
{
|
||||
if (!all_anims[++g_led.ui_anim])
|
||||
g_led.ui_anim = 0;
|
||||
|
||||
all_anims[g_led.ui_anim]->init(g_anim);
|
||||
}
|
||||
|
||||
void
|
||||
led_poll(bool suspend)
|
||||
{
|
||||
|
|
|
@ -11,4 +11,8 @@ void led_init(void);
|
|||
void led_start(void);
|
||||
void led_stop(void);
|
||||
|
||||
void led_cycle_dim(void);
|
||||
void led_cycle_speed(void);
|
||||
void led_cycle_anim(void);
|
||||
|
||||
void led_poll(bool suspend);
|
||||
|
|
|
@ -122,15 +122,15 @@ void main()
|
|||
uint8_t btn = pmu_get_buttons();
|
||||
|
||||
if (btn & BTN_1_SHORT) {
|
||||
/* FIXME */
|
||||
led_cycle_speed();
|
||||
} else if (btn & BTN_1_LONG) {
|
||||
/* FIXME */
|
||||
led_cycle_dim();
|
||||
}
|
||||
|
||||
if (btn & BTN_2_SHORT) {
|
||||
/* FIXME */
|
||||
led_cycle_anim();
|
||||
} else if (btn & BTN_2_LONG) {
|
||||
/* FIXME */
|
||||
pmu_sys_shutdown();
|
||||
}
|
||||
|
||||
/* LED */
|
||||
|
|
Loading…
Reference in New Issue