microscope-ring-light-remot.../animation.c

231 lines
4.9 KiB
C

#include <animation.h>
#include <stdint.h>
#include <systick.h>
#include <stdbool.h>
#define WHITE_FLASH_COUNT 5u
#define WHITE_FLASH_ON_TIME 60u
#define WHITE_FLASH_OFF_TIME 260u
#define RAINBOW_MAX_COL 240u
#define RAINBOW_INCR 40u
#define RAINBOW_LED_COUNT 32u
static void reset_rainbow_to_start(struct animation *ani)
{
ani->rb_start_state = RAINBOW_RY;
ani->rb_start_color.red = RAINBOW_MAX_COL;
ani->rb_start_color.blue = 0u;
ani->rb_start_color.green = 0u;
ani->rb_start_color.white = 0u;
}
void animation_reset(struct animation *ani)
{
if (!ani)
return;
ani->state = ANI_RED;
ani->step = 0u;
ani->last_tick = systick_get_global_tick();
ani->next_interval = 0ul;
reset_rainbow_to_start(ani);
}
static void clear_dmx_universe(uint8_t *universe, uint32_t len)
{
uint32_t idx;
for (idx = 0; idx < len; idx++) {
universe[idx] = 0u;
}
}
static enum animation_state next_col_state(enum animation_state current)
{
enum animation_state next;
switch (current) {
case ANI_RED:
next = ANI_GREEN;
break;
case ANI_GREEN:
next = ANI_BLUE;
break;
case ANI_BLUE:
next = ANI_RAINBOW;
break;
default:
next = ANI_RED;
break;
}
return next;
}
/**
* @brief Advance to the next color in the rainbow
*
* @param[in,out] current_state Current rainbow state
* @param[in, out] col Current color
*/
static void rainbow_color_advance(enum rainbow_state *current_state, struct rgbw_color *col)
{
enum rainbow_state state;
state = *current_state;
switch (state) {
case RAINBOW_RY:
/* Increment green color */
col->green += RAINBOW_INCR;
if (col->green >= RAINBOW_MAX_COL) {
state = RAINBOW_YG;
}
break;
case RAINBOW_YG:
/* Decrement red color */
col->red -= RAINBOW_INCR;
if (col->red == 0) {
state = RAINBOW_GC;
}
break;
case RAINBOW_GC:
/* Increment blue color */
col->blue += RAINBOW_INCR;
if (col->blue >= RAINBOW_MAX_COL) {
state = RAINBOW_CB;
}
break;
case RAINBOW_CB:
/* Decrement green color */
col->green -= RAINBOW_INCR;
if (col->green == 0) {
state = RAINBOW_BM;
}
break;
case RAINBOW_BM:
/* Increment red color */
col->red += RAINBOW_INCR;
if (col->red >= RAINBOW_MAX_COL) {
state = RAINBOW_MR;
}
break;
case RAINBOW_MR:
/* Decrement blue color */
col->blue -= RAINBOW_INCR;
if (col->blue == 0) {
state = RAINBOW_RY;
}
break;
}
*current_state = state;
}
/**
* @brief
*
* @param ani
* @param dmx_universe
* @return true Animation completed
* @return false Animation ongoing
*/
static bool animation_rainbow(struct animation *ani, uint8_t *dmx_universe)
{
bool ret = false;
enum rainbow_state led_state;
struct rgbw_color color;
uint32_t idx;
/* Get the start state of first led */
led_state= ani->rb_start_state;
color = ani->rb_start_color;
ani->step++;
for (idx = 0; idx < RAINBOW_LED_COUNT; idx++) {
dmx_universe[idx * 4 + 0] = color.red;
dmx_universe[idx * 4 + 1] = color.green;
dmx_universe[idx * 4 + 2] = color.blue;
dmx_universe[idx * 4 + 3] = color.white;
/* Sdvance rainbow color */
rainbow_color_advance(&led_state, &color);
}
/* Advance the start */
rainbow_color_advance(&ani->rb_start_state, &ani->rb_start_color);
if (ani->step > 500) {
reset_rainbow_to_start(ani);
ret = true;
}
return ret;
}
void animation_process(struct animation *ani, uint8_t *dmx_universe)
{
uint32_t tick;
uint32_t new_tick;
uint32_t i;
if (!ani || !dmx_universe)
return;
/* Check if time for next step is reached */
tick = systick_get_global_tick();
/* Calculate next tick. Overflow expected */
new_tick = ani->last_tick + ani->next_interval;
if (new_tick < ani->last_tick) {
/* Tick overflowed. */
if (!(tick >= new_tick && tick < ani->last_tick))
return;
} else {
if (tick < new_tick)
return;
}
ani->last_tick = tick;
switch (ani->state) {
case ANI_RED:
case ANI_GREEN:
case ANI_BLUE:
dmx_universe[RAINBOW_LED_COUNT * 4] = 0u;
for (i = 0; i < RAINBOW_LED_COUNT; i++) {
dmx_universe[i * 4 + 0] = ani->state == ANI_RED ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 1] = ani->state == ANI_GREEN ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 2] = ani->state == ANI_BLUE ? (uint8_t)ani->step : 0u;
dmx_universe[i * 4 + 3] = 0u;
}
if (ani->step + 10 > 0xFFul) {
ani->step = 0;
ani->state = next_col_state(ani->state);
} else {
ani->step += 10;
}
ani->next_interval = 20;
break;
case ANI_RAINBOW:
dmx_universe[RAINBOW_LED_COUNT * 4] = 0u;
/* TODO: Implement Rainbow mode */
ani->next_interval = 20;
if (animation_rainbow(ani, dmx_universe)) {
clear_dmx_universe(dmx_universe, RAINBOW_LED_COUNT * 4 + 1);
ani->state = ANI_FLASH_WHITE;
ani->step = 0ul;
}
break;
case ANI_FLASH_WHITE:
dmx_universe[RAINBOW_LED_COUNT * 4] = ani->step & 1 ? 0xFF : 0;
ani->next_interval = ani->step & 1 ? WHITE_FLASH_ON_TIME : WHITE_FLASH_OFF_TIME;
ani->step += 1u;
if (ani->step >= (2 * WHITE_FLASH_COUNT + 1)) {
ani->state = ANI_RED;
ani->step = 0u;
}
break;
}
}