diff options
-rw-r--r-- | led_test.c | 170 | ||||
-rw-r--r-- | led_test.h | 41 | ||||
-rw-r--r-- | visualizer.c | 36 | ||||
-rw-r--r-- | visualizer.h | 12 | ||||
-rw-r--r-- | visualizer.mk | 18 |
5 files changed, 272 insertions, 5 deletions
diff --git a/led_test.c b/led_test.c new file mode 100644 index 0000000000..d358ef81eb --- /dev/null +++ b/led_test.c @@ -0,0 +1,170 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "led_test.h" +#include "gfx.h" +#include "math.h" + +keyframe_animation_t led_test_animation = { + .num_frames = 14, + .loop = true, + .frame_lengths = { + MS2ST(1000), // fade in + MS2ST(1000), // no op (leds on) + MS2ST(1000), // fade out + MS2ST(1000), // crossfade + MS2ST(3000), // left to rigt (outside in) + MS2ST(1000), // crossfade + MS2ST(3000), // top_to_bottom + 0, // mirror leds + MS2ST(1000), // crossfade + MS2ST(3000), // left_to_right (mirrored, so inside out) + MS2ST(1000), // crossfade + MS2ST(3000), // top_to_bottom + 0, // normal leds + MS2ST(1000), // crossfade + + }, + .frame_functions = { + keyframe_fade_in_all_leds, + keyframe_no_operation, + keyframe_fade_out_all_leds, + keyframe_led_crossfade, + keyframe_led_left_to_right_gradient, + keyframe_led_crossfade, + keyframe_led_top_to_bottom_gradient, + keyframe_mirror_led_orientation, + keyframe_led_crossfade, + keyframe_led_left_to_right_gradient, + keyframe_led_crossfade, + keyframe_led_top_to_bottom_gradient, + keyframe_normal_led_orientation, + keyframe_led_crossfade, + }, +}; + +static uint8_t fade_led_color(keyframe_animation_t* animation, int from, int to) { + int frame_length = animation->frame_lengths[animation->current_frame]; + int current_pos = frame_length - animation->time_left_in_frame; + int delta = to - from; + int luma = (delta * current_pos) / frame_length; + luma += from; + return luma; +} + +static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) { + uint8_t luma = fade_led_color(animation, from, to); + color_t color = LUMA2COLOR(luma); + gdispGClear(LED_DISPLAY, color); +} + +// TODO: Should be customizable per keyboard +#define NUM_ROWS 7 +#define NUM_COLS 7 + +static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS]; +static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS]; + +static uint8_t compute_gradient_color(float t, float index, float num) { + const float target = t * (num - 1.0f); + const float half_num = num / 2.0f; + float d = fabs(index - target); + if (d > half_num) { + d = num - d; + } + d = 1.0f - (d / half_num); + return (uint8_t)(255.0f * d); +} + +bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + keyframe_fade_all_leds_from_to(animation, 0, 255); + return true; +} + +bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + keyframe_fade_all_leds_from_to(animation, 255, 0); + return true; +} + +bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + float frame_length = animation->frame_lengths[animation->current_frame]; + float current_pos = frame_length - animation->time_left_in_frame; + float t = current_pos / frame_length; + for (int i=0; i< NUM_COLS; i++) { + uint8_t color = compute_gradient_color(t, i, NUM_COLS); + gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color)); + } + return true; +} + +bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + float frame_length = animation->frame_lengths[animation->current_frame]; + float current_pos = frame_length - animation->time_left_in_frame; + float t = current_pos / frame_length; + for (int i=0; i< NUM_ROWS; i++) { + uint8_t color = compute_gradient_color(t, i, NUM_ROWS); + gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color)); + } + return true; +} + +static void copy_current_led_state(uint8_t* dest) { + for (int i=0;i<NUM_ROWS;i++) { + for (int j=0;j<NUM_COLS;j++) { + dest[i*NUM_COLS + j] = gdispGGetPixelColor(LED_DISPLAY, j, i); + } + } +} +bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + if (animation->first_update_of_frame) { + copy_current_led_state(&crossfade_start_frame[0][0]); + run_next_keyframe(animation, state); + copy_current_led_state(&crossfade_end_frame[0][0]); + } + for (int i=0;i<NUM_ROWS;i++) { + for (int j=0;j<NUM_COLS;j++) { + color_t color = LUMA2COLOR(fade_led_color(animation, crossfade_start_frame[i][j], crossfade_end_frame[i][j])); + gdispGDrawPixel(LED_DISPLAY, j, i, color); + } + } + return true; +} + +bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + (void)animation; + gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180); + return false; +} + +bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + (void)animation; + gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0); + return false; +} diff --git a/led_test.h b/led_test.h new file mode 100644 index 0000000000..5e2325753e --- /dev/null +++ b/led_test.h @@ -0,0 +1,41 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef TMK_VISUALIZER_LED_TEST_H_ +#define TMK_VISUALIZER_LED_TEST_H_ + +#include "visualizer.h" + +bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state); + +extern keyframe_animation_t led_test_animation; + + +#endif /* TMK_VISUALIZER_LED_TEST_H_ */ diff --git a/visualizer.c b/visualizer.c index 605be30594..579837edcd 100644 --- a/visualizer.c +++ b/visualizer.c @@ -83,6 +83,9 @@ static remote_object_t* remote_objects[] = { #endif +GDisplay* LCD_DISPLAY = 0; +GDisplay* LED_DISPLAY = 0; + void start_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = -1; @@ -106,6 +109,8 @@ void stop_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = animation->num_frames; animation->time_left_in_frame = 0; animation->need_update = true; + animation->first_update_of_frame = false; + animation->last_update_of_frame = false; for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) { if (animations[i] == animation) { animations[i] = NULL; @@ -120,12 +125,15 @@ void stop_all_keyframe_animations(void) { animations[i]->current_frame = animations[i]->num_frames; animations[i]->time_left_in_frame = 0; animations[i]->need_update = true; + animations[i]->first_update_of_frame = false; + animations[i]->last_update_of_frame = false; animations[i] = NULL; } } } static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) { + // TODO: Clean up this messy code dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta); if (animation->current_frame == animation->num_frames) { @@ -136,16 +144,20 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize animation->current_frame = 0; animation->time_left_in_frame = animation->frame_lengths[0]; animation->need_update = true; + animation->first_update_of_frame = true; } else { animation->time_left_in_frame -= delta; while (animation->time_left_in_frame <= 0) { int left = animation->time_left_in_frame; if (animation->need_update) { animation->time_left_in_frame = 0; + animation->last_update_of_frame = true; (*animation->frame_functions[animation->current_frame])(animation, state); + animation->last_update_of_frame = false; } animation->current_frame++; animation->need_update = true; + animation->first_update_of_frame = true; if (animation->current_frame == animation->num_frames) { if (animation->loop) { animation->current_frame = 0; @@ -162,6 +174,7 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize } if (animation->need_update) { animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state); + animation->first_update_of_frame = false; } int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame; @@ -172,6 +185,21 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize return true; } +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) { + int next_frame = animation->current_frame + 1; + if (next_frame == animation->num_frames) { + next_frame = 0; + } + keyframe_animation_t temp_animation = *animation; + temp_animation.current_frame = next_frame; + temp_animation.time_left_in_frame = animation->frame_lengths[next_frame]; + temp_animation.first_update_of_frame = true; + temp_animation.last_update_of_frame = false; + temp_animation.need_update = false; + visualizer_state_t temp_state = *state; + (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state); +} + bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; @@ -373,6 +401,9 @@ static THD_FUNCTION(visualizerThread, arg) { update_keyframe_animation(animations[i], &state, delta, &sleep_time); } } +#ifdef LED_ENABLE + gdispGFlush(LED_DISPLAY); +#endif // The animation can enable the visualizer // And we might need to update the state when that happens // so don't sleep @@ -411,6 +442,11 @@ void visualizer_init(void) { #ifdef USE_SERIAL_LINK add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); #endif + // TODO: Make sure these works when either of these are disabled + LCD_DISPLAY = gdispGetDisplay(0); + LED_DISPLAY = gdispGetDisplay(1); + + // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); diff --git a/visualizer.h b/visualizer.h index 22798cda6b..8a2772c6da 100644 --- a/visualizer.h +++ b/visualizer.h @@ -45,8 +45,8 @@ void visualizer_suspend(void); // This should be called when the keyboard wakes up from suspend state void visualizer_resume(void); -// If you need support for more than 8 keyframes per animation, you can change this -#define MAX_VISUALIZER_KEY_FRAMES 8 +// If you need support for more than 16 keyframes per animation, you can change this +#define MAX_VISUALIZER_KEY_FRAMES 16 struct keyframe_animation_t; @@ -95,12 +95,20 @@ typedef struct keyframe_animation_t { // keyframe update functions int current_frame; int time_left_in_frame; + bool first_update_of_frame; + bool last_update_of_frame; bool need_update; } keyframe_animation_t; +extern GDisplay* LCD_DISPLAY; +extern GDisplay* LED_DISPLAY; + void start_keyframe_animation(keyframe_animation_t* animation); void stop_keyframe_animation(keyframe_animation_t* animation); +// This runs the next keyframe, but does not update the animation state +// Useful for crossfades for example +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state); // Some predefined keyframe functions that can be used by the user code // Does nothing, useful for adding delays diff --git a/visualizer.mk b/visualizer.mk index 13c5d31587..5cc199cf48 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,13 +21,14 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx +SRC += $(VISUALIZER_DIR)/visualizer.c +UINCDIR += $(GFXINC) $(VISUALIZER_DIR) + ifdef LCD_ENABLE -include $(GFXLIB)/gfx.mk UDEFS += -DLCD_ENABLE ULIBS += -lm +USE_UGFX = yes endif -SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c -UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c @@ -35,6 +36,17 @@ SRC += lcd_backlight_hal.c UDEFS += -DLCD_BACKLIGHT_ENABLE endif +ifdef LED_ENABLE +SRC += $(VISUALIZER_DIR)/led_test.c +UDEFS += -DLED_ENABLE +USE_UGFX = yes +endif + +ifdef USE_UGFX +include $(GFXLIB)/gfx.mk +SRC += $(GFXSRC) +endif + ifndef VISUALIZER_USER VISUALIZER_USER = visualizer_user.c endif |