|
|
|
@ -16,18 +16,25 @@
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* Hardware definitions */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 31 24 23 16 15 8 7 0
|
|
|
|
|
* --------------------------------------------------------------------------------
|
|
|
|
|
* | stop[0] | start[0] | / | anode_sel |
|
|
|
|
|
* --------------------------------------------------------------------------------
|
|
|
|
|
* | stop[2] | start[2] | stop[1] | start[1] |
|
|
|
|
|
* --------------------------------------------------------------------------------
|
|
|
|
|
* Subframe format:
|
|
|
|
|
*
|
|
|
|
|
* 31 24 23 16 15 8 7 0
|
|
|
|
|
* -----------------------------------------------------------------------
|
|
|
|
|
* | stop[0] | start[0] | / | anode_sel |
|
|
|
|
|
* -----------------------------------------------------------------------
|
|
|
|
|
* | stop[2] | start[2] | stop[1] | start[1] |
|
|
|
|
|
* -----------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define LED_N_FRAMES 16
|
|
|
|
|
#define LED_N_SUBFRAMES 16
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct led_subframe {
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
@ -53,6 +60,7 @@ struct wb_led_ctrl {
|
|
|
|
|
} frame[LED_N_FRAMES];
|
|
|
|
|
} __attribute__((packed,aligned(4)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define LED_CSR_TRIG_CLR (1 << 31)
|
|
|
|
|
#define LED_CSR_TRIG_ENA (1 << 30)
|
|
|
|
|
#define LED_CSR_TRIG_FRAME(n) (((n) & 0xf) << 24)
|
|
|
|
@ -65,15 +73,11 @@ struct wb_led_ctrl {
|
|
|
|
|
static volatile struct wb_led_ctrl * const led_regs = (void*)(LED_CTRL_BASE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
uint8_t frame_nxt;
|
|
|
|
|
uint32_t time;
|
|
|
|
|
uint16_t leds[14*3];
|
|
|
|
|
struct led_subframe subframe[LED_N_SUBFRAMES+1];
|
|
|
|
|
} g_led;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* CIE luminosity */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
/* Maps every PWM [1-256] to a perceived luminosity value [0-65535] */
|
|
|
|
|
static const uint16_t cie[] = {
|
|
|
|
|
1156, 3468, 5703, 7651, 9253, 10628, 11843, 12938,
|
|
|
|
|
13938, 14863, 15724, 16533, 17295, 18018, 18705, 19362,
|
|
|
|
@ -109,16 +113,7 @@ static const uint16_t cie[] = {
|
|
|
|
|
64785, 64886, 64986, 65087, 65187, 65287, 65386, 65485,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const int k_scale[] = {
|
|
|
|
|
189, /* Emerald */
|
|
|
|
|
256, /* Pink */
|
|
|
|
|
236, /* Blue */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define K0 1 /* Pink */
|
|
|
|
|
#define K1 0 /* Emerald */
|
|
|
|
|
#define K2 2 /* Blue */
|
|
|
|
|
|
|
|
|
|
/* Converts a requested luminosity value to PWM length */
|
|
|
|
|
static uint16_t
|
|
|
|
|
cie_lum2pwm(uint16_t lum)
|
|
|
|
|
{
|
|
|
|
@ -139,12 +134,13 @@ cie_lum2pwm(uint16_t lum)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* Animation */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_led_render(uint32_t frame, uint16_t *leds)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
|
/* Decrease luminosoty so they all fade out in 15 second */
|
|
|
|
|
for (int i=0; i<42; i++)
|
|
|
|
|
{
|
|
|
|
@ -167,23 +163,30 @@ _led_render(uint32_t frame, uint16_t *leds)
|
|
|
|
|
i = (rand16() % 42);
|
|
|
|
|
leds[i] = 35000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
for (int i=0; i<42; i+=3)
|
|
|
|
|
{
|
|
|
|
|
leds[i+0] = 35000; /* Emerald */
|
|
|
|
|
leds[i+1] = 35000; /* Pink */
|
|
|
|
|
leds[i+2] = 35000; /* Blue */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* Frame mapping */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
/* Mapping priority */
|
|
|
|
|
#define K0 1 /* Pink */
|
|
|
|
|
#define K1 0 /* Emerald */
|
|
|
|
|
#define K2 2 /* Blue */
|
|
|
|
|
|
|
|
|
|
/* Scaling factor to match intensity */
|
|
|
|
|
static const int k_scale[] = {
|
|
|
|
|
189, /* Emerald */
|
|
|
|
|
256, /* Pink */
|
|
|
|
|
236, /* Blue */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_led_map_one(int sf, int anode, uint16_t *pwm)
|
|
|
|
|
_led_map_one(struct led_subframe *subframe, int anode, uint16_t *pwm)
|
|
|
|
|
{
|
|
|
|
|
/* Select anode */
|
|
|
|
|
g_led.subframe[sf].anode = anode;
|
|
|
|
|
subframe->anode = anode;
|
|
|
|
|
|
|
|
|
|
/* Check various cases */
|
|
|
|
|
if (pwm[0] + pwm[1] + pwm[2] <= 128)
|
|
|
|
@ -194,8 +197,8 @@ _led_map_one(int sf, int anode, uint16_t *pwm)
|
|
|
|
|
|
|
|
|
|
for (int k=0; k<3; k++) {
|
|
|
|
|
if (pwm[k] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[k].start = o;
|
|
|
|
|
g_led.subframe[sf].cathode[k].stop = o + pwm[k] - 1;
|
|
|
|
|
subframe->cathode[k].start = o;
|
|
|
|
|
subframe->cathode[k].stop = o + pwm[k] - 1;
|
|
|
|
|
o += pwm[k] + s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -207,21 +210,21 @@ _led_map_one(int sf, int anode, uint16_t *pwm)
|
|
|
|
|
/* K0 split, K1 & K2 overlap */
|
|
|
|
|
/* K0 starting at 0 */
|
|
|
|
|
if (pwm[K0] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K0].start = o;
|
|
|
|
|
g_led.subframe[sf].cathode[K0].stop = o + pwm[K0] - 1;
|
|
|
|
|
subframe->cathode[K0].start = o;
|
|
|
|
|
subframe->cathode[K0].stop = o + pwm[K0] - 1;
|
|
|
|
|
o += pwm[K0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* K1 starts right after K0 */
|
|
|
|
|
if (pwm[K1] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K1].start = o;
|
|
|
|
|
g_led.subframe[sf].cathode[K1].stop = o + pwm[K1] - 1;
|
|
|
|
|
subframe->cathode[K1].start = o;
|
|
|
|
|
subframe->cathode[K1].stop = o + pwm[K1] - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* K2 aligned to the end */
|
|
|
|
|
if (pwm[K2] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K2].start = 0x7f - pwm[K2] + 1;
|
|
|
|
|
g_led.subframe[sf].cathode[K2].stop = 0x7f;
|
|
|
|
|
subframe->cathode[K2].start = 0x7f - pwm[K2] + 1;
|
|
|
|
|
subframe->cathode[K2].stop = 0x7f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -229,25 +232,25 @@ _led_map_one(int sf, int anode, uint16_t *pwm)
|
|
|
|
|
/* Default, K0 on one side, then K1 & K2 full overlap on the other */
|
|
|
|
|
/* K0 aligned at the end */
|
|
|
|
|
if (pwm[K0] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K0].start = 0x7f - pwm[K0] + 1;
|
|
|
|
|
g_led.subframe[sf].cathode[K0].stop = 0x7f;
|
|
|
|
|
subframe->cathode[K0].start = 0x7f - pwm[K0] + 1;
|
|
|
|
|
subframe->cathode[K0].stop = 0x7f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* K1 & K2 overlapping at beginning */
|
|
|
|
|
if (pwm[K1] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K1].start = 0;
|
|
|
|
|
g_led.subframe[sf].cathode[K1].stop = pwm[K1] - 1;
|
|
|
|
|
subframe->cathode[K1].start = 0;
|
|
|
|
|
subframe->cathode[K1].stop = pwm[K1] - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pwm[K2] > 0) {
|
|
|
|
|
g_led.subframe[sf].cathode[K2].start = 0;
|
|
|
|
|
g_led.subframe[sf].cathode[K2].stop = pwm[K2] - 1;
|
|
|
|
|
subframe->cathode[K2].start = 0;
|
|
|
|
|
subframe->cathode[K2].stop = pwm[K2] - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_led_map(void)
|
|
|
|
|
_led_map(struct led_subframe *subframes, uint16_t *leds)
|
|
|
|
|
{
|
|
|
|
|
uint16_t pwm[42];
|
|
|
|
|
int k, a, l;
|
|
|
|
@ -255,14 +258,14 @@ _led_map(void)
|
|
|
|
|
|
|
|
|
|
/* Pre-clear */
|
|
|
|
|
for (int sf=0; sf<16; sf++) {
|
|
|
|
|
g_led.subframe[sf].w[0] = 0x007f0000;
|
|
|
|
|
g_led.subframe[sf].w[1] = 0x007f007f;
|
|
|
|
|
subframes[sf].w[0] = 0x007f0000;
|
|
|
|
|
subframes[sf].w[1] = 0x007f007f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert all linear brightness to pwm times */
|
|
|
|
|
for (a=0, l=0; a<14; a++)
|
|
|
|
|
for (k=0; k<3; k++, l++)
|
|
|
|
|
pwm[l] = (cie_lum2pwm(g_led.leds[l]) * k_scale[k]) >> 8;
|
|
|
|
|
pwm[l] = (cie_lum2pwm(leds[l]) * k_scale[k]) >> 8;
|
|
|
|
|
|
|
|
|
|
/* Attempt packing */
|
|
|
|
|
sf_l = 0; /* Low boundary */
|
|
|
|
@ -283,31 +286,46 @@ _led_map(void)
|
|
|
|
|
p1[i] = (pwm[l+i] + 1) >> 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_led_map_one(sf_l++, a, p0);
|
|
|
|
|
_led_map_one(--sf_h, a, p1);
|
|
|
|
|
_led_map_one(&subframes[sf_l++], a, p0);
|
|
|
|
|
_led_map_one(&subframes[--sf_h], a, p1);
|
|
|
|
|
}
|
|
|
|
|
else if ((pwm[l+0] > 0) ||
|
|
|
|
|
(pwm[l+1] > 0) ||
|
|
|
|
|
(pwm[l+2] > 0))
|
|
|
|
|
{
|
|
|
|
|
/* Map all in one subframe */
|
|
|
|
|
_led_map_one(sf_l++, a, &pwm[l]);
|
|
|
|
|
_led_map_one(&subframes[sf_l++], a, &pwm[l]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* Frame mapping */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
|
uint8_t frame_nxt;
|
|
|
|
|
uint32_t time;
|
|
|
|
|
uint16_t leds[14*3];
|
|
|
|
|
struct led_subframe subframe[LED_N_SUBFRAMES];
|
|
|
|
|
} g_led;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
_led_fill(void)
|
|
|
|
|
{
|
|
|
|
|
int f0, f1;
|
|
|
|
|
int frame_limit;
|
|
|
|
|
bool work = false;
|
|
|
|
|
|
|
|
|
|
int f0, f1;
|
|
|
|
|
/* "Safe" read from hardware */
|
|
|
|
|
do {
|
|
|
|
|
f0 = LED_CSR_GET_FRAME(led_regs->csr);
|
|
|
|
|
f1 = LED_CSR_GET_FRAME(led_regs->csr);
|
|
|
|
|
} while (f0 != f1);
|
|
|
|
|
|
|
|
|
|
/* Compute the first frame we can't write to */
|
|
|
|
|
frame_limit = (f0 - 1) & (LED_N_FRAMES - 1);
|
|
|
|
|
|
|
|
|
|
/* Fill to catch up */
|
|
|
|
@ -317,7 +335,7 @@ _led_fill(void)
|
|
|
|
|
_led_render(g_led.time++, g_led.leds);
|
|
|
|
|
|
|
|
|
|
/* Convert to subframe */
|
|
|
|
|
_led_map();
|
|
|
|
|
_led_map(g_led.subframe, g_led.leds);
|
|
|
|
|
|
|
|
|
|
/* Copy to hardware */
|
|
|
|
|
for (int subframe=0; subframe<LED_N_SUBFRAMES; subframe++) {
|
|
|
|
@ -333,9 +351,17 @@ _led_fill(void)
|
|
|
|
|
return work;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* External API */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
led_init(void)
|
|
|
|
|
{
|
|
|
|
|
/* Shutdown hardware */
|
|
|
|
|
led_regs->csr = 0;
|
|
|
|
|
|
|
|
|
|
/* Clear internal state */
|
|
|
|
|
memset(&g_led, 0x00, sizeof(g_led));
|
|
|
|
|
|
|
|
|
@ -346,18 +372,6 @@ led_init(void)
|
|
|
|
|
led_regs->frame[frame].subframe[subframe].w[1] = 0x007f007f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test frames */
|
|
|
|
|
for (int frame=0; frame<LED_N_FRAMES; frame++) {
|
|
|
|
|
led_regs->frame[frame].subframe[0].w[0] = 0x007f0001;
|
|
|
|
|
led_regs->frame[frame].subframe[0].w[1] = 0x007f007f;
|
|
|
|
|
|
|
|
|
|
led_regs->frame[frame].subframe[1].w[0] = 0x7f000006;
|
|
|
|
|
led_regs->frame[frame].subframe[1].w[1] = 0x7f007f00;
|
|
|
|
|
|
|
|
|
|
led_regs->frame[frame].subframe[2].w[0] = 0x007f0001;
|
|
|
|
|
led_regs->frame[frame].subframe[2].w[1] = 0x007f007f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|