diff --git a/.gitignore b/.gitignore index 0e0f15e..006e562 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.jdebug *.jdebug.user +.vscode/* # ClangD Ignopre files .cache/* diff --git a/animation.c b/animation.c index 9c11a41..f89efa5 100644 --- a/animation.c +++ b/animation.c @@ -1,6 +1,23 @@ #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; +} void animation_reset(struct animation *ani) { @@ -11,6 +28,7 @@ void animation_reset(struct animation *ani) 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) @@ -44,6 +62,107 @@ static enum animation_state next_col_state(enum animation_state current) 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 rgb_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 rgb_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] = 0u; + + /* 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; @@ -72,7 +191,8 @@ void animation_process(struct animation *ani, uint8_t *dmx_universe) case ANI_RED: case ANI_GREEN: case ANI_BLUE: - for (i = 0; i < 32; i++) { + 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; @@ -87,17 +207,21 @@ void animation_process(struct animation *ani, uint8_t *dmx_universe) ani->next_interval = 20; break; case ANI_RAINBOW: + dmx_universe[RAINBOW_LED_COUNT * 4] = 0u; /* TODO: Implement Rainbow mode */ - ani->next_interval = 10; - clear_dmx_universe(dmx_universe, 129u); - ani->state = ANI_FLASH_WHITE; + 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[128] = ani->step & 1 ? 0xFF : 0; - ani->next_interval = ani->step & 1 ? 60 : 260; + 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 >= 11u) { + if (ani->step >= (2 * WHITE_FLASH_COUNT + 1)) { ani->state = ANI_RED; ani->step = 0u; } diff --git a/include/animation.h b/include/animation.h index d694b9a..85aab2b 100644 --- a/include/animation.h +++ b/include/animation.h @@ -11,8 +11,25 @@ enum animation_state { ANI_FLASH_WHITE, }; +struct rgb_color { + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +enum rainbow_state { + RAINBOW_RY, + RAINBOW_YG, + RAINBOW_GC, + RAINBOW_CB, + RAINBOW_BM, + RAINBOW_MR, +}; + struct animation { enum animation_state state; + enum rainbow_state rb_start_state; + struct rgb_color rb_start_color; uint32_t step; uint32_t last_tick; uint32_t next_interval; diff --git a/include/systick.h b/include/systick.h index e839225..2e3d009 100644 --- a/include/systick.h +++ b/include/systick.h @@ -2,8 +2,10 @@ #define _SYSTICK_H_ #include +#include void systick_wait_ms(uint32_t ms); uint32_t systick_get_global_tick(void); +bool systick_get_blink_state(void); #endif /* _SYSTICK_H_ */ diff --git a/main.c b/main.c index 1fbefb5..b4fb76d 100644 --- a/main.c +++ b/main.c @@ -69,15 +69,13 @@ static void setup_pins(void) static void process_mode(enum color_mode mode, const struct poti_values *poti_vals) { uint32_t odr; - static uint32_t last_tick = 0ul; - uint32_t tick; + static bool last_blink_state = false; + bool blink_state; int i; odr = GPIOA->ODR; odr &= ~((1<<5) | (1<<6) | (1<<7)); - tick = systick_get_global_tick(); - switch (mode) { case MODE_RED: case MODE_GREEN: @@ -115,14 +113,17 @@ static void process_mode(enum color_mode mode, const struct poti_values *poti_va animation_process(&led_animation, dmx_universe); break; case MODE_DMX_SHUTDOWN: - port_expander_set_leds(0xFF); + blink_state = systick_get_blink_state(); + if (blink_state != last_blink_state) { + port_expander_set_leds(blink_state ? (1<<0) : (1<<4)); + } + last_blink_state = blink_state; break; default: break; } - last_tick = tick; GPIOA->ODR = odr; } @@ -154,6 +155,7 @@ static bool mode_is_continuous(enum color_mode mode) switch (mode) { case MODE_ANIMATION: + case MODE_DMX_SHUTDOWN: ret = true; break; default: diff --git a/systick.c b/systick.c index b779051..8c23321 100644 --- a/systick.c +++ b/systick.c @@ -1,8 +1,10 @@ #include #include -volatile uint32_t global_systick = 0; -volatile uint32_t systick_wait_counter; +static volatile uint32_t global_systick = 0; +static volatile uint32_t systick_wait_counter; +static volatile uint32_t blink_cntr = 0; +static volatile bool blink_state = false; void systick_wait_ms(uint32_t ms) { @@ -12,6 +14,11 @@ void systick_wait_ms(uint32_t ms) __WFI(); } +bool systick_get_blink_state(void) +{ + return blink_state; +} + uint32_t systick_get_global_tick(void) { return global_systick; @@ -21,4 +28,8 @@ void SysTick_Handler(void) { global_systick++; systick_wait_counter--; + if (++blink_cntr > 250ul) { + blink_cntr = 0ul; + blink_state = !blink_state; + } }