diff --git a/firmware/app/led/ledprog.c b/firmware/app/led/ledprog.c index f3dcc58..5f5d89e 100644 --- a/firmware/app/led/ledprog.c +++ b/firmware/app/led/ledprog.c @@ -3,7 +3,7 @@ */ #include "matrix.h" - +#include "misc/tinymt.h" static uint32_t s[8] = {0}; @@ -31,6 +31,17 @@ void lp_ribbon_upward(uint16_t wait, uint16_t rate, uint8_t fade) 1: timeout 2: index */ + + if (fade > 0x80) + fade = 0x80; + + // reduce 1-frame bright blip + for (i = 0; i < sizeof(led_set.ribbon); i++) { + if (led_set.ribbon[i] >= 0xd0) { + led_set.ribbon[i] = 0xd0; + } + } + switch (s[0]) { case 0: { // timeout if (!s[1]) { @@ -64,16 +75,62 @@ void lp_ribbon_upward(uint16_t wait, uint16_t rate, uint8_t fade) // fade in and up led_set.ribbon[x] = 0x20; if (x) led_set.ribbon[x - 1] = 0x66; - if (x > 1) led_set.ribbon[x - 2] = 0xd0; + if (x > 1) led_set.ribbon[x - 2] = 0xef; } } // fade out LEDs for (i = 0; i < sizeof(led_set.ribbon); i++) { + if (led_set.ribbon[i] >= 0x70) { + led_set.ribbon[i] -= fade; + } if (led_set.ribbon[i] >= fade) { led_set.ribbon[i] -= fade; } else { - led_set.ribbon[i] >>= 1; + // led_set.ribbon[i] >>= 1; + if (led_set.ribbon[i]) + led_set.ribbon[i]--; + } + } + + matrix_flag_update(); +} + +void lp_ribbon_twinkle(uint8_t idle, uint8_t max, uint8_t thresh_lo, uint8_t thresh_hi) +{ + /* + idle: attempted idle target. ideally an odd number not a power of two. + max: maximum brightness. anything higher is clamped to this + thresh_lo: rand must be lower than this to make any adjustment this cycle + thresh_hi: rand must be this or higher to force a bright twinkle + */ + + uint32_t i; + uint32_t w, x; + + s[1]++; + + for (i = 0; i < sizeof(led_set.ribbon); i++) { + x = prng_get8(); + + if (x < thresh_lo) { + w = (idle > 3) ? 3 : idle; + + // swing around idle level + if (led_set.ribbon[i] > idle) { + led_set.ribbon[i] >>= 1; + } else { + led_set.ribbon[i] += (w - 1); + } + } + + // only make bright stars every 4 frames + if ((x >= thresh_hi) && ((s[1] & 3) == 0)) { + w = prng_get8(); + led_set.ribbon[i] += w; + if (led_set.ribbon[i] > max) { + led_set.ribbon[i] = max; + } } } diff --git a/firmware/app/led/ledprog.h b/firmware/app/led/ledprog.h index 1e8bf65..43fa354 100644 --- a/firmware/app/led/ledprog.h +++ b/firmware/app/led/ledprog.h @@ -9,6 +9,7 @@ void lp_ribbon_init(); void lp_ribbon_upward(uint16_t wait, uint16_t rate, uint8_t fade); +void lp_ribbon_twinkle(uint8_t idle, uint8_t max, uint8_t thresh_lo, uint8_t thresh_hi); diff --git a/firmware/app/main.c b/firmware/app/main.c index d17d701..5edf202 100644 --- a/firmware/app/main.c +++ b/firmware/app/main.c @@ -29,6 +29,8 @@ #include "ui/btn.h" #include "ui/temp_ui.h" +#include "misc/tinymt.h" + /* *@Note ***Only PA0--PA15 and PC16--PC17 support input pull-down. @@ -166,6 +168,9 @@ int main(void) // set up user interface tempui_init(); + // set up random + tinymt32_init(&tinymt32_s, 1337); + // configure AWU to provide ~997Hz wakeup interrupt for main program // TODO: confirm if the counter is reset / preloaded awu_init(); @@ -175,7 +180,7 @@ int main(void) // rendering next LED program output frame if (lp_render) { lp_render = 0; - lp_ribbon_upward(200, 3, 4); + tempui_lp_runprog(); } // stay a while diff --git a/firmware/app/misc/tinymt.c b/firmware/app/misc/tinymt.c new file mode 100644 index 0000000..3058f80 --- /dev/null +++ b/firmware/app/misc/tinymt.c @@ -0,0 +1,162 @@ +/** + * Tiny Mersenne Twister: only 127-bit internal state. + * Derived from the reference implementation version 1.1 (2015/04/24) + * by Mutsuo Saito (Hiroshima University) and Makoto Matsumoto + * (Hiroshima University). + */ +#include +#include "tinymt.h" + + + +static void tinymt32_next_state(tinymt32_t *s); +static uint32_t tinymt32_temper(tinymt32_t *s); + + + +tinymt32_t tinymt32_s; + + + +/** + * Parameter set to use for this IETF specification. Don't change. + * This parameter set is the first entry of the precalculated + * parameter sets in tinymt32dc/tinymt32dc.0.1048576.txt by + * Kenji Rikitake, available at: + * https://github.com/jj1bdx/tinymtdc-longbatch/. + * It is also the parameter set used in: + * Rikitake, K., "TinyMT pseudo random number generator for + * Erlang", Proceedings of the 11th ACM SIGPLAN Erlang Workshop, + * September 2012. + */ +const uint32_t TINYMT32_MAT1_PARAM = UINT32_C(0x8f7011ee); +const uint32_t TINYMT32_MAT2_PARAM = UINT32_C(0xfc78ff1f); +const uint32_t TINYMT32_TMAT_PARAM = UINT32_C(0x3793fdff); + +/** + * This function initializes the internal state array with a + * 32-bit unsigned integer seed. + * @param s pointer to tinymt internal state. + * @param seed a 32-bit unsigned integer used as a seed. + */ +void tinymt32_init(tinymt32_t* s, uint32_t seed) +{ + const uint32_t MIN_LOOP = 8; + const uint32_t PRE_LOOP = 8; + s->status[0] = seed; + s->status[1] = s->mat1 = TINYMT32_MAT1_PARAM; + s->status[2] = s->mat2 = TINYMT32_MAT2_PARAM; + s->status[3] = s->tmat = TINYMT32_TMAT_PARAM; + for (int i = 1; i < MIN_LOOP; i++) { + s->status[i & 3] ^= i + UINT32_C(1812433253) + * (s->status[(i - 1) & 3] + ^ (s->status[(i - 1) & 3] >> 30)); + } + /* + * NB: The parameter set of this specification warrants + * that none of the possible 2^^32 seeds leads to an + * all-zero 127-bit internal state. Therefore, the + * period_certification() function of the original + * TinyMT32 source code has been safely removed. If + * another parameter set is used, this function will + * have to be reintroduced here. + */ + for (int i = 0; i < PRE_LOOP; i++) { + tinymt32_next_state(s); + } +} + +/** + * This function outputs a 32-bit unsigned integer from + * the internal state. + * @param s pointer to tinymt internal state. + * @return 32-bit unsigned integer r (0 <= r < 2^32). + */ +uint32_t tinymt32_get_uint32(tinymt32_t* s) +{ + tinymt32_next_state(s); + return tinymt32_temper(s); +} + +/** + * Internal tinymt32 constants and functions. + * Users should not call these functions directly. + */ +const uint32_t TINYMT32_SH0 = 1; +const uint32_t TINYMT32_SH1 = 10; +const uint32_t TINYMT32_SH8 = 8; +const uint32_t TINYMT32_MASK = UINT32_C(0x7fffffff); + +/** + * This function changes the internal state of tinymt32. + * @param s pointer to tinymt internal state. + */ +void tinymt32_next_state(tinymt32_t* s) +{ + uint32_t x; + uint32_t y; + + y = s->status[3]; + x = (s->status[0] & TINYMT32_MASK) + ^ s->status[1] + ^ s->status[2]; + x ^= (x << TINYMT32_SH0); + y ^= (y >> TINYMT32_SH0) ^ x; + s->status[0] = s->status[1]; + s->status[1] = s->status[2]; + s->status[2] = x ^ (y << TINYMT32_SH1); + s->status[3] = y; + /* + * The if (y & 1) {...} block below replaces: + * s->status[1] ^= -((int32_t)(y & 1)) & s->mat1; + * s->status[2] ^= -((int32_t)(y & 1)) & s->mat2; + * The adopted code is equivalent to the original code + * but does not depend on the representation of negative + * integers by 2's complements. It is therefore more + * portable but includes an if branch, which may slow + * down the generation speed. + */ + if (y & 1) { + s->status[1] ^= s->mat1; + s->status[2] ^= s->mat2; + } +} + +/** + * This function outputs a 32-bit unsigned integer from + * the internal state. + * @param s pointer to tinymt internal state. + * @return 32-bit unsigned pseudorandom number. + */ +uint32_t tinymt32_temper (tinymt32_t* s) +{ + uint32_t t0, t1; + t0 = s->status[3]; + t1 = s->status[0] + (s->status[2] >> TINYMT32_SH8); + t0 ^= t1; + /* + * The if (t1 & 1) {...} block below replaces: + * t0 ^= -((int32_t)(t1 & 1)) & s->tmat; + * The adopted code is equivalent to the original code + * but does not depend on the representation of negative + * integers by 2's complements. It is therefore more + * portable but includes an if branch, which may slow + * down the generation speed. + */ + if (t1 & 1) { + t0 ^= s->tmat; + } + return t0; +} + +uint16_t prng_scale16(uint16_t min, uint16_t max) +{ + uint32_t rnd; + + rnd = prng_get16(); + rnd *= (max - min); + rnd >>= 16; + rnd += min; + + return rnd; +} diff --git a/firmware/app/misc/tinymt.h b/firmware/app/misc/tinymt.h new file mode 100644 index 0000000..42ec304 --- /dev/null +++ b/firmware/app/misc/tinymt.h @@ -0,0 +1,39 @@ +/** + * Tiny Mersenne Twister + */ + +#ifndef TINYMT_RAND_H_ +#define TINYMT_RAND_H_ + + + +/** + * tinymt32 internal state vector and parameters + */ +typedef struct { + uint32_t status[4]; + uint32_t mat1; + uint32_t mat2; + uint32_t tmat; +} tinymt32_t; + + + +extern tinymt32_t tinymt32_s; + + + +void tinymt32_init(tinymt32_t *s, uint32_t seed); +uint32_t tinymt32_get_uint32(tinymt32_t* s); + +#define prng_get8() (tinymt32_get_uint32(&tinymt32_s) & 0xff) +#define prng_get16() (tinymt32_get_uint32(&tinymt32_s) & 0xffff) +#define prng_get32() tinymt32_get_uint32(&tinymt32_s) + + + +uint16_t prng_scale16(uint16_t min, uint16_t max); + + + +#endif /* TINYMT_RAND_H */ diff --git a/firmware/app/ui/temp_ui.c b/firmware/app/ui/temp_ui.c index c2ab37e..9c0a16c 100644 --- a/firmware/app/ui/temp_ui.c +++ b/firmware/app/ui/temp_ui.c @@ -6,14 +6,19 @@ #include "btn.h" #include "led/matrix.h" +#include "led/ledprog.h" static uint32_t brt = 28; +uint8_t ribbon_prog = 0; +uint8_t a_prog = 0; +uint8_t ii_prog = 0; -void brt_set(uint8_t idx) + +static void brt_set(uint8_t idx) { switch (idx) { case 1: { @@ -31,8 +36,48 @@ void brt_set(uint8_t idx) aw20x_set_dim_global(&awled, brt); } +static void prog_set(uint8_t idx) +{ + switch (idx) { + case 3: { // trio bottom left + ribbon_prog = 0; + lp_ribbon_init(); + break; + } + case 4: { // trio upper + ribbon_prog = 1; + lp_ribbon_init(); + break; + } + case 5: { // da erf + ribbon_prog = 3; + lp_ribbon_init(); + break; + } + case 6: { // big crater + lp_ribbon_init(); + break; + } + case 7: { // next to big crater + lp_ribbon_init(); + break; + } + case 8: { // bottom crater + lp_ribbon_init(); + break; + } + case 9: { // trio right + ribbon_prog = 2; + lp_ribbon_init(); + break; + } + } +} + void tempui_init() { + int i; + btn[2].cb_push = brt_set; btn[2].cb_hold = brt_set; btn[2].repeat = 200; @@ -40,4 +85,30 @@ void tempui_init() btn[1].cb_push = brt_set; btn[1].cb_hold = brt_set; btn[1].repeat = 200; + + for (i = 3; i < 10; i++) { + btn[i].cb_push = prog_set; + } +} + +void tempui_lp_runprog() +{ + switch (ribbon_prog) { + case 0: { + lp_ribbon_upward(400, 3, 4); + break; + } + case 1: { + lp_ribbon_upward(800, 1, 2); + break; + } + case 2: { + lp_ribbon_upward(1500, 4, 7); + break; + } + case 3: { + lp_ribbon_twinkle(3, 0x7f, 0x1f, 0xfd); + break; + } + } } \ No newline at end of file diff --git a/firmware/app/ui/temp_ui.h b/firmware/app/ui/temp_ui.h index acd91a9..d61a1ca 100644 --- a/firmware/app/ui/temp_ui.h +++ b/firmware/app/ui/temp_ui.h @@ -1 +1,3 @@ -void tempui_init(); \ No newline at end of file +void tempui_init(); + +void tempui_lp_runprog(); \ No newline at end of file