Merge remote-tracking branch 'origin/develop' into xap

This commit is contained in:
QMK Bot
2026-08-02 19:10:53 +00:00
8 changed files with 192 additions and 60 deletions
+45
View File
@@ -119,6 +119,7 @@ enum led_matrix_effects {
LED_MATRIX_SOLID_MULTISPLASH, // Value pulses away from multiple key hits then fades out
LED_MATRIX_WAVE_LEFT_RIGHT, // Sine wave scrolling from left to right
LED_MATRIX_WAVE_UP_DOWN, // Sine wave scrolling from up to down
LED_MATRIX_TYPING_HEATMAP, // How hot is your WPM!
LED_MATRIX_EFFECT_MAX
};
```
@@ -140,6 +141,14 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` |
|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` |
|Framebuffer Defines |Description |
|------------------------------------------------------|-----------------------------------------------|
|`#define ENABLE_LED_MATRIX_TYPING_HEATMAP` |Enables `LED_MATRIX_TYPING_HEATMAP` |
::: tip
These modes introduce additional logic that can increase firmware size.
:::
|Reactive Defines |Description |
|-------------------------------------------------------|----------------------------------------------|
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` |
@@ -156,6 +165,42 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
These modes introduce additional logic that can increase firmware size.
:::
### LED Matrix Effect Typing Heatmap {#led-matrix-effect-typing-heatmap}
This effect will scale the LED matrix brightness according to a heatmap of recently pressed keys. Whenever a key is pressed its "temperature" increases as well as that of its neighboring keys. The temperature of each key is then decreased automatically every 25 milliseconds by default.
In order to change the delay of temperature decrease define `LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`:
```c
#define LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 50
```
As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`.
Limit the distance the effect spreads to surrounding keys.
```c
#define LED_MATRIX_TYPING_HEATMAP_SPREAD 40
```
Limit how hot surrounding keys get from each press.
```c
#define LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16
```
Remove the spread effect entirely.
```c
#define LED_MATRIX_TYPING_HEATMAP_SLIM
```
It's also possible to adjust the tempo of *heating up*. It's defined as the number of steps by which to increment the brightness. Decreasing this value increases the number of keystrokes needed to fully heat up the key.
```c
#define LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32
```
## Custom LED Matrix Effects {#custom-led-matrix-effects}
By setting `LED_MATRIX_CUSTOM_USER = yes` in `rules.mk`, new effects can be defined directly from your keymap or userspace, without having to edit any QMK core files. To declare new effects, create a `led_matrix_user.inc` file in the user keymap directory or userspace folder.
-24
View File
@@ -152,27 +152,3 @@ const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT] = {
{1, SW10_CS16, SW10_CS17, SW10_CS18} // 120, LED 20
};
#endif
#ifdef EEPROM_ENABLE
#include "spi_master.h"
void spi_init(void) {
static bool is_initialised = false;
if (!is_initialised) {
is_initialised = true;
// Try releasing special pins for a short time
gpio_set_pin_input(SPI_SCK_PIN);
gpio_set_pin_input(SPI_MOSI_PIN);
gpio_set_pin_input(SPI_MISO_PIN);
chThdSleepMilliseconds(10);
palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST | PAL_WB32_CURRENT_LEVEL3);
palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
}
}
#endif
-24
View File
@@ -153,27 +153,3 @@ const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT] = {
{1, SW10_CS16, SW10_CS17, SW10_CS18} // 120, LED 20
};
#endif
#ifdef EEPROM_ENABLE
#include "spi_master.h"
void spi_init(void) {
static bool is_initialised = false;
if (!is_initialised) {
is_initialised = true;
// Try releasing special pins for a short time
gpio_set_pin_input(SPI_SCK_PIN);
gpio_set_pin_input(SPI_MOSI_PIN);
gpio_set_pin_input(SPI_MISO_PIN);
chThdSleepMilliseconds(10);
palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST | PAL_WB32_CURRENT_LEVEL3);
palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);
}
}
#endif
@@ -16,3 +16,4 @@
#include "solid_splash_anim.h"
#include "wave_left_right_anim.h"
#include "wave_up_down_anim.h"
#include "typing_heatmap_anim.h"
@@ -0,0 +1,112 @@
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
LED_MATRIX_EFFECT(TYPING_HEATMAP)
# ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS
# ifndef LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP
# define LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32
# endif
# ifndef LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS
# define LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 25
# endif
# ifndef LED_MATRIX_TYPING_HEATMAP_SPREAD
# define LED_MATRIX_TYPING_HEATMAP_SPREAD 40
# endif
# ifndef LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT
# define LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16
# endif
void process_led_matrix_typing_heatmap(uint8_t row, uint8_t col) {
# ifdef LED_MATRIX_TYPING_HEATMAP_SLIM
// Limit effect to pressed keys
g_led_frame_buffer[row][col] = qadd8(g_led_frame_buffer[row][col], LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP);
# else
if (g_led_config.matrix_co[row][col] == NO_LED) { // skip as pressed key doesn't have an led position
return;
}
for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) {
for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) {
if (g_led_config.matrix_co[i_row][i_col] == NO_LED) { // skip as target key doesn't have an led position
continue;
}
if (i_row == row && i_col == col) {
g_led_frame_buffer[row][col] = qadd8(g_led_frame_buffer[row][col], LED_MATRIX_TYPING_HEATMAP_INCREASE_STEP);
} else {
# define LED_DISTANCE(led_a, led_b) sqrt16(((int32_t)(led_a.x - led_b.x) * (int32_t)(led_a.x - led_b.x)) + ((int32_t)(led_a.y - led_b.y) * (int32_t)(led_a.y - led_b.y)))
uint8_t distance = LED_DISTANCE(g_led_config.point[g_led_config.matrix_co[row][col]], g_led_config.point[g_led_config.matrix_co[i_row][i_col]]);
# undef LED_DISTANCE
if (distance <= LED_MATRIX_TYPING_HEATMAP_SPREAD) {
uint8_t amount = qsub8(LED_MATRIX_TYPING_HEATMAP_SPREAD, distance);
if (amount > LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT) {
amount = LED_MATRIX_TYPING_HEATMAP_AREA_LIMIT;
}
g_led_frame_buffer[i_row][i_col] = qadd8(g_led_frame_buffer[i_row][i_col], amount);
}
}
}
}
# endif
}
// A timer to track the last time we decremented all heatmap values.
static uint16_t heatmap_decrease_timer;
// Whether we should decrement the heatmap values during the next update.
static bool decrease_heatmap_values;
bool TYPING_HEATMAP(effect_params_t* params) {
LED_MATRIX_USE_LIMITS(led_min, led_max);
if (params->init) {
led_matrix_set_value_all(0);
memset(g_led_frame_buffer, 0, sizeof g_led_frame_buffer);
}
// The heatmap animation might run in several iterations depending on
// `LED_MATRIX_LED_PROCESS_LIMIT`, therefore we only want to update the
// timer when the animation starts.
if (params->iter == 0) {
decrease_heatmap_values = timer_elapsed(heatmap_decrease_timer) >= LED_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS;
// Restart the timer if we are going to decrease the heatmap this frame.
if (decrease_heatmap_values) {
heatmap_decrease_timer = timer_read();
}
}
// Render heatmap & decrease
uint8_t count = 0;
for (uint8_t row = 0; row < MATRIX_ROWS && count < LED_MATRIX_LED_PROCESS_LIMIT; row++) {
for (uint8_t col = 0; col < MATRIX_COLS && LED_MATRIX_LED_PROCESS_LIMIT; col++) {
uint8_t val = g_led_frame_buffer[row][col];
bool processed = false;
uint8_t led[LED_HITS_TO_REMEMBER];
uint8_t led_count = led_matrix_map_row_column_to_led(row, col, led);
for (uint8_t index = 0; index < led_count; index++) {
uint8_t led_index = led[index];
if (led_index >= led_min && led_index < led_max) {
count++;
if (!HAS_ANY_FLAGS(g_led_config.flags[led_index], params->flags)) {
continue;
}
uint8_t value = scale8(led_matrix_eeconfig.val, val);
led_matrix_set_value(led_index, value);
processed = true;
}
}
if (processed && decrease_heatmap_values) {
g_led_frame_buffer[row][col] = qsub8(val, 1);
}
}
}
return led_matrix_check_finished_leds(led_max);
}
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
+5
View File
@@ -5,6 +5,11 @@
// clang-format off
// framebuffer
#if defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
# define LED_MATRIX_FRAMEBUFFER_EFFECTS
#endif
// reactive
#if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE) || \
defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || \
@@ -37,18 +37,22 @@ bool DIGITAL_RAIN(effect_params_t* params) {
g_rgb_frame_buffer[row][col]--;
}
}
// set the pixel colour
uint8_t led[LED_HITS_TO_REMEMBER];
uint8_t led_count = rgb_matrix_map_row_column_to_led(row, col, led);
// TODO: multiple leds are supported mapped to the same row/column
if (led_count > 0) {
if (g_rgb_frame_buffer[row][col] > pure_green_intensity) {
const uint8_t boost = (uint8_t)((uint16_t)max_brightness_boost * (g_rgb_frame_buffer[row][col] - pure_green_intensity) / (max_intensity - pure_green_intensity));
rgb_matrix_set_color(led[0], boost, max_intensity, boost);
for (uint8_t index = 0; index < led_count; index++) {
rgb_matrix_set_color(led[index], boost, max_intensity, boost);
}
} else {
const uint8_t green = (uint8_t)((uint16_t)max_intensity * g_rgb_frame_buffer[row][col] / pure_green_intensity);
rgb_matrix_set_color(led[0], 0, green, 0);
for (uint8_t index = 0; index < led_count; index++) {
rgb_matrix_set_color(led[index], 0, green, 0);
}
}
}
}
@@ -77,19 +77,32 @@ bool TYPING_HEATMAP(effect_params_t* params) {
uint8_t count = 0;
for (uint8_t row = 0; row < MATRIX_ROWS && count < RGB_MATRIX_LED_PROCESS_LIMIT; row++) {
for (uint8_t col = 0; col < MATRIX_COLS && RGB_MATRIX_LED_PROCESS_LIMIT; col++) {
if (g_led_config.matrix_co[row][col] >= led_min && g_led_config.matrix_co[row][col] < led_max) {
count++;
uint8_t val = g_rgb_frame_buffer[row][col];
if (!HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[row][col]], params->flags)) continue;
uint8_t val = g_rgb_frame_buffer[row][col];
hsv_t hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)};
rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
rgb_matrix_set_color(g_led_config.matrix_co[row][col], rgb.r, rgb.g, rgb.b);
bool processed = false;
if (decrease_heatmap_values) {
g_rgb_frame_buffer[row][col] = qsub8(val, 1);
uint8_t led[LED_HITS_TO_REMEMBER];
uint8_t led_count = rgb_matrix_map_row_column_to_led(row, col, led);
for (uint8_t index = 0; index < led_count; index++) {
uint8_t led_index = led[index];
if (led_index >= led_min && led_index < led_max) {
count++;
if (!HAS_ANY_FLAGS(g_led_config.flags[led_index], params->flags)) {
continue;
}
hsv_t hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)};
rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
rgb_matrix_set_color(led_index, rgb.r, rgb.g, rgb.b);
processed = true;
}
}
if (processed && decrease_heatmap_values) {
g_rgb_frame_buffer[row][col] = qsub8(val, 1);
}
}
}