diff --git a/firmware/main/led_ctrl.c b/firmware/main/led_ctrl.c index 1a9a990..65ed2a5 100644 --- a/firmware/main/led_ctrl.c +++ b/firmware/main/led_ctrl.c @@ -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; subframecsr = 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; frameframe[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