firmware/main: Add the new animation led code

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2023-03-19 19:47:39 +01:00
parent 4da1f456ad
commit 69d8da7f2a
6 changed files with 433 additions and 39 deletions

View File

@ -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 \

333
firmware/main/led_anim.c Normal file
View File

@ -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 */
};

26
firmware/main/led_anim.h Normal file
View File

@ -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[];

View File

@ -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)
{

View File

@ -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);

View File

@ -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 */