1
0
Fork 0
forked from forks/qmk_firmware

Merge commit '73d890a2c9c34b905cd5e74e7146fdd4578dcb96' into add_visualizer

This commit is contained in:
Fred Sundvik 2016-07-06 20:30:58 +03:00
commit 6c29655790
5 changed files with 342 additions and 25 deletions

View file

@ -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"
#define CROSSFADE_TIME 1000
#define GRADIENT_TIME 3000
keyframe_animation_t led_test_animation = {
.num_frames = 14,
.loop = true,
.frame_lengths = {
gfxMillisecondsToTicks(1000), // fade in
gfxMillisecondsToTicks(1000), // no op (leds on)
gfxMillisecondsToTicks(1000), // fade out
gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
gfxMillisecondsToTicks(GRADIENT_TIME), // left to rigt (outside in)
gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
0, // mirror leds
gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
gfxMillisecondsToTicks(GRADIENT_TIME), // left_to_right (mirrored, so inside out)
gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade
gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom
0, // normal leds
gfxMillisecondsToTicks(CROSSFADE_TIME), // 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 two_pi = 2.0f * PI;
float normalized_index = (1.0f - index / (num - 1)) * two_pi;
float x = t * two_pi + normalized_index;
float v = 0.5 * (cosf(x) + 1.0f);
return (uint8_t)(255.0f * v);
}
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;
}

View file

@ -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_ */

View file

@ -23,9 +23,11 @@ SOFTWARE.
*/
#include "visualizer.h"
#include "ch.h"
#include "config.h"
#include <string.h>
#ifdef PROTOCOL_CHIBIOS
#include "ch.h"
#endif
#ifdef LCD_ENABLE
#include "gfx.h"
@ -68,7 +70,6 @@ static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboa
status1->suspended == status2->suspended;
}
static event_source_t layer_changed_event;
static bool visualizer_enabled = false;
#define MAX_SIMULTANEOUS_ANIMATIONS 4
@ -83,6 +84,18 @@ static remote_object_t* remote_objects[] = {
#endif
GDisplay* LCD_DISPLAY = 0;
GDisplay* LED_DISPLAY = 0;
__attribute__((weak))
GDisplay* get_lcd_display(void) {
return gdispGetDisplay(0);
}
__attribute__((weak))
GDisplay* get_led_display(void) {
return gdispGetDisplay(1);
}
void start_keyframe_animation(keyframe_animation_t* animation) {
animation->current_frame = -1;
@ -106,6 +119,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 +135,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) {
static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_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 +154,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,16 +184,32 @@ 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;
if ((unsigned)wanted_sleep < *sleep_time) {
systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
if (wanted_sleep < *sleep_time) {
*sleep_time = wanted_sleep;
}
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;
@ -303,12 +341,13 @@ bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* s
}
// TODO: Optimize the stack size, this is probably way too big
static THD_WORKING_AREA(visualizerThreadStack, 1024);
static THD_FUNCTION(visualizerThread, arg) {
static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
(void)arg;
event_listener_t event_listener;
chEvtRegister(&layer_changed_event, &event_listener, 0);
GListener event_listener;
geventListenerInit(&event_listener);
geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
visualizer_keyboard_status_t initial_status = {
.default_layer = 0xFFFFFFFF,
@ -335,12 +374,12 @@ static THD_FUNCTION(visualizerThread, arg) {
LCD_INT(state.current_lcd_color));
#endif
systime_t sleep_time = TIME_INFINITE;
systime_t current_time = chVTGetSystemTimeX();
systemticks_t sleep_time = TIME_INFINITE;
systemticks_t current_time = gfxSystemTicks();
while(true) {
systime_t new_time = chVTGetSystemTimeX();
systime_t delta = new_time - current_time;
systemticks_t new_time = gfxSystemTicks();
systemticks_t delta = new_time - current_time;
current_time = new_time;
bool enabled = visualizer_enabled;
if (!same_status(&state.status, &current_status)) {
@ -373,6 +412,13 @@ static THD_FUNCTION(visualizerThread, arg) {
update_keyframe_animation(animations[i], &state, delta, &sleep_time);
}
}
#ifdef LED_ENABLE
gdispGFlush(LED_DISPLAY);
#endif
#ifdef EMULATOR
draw_emulator();
#endif
// The animation can enable the visualizer
// And we might need to update the state when that happens
// so don't sleep
@ -380,7 +426,7 @@ static THD_FUNCTION(visualizerThread, arg) {
sleep_time = 0;
}
systime_t after_update = chVTGetSystemTimeX();
systemticks_t after_update = gfxSystemTicks();
unsigned update_delta = after_update - current_time;
if (sleep_time != TIME_INFINITE) {
if (sleep_time > update_delta) {
@ -391,12 +437,24 @@ static THD_FUNCTION(visualizerThread, arg) {
}
}
dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time);
#ifdef PROTOCOL_CHIBIOS
// The gEventWait function really takes milliseconds, even if the documentation says ticks.
// Unfortunately there's no generic ugfx conversion from system time to milliseconds,
// so let's do it in a platform dependent way.
// On windows the system ticks is the same as milliseconds anyway
if (sleep_time != TIME_INFINITE) {
sleep_time = ST2MS(sleep_time);
}
#endif
geventEventWait(&event_listener, sleep_time);
}
#ifdef LCD_ENABLE
gdispCloseFont(state.font_fixed5x8);
gdispCloseFont(state.font_dejavusansbold12);
#endif
return 0;
}
void visualizer_init(void) {
@ -411,16 +469,26 @@ void visualizer_init(void) {
#ifdef USE_SERIAL_LINK
add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
#endif
#ifdef LCD_ENABLE
LCD_DISPLAY = get_lcd_display();
#endif
#ifdef LED_ENABLE
LED_DISPLAY = get_led_display();
#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);
(void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack),
gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
}
void update_status(bool changed) {
if (changed) {
chEvtBroadcast(&layer_changed_event);
GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
if (listener) {
geventSendEvent(listener);
}
}
#ifdef USE_SERIAL_LINK
static systime_t last_update = 0;

View file

@ -45,8 +45,18 @@ 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
// These functions are week, so they can be overridden by the keyboard
// if needed
GDisplay* get_lcd_display(void);
GDisplay* get_led_display(void);
// For emulator builds, this function need to be implemented
#ifdef EMULATOR
void draw_emulator(void);
#endif
// 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 +105,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
@ -121,7 +139,7 @@ bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualiz
// directly from the initalize_user_visualizer function (the animation can be null)
bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state);
// These two functions have to be implemented by the user
// These 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);

View file

@ -21,21 +21,41 @@
# 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
ifndef EMULATOR
SRC += lcd_backlight_hal.c
endif
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)
UDEFS += $(patsubst %,-D%,$(patsubst -D%,%,$(GFXDEFS)))
ULIBS += $(patsubst %,-l%,$(patsubst -l%,%,$(GFXLIBS)))
endif
ifndef VISUALIZER_USER
VISUALIZER_USER = visualizer_user.c
endif
SRC += $(VISUALIZER_USER)
ifdef EMULATOR
UINCDIR += $(TMK_DIR)/common
endif