#include #include #include #include #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; } }