From 3568065141279baa3dfff0e6a65fbf6ab8d14676 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Fri, 12 Feb 2016 09:33:38 +0200 Subject: [PATCH 01/17] Add readme --- readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000000..82bf8bfdfa --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +A visualization library for the TMK keyboard firmware From 4452f4965dae25bb3707e64858221784318c4f56 Mon Sep 17 00:00:00 2001 From: fredizzimo Date: Fri, 12 Feb 2016 09:59:39 +0200 Subject: [PATCH 02/17] Add MIT license --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..d7cc3198cb --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +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. From bb75446b0beb4db28b775a2e2db61d91f0f0324a Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 16:44:14 +0200 Subject: [PATCH 03/17] Add uGFX submodule v 2.4 --- .gitmodules | 3 +++ ugfx | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 ugfx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..2ab25f6883 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ugfx"] + path = ugfx + url = https://bitbucket.org/Tectu/ugfx.git diff --git a/ugfx b/ugfx new file mode 160000 index 0000000000..2b66ac524b --- /dev/null +++ b/ugfx @@ -0,0 +1 @@ +Subproject commit 2b66ac524bd56853ba97b917683971f3ebc0104c From fb681b5a60ae5b13a0413c815b4564d60fa200c5 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 17:47:39 +0200 Subject: [PATCH 04/17] Add makefile that includes ugfx --- visualizer.mk | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 visualizer.mk diff --git a/visualizer.mk b/visualizer.mk new file mode 100644 index 0000000000..c5ae0dfa95 --- /dev/null +++ b/visualizer.mk @@ -0,0 +1,26 @@ +# 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. + +GFXLIB = $(VISUALIZER_DIR)/ugfx +include $(GFXLIB)/gfx.mk +SRC += $(GFXSRC) +INC += $(GFXINC) From 01b955aa64766d51191a716d3a9d74d35f221b28 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:21:16 +0200 Subject: [PATCH 05/17] Add LCD backlight support Also possibility to disable the LCD support --- lcd_backlight.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ lcd_backlight.h | 42 ++++++++++++++++++++++++ visualizer.mk | 9 +++++- 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 lcd_backlight.c create mode 100644 lcd_backlight.h diff --git a/lcd_backlight.c b/lcd_backlight.c new file mode 100644 index 0000000000..70187d1e00 --- /dev/null +++ b/lcd_backlight.c @@ -0,0 +1,85 @@ +/* +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 "lcd_backlight.h" +#include + +static uint8_t current_hue = 0x00; +static uint8_t current_saturation = 0x00; +static uint8_t current_intensity = 0xFF; +static uint8_t current_brightness = 0x7F; + +void lcd_backlight_init(void) { + lcd_backlight_hal_init(); + lcd_backlight_color(current_hue, current_saturation, current_intensity); +} + +// This code is based on Brian Neltner's blogpost and example code +// "Why every LED light should be using HSI colorspace". +// http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi +static void hsi_to_rgb(float h, float s, float i, uint16_t* r_out, uint16_t* g_out, uint16_t* b_out) { + unsigned int r, g, b; + h = fmodf(h, 360.0f); // cycle h around to 0-360 degrees + h = 3.14159f * h / 180.0f; // Convert to radians. + s = s > 0.0f ? (s < 1.0f ? s : 1.0f) : 0.0f; // clamp s and i to interval [0,1] + i = i > 0.0f ? (i < 1.0f ? i : 1.0f) : 0.0f; + + // Math! Thanks in part to Kyle Miller. + if(h < 2.09439f) { + r = 65535.0f * i/3.0f *(1.0f + s * cos(h) / cosf(1.047196667f - h)); + g = 65535.0f * i/3.0f *(1.0f + s *(1.0f - cosf(h) / cos(1.047196667f - h))); + b = 65535.0f * i/3.0f *(1.0f - s); + } else if(h < 4.188787) { + h = h - 2.09439; + g = 65535.0f * i/3.0f *(1.0f + s * cosf(h) / cosf(1.047196667f - h)); + b = 65535.0f * i/3.0f *(1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); + r = 65535.0f * i/3.0f *(1.0f - s); + } else { + h = h - 4.188787; + b = 65535.0f*i/3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h)); + r = 65535.0f*i/3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); + g = 65535.0f*i/3.0f * (1.0f - s); + } + *r_out = r > 65535 ? 65535 : r; + *g_out = g > 65535 ? 65535 : g; + *b_out = b > 65535 ? 65535 : b; +} + +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity) { + uint16_t r, g, b; + float hue_f = 360.0f * (float)hue / 255.0f; + float saturation_f = (float)saturation / 255.0f; + float intensity_f = (float)intensity / 255.0f; + intensity_f *= (float)current_brightness / 255.0f; + hsi_to_rgb(hue_f, saturation_f, intensity_f, &r, &g, &b); + current_hue = hue; + current_saturation = saturation; + current_intensity = intensity; + lcd_backlight_hal_color(r, g, b); +} + +void lcd_backlight_brightness(uint8_t b) { + current_brightness = b; + lcd_backlight_color(current_hue, current_saturation, current_intensity); +} diff --git a/lcd_backlight.h b/lcd_backlight.h new file mode 100644 index 0000000000..dd3e37a064 --- /dev/null +++ b/lcd_backlight.h @@ -0,0 +1,42 @@ +/* +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 LCD_BACKLIGHT_H_ +#define LCD_BACKLIGHT_H_ +#include "stdint.h" + +// Helper macros for storing hue, staturation and intensity as unsigned integers +#define LCD_COLOR(hue, saturation, intensity) (hue << 16 | saturation << 8 | intensity) +#define LCD_HUE(color) ((color >> 16) & 0xFF) +#define LCD_SAT(color) ((color >> 8) & 0xFF) +#define LCD_INT(color) (color & 0xFF) + +void lcd_backlight_init(void); +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity); +void lcd_backlight_brightness(uint8_t b); + +void lcd_backlight_hal_init(void); +void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b); + +#endif /* LCD_BACKLIGHT_H_ */ diff --git a/visualizer.mk b/visualizer.mk index c5ae0dfa95..8ffc1e4aca 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,6 +21,13 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx +ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk +endif SRC += $(GFXSRC) -INC += $(GFXINC) +INC += $(GFXINC) $(VISUALIZER_DIR) + +ifdef LCD_BACKLIGHT_ENABLE +SRC += $(VISUALIZER_DIR)/lcd_backlight.c +SRC += lcd_backlight_hal.c +endif From 9e58d022ba4320ce0917defe4b81c1c7b5de77bb Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:38:23 +0200 Subject: [PATCH 06/17] Add visualizer A generic visualizer that supports animations. There's a few predefined keyframe types included, and more can be added by the user. --- visualizer.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++ visualizer.h | 120 +++++++++++++++++ visualizer.mk | 4 +- 3 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 visualizer.c create mode 100644 visualizer.h diff --git a/visualizer.c b/visualizer.c new file mode 100644 index 0000000000..2a92524e25 --- /dev/null +++ b/visualizer.c @@ -0,0 +1,349 @@ +/* +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 "visualizer.h" +#include "ch.h" +#include + +#ifdef LCD_ENABLE +#include "gfx.h" +#endif + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +//#define DEBUG_VISUALIZER + +#ifdef DEBUG_VISUALIZER +#include "debug.h" +#else +#include "nodebug.h" +#endif + + +static visualizer_keyboard_status_t current_status = { + .layer = 0xFFFFFFFF, + .default_layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, +}; + +static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { + return memcmp(status1, status2, sizeof(visualizer_keyboard_status_t)) == 0; +} + +static event_source_t layer_changed_event; +static bool visualizer_enabled = false; + +#define MAX_SIMULTANEOUS_ANIMATIONS 4 +static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; + +void start_keyframe_animation(keyframe_animation_t* animation) { + animation->current_frame = -1; + animation->time_left_in_frame = 0; + animation->need_update = true; + int free_index = -1; + for (int i=0;icurrent_frame = animation->num_frames; + animation->time_left_in_frame = 0; + animation->need_update = true; + for (int i=0;icurrent_frame, + animation->time_left_in_frame, delta); + if (animation->current_frame == animation->num_frames) { + animation->need_update = false; + return false; + } + if (animation->current_frame == -1) { + animation->current_frame = 0; + animation->time_left_in_frame = animation->frame_lengths[0]; + animation->need_update = 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->frame_functions[animation->current_frame])(animation, state); + } + animation->current_frame++; + animation->need_update = true; + if (animation->current_frame == animation->num_frames) { + if (animation->loop) { + animation->current_frame = 0; + } + else { + stop_keyframe_animation(animation); + return false; + } + } + delta = -left; + animation->time_left_in_frame = animation->frame_lengths[animation->current_frame]; + animation->time_left_in_frame -= delta; + } + } + if (animation->need_update) { + animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state); + } + + int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame; + if ((unsigned)wanted_sleep < *sleep_time) { + *sleep_time = wanted_sleep; + } + + return true; +} + +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +#ifdef LCD_BACKLIGHT_ENABLE +bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { + int frame_length = animation->frame_lengths[1]; + int current_pos = frame_length - animation->time_left_in_frame; + uint8_t t_h = LCD_HUE(state->target_lcd_color); + uint8_t t_s = LCD_SAT(state->target_lcd_color); + uint8_t t_i = LCD_INT(state->target_lcd_color); + uint8_t p_h = LCD_HUE(state->prev_lcd_color); + uint8_t p_s = LCD_SAT(state->prev_lcd_color); + uint8_t p_i = LCD_INT(state->prev_lcd_color); + + uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around + int d_h2 = t_h - p_h; + // Chose the shortest way around + int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1; + int d_s = t_s - p_s; + int d_i = t_i - p_i; + + int hue = (d_h * current_pos) / frame_length; + int sat = (d_s * current_pos) / frame_length; + int intensity = (d_i * current_pos) / frame_length; + //dprintf("%X -> %X = %X\n", p_h, t_h, hue); + hue += p_h; + sat += p_s; + intensity += p_i; + state->current_lcd_color = LCD_COLOR(hue, sat, intensity); + lcd_backlight_color( + LCD_HUE(state->current_lcd_color), + LCD_SAT(state->current_lcd_color), + LCD_INT(state->current_lcd_color)); + + return true; +} + +bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + state->prev_lcd_color = state->target_lcd_color; + state->current_lcd_color = state->target_lcd_color; + lcd_backlight_color( + LCD_HUE(state->current_lcd_color), + LCD_SAT(state->current_lcd_color), + LCD_INT(state->current_lcd_color)); + return false; +} +#endif // LCD_BACKLIGHT_ENABLE + +#ifdef LCD_ENABLE +bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + gdispClear(White); + gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black); + gdispFlush(); + return false; +} + +static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) { + for (int i=0; i<16;i++) + { + uint32_t mask = (1u << i); + if (default_layer & mask) { + if (layer & mask) { + *buffer = 'B'; + } else { + *buffer = 'D'; + } + } else if (layer & mask) { + *buffer = '1'; + } else { + *buffer = '0'; + } + ++buffer; + + if (i==3 || i==7 || i==11) { + *buffer = ' '; + ++buffer; + } + } + *buffer = 0; +} + +bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + const char* layer_help = "1=On D=Default B=Both"; + char layer_buffer[16 + 4]; // 3 spaces and one null terminator + gdispClear(White); + gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black); + format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer); + gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black); + format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer); + gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black); + gdispFlush(); + return false; +} +#endif // LCD_ENABLE + +bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + dprint("User visualizer inited\n"); + visualizer_enabled = true; + return false; +} + +// TODO: Optimize the stack size, this is probably way too big +static THD_WORKING_AREA(visualizerThreadStack, 1024); +static THD_FUNCTION(visualizerThread, arg) { + (void)arg; + + event_listener_t event_listener; + chEvtRegister(&layer_changed_event, &event_listener, 0); + + visualizer_state_t state = { + .status = { + .default_layer = 0xFFFFFFFF, + .layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, + }, + + .current_lcd_color = 0, +#ifdef LCD_ENABLE + .font_fixed5x8 = gdispOpenFont("fixed_5x8"), + .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12") +#endif + }; + initialize_user_visualizer(&state); + state.prev_lcd_color = state.current_lcd_color; + +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_color( + LCD_HUE(state.current_lcd_color), + LCD_SAT(state.current_lcd_color), + LCD_INT(state.current_lcd_color)); +#endif + + systime_t sleep_time = TIME_INFINITE; + systime_t current_time = chVTGetSystemTimeX(); + + while(true) { + systime_t new_time = chVTGetSystemTimeX(); + systime_t delta = new_time - current_time; + current_time = new_time; + bool enabled = visualizer_enabled; + if (!same_status(&state.status, ¤t_status)) { + if (visualizer_enabled) { + state.status = current_status; + update_user_visualizer_state(&state); + state.prev_lcd_color = state.current_lcd_color; + } + } + sleep_time = TIME_INFINITE; + for (int i=0;i update_delta) { + sleep_time -= update_delta; + } + else { + sleep_time = 0; + } + } + dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); + chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time); + } +#ifdef LCD_ENABLE + gdispCloseFont(state.font_fixed5x8); + gdispCloseFont(state.font_dejavusansbold12); +#endif +} + +void visualizer_init(void) { + // 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); + (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack), + LOWPRIO, visualizerThread, NULL); +} + +void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) { + // Note that there's a small race condition here, the thread could read + // a state where one of these are set but not the other. But this should + // not really matter as it will be fixed during the next loop step. + // Alternatively a mutex could be used instead of the volatile variables + bool changed = false; + visualizer_keyboard_status_t new_status = { + .layer = state, + .default_layer = default_state, + .leds = leds, + }; + if (!same_status(¤t_status, &new_status)) { + changed = true; + } + current_status = new_status; + if (changed) { + chEvtBroadcast(&layer_changed_event); + } +} diff --git a/visualizer.h b/visualizer.h new file mode 100644 index 0000000000..b7b0a3a7d0 --- /dev/null +++ b/visualizer.h @@ -0,0 +1,120 @@ +/* +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 VISUALIZER_H +#define VISUALIZER_H +#include +#include +#include + +#ifdef LCD_ENABLE +#include "gfx.h" +#endif + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +// This need to be called once at the start +void visualizer_init(void); +// This should be called before every matrix scan +void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds); + +// If you need support for more than 8 keyframes per animation, you can change this +#define MAX_VISUALIZER_KEY_FRAMES 8 + +struct keyframe_animation_t; + +typedef struct { + uint32_t layer; + uint32_t default_layer; + uint32_t leds; // See led.h for available statuses +} visualizer_keyboard_status_t; + +// The state struct is used by the various keyframe functions +// It's also used for setting the LCD color and layer text +// from the user customized code +typedef struct visualizer_state_t { + // The user code should primarily be modifying these + uint32_t target_lcd_color; + const char* layer_text; + + // The user visualizer(and animation functions) can read these + visualizer_keyboard_status_t status; + + // These are used by the animation functions + uint32_t current_lcd_color; + uint32_t prev_lcd_color; +#ifdef LCD_ENABLE + font_t font_fixed5x8; + font_t font_dejavusansbold12; +#endif +} visualizer_state_t; + +// Any custom keyframe function should have this signature +// return true to get continuous updates, otherwise you will only get one +// update per frame +typedef bool (*frame_func)(struct keyframe_animation_t*, visualizer_state_t*); + +// Represents a keyframe animation, so fields are internal to the system +// while others are meant to be initialized by the user code +typedef struct keyframe_animation_t { + // These should be initialized + int num_frames; + bool loop; + int frame_lengths[MAX_VISUALIZER_KEY_FRAMES]; + frame_func frame_functions[MAX_VISUALIZER_KEY_FRAMES]; + + // Used internally by the system, and can also be read by + // keyframe update functions + int current_frame; + int time_left_in_frame; + bool need_update; + +} keyframe_animation_t; + +void start_keyframe_animation(keyframe_animation_t* animation); +void stop_keyframe_animation(keyframe_animation_t* animation); + +// Some predefined keyframe functions that can be used by the user code +// Does nothing, useful for adding delays +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state); +// Animates the LCD backlight color between the current color and the target color (of the state) +bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state); +// Sets the backlight color to the target color +bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays the layer text centered vertically on the screen +bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays a bitmap (0/1) of all the currently active layers +bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); +// Call this once, when the initial animation has finished, alternatively you can call it +// directly from the initalize_user_visualizer function (the animation can be null) +bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state); + +// These two functions have to be implemented by the user +void initialize_user_visualizer(visualizer_state_t* state); +void update_user_visualizer_state(visualizer_state_t* state); + + +#endif /* VISUALIZER_H */ diff --git a/visualizer.mk b/visualizer.mk index 8ffc1e4aca..ff4b61f089 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -23,11 +23,13 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk +OPT_DEFS += -DLCD_ENABLE endif -SRC += $(GFXSRC) +SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c INC += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c +OPT_DEFS += -DLCD_BACKLIGHT_ENABLE endif From 209167d4d6a7e65a728fab5d996904cc5fb33a3c Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:52:57 +0200 Subject: [PATCH 07/17] Compile the user visualizer --- visualizer.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/visualizer.mk b/visualizer.mk index ff4b61f089..e6e0d63d03 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -33,3 +33,8 @@ SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c OPT_DEFS += -DLCD_BACKLIGHT_ENABLE endif + +ifndef VISUALIZER_USER +VISUALIZER_USER = visualizer_user.c +endif +SRC += $(VISUALIZER_USER) \ No newline at end of file From 8ce60649c85f1ad5371d443675f91c8fc36c3d2e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 20:29:49 +0200 Subject: [PATCH 08/17] Initialize backlight and LCD during visualizer_init --- visualizer.c | 7 +++++++ visualizer.mk | 1 + 2 files changed, 8 insertions(+) diff --git a/visualizer.c b/visualizer.c index 2a92524e25..402bbd1517 100644 --- a/visualizer.c +++ b/visualizer.c @@ -321,6 +321,13 @@ static THD_FUNCTION(visualizerThread, arg) { } void visualizer_init(void) { +#ifdef LCD_ENABLE + gfxInit(); +#endif + +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_init(); +#endif // 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.mk b/visualizer.mk index e6e0d63d03..eef2d5cc8a 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -24,6 +24,7 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk OPT_DEFS += -DLCD_ENABLE +OPT_LIBS += -lm endif SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c INC += $(GFXINC) $(VISUALIZER_DIR) From 8479e6aa390bdd7711753d9fa1141bd1073e2b0a Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 22:08:49 +0200 Subject: [PATCH 09/17] Update readme and license Also add integration examples --- LICENSE.md | 8 + example_integration/callbacks.c | 50 ++++ example_integration/gfxconf.h | 325 ++++++++++++++++++++++++ example_integration/lcd_backlight_hal.c | 90 +++++++ example_integration/visualizer_user.c | 135 ++++++++++ readme.md | 19 +- 6 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 example_integration/callbacks.c create mode 100644 example_integration/gfxconf.h create mode 100644 example_integration/lcd_backlight_hal.c create mode 100644 example_integration/visualizer_user.c diff --git a/LICENSE.md b/LICENSE.md index d7cc3198cb..22d4c3f08b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,3 +1,11 @@ +The files in this project are licensed under the MIT license +It uses the following libraries +uGFX - with it's own license, see the license.html file in the uGFX subfolder for more information +tmk_core - is indirectly used and not included in the repository. It's licensed under the GPLv2 license +Chibios - which is used by tmk_core is licensed under GPLv3. + +Therefore the effective license for any project using the library is GPLv3 + The MIT License (MIT) Copyright (c) 2016 Fred Sundvik diff --git a/example_integration/callbacks.c b/example_integration/callbacks.c new file mode 100644 index 0000000000..21b2d9f748 --- /dev/null +++ b/example_integration/callbacks.c @@ -0,0 +1,50 @@ +/* +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. +*/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "keyboard.h" +#include "action_layer.h" +#include "visualizer.h" +#include "host.h" + +void post_keyboard_init(void) { + visualizer_init(); +} + +void post_keyboard_task() { + visualizer_set_state(default_layer_state, layer_state, host_keyboard_leds()); +} diff --git a/example_integration/gfxconf.h b/example_integration/gfxconf.h new file mode 100644 index 0000000000..304c5d187a --- /dev/null +++ b/example_integration/gfxconf.h @@ -0,0 +1,325 @@ +/** + * This file has a different license to the rest of the uGFX system. + * You can copy, modify and distribute this file as you see fit. + * You do not need to publish your source modifications to this file. + * The only thing you are not permitted to do is to relicense it + * under a different license. + */ + +/** + * Copy this file into your project directory and rename it as gfxconf.h + * Edit your copy to turn on the uGFX features you want to use. + * The values below are the defaults. + * + * Only remove the comments from lines where you want to change the + * default value. This allows definitions to be included from + * driver makefiles when required and provides the best future + * compatibility for your project. + * + * Please use spaces instead of tabs in this file. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + + +/////////////////////////////////////////////////////////////////////////// +// GOS - One of these must be defined, preferably in your Makefile // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_OS_CHIBIOS TRUE +//#define GFX_USE_OS_FREERTOS FALSE +// #define GFX_FREERTOS_USE_TRACE FALSE +//#define GFX_USE_OS_WIN32 FALSE +//#define GFX_USE_OS_LINUX FALSE +//#define GFX_USE_OS_OSX FALSE +//#define GFX_USE_OS_ECOS FALSE +//#define GFX_USE_OS_RAWRTOS FALSE +//#define GFX_USE_OS_ARDUINO FALSE +//#define GFX_USE_OS_KEIL FALSE +//#define GFX_USE_OS_CMSIS FALSE +//#define GFX_USE_OS_RAW32 FALSE +// #define INTERRUPTS_OFF() optional_code +// #define INTERRUPTS_ON() optional_code +// These are not defined by default for some reason +#define GOS_NEED_X_THREADS FALSE +#define GOS_NEED_X_HEAP FALSE + +// Options that (should where relevant) apply to all operating systems + #define GFX_NO_INLINE FALSE +// #define GFX_COMPILER GFX_COMPILER_UNKNOWN +// #define GFX_CPU GFX_CPU_UNKNOWN +// #define GFX_OS_HEAP_SIZE 0 +// #define GFX_OS_NO_INIT FALSE +// #define GFX_OS_INIT_NO_WARNING FALSE +// #define GFX_OS_PRE_INIT_FUNCTION myHardwareInitRoutine +// #define GFX_OS_EXTRA_INIT_FUNCTION myOSInitRoutine +// #define GFX_OS_EXTRA_DEINIT_FUNCTION myOSDeInitRoutine + + +/////////////////////////////////////////////////////////////////////////// +// GDISP // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GDISP TRUE + +//#define GDISP_NEED_AUTOFLUSH FALSE +//#define GDISP_NEED_TIMERFLUSH FALSE +//#define GDISP_NEED_VALIDATION TRUE +//#define GDISP_NEED_CLIP TRUE +//#define GDISP_NEED_CIRCLE FALSE +//#define GDISP_NEED_ELLIPSE FALSE +//#define GDISP_NEED_ARC FALSE +//#define GDISP_NEED_ARCSECTORS FALSE +//#define GDISP_NEED_CONVEX_POLYGON FALSE +//#define GDISP_NEED_SCROLL FALSE +//#define GDISP_NEED_PIXELREAD FALSE +//#define GDISP_NEED_CONTROL FALSE +//#define GDISP_NEED_QUERY FALSE +//#define GDISP_NEED_MULTITHREAD FALSE +//#define GDISP_NEED_STREAMING FALSE +#define GDISP_NEED_TEXT TRUE +// #define GDISP_NEED_TEXT_WORDWRAP FALSE +// #define GDISP_NEED_ANTIALIAS FALSE +// #define GDISP_NEED_UTF8 FALSE + #define GDISP_NEED_TEXT_KERNING TRUE +// #define GDISP_INCLUDE_FONT_UI1 FALSE +// #define GDISP_INCLUDE_FONT_UI2 FALSE // The smallest preferred font. +// #define GDISP_INCLUDE_FONT_LARGENUMBERS FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS10 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS12 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS16 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS20 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS24 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS32 FALSE + #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12 TRUE +// #define GDISP_INCLUDE_FONT_FIXED_10X20 FALSE +// #define GDISP_INCLUDE_FONT_FIXED_7X14 FALSE + #define GDISP_INCLUDE_FONT_FIXED_5X8 TRUE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS12_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS16_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS20_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS24_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS32_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA FALSE +// #define GDISP_INCLUDE_USER_FONTS FALSE + +//#define GDISP_NEED_IMAGE FALSE +// #define GDISP_NEED_IMAGE_NATIVE FALSE +// #define GDISP_NEED_IMAGE_GIF FALSE +// #define GDISP_NEED_IMAGE_BMP FALSE +// #define GDISP_NEED_IMAGE_BMP_1 FALSE +// #define GDISP_NEED_IMAGE_BMP_4 FALSE +// #define GDISP_NEED_IMAGE_BMP_4_RLE FALSE +// #define GDISP_NEED_IMAGE_BMP_8 FALSE +// #define GDISP_NEED_IMAGE_BMP_8_RLE FALSE +// #define GDISP_NEED_IMAGE_BMP_16 FALSE +// #define GDISP_NEED_IMAGE_BMP_24 FALSE +// #define GDISP_NEED_IMAGE_BMP_32 FALSE +// #define GDISP_NEED_IMAGE_JPG FALSE +// #define GDISP_NEED_IMAGE_PNG FALSE +// #define GDISP_NEED_IMAGE_ACCOUNTING FALSE + +//#define GDISP_NEED_PIXMAP FALSE +// #define GDISP_NEED_PIXMAP_IMAGE FALSE + +//#define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE // If not defined the native hardware orientation is used. +//#define GDISP_LINEBUF_SIZE 128 +//#define GDISP_STARTUP_COLOR Black +#define GDISP_NEED_STARTUP_LOGO FALSE + +//#define GDISP_TOTAL_DISPLAYS 1 + +//#define GDISP_DRIVER_LIST GDISPVMT_Win32, GDISPVMT_Win32 +// #ifdef GDISP_DRIVER_LIST +// // For code and speed optimization define as TRUE or FALSE if all controllers have the same capability +// #define GDISP_HARDWARE_STREAM_WRITE FALSE +// #define GDISP_HARDWARE_STREAM_READ FALSE +// #define GDISP_HARDWARE_STREAM_POS FALSE +// #define GDISP_HARDWARE_DRAWPIXEL FALSE +// #define GDISP_HARDWARE_CLEARS FALSE +// #define GDISP_HARDWARE_FILLS FALSE +// #define GDISP_HARDWARE_BITFILLS FALSE +// #define GDISP_HARDWARE_SCROLL FALSE +// #define GDISP_HARDWARE_PIXELREAD FALSE +// #define GDISP_HARDWARE_CONTROL FALSE +// #define GDISP_HARDWARE_QUERY FALSE +// #define GDISP_HARDWARE_CLIP FALSE + + #define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_RGB888 +// #endif + +// The custom format is not defined for some reason, so define it as error +// so we don't get compiler warnings +#define GDISP_PIXELFORMAT_CUSTOM GDISP_PIXELFORMAT_ERROR + +#define GDISP_USE_GFXNET FALSE +// #define GDISP_GFXNET_PORT 13001 +// #define GDISP_GFXNET_CUSTOM_LWIP_STARTUP FALSE +// #define GDISP_DONT_WAIT_FOR_NET_DISPLAY FALSE +// #define GDISP_GFXNET_UNSAFE_SOCKETS FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GWIN // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GWIN FALSE + +//#define GWIN_NEED_WINDOWMANAGER FALSE +// #define GWIN_REDRAW_IMMEDIATE FALSE +// #define GWIN_REDRAW_SINGLEOP FALSE +// #define GWIN_NEED_FLASHING FALSE +// #define GWIN_FLASHING_PERIOD 250 + +//#define GWIN_NEED_CONSOLE FALSE +// #define GWIN_CONSOLE_USE_HISTORY FALSE +// #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE +// #define GWIN_CONSOLE_HISTORY_ATCREATE FALSE +// #define GWIN_CONSOLE_ESCSEQ FALSE +// #define GWIN_CONSOLE_USE_BASESTREAM FALSE +// #define GWIN_CONSOLE_USE_FLOAT FALSE +//#define GWIN_NEED_GRAPH FALSE +//#define GWIN_NEED_GL3D FALSE + +//#define GWIN_NEED_WIDGET FALSE +//#define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 +// #define GWIN_NEED_LABEL FALSE +// #define GWIN_LABEL_ATTRIBUTE FALSE +// #define GWIN_NEED_BUTTON FALSE +// #define GWIN_BUTTON_LAZY_RELEASE FALSE +// #define GWIN_NEED_SLIDER FALSE +// #define GWIN_SLIDER_NOSNAP FALSE +// #define GWIN_SLIDER_DEAD_BAND 5 +// #define GWIN_SLIDER_TOGGLE_INC 20 +// #define GWIN_NEED_CHECKBOX FALSE +// #define GWIN_NEED_IMAGE FALSE +// #define GWIN_NEED_IMAGE_ANIMATION FALSE +// #define GWIN_NEED_RADIO FALSE +// #define GWIN_NEED_LIST FALSE +// #define GWIN_NEED_LIST_IMAGES FALSE +// #define GWIN_NEED_PROGRESSBAR FALSE +// #define GWIN_PROGRESSBAR_AUTO FALSE +// #define GWIN_NEED_KEYBOARD FALSE +// #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1 +// #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE +// #define GWIN_NEED_TEXTEDIT FALSE +// #define GWIN_FLAT_STYLING FALSE +// #define GWIN_WIDGET_TAGS FALSE + +//#define GWIN_NEED_CONTAINERS FALSE +// #define GWIN_NEED_CONTAINER FALSE +// #define GWIN_NEED_FRAME FALSE +// #define GWIN_NEED_TABSET FALSE +// #define GWIN_TABSET_TABHEIGHT 18 + + +/////////////////////////////////////////////////////////////////////////// +// GEVENT // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GEVENT FALSE + +//#define GEVENT_ASSERT_NO_RESOURCE FALSE +//#define GEVENT_MAXIMUM_SIZE 32 +//#define GEVENT_MAX_SOURCE_LISTENERS 32 + + +/////////////////////////////////////////////////////////////////////////// +// GTIMER // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GTIMER FALSE + +//#define GTIMER_THREAD_PRIORITY HIGH_PRIORITY +//#define GTIMER_THREAD_WORKAREA_SIZE 2048 + + +/////////////////////////////////////////////////////////////////////////// +// GQUEUE // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GQUEUE FALSE + +//#define GQUEUE_NEED_ASYNC FALSE +//#define GQUEUE_NEED_GSYNC FALSE +//#define GQUEUE_NEED_FSYNC FALSE +//#define GQUEUE_NEED_BUFFERS FALSE + +/////////////////////////////////////////////////////////////////////////// +// GINPUT // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GINPUT FALSE + +//#define GINPUT_NEED_MOUSE FALSE +// #define GINPUT_TOUCH_STARTRAW FALSE +// #define GINPUT_TOUCH_NOTOUCH FALSE +// #define GINPUT_TOUCH_NOCALIBRATE FALSE +// #define GINPUT_TOUCH_NOCALIBRATE_GUI FALSE +// #define GINPUT_MOUSE_POLL_PERIOD 25 +// #define GINPUT_MOUSE_CLICK_TIME 300 +// #define GINPUT_TOUCH_CXTCLICK_TIME 700 +// #define GINPUT_TOUCH_USER_CALIBRATION_LOAD FALSE +// #define GINPUT_TOUCH_USER_CALIBRATION_SAVE FALSE +// #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 +//#define GINPUT_NEED_KEYBOARD FALSE +// #define GINPUT_KEYBOARD_POLL_PERIOD 200 +// #define GKEYBOARD_DRIVER_LIST GKEYBOARDVMT_Win32, GKEYBOARDVMT_Win32 +// #define GKEYBOARD_LAYOUT_OFF FALSE +// #define GKEYBOARD_LAYOUT_SCANCODE2_US FALSE +//#define GINPUT_NEED_TOGGLE FALSE +//#define GINPUT_NEED_DIAL FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GFILE // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GFILE FALSE + +//#define GFILE_NEED_PRINTG FALSE +//#define GFILE_NEED_SCANG FALSE +//#define GFILE_NEED_STRINGS FALSE +//#define GFILE_NEED_FILELISTS FALSE +//#define GFILE_NEED_STDIO FALSE +//#define GFILE_NEED_NOAUTOMOUNT FALSE +//#define GFILE_NEED_NOAUTOSYNC FALSE + +//#define GFILE_NEED_MEMFS FALSE +//#define GFILE_NEED_ROMFS FALSE +//#define GFILE_NEED_RAMFS FALSE +//#define GFILE_NEED_FATFS FALSE +//#define GFILE_NEED_NATIVEFS FALSE +//#define GFILE_NEED_CHBIOSFS FALSE + +//#define GFILE_ALLOW_FLOATS FALSE +//#define GFILE_ALLOW_DEVICESPECIFIC FALSE +//#define GFILE_MAX_GFILES 3 + +/////////////////////////////////////////////////////////////////////////// +// GADC // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GADC FALSE + +//#define GADC_MAX_LOWSPEED_DEVICES 4 + + +/////////////////////////////////////////////////////////////////////////// +// GAUDIO // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GAUDIO FALSE +// There seems to be a bug in the ugfx code, the wrong define is used +// So define it in order to avoid warnings +#define GFX_USE_GAUDIN GFX_USE_GAUDIO +// #define GAUDIO_NEED_PLAY FALSE +// #define GAUDIO_NEED_RECORD FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GMISC // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GMISC FALSE + +//#define GMISC_NEED_ARRAYOPS FALSE +//#define GMISC_NEED_FASTTRIG FALSE +//#define GMISC_NEED_FIXEDTRIG FALSE +//#define GMISC_NEED_INVSQRT FALSE +// #define GMISC_INVSQRT_MIXED_ENDIAN FALSE +// #define GMISC_INVSQRT_REAL_SLOW FALSE +//#define GMISC_NEED_MATRIXFLOAT2D FALSE +//#define GMISC_NEED_MATRIXFIXED2D FALSE + +#endif /* _GFXCONF_H */ diff --git a/example_integration/lcd_backlight_hal.c b/example_integration/lcd_backlight_hal.c new file mode 100644 index 0000000000..0f35cb6755 --- /dev/null +++ b/example_integration/lcd_backlight_hal.c @@ -0,0 +1,90 @@ +/* +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 "lcd_backlight.h" +#include "hal.h" + +#define RED_PIN 1 +#define GREEN_PIN 2 +#define BLUE_PIN 3 +#define CHANNEL_RED FTM0->CHANNEL[0] +#define CHANNEL_GREEN FTM0->CHANNEL[1] +#define CHANNEL_BLUE FTM0->CHANNEL[2] + +#define RGB_PORT PORTC +#define RGB_PORT_GPIO GPIOC + +// Base FTM clock selection (72 MHz system clock) +// @ 0xFFFF period, 72 MHz / (0xFFFF * 2) = Actual period +// Higher pre-scalar will use the most power (also look the best) +// Pre-scalar calculations +// 0 - 72 MHz -> 549 Hz +// 1 - 36 MHz -> 275 Hz +// 2 - 18 MHz -> 137 Hz +// 3 - 9 MHz -> 69 Hz (Slightly visible flicker) +// 4 - 4 500 kHz -> 34 Hz (Visible flickering) +// 5 - 2 250 kHz -> 17 Hz +// 6 - 1 125 kHz -> 9 Hz +// 7 - 562 500 Hz -> 4 Hz +// Using a higher pre-scalar without flicker is possible but FTM0_MOD will need to be reduced +// Which will reduce the brightness range +#define PRESCALAR_DEFINE 0 + +void lcd_backlight_hal_init(void) { + // Setup Backlight + SIM->SCGC6 |= SIM_SCGC6_FTM0; + FTM0->CNT = 0; // Reset counter + + // PWM Period + // 16-bit maximum + FTM0->MOD = 0xFFFF; + + // Set FTM to PWM output - Edge Aligned, Low-true pulses +#define CNSC_MODE FTM_SC_CPWMS | FTM_SC_PS(4) | FTM_SC_CLKS(0) + CHANNEL_RED.CnSC = CNSC_MODE; + CHANNEL_GREEN.CnSC = CNSC_MODE; + CHANNEL_BLUE.CnSC = CNSC_MODE; + + // System clock, /w prescalar setting + FTM0->SC = FTM_SC_CLKS(1) | FTM_SC_PS(PRESCALAR_DEFINE); + + CHANNEL_RED.CnV = 0; + CHANNEL_GREEN.CnV = 0; + CHANNEL_BLUE.CnV = 0; + + RGB_PORT_GPIO->PDDR |= (1 << RED_PIN); + RGB_PORT_GPIO->PDDR |= (1 << GREEN_PIN); + RGB_PORT_GPIO->PDDR |= (1 << BLUE_PIN); + +#define RGB_MODE PORTx_PCRn_SRE | PORTx_PCRn_DSE | PORTx_PCRn_MUX(4) + RGB_PORT->PCR[RED_PIN] = RGB_MODE; + RGB_PORT->PCR[GREEN_PIN] = RGB_MODE; + RGB_PORT->PCR[BLUE_PIN] = RGB_MODE; +} + +void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b) { + CHANNEL_RED.CnV = r; + CHANNEL_GREEN.CnV = g; + CHANNEL_BLUE.CnV = b; +} + diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c new file mode 100644 index 0000000000..a50ecc60da --- /dev/null +++ b/example_integration/visualizer_user.c @@ -0,0 +1,135 @@ +/* +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. +*/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// Currently we are assuming that both the backlight and LCD are enabled +// But it's entirely possible to write a custom visualizer that use only +// one of them +#ifndef LCD_BACKLIGHT_ENABLE +#error This visualizer needs that LCD backlight is enabled +#endif + +#ifndef LCD_ENABLE +#error This visualizer needs that LCD is enabled +#endif + +#include "visualizer.h" + +static const char* welcome_text[] = {"TMK", "Infinity Ergodox"}; + +// Just an example how to write custom keyframe functions, we could have moved +// all this into the init function +bool display_welcome(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + // Read the uGFX documentation for information how to use the displays + // http://wiki.ugfx.org/index.php/Main_Page + gdispClear(White); + // You can use static variables for things that can't be found in the animation + // or state structs + gdispDrawString(0, 3, welcome_text[0], state->font_dejavusansbold12, Black); + gdispDrawString(0, 15, welcome_text[1], state->font_dejavusansbold12, Black); + // Always remember to flush the display + gdispFlush(); + // you could set the backlight color as well, but we won't do it here, since + // it's part of the following animation + // lcd_backlight_color(hue, saturation, intensity); + // We don't need constant updates, just drawing the screen once is enough + return false; +} + +// Feel free to modify the animations below, or even add new ones if needed + +// Don't worry, if the startup animation is long, you can use the keyboard like normal +// during that time +static keyframe_animation_t startup_animation = { + .num_frames = 4, + .loop = false, + .frame_lengths = {0, MS2ST(1000), MS2ST(5000), 0}, + .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, user_visualizer_inited}, +}; + +// The color animation animates the LCD color when you change layers +static keyframe_animation_t color_animation = { + .num_frames = 2, + .loop = false, + // Note that there's a 200 ms no-operation frame, + // this prevents the color from changing when activating the layer + // momentarily + .frame_lengths = {MS2ST(200), MS2ST(500)}, + .frame_functions = {keyframe_no_operation, keyframe_animate_backlight_color}, +}; + +// The LCD animation alternates between the layer name display and a +// bitmap that displays all active layers +static keyframe_animation_t lcd_animation = { + .num_frames = 2, + .loop = true, + .frame_lengths = {MS2ST(2000), MS2ST(2000)}, + .frame_functions = {keyframe_display_layer_text, keyframe_display_layer_bitmap}, +}; + +void initialize_user_visualizer(visualizer_state_t* state) { + // The brightness will be dynamically adjustable in the future + // But for now, change it here. + lcd_backlight_brightness(0x50); + state->current_lcd_color = LCD_COLOR(0x00, 0x00, 0xFF); + state->target_lcd_color = LCD_COLOR(0x10, 0xFF, 0xFF); + start_keyframe_animation(&startup_animation); +} + +void update_user_visualizer_state(visualizer_state_t* state) { + // Add more tests, change the colors and layer texts here + // Usually you want to check the high bits (higher layers first) + // because that's the order layers are processed for keypresses + // You can for check for example: + // state->status.layer + // state->status.default_layer + // state->status.leds (see led.h for available statuses) + if (state->status.layer & 0x2) { + state->target_lcd_color = LCD_COLOR(0xA0, 0xB0, 0xFF); + state->layer_text = "Layer 2"; + } + else { + state->target_lcd_color = LCD_COLOR(0x50, 0xB0, 0xFF); + state->layer_text = "Layer 1"; + } + // You can also stop existing animations, and start your custom ones here + // remember that you should normally have only one animation for the LCD + // and one for the background. But you can also combine them if you want. + start_keyframe_animation(&lcd_animation); + start_keyframe_animation(&color_animation); +} diff --git a/readme.md b/readme.md index 82bf8bfdfa..545ba22707 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,18 @@ -A visualization library for the TMK keyboard firmware +# A visualization library for the TMK keyboard firmware + +This library is designed to work together with the [TMK keyboard firmware](https://github.com/tmk/tmk_keyboard). Currently it only works for [Chibios](http://www.chibios.org/) + flavors, but it would be possible to add support for other configurations as well. The LCD display functionality is provided by the [uGFX library](http://www.ugfx.org/). + +## To use this library as a user +You can and should modify the visualizer\_user.c file. Check the comments in the file for more information. + +## To add this library to custom keyboard projects + +1. Add tmk_visualizer as a submodule to your project +1. Set VISUALIZER_DIR in the main keyboard project makefile to point to the submodule +1. Define LCD\_ENABLE and/or LCD\_BACKLIGHT\_ENABLE, to enable support +1. Include the visualizer.mk make file +1. Copy the files in the example\_integration folder to your keyboard project +1. All other files than the callback.c file are included automatically, so you will need to add callback.c to your makefile manually. If you already have a similar file in your project, you can just copy the functions instead of the whole file. +1. Edit the files to match your hardware. You might might want to read the Chibios and UGfx documentation, for more information. +1. If you enable LCD support you might also have to write a custom uGFX display driver, check the uGFX documentation for that. You probably also want to enable SPI support in your Chibios configuration. From c5db02925c6bf73ef1df1f718e09ff304266a535 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 22:19:49 +0200 Subject: [PATCH 10/17] Clean up wrong license information --- example_integration/callbacks.c | 14 -------------- example_integration/lcd_backlight_hal.c | 1 + example_integration/visualizer_user.c | 14 -------------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/example_integration/callbacks.c b/example_integration/callbacks.c index 21b2d9f748..2539615d65 100644 --- a/example_integration/callbacks.c +++ b/example_integration/callbacks.c @@ -22,20 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - #include "keyboard.h" #include "action_layer.h" #include "visualizer.h" diff --git a/example_integration/lcd_backlight_hal.c b/example_integration/lcd_backlight_hal.c index 0f35cb6755..913131b169 100644 --- a/example_integration/lcd_backlight_hal.c +++ b/example_integration/lcd_backlight_hal.c @@ -21,6 +21,7 @@ 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 "lcd_backlight.h" #include "hal.h" diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c index a50ecc60da..6c4619d954 100644 --- a/example_integration/visualizer_user.c +++ b/example_integration/visualizer_user.c @@ -22,20 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - // Currently we are assuming that both the backlight and LCD are enabled // But it's entirely possible to write a custom visualizer that use only // one of them From 725929ec8bb5d57b6c4f53fa7d15e04df15f2535 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 23 Feb 2016 09:37:24 +0200 Subject: [PATCH 11/17] Fix include paths for new tmk_core update --- visualizer.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/visualizer.mk b/visualizer.mk index eef2d5cc8a..13c5d31587 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -23,16 +23,16 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk -OPT_DEFS += -DLCD_ENABLE -OPT_LIBS += -lm +UDEFS += -DLCD_ENABLE +ULIBS += -lm endif SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c -INC += $(GFXINC) $(VISUALIZER_DIR) +UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c -OPT_DEFS += -DLCD_BACKLIGHT_ENABLE +UDEFS += -DLCD_BACKLIGHT_ENABLE endif ifndef VISUALIZER_USER From 315edb48265e6baedd07f34c9e6e323d28814b4e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 12 Mar 2016 19:42:57 +0200 Subject: [PATCH 12/17] Add serial link support for visualizer --- visualizer.c | 63 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/visualizer.c b/visualizer.c index 402bbd1517..ca7bcb7765 100644 --- a/visualizer.c +++ b/visualizer.c @@ -42,6 +42,11 @@ SOFTWARE. #include "nodebug.h" #endif +#ifdef USE_SERIAL_LINK +#include "serial_link/protocol/transport.h" +#include "serial_link/system/driver.h" +#endif + static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, @@ -59,6 +64,16 @@ static bool visualizer_enabled = false; #define MAX_SIMULTANEOUS_ANIMATIONS 4 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; +#ifdef USE_SERIAL_LINK +MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t); + +static remote_object_t* remote_objects[] = { + REMOTE_OBJECT(current_status), +}; + +#endif + + void start_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = -1; animation->time_left_in_frame = 0; @@ -328,6 +343,10 @@ void visualizer_init(void) { #ifdef LCD_BACKLIGHT_ENABLE lcd_backlight_init(); #endif + +#ifdef USE_SERIAL_LINK + add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); +#endif // 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); @@ -340,17 +359,45 @@ void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) // a state where one of these are set but not the other. But this should // not really matter as it will be fixed during the next loop step. // Alternatively a mutex could be used instead of the volatile variables + bool changed = false; - visualizer_keyboard_status_t new_status = { - .layer = state, - .default_layer = default_state, - .leds = leds, - }; - if (!same_status(¤t_status, &new_status)) { - changed = true; +#ifdef USE_SERIAL_LINK + if (is_serial_link_connected ()) { + visualizer_keyboard_status_t* new_status = read_current_status(); + if (new_status) { + if (!same_status(¤t_status, new_status)) { + changed = true; + current_status = *new_status; + } + } + } + else { +#else + { +#endif + visualizer_keyboard_status_t new_status = { + .layer = state, + .default_layer = default_state, + .leds = leds, + }; + if (!same_status(¤t_status, &new_status)) { + changed = true; + current_status = new_status; + } } - current_status = new_status; if (changed) { chEvtBroadcast(&layer_changed_event); + } +#ifdef USE_SERIAL_LINK + static systime_t last_update = 0; + systime_t current_update = chVTGetSystemTimeX(); + systime_t delta = current_update - last_update; + if (changed || delta > MS2ST(10)) { + last_update = current_update; + visualizer_keyboard_status_t* r = begin_write_current_status(); + *r = current_status; + end_write_current_status(); + } +#endif } From b93d07198a18063594a59dd193d0961622087868 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 13 Mar 2016 17:54:45 +0200 Subject: [PATCH 13/17] Suspend support for the visualizer --- example_integration/visualizer_user.c | 2 +- visualizer.c | 114 ++++++++++++++++++++------ visualizer.h | 17 +++- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c index 6c4619d954..fc09fe2eaf 100644 --- a/example_integration/visualizer_user.c +++ b/example_integration/visualizer_user.c @@ -65,7 +65,7 @@ static keyframe_animation_t startup_animation = { .num_frames = 4, .loop = false, .frame_lengths = {0, MS2ST(1000), MS2ST(5000), 0}, - .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, user_visualizer_inited}, + .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, enable_visualization}, }; // The color animation animates the LCD color when you change layers diff --git a/visualizer.c b/visualizer.c index ca7bcb7765..5b0d560ed9 100644 --- a/visualizer.c +++ b/visualizer.c @@ -52,10 +52,14 @@ static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, .default_layer = 0xFFFFFFFF, .leds = 0xFFFFFFFF, + .suspended = false, }; static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { - return memcmp(status1, status2, sizeof(visualizer_keyboard_status_t)) == 0; + return status1->layer == status2->layer && + status1->default_layer == status2->default_layer && + status1->leds == status2->leds && + status1->suspended == status2->suspended; } static event_source_t layer_changed_event; @@ -104,6 +108,17 @@ void stop_keyframe_animation(keyframe_animation_t* animation) { } } +void stop_all_keyframe_animations(void) { + for (int i=0;icurrent_frame = animations[i]->num_frames; + animations[i]->time_left_in_frame = 0; + animations[i]->need_update = true; + animations[i] = NULL; + } + } +} + static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) { dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta); @@ -252,7 +267,19 @@ bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_s } #endif // LCD_ENABLE -bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state) { +bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; dprint("User visualizer inited\n"); @@ -268,13 +295,15 @@ static THD_FUNCTION(visualizerThread, arg) { event_listener_t event_listener; chEvtRegister(&layer_changed_event, &event_listener, 0); - visualizer_state_t state = { - .status = { - .default_layer = 0xFFFFFFFF, - .layer = 0xFFFFFFFF, - .leds = 0xFFFFFFFF, - }, + visualizer_keyboard_status_t initial_status = { + .default_layer = 0xFFFFFFFF, + .layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, + .suspended = false, + }; + visualizer_state_t state = { + .status = initial_status, .current_lcd_color = 0, #ifdef LCD_ENABLE .font_fixed5x8 = gdispOpenFont("fixed_5x8"), @@ -301,17 +330,36 @@ static THD_FUNCTION(visualizerThread, arg) { bool enabled = visualizer_enabled; if (!same_status(&state.status, ¤t_status)) { if (visualizer_enabled) { - state.status = current_status; - update_user_visualizer_state(&state); - state.prev_lcd_color = state.current_lcd_color; + if (current_status.suspended) { + stop_all_keyframe_animations(); + visualizer_enabled = false; + state.status = current_status; + user_visualizer_suspend(&state); + } + else { + state.status = current_status; + update_user_visualizer_state(&state); + state.prev_lcd_color = state.current_lcd_color; + } } } + if (!enabled && state.status.suspended && current_status.suspended == false) { + // Setting the status to the initial status will force an update + // when the visualizer is enabled again + state.status = initial_status; + state.status.suspended = false; + stop_all_keyframe_animations(); + user_visualizer_resume(&state); + } sleep_time = TIME_INFINITE; for (int i=0;i MS2ST(10)) { + last_update = current_update; + visualizer_keyboard_status_t* r = begin_write_current_status(); + *r = current_status; + end_write_current_status(); + } +#endif +} + +void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) { // Note that there's a small race condition here, the thread could read // a state where one of these are set but not the other. But this should // not really matter as it will be fixed during the next loop step. @@ -379,25 +444,22 @@ void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) .layer = state, .default_layer = default_state, .leds = leds, + .suspended = current_status.suspended, }; if (!same_status(¤t_status, &new_status)) { changed = true; current_status = new_status; } } - if (changed) { - chEvtBroadcast(&layer_changed_event); + update_status(changed); +} - } -#ifdef USE_SERIAL_LINK - static systime_t last_update = 0; - systime_t current_update = chVTGetSystemTimeX(); - systime_t delta = current_update - last_update; - if (changed || delta > MS2ST(10)) { - last_update = current_update; - visualizer_keyboard_status_t* r = begin_write_current_status(); - *r = current_status; - end_write_current_status(); - } -#endif +void visualizer_suspend(void) { + current_status.suspended = true; + update_status(true); +} + +void visualizer_resume(void) { + current_status.suspended = false; + update_status(true); } diff --git a/visualizer.h b/visualizer.h index b7b0a3a7d0..22798cda6b 100644 --- a/visualizer.h +++ b/visualizer.h @@ -38,8 +38,12 @@ SOFTWARE. // This need to be called once at the start void visualizer_init(void); -// This should be called before every matrix scan -void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds); +// This should be called at every matrix scan +void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds); +// This should be called when the keyboard goes to suspend state +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 @@ -50,6 +54,7 @@ typedef struct { uint32_t layer; uint32_t default_layer; uint32_t leds; // See led.h for available statuses + bool suspended; } visualizer_keyboard_status_t; // The state struct is used by the various keyframe functions @@ -108,13 +113,19 @@ bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_st bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state); // Displays a bitmap (0/1) of all the currently active layers bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); + +bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state); + // Call this once, when the initial animation has finished, alternatively you can call it // directly from the initalize_user_visualizer function (the animation can be null) -bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state); +bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state); // These two functions have to be implemented by the user void initialize_user_visualizer(visualizer_state_t* state); void update_user_visualizer_state(visualizer_state_t* state); +void user_visualizer_suspend(visualizer_state_t* state); +void user_visualizer_resume(visualizer_state_t* state); #endif /* VISUALIZER_H */ From f4c11740f7bac033194ab1d5f5a52721b1d7c6d2 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 13 Mar 2016 21:35:42 +0200 Subject: [PATCH 14/17] Suspend power off, fix backlight animation The backlight color animation was using the wrong frame number, so it didn't work properly. --- visualizer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/visualizer.c b/visualizer.c index 5b0d560ed9..867a1d3347 100644 --- a/visualizer.c +++ b/visualizer.c @@ -174,7 +174,7 @@ bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* #ifdef LCD_BACKLIGHT_ENABLE bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { - int frame_length = animation->frame_lengths[1]; + int frame_length = animation->frame_lengths[animation->current_frame]; int current_pos = frame_length - animation->time_left_in_frame; uint8_t t_h = LCD_HUE(state->target_lcd_color); uint8_t t_s = LCD_SAT(state->target_lcd_color); @@ -270,12 +270,21 @@ bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_s bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; +#ifdef LCD_ENABLE + gdispSetPowerMode(powerOff); +#endif +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_hal_color(0, 0, 0); +#endif return false; } bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; +#ifdef LCD_ENABLE + gdispSetPowerMode(powerOn); +#endif return false; } @@ -339,8 +348,8 @@ static THD_FUNCTION(visualizerThread, arg) { else { state.status = current_status; update_user_visualizer_state(&state); - state.prev_lcd_color = state.current_lcd_color; } + state.prev_lcd_color = state.current_lcd_color; } } if (!enabled && state.status.suspended && current_status.suspended == false) { @@ -350,6 +359,7 @@ static THD_FUNCTION(visualizerThread, arg) { state.status.suspended = false; stop_all_keyframe_animations(); user_visualizer_resume(&state); + state.prev_lcd_color = state.current_lcd_color; } sleep_time = TIME_INFINITE; for (int i=0;i Date: Sat, 9 Apr 2016 19:49:52 +0300 Subject: [PATCH 15/17] Update ugfx with ChibiOS 4 support. Also change the URL to fredizzimo bitbucket --- .gitmodules | 2 +- ugfx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2ab25f6883..b320458c0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "ugfx"] path = ugfx - url = https://bitbucket.org/Tectu/ugfx.git + url = https://bitbucket.org/fredizzimo/ugfx.git diff --git a/ugfx b/ugfx index 2b66ac524b..e221a69061 160000 --- a/ugfx +++ b/ugfx @@ -1 +1 @@ -Subproject commit 2b66ac524bd56853ba97b917683971f3ebc0104c +Subproject commit e221a690616e20f87e0b0088baffdbd5427be862 From 5651be7a2f50857b1ddd120a4b0184a28cdca4aa Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 15 May 2016 13:00:27 +0300 Subject: [PATCH 16/17] Configurable visualizer thread priority --- visualizer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index 867a1d3347..4c47114990 100644 --- a/visualizer.c +++ b/visualizer.c @@ -24,6 +24,7 @@ SOFTWARE. #include "visualizer.h" #include "ch.h" +#include "config.h" #include #ifdef LCD_ENABLE @@ -47,6 +48,11 @@ SOFTWARE. #include "serial_link/system/driver.h" #endif +// Define this in config.h +#ifndef VISUALIZER_THREAD_PRIORITY +#define "Visualizer thread priority not defined" +#endif + static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, @@ -409,7 +415,7 @@ void visualizer_init(void) { // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack), - LOWPRIO, visualizerThread, NULL); + VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL); } void update_status(bool changed) { From 81f89cc6725d4e513f85ed1c841d25d0ac64bfe3 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 15 May 2016 13:39:58 +0300 Subject: [PATCH 17/17] Update include dir for new serial_link include --- visualizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index 4c47114990..605be30594 100644 --- a/visualizer.c +++ b/visualizer.c @@ -45,7 +45,7 @@ SOFTWARE. #ifdef USE_SERIAL_LINK #include "serial_link/protocol/transport.h" -#include "serial_link/system/driver.h" +#include "serial_link/system/serial_link.h" #endif // Define this in config.h