From 19790f2e1a4f1652522584db6030052758cc0699 Mon Sep 17 00:00:00 2001 From: Kuba Birecki Date: Tue, 21 Dec 2021 18:14:42 +0100 Subject: [PATCH] Implement animation activation/deactivation API and frame scheduling --- app/include/drivers/animation.h | 32 +++++++++++++++++++++- app/include/zmk/animation.h | 2 ++ app/src/animation/animation.c | 29 ++++++++++++++++++-- app/src/animation/animation_compose.c | 18 +++++++++++++ app/src/animation/animation_ripple.c | 21 +++++++++++++-- app/src/animation/animation_solid.c | 38 ++++++++++++++------------- 6 files changed, 117 insertions(+), 23 deletions(-) diff --git a/app/include/drivers/animation.h b/app/include/drivers/animation.h index ad4ad956..df05d194 100644 --- a/app/include/drivers/animation.h +++ b/app/include/drivers/animation.h @@ -31,9 +31,25 @@ struct animation_pixel { extern "C" { #endif +/** + * @typedef animation_start + * @brief Callback API for starting an animation. + * + * @see animation_start() for argument descriptions. + */ +typedef void (*animation_api_start)(const struct device *dev); + +/** + * @typedef animation_stop + * @brief Callback API for stopping an animation. + * + * @see animation_stop() for argument descriptions. + */ +typedef void (*animation_api_stop)(const struct device *dev); + /** * @typedef animation_render_frame - * @brief Callback API for generating the next animation frame + * @brief Callback API for generating the next animation frame. * * @see animation_render_frame() for argument descriptions. */ @@ -41,9 +57,23 @@ typedef void (*animation_api_render_frame)(const struct device *dev, struct anim size_t num_pixels); struct animation_api { + animation_api_start on_start; + animation_api_stop on_stop; animation_api_render_frame render_frame; }; +static inline void animation_start(const struct device *dev) { + const struct animation_api *api = (const struct animation_api *)dev->api; + + return api->on_start(dev); +} + +static inline void animation_stop(const struct device *dev) { + const struct animation_api *api = (const struct animation_api *)dev->api; + + return api->on_stop(dev); +} + static inline void animation_render_frame(const struct device *dev, struct animation_pixel *pixels, size_t num_pixels) { const struct animation_api *api = (const struct animation_api *)dev->api; diff --git a/app/include/zmk/animation.h b/app/include/zmk/animation.h index e48b38e3..c17dccf4 100644 --- a/app/include/zmk/animation.h +++ b/app/include/zmk/animation.h @@ -78,6 +78,8 @@ bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b); void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to, struct zmk_color_hsl *result, float step); +void zmk_animation_request_frames(uint32_t frames); + struct zmk_color_rgb __zmk_apply_blending_mode(struct zmk_color_rgb base_value, struct zmk_color_rgb blend_value, uint8_t mode); diff --git a/app/src/animation/animation.c b/app/src/animation/animation.c index ac8a86ad..bd29bb03 100644 --- a/app/src/animation/animation.c +++ b/app/src/animation/animation.c @@ -77,6 +77,11 @@ static const size_t pixels_size = DT_INST_PROP_LEN(0, pixels); */ static struct led_rgb px_buffer[DT_INST_PROP_LEN(0, pixels)]; +/** + * Counter for animation frames that have been requested but have yet to be executed. + */ +static uint32_t animation_timer_countdown = 0; + /** * Conditional implementation of zmk_animation_get_pixel_by_key_position * if key-pixels is set. @@ -103,6 +108,7 @@ uint8_t zmk_animation_get_pixel_distance(size_t pixel_idx, size_t other_pixel_id #endif static void zmk_animation_tick(struct k_work *work) { + LOG_DBG("Animation tick"); animation_render_frame(animation_root, &pixels[0], pixels_size); for (size_t i = 0; i < pixels_size; ++i) { @@ -120,10 +126,29 @@ static void zmk_animation_tick(struct k_work *work) { K_WORK_DEFINE(animation_work, zmk_animation_tick); -static void zmk_animation_tick_handler(struct k_timer *timer) { k_work_submit(&animation_work); } +static void zmk_animation_tick_handler(struct k_timer *timer) { + if (--animation_timer_countdown == 0) { + k_timer_stop(timer); + } + + k_work_submit(&animation_work); +} K_TIMER_DEFINE(animation_tick, zmk_animation_tick_handler, NULL); +void zmk_animation_request_frames(uint32_t frames) { + if (frames <= animation_timer_countdown) { + return; + } + + if (animation_timer_countdown == 0) { + k_timer_start(&animation_tick, K_MSEC(1000 / CONFIG_ZMK_ANIMATION_FPS), + K_MSEC(1000 / CONFIG_ZMK_ANIMATION_FPS)); + } + + animation_timer_countdown = frames; +} + static int zmk_animation_init(const struct device *dev) { #if defined(CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE) && (CONFIG_ZMK_ANIMATION_PIXEL_DISTANCE == 1) // Prefill the pixel distance lookup table @@ -140,7 +165,7 @@ static int zmk_animation_init(const struct device *dev) { LOG_INF("ZMK Animation Ready"); - k_timer_start(&animation_tick, K_NO_WAIT, K_MSEC(1000 / CONFIG_ZMK_ANIMATION_FPS)); + animation_start(animation_root); return 0; } diff --git a/app/src/animation/animation_compose.c b/app/src/animation/animation_compose.c index 5acce91e..b946791f 100644 --- a/app/src/animation/animation_compose.c +++ b/app/src/animation/animation_compose.c @@ -36,9 +36,27 @@ static void animation_compose_render_frame(const struct device *dev, struct anim } } +static void animation_compose_start(const struct device *dev) { + const struct animation_compose_config *config = dev->config; + + for (size_t i = 0; i < config->animations_size; ++i) { + animation_start(config->animations[i]); + } +} + +static void animation_compose_stop(const struct device *dev) { + const struct animation_compose_config *config = dev->config; + + for (size_t i = 0; i < config->animations_size; ++i) { + animation_stop(config->animations[i]); + } +} + static int animation_compose_init(const struct device *dev) { return 0; } static const struct animation_api animation_compose_api = { + .on_start = animation_compose_start, + .on_stop = animation_compose_stop, .render_frame = animation_compose_render_frame, }; diff --git a/app/src/animation/animation_ripple.c b/app/src/animation/animation_ripple.c index 03e24f59..e57eae5c 100644 --- a/app/src/animation/animation_ripple.c +++ b/app/src/animation/animation_ripple.c @@ -76,6 +76,8 @@ static int animation_ripple_on_key_press(const struct device *dev, const zmk_eve data->events_end = (data->events_end + 1) % config->event_buffer_size; data->num_events += 1; + zmk_animation_request_frames(config->event_frames); + return 0; } @@ -96,8 +98,8 @@ static void animation_ripple_render_frame(const struct device *dev, struct anima uint8_t pixel_distance = zmk_animation_get_pixel_distance(event->pixel_id, j); if (config->ripple_width > abs(pixel_distance - event->distance)) { - float intensity = - 1.0f - (float)abs(pixel_distance - event->distance) / (float)config->ripple_width; + float intensity = 1.0f - (float)abs(pixel_distance - event->distance) / + (float)config->ripple_width; struct zmk_color_rgb color = { .r = intensity * data->color_rgb.r, @@ -125,6 +127,19 @@ static void animation_ripple_render_frame(const struct device *dev, struct anima } } +static void animation_ripple_start(const struct device *dev) { + // Nothing to do. +} + +static void animation_ripple_stop(const struct device *dev) { + struct animation_ripple_data *data = dev->data; + + // Cancel the processing of any ongoing events. + data->num_events = 0; + data->events_start = 0; + data->events_end = 0; +} + static int animation_ripple_init(const struct device *dev) { const struct animation_ripple_config *config = dev->config; struct animation_ripple_data *data = dev->data; @@ -135,6 +150,8 @@ static int animation_ripple_init(const struct device *dev) { } static const struct animation_api animation_ripple_api = { + .on_start = animation_ripple_start, + .on_stop = animation_ripple_stop, .render_frame = animation_ripple_render_frame, }; diff --git a/app/src/animation/animation_solid.c b/app/src/animation/animation_solid.c index d9d22aa7..b13a2835 100644 --- a/app/src/animation/animation_solid.c +++ b/app/src/animation/animation_solid.c @@ -25,23 +25,16 @@ struct animation_solid_config { }; struct animation_solid_data { - bool has_changed; - uint16_t counter; struct zmk_color_hsl current_hsl; struct zmk_color_rgb current_rgb; }; -static void animation_solid_tick(const struct device *dev) { +static void animation_solid_update_color(const struct device *dev) { const struct animation_solid_config *config = dev->config; struct animation_solid_data *data = dev->data; - // Animation only contains a single color, nothing to do - if (config->num_colors == 1) { - return; - } - const size_t from = data->counter / config->transition_duration; const size_t to = (from + 1) % config->num_colors; @@ -51,8 +44,6 @@ static void animation_solid_tick(const struct device *dev) { (data->counter % config->transition_duration) / (float)config->transition_duration); - data->has_changed = !zmk_cmp_hsl(&data->current_hsl, &next_hsl); - data->current_hsl = next_hsl; zmk_hsl_to_rgb(&data->current_hsl, &data->current_rgb); @@ -64,11 +55,26 @@ static void animation_solid_render_frame(const struct device *dev, struct animat const struct animation_solid_config *config = dev->config; struct animation_solid_data *data = dev->data; - animation_solid_tick(dev); - for (size_t i = 0; i < config->pixel_map_size; ++i) { pixels[config->pixel_map[i]].value = data->current_rgb; } + + if (config->num_colors == 1) { + return; + } + + // Request frames on counter reset + if (data->counter == 0) { + zmk_animation_request_frames(config->duration); + } + + animation_solid_update_color(dev); +} + +static void animation_solid_start(const struct device *dev) { zmk_animation_request_frames(1); } + +static void animation_solid_stop(const struct device *dev) { + // Nothing to do. } static int animation_solid_init(const struct device *dev) { @@ -80,16 +86,12 @@ static int animation_solid_init(const struct device *dev) { zmk_hsl_to_rgb(&data->current_hsl, &data->current_rgb); - // if (config->num_colors == 1) { - // return 0; - // } - - // start timer here, so the only option is inline - return 0; } static const struct animation_api animation_solid_api = { + .on_start = animation_solid_start, + .on_stop = animation_solid_stop, .render_frame = animation_solid_render_frame, };