Merge pull request #2 from drashna/ez_update_rgb

Update Ergodox EZ Repo with RGB Overhaul (Proper)
pull/5613/head
Florian Didron 6 years ago committed by GitHub
commit ea4581cef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -26,4 +26,4 @@ VOLUME /qmk_firmware
WORKDIR /qmk_firmware
COPY . .
CMD make $KEYBOARD:$KEYMAP
CMD make clean ; make git-submodule ; make $KEYBOARD:$KEYMAP

@ -114,7 +114,7 @@ ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
endif
endif
VALID_MATRIX_TYPES := yes IS31FL3731 IS31FL3733 custom
VALID_MATRIX_TYPES := yes IS31FL3731 IS31FL3733 IS31FL3737 custom
LED_MATRIX_ENABLE ?= no
ifneq ($(strip $(LED_MATRIX_ENABLE)), no)
@ -135,6 +135,7 @@ ifeq ($(strip $(LED_MATRIX_ENABLE)), IS31FL3731)
endif
RGB_MATRIX_ENABLE ?= no
ifneq ($(strip $(RGB_MATRIX_ENABLE)), no)
ifeq ($(filter $(RGB_MATRIX_ENABLE),$(VALID_MATRIX_TYPES)),)
$(error RGB_MATRIX_ENABLE="$(RGB_MATRIX_ENABLE)" is not a valid matrix type)
@ -151,19 +152,26 @@ ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
endif
ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3731)
OPT_DEFS += -DIS31FL3731
OPT_DEFS += -DIS31FL3731 -DSTM32_I2C -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/issi
SRC += is31fl3731.c
SRC += i2c_master.c
endif
ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3733)
OPT_DEFS += -DIS31FL3733
OPT_DEFS += -DIS31FL3733 -DSTM32_I2C -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/issi
SRC += is31fl3733.c
SRC += i2c_master.c
endif
ifeq ($(strip $(RGB_MATRIX_ENABLE)), IS31FL3737)
OPT_DEFS += -DIS31FL3737 -DSTM32_I2C -DHAL_USE_I2C=TRUE
COMMON_VPATH += $(DRIVER_PATH)/issi
SRC += is31fl3737.c
SRC += i2c_master.c
endif
ifeq ($(strip $(TAP_DANCE_ENABLE)), yes)
OPT_DEFS += -DTAP_DANCE_ENABLE
SRC += $(QUANTUM_DIR)/process_keycode/process_tap_dance.c

@ -0,0 +1,252 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef __AVR__
#include <avr/interrupt.h>
#include <avr/io.h>
#include <util/delay.h>
#else
#include "wait.h"
#endif
#include <string.h>
#include "i2c_master.h"
#include "progmem.h"
#include "rgb_matrix.h"
// This is a 7-bit address, that gets left-shifted and bit 0
// set to 0 for write, 1 for read (as per I2C protocol)
// The address will vary depending on your wiring:
// 00 <-> GND
// 01 <-> SCL
// 10 <-> SDA
// 11 <-> VCC
// ADDR1 represents A1:A0 of the 7-bit address.
// ADDR2 represents A3:A2 of the 7-bit address.
// The result is: 0b101(ADDR2)(ADDR1)
#define ISSI_ADDR_DEFAULT 0x50
#define ISSI_COMMANDREGISTER 0xFD
#define ISSI_COMMANDREGISTER_WRITELOCK 0xFE
#define ISSI_INTERRUPTMASKREGISTER 0xF0
#define ISSI_INTERRUPTSTATUSREGISTER 0xF1
#define ISSI_PAGE_LEDCONTROL 0x00 //PG0
#define ISSI_PAGE_PWM 0x01 //PG1
#define ISSI_PAGE_AUTOBREATH 0x02 //PG2
#define ISSI_PAGE_FUNCTION 0x03 //PG3
#define ISSI_REG_CONFIGURATION 0x00 //PG3
#define ISSI_REG_GLOBALCURRENT 0x01 //PG3
#define ISSI_REG_RESET 0x11// PG3
#define ISSI_REG_SWPULLUP 0x0F //PG3
#define ISSI_REG_CSPULLUP 0x10 //PG3
#ifndef ISSI_TIMEOUT
#define ISSI_TIMEOUT 100
#endif
#ifndef ISSI_PERSISTENCE
#define ISSI_PERSISTENCE 0
#endif
// Transfer buffer for TWITransmitData()
uint8_t g_twi_transfer_buffer[20];
// These buffers match the IS31FL3737 PWM registers.
// The control buffers match the PG0 LED On/Off registers.
// Storing them like this is optimal for I2C transfers to the registers.
// We could optimize this and take out the unused registers from these
// buffers and the transfers in IS31FL3737_write_pwm_buffer() but it's
// probably not worth the extra complexity.
uint8_t g_pwm_buffer[DRIVER_COUNT][192];
bool g_pwm_buffer_update_required = false;
uint8_t g_led_control_registers[DRIVER_COUNT][24] = { { 0 } };
bool g_led_control_registers_update_required = false;
void IS31FL3737_write_register( uint8_t addr, uint8_t reg, uint8_t data )
{
g_twi_transfer_buffer[0] = reg;
g_twi_transfer_buffer[1] = data;
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT) == 0)
break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 2, ISSI_TIMEOUT);
#endif
}
void IS31FL3737_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer )
{
// assumes PG1 is already selected
// transmit PWM registers in 12 transfers of 16 bytes
// g_twi_transfer_buffer[] is 20 bytes
// iterate over the pwm_buffer contents at 16 byte intervals
for ( int i = 0; i < 192; i += 16 ) {
g_twi_transfer_buffer[0] = i;
// copy the data from i to i+15
// device will auto-increment register for data after the first byte
// thus this sets registers 0x00-0x0F, 0x10-0x1F, etc. in one transfer
for ( int j = 0; j < 16; j++ ) {
g_twi_transfer_buffer[1 + j] = pwm_buffer[i + j];
}
#if ISSI_PERSISTENCE > 0
for (uint8_t i = 0; i < ISSI_PERSISTENCE; i++) {
if (i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT) == 0)
break;
}
#else
i2c_transmit(addr << 1, g_twi_transfer_buffer, 17, ISSI_TIMEOUT);
#endif
}
}
void IS31FL3737_init( uint8_t addr )
{
// In order to avoid the LEDs being driven with garbage data
// in the LED driver's PWM registers, shutdown is enabled last.
// Set up the mode and other settings, clear the PWM registers,
// then disable software shutdown.
// Unlock the command register.
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 );
// Select PG0
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL );
// Turn off all LEDs.
for ( int i = 0x00; i <= 0x17; i++ )
{
IS31FL3737_write_register( addr, i, 0x00 );
}
// Unlock the command register.
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 );
// Select PG1
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM );
// Set PWM on all LEDs to 0
// No need to setup Breath registers to PWM as that is the default.
for ( int i = 0x00; i <= 0xBF; i++ )
{
IS31FL3737_write_register( addr, i, 0x00 );
}
// Unlock the command register.
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 );
// Select PG3
IS31FL3737_write_register( addr, ISSI_COMMANDREGISTER, ISSI_PAGE_FUNCTION );
// Set global current to maximum.
IS31FL3737_write_register( addr, ISSI_REG_GLOBALCURRENT, 0xFF );
// Disable software shutdown.
IS31FL3737_write_register( addr, ISSI_REG_CONFIGURATION, 0x01 );
// Wait 10ms to ensure the device has woken up.
#ifdef __AVR__
_delay_ms( 10 );
#else
wait_ms(10);
#endif
}
void IS31FL3737_set_color( int index, uint8_t red, uint8_t green, uint8_t blue )
{
if ( index >= 0 && index < DRIVER_LED_TOTAL ) {
is31_led led = g_is31_leds[index];
g_pwm_buffer[led.driver][led.r] = red;
g_pwm_buffer[led.driver][led.g] = green;
g_pwm_buffer[led.driver][led.b] = blue;
g_pwm_buffer_update_required = true;
}
}
void IS31FL3737_set_color_all( uint8_t red, uint8_t green, uint8_t blue )
{
for ( int i = 0; i < DRIVER_LED_TOTAL; i++ )
{
IS31FL3737_set_color( i, red, green, blue );
}
}
void IS31FL3737_set_led_control_register( uint8_t index, bool red, bool green, bool blue )
{
is31_led led = g_is31_leds[index];
uint8_t control_register_r = led.r / 8;
uint8_t control_register_g = led.g / 8;
uint8_t control_register_b = led.b / 8;
uint8_t bit_r = led.r % 8;
uint8_t bit_g = led.g % 8;
uint8_t bit_b = led.b % 8;
if ( red ) {
g_led_control_registers[led.driver][control_register_r] |= (1 << bit_r);
} else {
g_led_control_registers[led.driver][control_register_r] &= ~(1 << bit_r);
}
if ( green ) {
g_led_control_registers[led.driver][control_register_g] |= (1 << bit_g);
} else {
g_led_control_registers[led.driver][control_register_g] &= ~(1 << bit_g);
}
if ( blue ) {
g_led_control_registers[led.driver][control_register_b] |= (1 << bit_b);
} else {
g_led_control_registers[led.driver][control_register_b] &= ~(1 << bit_b);
}
g_led_control_registers_update_required = true;
}
void IS31FL3737_update_pwm_buffers( uint8_t addr1, uint8_t addr2 )
{
if ( g_pwm_buffer_update_required )
{
// Firstly we need to unlock the command register and select PG1
IS31FL3737_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 );
IS31FL3737_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_PWM );
IS31FL3737_write_pwm_buffer( addr1, g_pwm_buffer[0] );
//IS31FL3737_write_pwm_buffer( addr2, g_pwm_buffer[1] );
}
g_pwm_buffer_update_required = false;
}
void IS31FL3737_update_led_control_registers( uint8_t addr1, uint8_t addr2 )
{
if ( g_led_control_registers_update_required )
{
// Firstly we need to unlock the command register and select PG0
IS31FL3737_write_register( addr1, ISSI_COMMANDREGISTER_WRITELOCK, 0xC5 );
IS31FL3737_write_register( addr1, ISSI_COMMANDREGISTER, ISSI_PAGE_LEDCONTROL );
for ( int i=0; i<24; i++ )
{
IS31FL3737_write_register(addr1, i, g_led_control_registers[0][i] );
//IS31FL3737_write_register(addr2, i, g_led_control_registers[1][i] );
}
}
}

@ -0,0 +1,207 @@
/* Copyright 2017 Jason Williams
* Copyright 2018 Jack Humbert
* Copyright 2018 Yiancar
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef IS31FL3737_DRIVER_H
#define IS31FL3737_DRIVER_H
#include <stdint.h>
#include <stdbool.h>
typedef struct is31_led {
uint8_t driver:2;
uint8_t r;
uint8_t g;
uint8_t b;
} __attribute__((packed)) is31_led;
extern const is31_led g_is31_leds[DRIVER_LED_TOTAL];
void IS31FL3737_init( uint8_t addr );
void IS31FL3737_write_register( uint8_t addr, uint8_t reg, uint8_t data );
void IS31FL3737_write_pwm_buffer( uint8_t addr, uint8_t *pwm_buffer );
void IS31FL3737_set_color( int index, uint8_t red, uint8_t green, uint8_t blue );
void IS31FL3737_set_color_all( uint8_t red, uint8_t green, uint8_t blue );
void IS31FL3737_set_led_control_register( uint8_t index, bool red, bool green, bool blue );
// This should not be called from an interrupt
// (eg. from a timer interrupt).
// Call this while idle (in between matrix scans).
// If the buffer is dirty, it will update the driver with the buffer.
void IS31FL3737_update_pwm_buffers( uint8_t addr1, uint8_t addr2 );
void IS31FL3737_update_led_control_registers( uint8_t addr1, uint8_t addr2 );
#define A_1 0x00
#define A_2 0x01
#define A_3 0x02
#define A_4 0x03
#define A_5 0x04
#define A_6 0x05
#define A_7 0x08
#define A_8 0x09
#define A_9 0x0A
#define A_10 0x0B
#define A_11 0x0C
#define A_12 0x0D
#define B_1 0x10
#define B_2 0x11
#define B_3 0x12
#define B_4 0x13
#define B_5 0x14
#define B_6 0x15
#define B_7 0x18
#define B_8 0x19
#define B_9 0x1A
#define B_10 0x1B
#define B_11 0x1C
#define B_12 0x1D
#define C_1 0x20
#define C_2 0x21
#define C_3 0x22
#define C_4 0x23
#define C_5 0x24
#define C_6 0x25
#define C_7 0x28
#define C_8 0x29
#define C_9 0x2A
#define C_10 0x2B
#define C_11 0x2C
#define C_12 0x2D
#define D_1 0x30
#define D_2 0x31
#define D_3 0x32
#define D_4 0x33
#define D_5 0x34
#define D_6 0x35
#define D_7 0x38
#define D_8 0x39
#define D_9 0x3A
#define D_10 0x3B
#define D_11 0x3C
#define D_12 0x3D
#define E_1 0x40
#define E_2 0x41
#define E_3 0x42
#define E_4 0x43
#define E_5 0x44
#define E_6 0x45
#define E_7 0x48
#define E_8 0x49
#define E_9 0x4A
#define E_10 0x4B
#define E_11 0x4C
#define E_12 0x4D
#define F_1 0x50
#define F_2 0x51
#define F_3 0x52
#define F_4 0x53
#define F_5 0x54
#define F_6 0x55
#define F_7 0x58
#define F_8 0x59
#define F_9 0x5A
#define F_10 0x5B
#define F_11 0x5C
#define F_12 0x5D
#define G_1 0x60
#define G_2 0x61
#define G_3 0x62
#define G_4 0x63
#define G_5 0x64
#define G_6 0x65
#define G_7 0x68
#define G_8 0x69
#define G_9 0x6A
#define G_10 0x6B
#define G_11 0x6C
#define G_12 0x6D
#define H_1 0x70
#define H_2 0x71
#define H_3 0x72
#define H_4 0x73
#define H_5 0x74
#define H_6 0x75
#define H_7 0x78
#define H_8 0x79
#define H_9 0x7A
#define H_10 0x7B
#define H_11 0x7C
#define H_12 0x7D
#define I_1 0x80
#define I_2 0x81
#define I_3 0x82
#define I_4 0x83
#define I_5 0x84
#define I_6 0x85
#define I_7 0x88
#define I_8 0x89
#define I_9 0x8A
#define I_10 0x8B
#define I_11 0x8C
#define I_12 0x8D
#define J_1 0x90
#define J_2 0x91
#define J_3 0x92
#define J_4 0x93
#define J_5 0x94
#define J_6 0x95
#define J_7 0x98
#define J_8 0x99
#define J_9 0x9A
#define J_10 0x9B
#define J_11 0x9C
#define J_12 0x9D
#define K_1 0xA0
#define K_2 0xA1
#define K_3 0xA2
#define K_4 0xA3
#define K_5 0xA4
#define K_6 0xA5
#define K_7 0xA8
#define K_8 0xA9
#define K_9 0xAA
#define K_10 0xAB
#define K_11 0xAC
#define K_12 0xAD
#define L_1 0xB0
#define L_2 0xB1
#define L_3 0xB2
#define L_4 0xB3
#define L_5 0xB4
#define L_6 0xB5
#define L_7 0xB8
#define L_8 0xB9
#define L_9 0xBA
#define L_10 0xBB
#define L_11 0xBC
#define L_12 0xBD
#endif // IS31FL3737_DRIVER_H

@ -109,7 +109,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DRIVER_1_LED_TOTAL 24
#define DRIVER_2_LED_TOTAL 24
#define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL
#define RGB_MATRIX_SKIP_FRAMES 10
// #define RGBLIGHT_COLOR_LAYER_0 0x00, 0x00, 0xFF
/* #define RGBLIGHT_COLOR_LAYER_1 0x00, 0x00, 0xFF */

@ -33,14 +33,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debug.h"
#include "util.h"
#include "matrix.h"
#include "debounce.h"
#include QMK_KEYBOARD_H
#ifdef DEBUG_MATRIX_SCAN_RATE
# include "timer.h"
#endif
/*
* This constant define not debouncing time in msecs, but amount of matrix
* scan loops which should be made to get stable debounced results.
* This constant define not debouncing time in msecs, assuming eager_pr.
*
* On Ergodox matrix scan rate is relatively low, because of slow I2C.
* Now it's only 317 scans/second, or about 3.15 msec/scan.
@ -56,17 +56,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
/* matrix state(1:on, 0:off) */
static matrix_row_t matrix[MATRIX_ROWS];
/*
* matrix state(1:on, 0:off)
* contains the raw values without debounce filtering of the last read cycle.
*/
static matrix_row_t raw_matrix[MATRIX_ROWS];
// Debouncing: store for each key the number of scans until it's eligible to
// change. When scanning the matrix, ignore any changes in keys that have
// already changed in the last DEBOUNCE scans.
static uint8_t debounce_matrix[MATRIX_ROWS * MATRIX_COLS];
static matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
static matrix_row_t matrix[MATRIX_ROWS]; // debounced values
static matrix_row_t read_cols(uint8_t row);
static void init_cols(void);
@ -81,42 +72,23 @@ uint32_t matrix_timer;
uint32_t matrix_scan_count;
#endif
__attribute__((weak)) void matrix_init_user(void) {}
__attribute__ ((weak))
void matrix_init_user(void) {}
__attribute__((weak)) void matrix_scan_user(void) {}
__attribute__ ((weak))
void matrix_scan_user(void) {}
__attribute__((weak)) void matrix_init_kb(void) { matrix_init_user(); }
__attribute__ ((weak))
void matrix_init_kb(void) {
matrix_init_user();
}
__attribute__((weak)) void matrix_scan_kb(void) { matrix_scan_user(); }
__attribute__ ((weak))
void matrix_scan_kb(void) {
matrix_scan_user();
}
inline uint8_t matrix_rows(void) { return MATRIX_ROWS; }
inline
uint8_t matrix_rows(void)
{
return MATRIX_ROWS;
}
inline uint8_t matrix_cols(void) { return MATRIX_COLS; }
inline
uint8_t matrix_cols(void)
{
return MATRIX_COLS;
}
void matrix_init(void)
{
void matrix_init(void) {
// initialize row and col
mcp23018_status = init_mcp23018();
unselect_rows();
init_cols();
@ -124,18 +96,14 @@ void matrix_init(void)
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
matrix[i] = 0;
raw_matrix[i] = 0;
for (uint8_t j=0; j < MATRIX_COLS; ++j) {
debounce_matrix[i * MATRIX_COLS + j] = 0;
}
}
#ifdef DEBUG_MATRIX_SCAN_RATE
matrix_timer = timer_read32();
matrix_scan_count = 0;
#endif
debounce_init(MATRIX_ROWS);
matrix_init_quantum();
}
void matrix_power_up(void) {
@ -155,36 +123,18 @@ void matrix_power_up(void) {
#endif
}
// Returns a matrix_row_t whose bits are set if the corresponding key should be
// eligible to change in this scan.
matrix_row_t debounce_mask(matrix_row_t rawcols, uint8_t row) {
matrix_row_t result = 0;
matrix_row_t change = rawcols ^ raw_matrix[row];
raw_matrix[row] = rawcols;
for (uint8_t i = 0; i < MATRIX_COLS; ++i) {
if (debounce_matrix[row * MATRIX_COLS + i]) {
--debounce_matrix[row * MATRIX_COLS + i];
} else {
result |= (1 << i);
}
if (change & (1 << i)) {
debounce_matrix[row * MATRIX_COLS + i] = DEBOUNCE;
}
}
return result;
// Reads and stores a row, returning
// whether a change occurred.
static inline bool store_raw_matrix_row(uint8_t index) {
matrix_row_t temp = read_cols(index);
if (raw_matrix[index] != temp) {
raw_matrix[index] = temp;
return true;
}
matrix_row_t debounce_read_cols(uint8_t row) {
// Read the row without debouncing filtering and store it for later usage.
matrix_row_t cols = read_cols(row);
// Get the Debounce mask.
matrix_row_t mask = debounce_mask(cols, row);
// debounce the row and return the result.
return (cols & mask) | (matrix[row] & ~mask);;
return false;
}
uint8_t matrix_scan(void)
{
uint8_t matrix_scan(void) {
if (mcp23018_status) { // if there was an error
if (++mcp23018_reset_loop == 0) {
// if (++mcp23018_reset_loop >= 1300) {
@ -218,21 +168,24 @@ uint8_t matrix_scan(void)
#ifdef LEFT_LEDS
mcp23018_status = ergodox_left_leds_update();
#endif // LEFT_LEDS
bool changed = false;
for (uint8_t i = 0; i < MATRIX_ROWS_PER_SIDE; i++) {
select_row(i);
// and select on left hand
select_row(i + MATRIX_ROWS_PER_SIDE);
// select rows from left and right hands
uint8_t left_index = i;
uint8_t right_index = i + MATRIX_ROWS_PER_SIDE;
select_row(left_index);
select_row(right_index);
// we don't need a 30us delay anymore, because selecting a
// left-hand row requires more than 30us for i2c.
// grab cols from left hand
matrix[i] = debounce_read_cols(i);
// grab cols from right hand
matrix[i + MATRIX_ROWS_PER_SIDE] = debounce_read_cols(i + MATRIX_ROWS_PER_SIDE);
changed |= store_raw_matrix_row(left_index);
changed |= store_raw_matrix_row(right_index);
unselect_rows();
}
debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
matrix_scan_quantum();
return 1;
@ -243,30 +196,21 @@ bool matrix_is_modified(void) // deprecated and evidently not called.
return true;
}
inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
return (matrix[row] & ((matrix_row_t)1<<col));
}
inline bool matrix_is_on(uint8_t row, uint8_t col) { return (matrix[row] & ((matrix_row_t)1 << col)); }
inline
matrix_row_t matrix_get_row(uint8_t row)
{
return matrix[row];
}
inline matrix_row_t matrix_get_row(uint8_t row) { return matrix[row]; }
void matrix_print(void)
{
void matrix_print(void) {
print("\nr/c 0123456789ABCDEF\n");
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
phex(row); print(": ");
phex(row);
print(": ");
pbin_reverse16(matrix_get_row(row));
print("\n");
}
}
uint8_t matrix_key_count(void)
{
uint8_t matrix_key_count(void) {
uint8_t count = 0;
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
count += bitpop16(matrix[i]);
@ -284,8 +228,7 @@ uint8_t matrix_key_count(void)
* col: 0 1 2 3 4 5
* pin: B5 B4 B3 B2 B1 B0
*/
static void init_cols(void)
{
static void init_cols(void) {
// init on mcp23018
// not needed, already done as part of init_mcp23018()
@ -295,17 +238,20 @@ static void init_cols(void)
PORTF |= (1 << 7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 1 | 1 << 0);
}
static matrix_row_t read_cols(uint8_t row)
{
static matrix_row_t read_cols(uint8_t row) {
if (row < 7) {
if (mcp23018_status) { // if there was an error
return 0;
} else {
uint8_t data = 0;
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_write(GPIOB, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_start(I2C_ADDR_READ, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_read_nack(ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status < 0) goto out;
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_write(GPIOB, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_start(I2C_ADDR_READ, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_read_nack(ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status < 0) goto out;
data = ~((uint8_t)mcp23018_status);
mcp23018_status = I2C_STATUS_SUCCESS;
out:
@ -333,8 +279,7 @@ static matrix_row_t read_cols(uint8_t row)
* row: 0 1 2 3 4 5 6
* pin: A0 A1 A2 A3 A4 A5 A6
*/
static void unselect_rows(void)
{
static void unselect_rows(void) {
// no need to unselect on mcp23018, because the select step sets all
// the other row bits high, and it's not changing to a different
// direction
@ -349,8 +294,7 @@ static void unselect_rows(void)
PORTC &= ~(1 << 6);
}
static void select_row(uint8_t row)
{
static void select_row(uint8_t row) {
if (row < 7) {
// select on mcp23018
if (mcp23018_status) { // if there was an error
@ -358,9 +302,12 @@ static void select_row(uint8_t row)
} else {
// set active row low : 0
// set other rows hi-Z : 1
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_write(GPIOA, ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_write(0xFF & ~(1<<row), ERGODOX_EZ_I2C_TIMEOUT); if (mcp23018_status) goto out;
mcp23018_status = i2c_start(I2C_ADDR_WRITE, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_write(GPIOA, ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
mcp23018_status = i2c_write(0xFF & ~(1 << row), ERGODOX_EZ_I2C_TIMEOUT);
if (mcp23018_status) goto out;
out:
i2c_stop();
}

@ -83,6 +83,7 @@ SLEEP_LED_ENABLE = no
API_SYSEX_ENABLE = no
RGBLIGHT_ENABLE = yes
RGB_MATRIX_ENABLE = no # enable later
DEBOUNCE_TYPE = eager_pr
ifeq ($(strip $(RGB_MATRIX_ENABLE)), no)
SRC += i2c_master.c

@ -0,0 +1,141 @@
/*
* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
/* USB Device descriptor parameter */
#define DEVICE_VER 0x0000
#undef MATRIX_ROWS
#undef MATRIX_COLS
/* key matrix size */
#define MATRIX_ROWS 8
#define MATRIX_COLS 6
/*
* Keyboard Matrix Assignments
*
* Change this to how you wired your keyboard
* COLS: AVR pins used for columns, left to right
* ROWS: AVR pins used for rows, top to bottom
* DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode)
* ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
*
*/
#undef MATRIX_ROW_PINS
#undef MATRIX_COL_PINS
#define MATRIX_ROW_PINS { A10, A9, A8, B15, C13, C14, C15, A2 }
#define MATRIX_COL_PINS { B11, B10, B2, B1, A7, B0 }
#define NUMBER_OF_ENCODERS 1
#define ENCODERS_PAD_A { B12 }
#define ENCODERS_PAD_B { B13 }
#define MUSIC_MAP
#undef AUDIO_VOICES
#undef C6_AUDIO
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
#define DEBOUNCE 6
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
//#define LOCKING_SUPPORT_ENABLE
/* Locking resynchronize hack */
//#define LOCKING_RESYNC_ENABLE
/*
* Force NKRO
*
* Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved
* state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the
* makefile for this to work.)
*
* If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N)
* until the next keyboard reset.
*
* NKRO may prevent your keystrokes from being detected in the BIOS, but it is
* fully operational during normal computer usage.
*
* For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N)
* or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by
* bootmagic, NKRO mode will always be enabled until it is toggled again during a
* power-up.
*
*/
//#define FORCE_NKRO
/*
* Feature disable options
* These options are also useful to firmware size reduction.
*/
/* disable debug print */
//#define NO_DEBUG
/* disable print */
//#define NO_PRINT
/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
//#define NO_ACTION_MACRO
//#define NO_ACTION_FUNCTION
/*
* MIDI options
*/
/* Prevent use of disabled MIDI features in the keymap */
//#define MIDI_ENABLE_STRICT 1
/* enable basic MIDI features:
- MIDI notes can be sent when in Music mode is on
*/
//#define MIDI_BASIC
/* enable advanced MIDI features:
- MIDI notes can be added to the keymap
- Octave shift and transpose
- Virtual sustain, portamento, and modulation wheel
- etc.
*/
//#define MIDI_ADVANCED
/* override number of MIDI tone keycodes (each octave adds 12 keycodes and allocates 12 bytes) */
//#define MIDI_TONE_KEYCODE_OCTAVES 1
// #define WS2812_LED_N 2
// #define RGBLED_NUM WS2812_LED_N
// #define WS2812_TIM_N 2
// #define WS2812_TIM_CH 2
// #define PORT_WS2812 GPIOA
// #define PIN_WS2812 1
// #define WS2812_DMA_STREAM STM32_DMA1_STREAM2 // DMA stream for TIMx_UP (look up in reference manual under DMA Channel selection)
//#define WS2812_DMA_CHANNEL 7 // DMA channel for TIMx_UP
//#define WS2812_EXTERNAL_PULLUP
#define DRIVER_ADDR_1 0b1010000
#define DRIVER_ADDR_2 0b1010000 // this is here for compliancy reasons.
#define DRIVER_COUNT 1
#define DRIVER_1_LED_TOTAL 47
#define DRIVER_LED_TOTAL DRIVER_1_LED_TOTAL
#define RGB_MATRIX_KEYPRESSES

@ -0,0 +1,175 @@
/* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ez.h"
const is31_led g_is31_leds[DRIVER_LED_TOTAL] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
* | | G location
* | | | B location
* | | | | */
{0, A_12, B_12, C_12},
{0, A_11, B_11, C_11},
{0, A_10, B_10, C_10},
{0, A_9, B_9, C_9},
{0, A_8, B_8, C_8},
{0, A_7, B_7, C_7},
{0, G_12, H_12, I_12},
{0, G_11, H_11, I_11},
{0, G_10, H_10, I_10},
{0, G_9, H_9, I_9},
{0, G_8, H_8, I_8},
{0, G_7, H_7, I_7},
{0, A_6, B_6, C_6},
{0, A_5, B_5, C_5},
{0, A_4, B_4, C_4},
{0, A_3, B_3, C_3},
{0, A_2, B_2, C_2},
{0, A_1, B_1, C_1},
{0, G_6, H_6, I_6},
{0, G_5, H_5, I_5},
{0, G_4, H_4, I_4},
{0, G_3, H_3, I_3},
{0, G_2, H_2, I_2},
{0, G_1, H_1, I_1},
{0, D_12, E_12, F_12},
{0, D_11, E_11, F_11},
{0, D_10, E_10, F_10},
{0, D_9, E_9, F_9},
{0, D_8, E_8, F_8},
{0, D_7, E_7, F_7},
{0, J_12, K_12, L_12},
{0, J_11, K_11, L_11},
{0, J_10, K_10, L_10},
{0, J_9, K_9, L_9},
{0, J_8, K_8, L_8},
{0, J_7, K_7, L_7},
{0, D_6, E_6, F_6},
{0, D_5, E_5, F_5},
{0, D_4, E_4, F_4},
{0, D_3, E_3, F_3},
{0, D_2, E_2, F_2},
{0, D_1, E_1, F_1},
{0, J_6, K_6, L_6},
{0, J_5, K_5, L_5},
{0, J_4, K_4, L_4},
{0, J_3, K_3, L_3},
{0, J_2, K_2, L_2},
};
const rgb_led g_rgb_leds[DRIVER_LED_TOTAL] = {
/*{row | col << 4}
| {x=0..224, y=0..64}
| | modifier
| | | */
{{0|(0<<4)}, {20.36*0, 21.33*0}, 1},
{{0|(1<<4)}, {20.36*1, 21.33*0}, 0},
{{0|(2<<4)}, {20.36*2, 21.33*0}, 0},
{{0|(3<<4)}, {20.36*3, 21.33*0}, 0},
{{0|(4<<4)}, {20.36*4, 21.33*0}, 0},
{{0|(5<<4)}, {20.36*5, 21.33*0}, 0},
{{4|(0<<4)}, {20.36*6, 21.33*0}, 0},
{{4|(1<<4)}, {20.36*7, 21.33*0}, 0},
{{4|(2<<4)}, {20.36*8, 21.33*0}, 0},
{{4|(3<<4)}, {20.36*9, 21.33*0}, 0},
{{4|(4<<4)}, {20.36*10,21.33*0}, 0},
{{4|(5<<4)}, {20.36*11,21.33*0}, 1},
{{1|(0<<4)}, {20.36*0, 21.33*1}, 1},
{{1|(1<<4)}, {20.36*1, 21.33*1}, 0},
{{1|(2<<4)}, {20.36*2, 21.33*1}, 0},
{{1|(3<<4)}, {20.36*3, 21.33*1}, 0},
{{1|(4<<4)}, {20.36*4, 21.33*1}, 0},
{{1|(5<<4)}, {20.36*5, 21.33*1}, 0},
{{5|(0<<4)}, {20.36*6, 21.33*1}, 0},
{{5|(1<<4)}, {20.36*7, 21.33*1}, 0},
{{5|(2<<4)}, {20.36*8, 21.33*1}, 0},
{{5|(3<<4)}, {20.36*9, 21.33*1}, 0},
{{5|(4<<4)}, {20.36*10,21.33*1}, 0},
{{5|(5<<4)}, {20.36*11,21.33*1}, 1},
{{2|(0<<4)}, {20.36*0, 21.33*2}, 1},
{{2|(1<<4)}, {20.36*1, 21.33*2}, 0},
{{2|(2<<4)}, {20.36*2, 21.33*2}, 0},
{{2|(3<<4)}, {20.36*3, 21.33*2}, 0},
{{2|(4<<4)}, {20.36*4, 21.33*2}, 0},
{{2|(5<<4)}, {20.36*5, 21.33*2}, 0},
{{6|(0<<4)}, {20.36*6, 21.33*2}, 0},
{{6|(1<<4)}, {20.36*7, 21.33*2}, 0},
{{6|(2<<4)}, {20.36*8, 21.33*2}, 0},
{{6|(3<<4)}, {20.36*9, 21.33*2}, 0},
{{6|(4<<4)}, {20.36*10,21.33*2}, 0},
{{6|(5<<4)}, {20.36*11,21.33*2}, 1},
{{3|(0<<4)}, {20.36*0, 21.33*3}, 1},
{{3|(1<<4)}, {20.36*1, 21.33*3}, 1},
{{3|(2<<4)}, {20.36*2, 21.33*3}, 1},
{{7|(3<<4)}, {20.36*3, 21.33*3}, 1},
{{7|(4<<4)}, {20.36*4, 21.33*3}, 1},
{{7|(5<<4)}, {20.36*5.5,21.33*3}, 0},
{{7|(0<<4)}, {20.36*7, 21.33*3}, 1},
{{7|(1<<4)}, {20.36*8, 21.33*3}, 1},
{{7|(2<<4)}, {20.36*9, 21.33*3}, 1},
{{3|(3<<4)}, {20.36*10,21.33*3}, 1},
{{3|(4<<4)}, {20.36*11,21.33*3}, 1}
};
void matrix_init_kb(void) {
matrix_init_user();
palSetPadMode(GPIOB, 8, PAL_MODE_OUTPUT_PUSHPULL);
palSetPadMode(GPIOB, 9, PAL_MODE_OUTPUT_PUSHPULL);
palClearPad(GPIOB, 8);
palClearPad(GPIOB, 9);
}
void matrix_scan_kb(void) {
matrix_scan_user();
}
uint32_t layer_state_set_kb(uint32_t state) {
palClearPad(GPIOB, 8);
palClearPad(GPIOB, 9);
state = layer_state_set_user(state);
uint8_t layer = biton32(state);
switch (layer) {
case 3:
palSetPad(GPIOB, 9);
break;
case 4:
palSetPad(GPIOB, 8);
break;
case 6:
palSetPad(GPIOB, 9);
palSetPad(GPIOB, 8);
break;
default:
break;
}
return state;
}

@ -0,0 +1,107 @@
/* Copyright 2018 Jack Humbert <jack.humb@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "planck.h"
#define LAYOUT_planck_1x2uC( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3a \
) \
{ \
{ k00, k01, k02, k03, k04, k05 }, \
{ k10, k11, k12, k13, k14, k15 }, \
{ k20, k21, k22, k23, k24, k25 }, \
{ k30, k31, k32, k39, k3a, k3b }, \
{ k06, k07, k08, k09, k0a, k0b }, \
{ k16, k17, k18, k19, k1a, k1b }, \
{ k26, k27, k28, k29, k2a, k2b }, \
{ k36, k37, k38, k33, k34, k35 } \
}
#define LAYOUT_planck_1x2uR( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3a \
) \
{ \
{ k00, k01, k02, k03, k04, k05 }, \
{ k10, k11, k12, k13, k14, k15 }, \
{ k20, k21, k22, k23, k24, k25 }, \
{ k30, k31, k32, k39, k3a, k3b }, \
{ k06, k07, k08, k09, k0a, k0b }, \
{ k16, k17, k18, k19, k1a, k1b }, \
{ k26, k27, k28, k29, k2a, k2b }, \
{ k36, k37, k38, k33, k34, k35 } \
}
#define LAYOUT_planck_1x2uL( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k3a \
) \
{ \
{ k00, k01, k02, k03, k04, k05 }, \
{ k10, k11, k12, k13, k14, k15 }, \
{ k20, k21, k22, k23, k24, k25 }, \
{ k30, k31, k32, k39, k3a, k3b }, \
{ k06, k07, k08, k09, k0a, k0b }, \
{ k16, k17, k18, k19, k1a, k1b }, \
{ k26, k27, k28, k29, k2a, k2b }, \
{ k36, k37, k38, k33, k34, k35 } \
}
#define LAYOUT_planck_2x2u( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
k30, k31, k32, k33, k34, k36, k37, k38, k39, k3a \
) \
{ \
{ k00, k01, k02, k03, k04, k05 }, \
{ k10, k11, k12, k13, k14, k15 }, \
{ k20, k21, k22, k23, k24, k25 }, \
{ k30, k31, k32, k39, k3a, k3b }, \
{ k06, k07, k08, k09, k0a, k0b }, \
{ k16, k17, k18, k19, k1a, k1b }, \
{ k26, k27, k28, k29, k2a, k2b }, \
{ k36, k37, k38, k33, k34, k35 } \
}
#define LAYOUT_planck_grid( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k1a, k1b, \
k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k2a, k2b, \
k30, k31, k32, k33, k34, k35, KC_NO, k36, k37, k38, k39, k3a \
) \
{ \
{ k00, k01, k02, k03, k04, k05 }, \
{ k10, k11, k12, k13, k14, k15 }, \
{ k20, k21, k22, k23, k24, k25 }, \
{ k30, k31, k32, k39, k3a, KC_NO }, \
{ k06, k07, k08, k09, k0a, k0b }, \
{ k16, k17, k18, k19, k1a, k1b }, \
{ k26, k27, k28, k29, k2a, k2b }, \
{ k36, k37, k38, k33, k34, k35 } \
}
#define KEYMAP LAYOUT_planck_grid
#define LAYOUT_ortho_4x12 LAYOUT_planck_grid
#define KC_LAYOUT_ortho_4x12 KC_KEYMAP

@ -0,0 +1,24 @@
# project specific files
LAYOUTS += ortho_4x12
# Cortex version
MCU = STM32F303
# Build Options
# comment out to disable the options.
#
BACKLIGHT_ENABLE = no
BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration
## (Note that for BOOTMAGIC on Teensy LC you have to use a custom .ld script.)
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = yes # Console for debug
COMMAND_ENABLE = yes # Commands for debug and configuration
#SLEEP_LED_ENABLE = yes # Breathing sleep LED during USB suspend
NKRO_ENABLE = yes # USB Nkey Rollover
CUSTOM_MATRIX = no # Custom matrix file
AUDIO_ENABLE = yes
RGBLIGHT_ENABLE = no
# SERIAL_LINK_ENABLE = yes
ENCODER_ENABLE = yes
RGB_MATRIX_ENABLE = IS31FL3737

@ -5,6 +5,10 @@
#define encoder_update(clockwise) encoder_update_user(uint8_t index, clockwise)
#ifdef KEYBOARD_planck_ez
#include "ez.h"
#endif
#ifdef __AVR__
#define LAYOUT_planck_mit( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \
@ -50,7 +54,7 @@
#define LAYOUT_ortho_4x12 LAYOUT_planck_grid
#define KC_LAYOUT_ortho_4x12 KC_KEYMAP
#else
#elif KEYBOARD_planck_rev6
#define LAYOUT_planck_1x2uC( \
k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k0a, k0b, \

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 FastLED
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.

@ -0,0 +1,242 @@
#define FASTLED_INTERNAL
#include <stdint.h>
#define RAND16_SEED 1337
uint16_t rand16seed = RAND16_SEED;
// memset8, memcpy8, memmove8:
// optimized avr replacements for the standard "C" library
// routines memset, memcpy, and memmove.
//
// There are two techniques that make these routines
// faster than the standard avr-libc routines.
// First, the loops are unrolled 2X, meaning that
// the average loop overhead is cut in half.
// And second, the compare-and-branch at the bottom
// of each loop decrements the low byte of the
// counter, and if the carry is clear, it branches
// back up immediately. Only if the low byte math
// causes carry do we bother to decrement the high
// byte and check that result for carry as well.
// Results for a 100-byte buffer are 20-40% faster
// than standard avr-libc, at a cost of a few extra
// bytes of code.
#if defined(__AVR__)
//__attribute__ ((noinline))
void * memset8 ( void * ptr, uint8_t val, uint16_t num )
{
asm volatile(
" movw r26, %[ptr] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lseteven_%= \n\t"
" rjmp Lsetodd_%= \n\t"
"Lsetloop_%=: \n\t"
" st X+, %[val] \n\t"
"Lsetodd_%=: \n\t"
" st X+, %[val] \n\t"
"Lseteven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lsetloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lsetloop_%= \n\t"
: [num] "+r" (num)
: [ptr] "r" (ptr),
[val] "r" (val)
: "memory"
);
return ptr;
}
//__attribute__ ((noinline))
void * memcpy8 ( void * dst, const void* src, uint16_t num )
{
asm volatile(
" movw r30, %[src] \n\t"
" movw r26, %[dst] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lcpyeven_%= \n\t"
" rjmp Lcpyodd_%= \n\t"
"Lcpyloop_%=: \n\t"
" ld __tmp_reg__, Z+ \n\t"
" st X+, __tmp_reg__ \n\t"
"Lcpyodd_%=: \n\t"
" ld __tmp_reg__, Z+ \n\t"
" st X+, __tmp_reg__ \n\t"
"Lcpyeven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lcpyloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lcpyloop_%= \n\t"
: [num] "+r" (num)
: [src] "r" (src),
[dst] "r" (dst)
: "memory"
);
return dst;
}
//__attribute__ ((noinline))
void * memmove8 ( void * dst, const void* src, uint16_t num )
{
if( src > dst) {
// if src > dst then we can use the forward-stepping memcpy8
return memcpy8( dst, src, num);
} else {
// if src < dst then we have to step backward:
dst = (char*)dst + num;
src = (char*)src + num;
asm volatile(
" movw r30, %[src] \n\t"
" movw r26, %[dst] \n\t"
" sbrs %A[num], 0 \n\t"
" rjmp Lmoveven_%= \n\t"
" rjmp Lmovodd_%= \n\t"
"Lmovloop_%=: \n\t"
" ld __tmp_reg__, -Z \n\t"
" st -X, __tmp_reg__ \n\t"
"Lmovodd_%=: \n\t"
" ld __tmp_reg__, -Z \n\t"
" st -X, __tmp_reg__ \n\t"
"Lmoveven_%=: \n\t"
" subi %A[num], 2 \n\t"
" brcc Lmovloop_%= \n\t"
" sbci %B[num], 0 \n\t"
" brcc Lmovloop_%= \n\t"
: [num] "+r" (num)
: [src] "r" (src),
[dst] "r" (dst)
: "memory"
);
return dst;
}
}
#endif /* AVR */
#if 0
// TEST / VERIFICATION CODE ONLY BELOW THIS POINT
#include <Arduino.h>
#include "lib8tion.h"
void test1abs( int8_t i)
{
Serial.print("abs("); Serial.print(i); Serial.print(") = ");
int8_t j = abs8(i);
Serial.print(j); Serial.println(" ");
}
void testabs()
{
delay(5000);
for( int8_t q = -128; q != 127; q++) {
test1abs(q);
}
for(;;){};
}
void testmul8()
{
delay(5000);
byte r, c;
Serial.println("mul8:");
for( r = 0; r <= 20; r += 1) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 20; c += 1) {
byte t;
t = mul8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testscale8()
{
delay(5000);
byte r, c;
Serial.println("scale8:");
for( r = 0; r <= 240; r += 10) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 240; c += 10) {
byte t;
t = scale8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println(' ');
Serial.println("scale8_video:");
for( r = 0; r <= 100; r += 4) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 100; c += 4) {
byte t;
t = scale8_video( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testqadd8()
{
delay(5000);
byte r, c;
for( r = 0; r <= 240; r += 10) {
Serial.print(r); Serial.print(" : ");
for( c = 0; c <= 240; c += 10) {
byte t;
t = qadd8( r, c);
Serial.print(t); Serial.print(' ');
}
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
void testnscale8x3()
{
delay(5000);
byte r, g, b, sc;
for( byte z = 0; z < 10; z++) {
r = random8(); g = random8(); b = random8(); sc = random8();
Serial.print("nscale8x3_video( ");
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print(", ");
Serial.print(sc); Serial.print(") = [ ");
nscale8x3_video( r, g, b, sc);
Serial.print(r); Serial.print(", ");
Serial.print(g); Serial.print(", ");
Serial.print(b); Serial.print("]");
Serial.println(' ');
}
Serial.println("done.");
for(;;){};
}
#endif

@ -0,0 +1,934 @@
#ifndef __INC_LIB8TION_H
#define __INC_LIB8TION_H
/*
Fast, efficient 8-bit math functions specifically
designed for high-performance LED programming.
Because of the AVR(Arduino) and ARM assembly language
implementations provided, using these functions often
results in smaller and faster code than the equivalent
program using plain "C" arithmetic and logic.
Included are:
- Saturating unsigned 8-bit add and subtract.
Instead of wrapping around if an overflow occurs,
these routines just 'clamp' the output at a maxumum
of 255, or a minimum of 0. Useful for adding pixel
values. E.g., qadd8( 200, 100) = 255.
qadd8( i, j) == MIN( (i + j), 0xFF )
qsub8( i, j) == MAX( (i - j), 0 )
- Saturating signed 8-bit ("7-bit") add.
qadd7( i, j) == MIN( (i + j), 0x7F)
- Scaling (down) of unsigned 8- and 16- bit values.
Scaledown value is specified in 1/256ths.
scale8( i, sc) == (i * sc) / 256
scale16by8( i, sc) == (i * sc) / 256
Example: scaling a 0-255 value down into a
range from 0-99:
downscaled = scale8( originalnumber, 100);
A special version of scale8 is provided for scaling
LED brightness values, to make sure that they don't
accidentally scale down to total black at low
dimming levels, since that would look wrong:
scale8_video( i, sc) = ((i * sc) / 256) +? 1
Example: reducing an LED brightness by a
dimming factor:
new_bright = scale8_video( orig_bright, dimming);
- Fast 8- and 16- bit unsigned random numbers.
Significantly faster than Arduino random(), but
also somewhat less random. You can add entropy.
random8() == random from 0..255
random8( n) == random from 0..(N-1)
random8( n, m) == random from N..(M-1)
random16() == random from 0..65535
random16( n) == random from 0..(N-1)
random16( n, m) == random from N..(M-1)
random16_set_seed( k) == seed = k
random16_add_entropy( k) == seed += k
- Absolute value of a signed 8-bit value.
abs8( i) == abs( i)
- 8-bit math operations which return 8-bit values.
These are provided mostly for completeness,
not particularly for performance.
mul8( i, j) == (i * j) & 0xFF
add8( i, j) == (i + j) & 0xFF
sub8( i, j) == (i - j) & 0xFF
- Fast 16-bit approximations of sin and cos.
Input angle is a uint16_t from 0-65535.
Output is a signed int16_t from -32767 to 32767.
sin16( x) == sin( (x/32768.0) * pi) * 32767
cos16( x) == cos( (x/32768.0) * pi) * 32767
Accurate to more than 99% in all cases.
- Fast 8-bit approximations of sin and cos.
Input angle is a uint8_t from 0-255.
Output is an UNsigned uint8_t from 0 to 255.
sin8( x) == (sin( (x/128.0) * pi) * 128) + 128
cos8( x) == (cos( (x/128.0) * pi) * 128) + 128
Accurate to within about 2%.
- Fast 8-bit "easing in/out" function.
ease8InOutCubic(x) == 3(x^i) - 2(x^3)
ease8InOutApprox(x) ==
faster, rougher, approximation of cubic easing
ease8InOutQuad(x) == quadratic (vs cubic) easing
- Cubic, Quadratic, and Triangle wave functions.
Input is a uint8_t representing phase withing the wave,
similar to how sin8 takes an angle 'theta'.
Output is a uint8_t representing the amplitude of
the wave at that point.
cubicwave8( x)
quadwave8( x)
triwave8( x)
- Square root for 16-bit integers. About three times
faster and five times smaller than Arduino's built-in
generic 32-bit sqrt routine.
sqrt16( uint16_t x ) == sqrt( x)
- Dimming and brightening functions for 8-bit
light values.
dim8_video( x) == scale8_video( x, x)
dim8_raw( x) == scale8( x, x)
dim8_lin( x) == (x<128) ? ((x+1)/2) : scale8(x,x)
brighten8_video( x) == 255 - dim8_video( 255 - x)
brighten8_raw( x) == 255 - dim8_raw( 255 - x)
brighten8_lin( x) == 255 - dim8_lin( 255 - x)
The dimming functions in particular are suitable
for making LED light output appear more 'linear'.
- Linear interpolation between two values, with the
fraction between them expressed as an 8- or 16-bit
fixed point fraction (fract8 or fract16).
lerp8by8( fromU8, toU8, fract8 )
lerp16by8( fromU16, toU16, fract8 )
lerp15by8( fromS16, toS16, fract8 )
== from + (( to - from ) * fract8) / 256)
lerp16by16( fromU16, toU16, fract16 )
== from + (( to - from ) * fract16) / 65536)
map8( in, rangeStart, rangeEnd)
== map( in, 0, 255, rangeStart, rangeEnd);
- Optimized memmove, memcpy, and memset, that are
faster than standard avr-libc 1.8.
memmove8( dest, src, bytecount)
memcpy8( dest, src, bytecount)
memset8( buf, value, bytecount)
- Beat generators which return sine or sawtooth
waves in a specified number of Beats Per Minute.
Sine wave beat generators can specify a low and
high range for the output. Sawtooth wave beat
generators always range 0-255 or 0-65535.
beatsin8( BPM, low8, high8)
= (sine(beatphase) * (high8-low8)) + low8
beatsin16( BPM, low16, high16)
= (sine(beatphase) * (high16-low16)) + low16
beatsin88( BPM88, low16, high16)
= (sine(beatphase) * (high16-low16)) + low16
beat8( BPM) = 8-bit repeating sawtooth wave
beat16( BPM) = 16-bit repeating sawtooth wave
beat88( BPM88) = 16-bit repeating sawtooth wave
BPM is beats per minute in either simple form
e.g. 120, or Q8.8 fixed-point form.
BPM88 is beats per minute in ONLY Q8.8 fixed-point
form.
Lib8tion is pronounced like 'libation': lie-BAY-shun
*/
#include <stdint.h>
#define LIB8STATIC __attribute__ ((unused)) static inline
#define LIB8STATIC_ALWAYS_INLINE __attribute__ ((always_inline)) static inline
#if !defined(__AVR__)
#include <string.h>
// for memmove, memcpy, and memset if not defined here
#endif
#if defined(__arm__)
#if defined(FASTLED_TEENSY3)
// Can use Cortex M4 DSP instructions
#define QADD8_C 0
#define QADD7_C 0
#define QADD8_ARM_DSP_ASM 1
#define QADD7_ARM_DSP_ASM 1
#else
// Generic ARM
#define QADD8_C 1
#define QADD7_C 1
#endif
#define QSUB8_C 1
#define SCALE8_C 1
#define SCALE16BY8_C 1
#define SCALE16_C 1
#define ABS8_C 1
#define MUL8_C 1
#define QMUL8_C 1
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
#define AVG8_C 1
#define AVG7_C 1
#define AVG16_C 1
#define AVG15_C 1
#define BLEND8_C 1
#elif defined(__AVR__)
// AVR ATmega and friends Arduino
#define QADD8_C 0
#define QADD7_C 0
#define QSUB8_C 0
#define ABS8_C 0
#define ADD8_C 0
#define SUB8_C 0
#define AVG8_C 0
#define AVG7_C 0
#define AVG16_C 0
#define AVG15_C 0
#define QADD8_AVRASM 1
#define QADD7_AVRASM 1
#define QSUB8_AVRASM 1
#define ABS8_AVRASM 1
#define ADD8_AVRASM 1
#define SUB8_AVRASM 1
#define AVG8_AVRASM 1
#define AVG7_AVRASM 1
#define AVG16_AVRASM 1
#define AVG15_AVRASM 1
// Note: these require hardware MUL instruction
// -- sorry, ATtiny!
#if !defined(LIB8_ATTINY)
#define SCALE8_C 0
#define SCALE16BY8_C 0
#define SCALE16_C 0
#define MUL8_C 0
#define QMUL8_C 0
#define EASE8_C 0
#define BLEND8_C 0
#define SCALE8_AVRASM 1
#define SCALE16BY8_AVRASM 1
#define SCALE16_AVRASM 1
#define MUL8_AVRASM 1
#define QMUL8_AVRASM 1
#define EASE8_AVRASM 1
#define CLEANUP_R1_AVRASM 1
#define BLEND8_AVRASM 1
#else
// On ATtiny, we just use C implementations
#define SCALE8_C 1
#define SCALE16BY8_C 1
#define SCALE16_C 1
#define MUL8_C 1
#define QMUL8_C 1
#define EASE8_C 1
#define BLEND8_C 1
#define SCALE8_AVRASM 0
#define SCALE16BY8_AVRASM 0
#define SCALE16_AVRASM 0
#define MUL8_AVRASM 0
#define QMUL8_AVRASM 0
#define EASE8_AVRASM 0
#define BLEND8_AVRASM 0
#endif
#else
// unspecified architecture, so
// no ASM, everything in C
#define QADD8_C 1
#define QADD7_C 1
#define QSUB8_C 1
#define SCALE8_C 1
#define SCALE16BY8_C 1
#define SCALE16_C 1
#define ABS8_C 1
#define MUL8_C 1
#define QMUL8_C 1
#define ADD8_C 1
#define SUB8_C 1
#define EASE8_C 1
#define AVG8_C 1
#define AVG7_C 1
#define AVG16_C 1
#define AVG15_C 1
#define BLEND8_C 1
#endif
///@defgroup lib8tion Fast math functions
///A variety of functions for working with numbers.
///@{
///////////////////////////////////////////////////////////////////////
//
// typdefs for fixed-point fractional types.
//
// sfract7 should be interpreted as signed 128ths.
// fract8 should be interpreted as unsigned 256ths.
// sfract15 should be interpreted as signed 32768ths.
// fract16 should be interpreted as unsigned 65536ths.
//
// Example: if a fract8 has the value "64", that should be interpreted
// as 64/256ths, or one-quarter.
//
//
// fract8 range is 0 to 0.99609375
// in steps of 0.00390625
//
// sfract7 range is -0.9921875 to 0.9921875
// in steps of 0.0078125
//
// fract16 range is 0 to 0.99998474121
// in steps of 0.00001525878
//
// sfract15 range is -0.99996948242 to 0.99996948242
// in steps of 0.00003051757
//
/// ANSI unsigned short _Fract. range is 0 to 0.99609375
/// in steps of 0.00390625
typedef uint8_t fract8; ///< ANSI: unsigned short _Fract
/// ANSI: signed short _Fract. range is -0.9921875 to 0.9921875
/// in steps of 0.0078125
typedef int8_t sfract7; ///< ANSI: signed short _Fract
/// ANSI: unsigned _Fract. range is 0 to 0.99998474121
/// in steps of 0.00001525878
typedef uint16_t fract16; ///< ANSI: unsigned _Fract
/// ANSI: signed _Fract. range is -0.99996948242 to 0.99996948242
/// in steps of 0.00003051757
typedef int16_t sfract15; ///< ANSI: signed _Fract
// accumXY types should be interpreted as X bits of integer,
// and Y bits of fraction.
// E.g., accum88 has 8 bits of int, 8 bits of fraction
typedef uint16_t accum88; ///< ANSI: unsigned short _Accum. 8 bits int, 8 bits fraction
typedef int16_t saccum78; ///< ANSI: signed short _Accum. 7 bits int, 8 bits fraction
typedef uint32_t accum1616;///< ANSI: signed _Accum. 16 bits int, 16 bits fraction
typedef int32_t saccum1516;///< ANSI: signed _Accum. 15 bits int, 16 bits fraction
typedef uint16_t accum124; ///< no direct ANSI counterpart. 12 bits int, 4 bits fraction
typedef int32_t saccum114;///< no direct ANSI counterpart. 1 bit int, 14 bits fraction
#include "math8.h"
#include "scale8.h"
#include "random8.h"
#include "trig8.h"
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
//
// float-to-fixed and fixed-to-float conversions
//
// Note that anything involving a 'float' on AVR will be slower.
/// sfract15ToFloat: conversion from sfract15 fixed point to
/// IEEE754 32-bit float.
LIB8STATIC float sfract15ToFloat( sfract15 y)
{
return y / 32768.0;
}
/// conversion from IEEE754 float in the range (-1,1)
/// to 16-bit fixed point. Note that the extremes of
/// one and negative one are NOT representable. The
/// representable range is basically
LIB8STATIC sfract15 floatToSfract15( float f)
{
return f * 32768.0;
}
///////////////////////////////////////////////////////////////////////
//
// memmove8, memcpy8, and memset8:
// alternatives to memmove, memcpy, and memset that are
// faster on AVR than standard avr-libc 1.8
#if defined(__AVR__)
void * memmove8( void * dst, const void * src, uint16_t num );
void * memcpy8 ( void * dst, const void * src, uint16_t num ) __attribute__ ((noinline));
void * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinline)) ;
#else
// on non-AVR platforms, these names just call standard libc.
#define memmove8 memmove
#define memcpy8 memcpy
#define memset8 memset
#endif
///////////////////////////////////////////////////////////////////////
//
// linear interpolation, such as could be used for Perlin noise, etc.
//
// A note on the structure of the lerp functions:
// The cases for b>a and b<=a are handled separately for
// speed: without knowing the relative order of a and b,
// the value (a-b) might be overflow the width of a or b,
// and have to be promoted to a wider, slower type.
// To avoid that, we separate the two cases, and are able
// to do all the math in the same width as the arguments,
// which is much faster and smaller on AVR.
/// linear interpolation between two unsigned 8-bit values,
/// with 8-bit fraction
LIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac)
{
uint8_t result;
if( b > a) {
uint8_t delta = b - a;
uint8_t scaled = scale8( delta, frac);
result = a + scaled;
} else {
uint8_t delta = a - b;
uint8_t scaled = scale8( delta, frac);
result = a - scaled;
}
return result;
}
/// linear interpolation between two unsigned 16-bit values,
/// with 16-bit fraction
LIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac)
{
uint16_t result;
if( b > a ) {
uint16_t delta = b - a;
uint16_t scaled = scale16(delta, frac);
result = a + scaled;
} else {
uint16_t delta = a - b;
uint16_t scaled = scale16( delta, frac);
result = a - scaled;
}
return result;
}
/// linear interpolation between two unsigned 16-bit values,
/// with 8-bit fraction
LIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)
{
uint16_t result;
if( b > a) {
uint16_t delta = b - a;
uint16_t scaled = scale16by8( delta, frac);
result = a + scaled;
} else {
uint16_t delta = a - b;
uint16_t scaled = scale16by8( delta, frac);
result = a - scaled;
}
return result;
}
/// linear interpolation between two signed 15-bit values,
/// with 8-bit fraction
LIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)
{
int16_t result;
if( b > a) {
uint16_t delta = b - a;
uint16_t scaled = scale16by8( delta, frac);
result = a + scaled;
} else {
uint16_t delta = a - b;
uint16_t scaled = scale16by8( delta, frac);
result = a - scaled;
}
return result;
}
/// linear interpolation between two signed 15-bit values,
/// with 8-bit fraction
LIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)
{
int16_t result;
if( b > a) {
uint16_t delta = b - a;
uint16_t scaled = scale16( delta, frac);
result = a + scaled;
} else {
uint16_t delta = a - b;
uint16_t scaled = scale16( delta, frac);
result = a - scaled;
}
return result;
}
/// map8: map from one full-range 8-bit value into a narrower
/// range of 8-bit values, possibly a range of hues.
///
/// E.g. map myValue into a hue in the range blue..purple..pink..red
/// hue = map8( myValue, HUE_BLUE, HUE_RED);
///
/// Combines nicely with the waveform functions (like sin8, etc)
/// to produce continuous hue gradients back and forth:
///
/// hue = map8( sin8( myValue), HUE_BLUE, HUE_RED);
///
/// Mathematically simiar to lerp8by8, but arguments are more
/// like Arduino's "map"; this function is similar to
///
/// map( in, 0, 255, rangeStart, rangeEnd)
///
/// but faster and specifically designed for 8-bit values.
LIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)
{
uint8_t rangeWidth = rangeEnd - rangeStart;
uint8_t out = scale8( in, rangeWidth);
out += rangeStart;
return out;
}
///////////////////////////////////////////////////////////////////////
//
// easing functions; see http://easings.net
//
/// ease8InOutQuad: 8-bit quadratic ease-in / ease-out function
/// Takes around 13 cycles on AVR
#if EASE8_C == 1
LIB8STATIC uint8_t ease8InOutQuad( uint8_t i)
{
uint8_t j = i;
if( j & 0x80 ) {
j = 255 - j;
}
uint8_t jj = scale8( j, j);
uint8_t jj2 = jj << 1;
if( i & 0x80 ) {
jj2 = 255 - jj2;
}
return jj2;
}
#elif EASE8_AVRASM == 1
// This AVR asm version of ease8InOutQuad preserves one more
// low-bit of precision than the C version, and is also slightly
// smaller and faster.
LIB8STATIC uint8_t ease8InOutQuad(uint8_t val) {
uint8_t j=val;
asm volatile (
"sbrc %[val], 7 \n"
"com %[j] \n"
"mul %[j], %[j] \n"
"add r0, %[j] \n"
"ldi %[j], 0 \n"
"adc %[j], r1 \n"
"lsl r0 \n" // carry = high bit of low byte of mul product
"rol %[j] \n" // j = (j * 2) + carry // preserve add'l bit of precision
"sbrc %[val], 7 \n"
"com %[j] \n"
"clr __zero_reg__ \n"
: [j] "+&a" (j)
: [val] "a" (val)
: "r0", "r1"
);
return j;
}
#else
#error "No implementation for ease8InOutQuad available."
#endif
/// ease16InOutQuad: 16-bit quadratic ease-in / ease-out function
// C implementation at this point
LIB8STATIC uint16_t ease16InOutQuad( uint16_t i)
{
uint16_t j = i;
if( j & 0x8000 ) {
j = 65535 - j;
}
uint16_t jj = scale16( j, j);
uint16_t jj2 = jj << 1;
if( i & 0x8000 ) {
jj2 = 65535 - jj2;
}
return jj2;
}
/// ease8InOutCubic: 8-bit cubic ease-in / ease-out function
/// Takes around 18 cycles on AVR
LIB8STATIC fract8 ease8InOutCubic( fract8 i)
{
uint8_t ii = scale8_LEAVING_R1_DIRTY( i, i);
uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i);
uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii));
/* the code generated for the above *'s automatically
cleans up R1, so there's no need to explicitily call
cleanup_R1(); */
uint8_t result = r1;
// if we got "256", return 255:
if( r1 & 0x100 ) {
result = 255;
}
return result;
}
/// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function
/// shaped approximately like 'ease8InOutCubic',
/// it's never off by more than a couple of percent
/// from the actual cubic S-curve, and it executes
/// more than twice as fast. Use when the cycles
/// are more important than visual smoothness.
/// Asm version takes around 7 cycles on AVR.
#if EASE8_C == 1
LIB8STATIC fract8 ease8InOutApprox( fract8 i)
{
if( i < 64) {
// start with slope 0.5
i /= 2;
} else if( i > (255 - 64)) {
// end with slope 0.5
i = 255 - i;
i /= 2;
i = 255 - i;
} else {
// in the middle, use slope 192/128 = 1.5
i -= 64;
i += (i / 2);
i += 32;
}
return i;
}
#elif EASE8_AVRASM == 1
LIB8STATIC uint8_t ease8InOutApprox( fract8 i)
{
// takes around 7 cycles on AVR
asm volatile (
" subi %[i], 64 \n\t"
" cpi %[i], 128 \n\t"
" brcc Lshift_%= \n\t"
// middle case
" mov __tmp_reg__, %[i] \n\t"
" lsr __tmp_reg__ \n\t"
" add %[i], __tmp_reg__ \n\t"
" subi %[i], 224 \n\t"
" rjmp Ldone_%= \n\t"
// start or end case
"Lshift_%=: \n\t"
" lsr %[i] \n\t"
" subi %[i], 96 \n\t"
"Ldone_%=: \n\t"
: [i] "+&a" (i)
:
: "r0", "r1"
);
return i;
}
#else
#error "No implementation for ease8 available."
#endif
/// triwave8: triangle (sawtooth) wave generator. Useful for
/// turning a one-byte ever-increasing value into a
/// one-byte value that oscillates up and down.
///
/// input output
/// 0..127 0..254 (positive slope)
/// 128..255 254..0 (negative slope)
///
/// On AVR this function takes just three cycles.
///
LIB8STATIC uint8_t triwave8(uint8_t in)
{
if( in & 0x80) {
in = 255 - in;
}
uint8_t out = in << 1;
return out;
}
// quadwave8 and cubicwave8: S-shaped wave generators (like 'sine').
// Useful for turning a one-byte 'counter' value into a
// one-byte oscillating value that moves smoothly up and down,
// with an 'acceleration' and 'deceleration' curve.
//
// These are even faster than 'sin8', and have
// slightly different curve shapes.
//
/// quadwave8: quadratic waveform generator. Spends just a little more
/// time at the limits than 'sine' does.
LIB8STATIC uint8_t quadwave8(uint8_t in)
{
return ease8InOutQuad( triwave8( in));
}
/// cubicwave8: cubic waveform generator. Spends visibly more time
/// at the limits than 'sine' does.
LIB8STATIC uint8_t cubicwave8(uint8_t in)
{
return ease8InOutCubic( triwave8( in));
}
/// squarewave8: square wave generator. Useful for
/// turning a one-byte ever-increasing value
/// into a one-byte value that is either 0 or 255.
/// The width of the output 'pulse' is
/// determined by the pulsewidth argument:
///
///~~~
/// If pulsewidth is 255, output is always 255.
/// If pulsewidth < 255, then
/// if input < pulsewidth then output is 255
/// if input >= pulsewidth then output is 0
///~~~
///
/// the output looking like:
///
///~~~
/// 255 +--pulsewidth--+
/// . | |
/// 0 0 +--------(256-pulsewidth)--------
///~~~
///
/// @param in
/// @param pulsewidth
/// @returns square wave output
LIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth)
{
if( in < pulsewidth || (pulsewidth == 255)) {
return 255;
} else {
return 0;
}
}
// Beat generators - These functions produce waves at a given
// number of 'beats per minute'. Internally, they use
// the Arduino function 'millis' to track elapsed time.
// Accuracy is a bit better than one part in a thousand.
//
// beat8( BPM ) returns an 8-bit value that cycles 'BPM' times
// per minute, rising from 0 to 255, resetting to zero,
// rising up again, etc.. The output of this function
// is suitable for feeding directly into sin8, and cos8,
// triwave8, quadwave8, and cubicwave8.
// beat16( BPM ) returns a 16-bit value that cycles 'BPM' times
// per minute, rising from 0 to 65535, resetting to zero,
// rising up again, etc. The output of this function is
// suitable for feeding directly into sin16 and cos16.
// beat88( BPM88) is the same as beat16, except that the BPM88 argument
// MUST be in Q8.8 fixed point format, e.g. 120BPM must
// be specified as 120*256 = 30720.
// beatsin8( BPM, uint8_t low, uint8_t high) returns an 8-bit value that
// rises and falls in a sine wave, 'BPM' times per minute,
// between the values of 'low' and 'high'.
// beatsin16( BPM, uint16_t low, uint16_t high) returns a 16-bit value
// that rises and falls in a sine wave, 'BPM' times per
// minute, between the values of 'low' and 'high'.
// beatsin88( BPM88, ...) is the same as beatsin16, except that the
// BPM88 argument MUST be in Q8.8 fixed point format,
// e.g. 120BPM must be specified as 120*256 = 30720.
//
// BPM can be supplied two ways. The simpler way of specifying BPM is as
// a simple 8-bit integer from 1-255, (e.g., "120").
// The more sophisticated way of specifying BPM allows for fractional
// "Q8.8" fixed point number (an 'accum88') with an 8-bit integer part and
// an 8-bit fractional part. The easiest way to construct this is to multiply
// a floating point BPM value (e.g. 120.3) by 256, (e.g. resulting in 30796
// in this case), and pass that as the 16-bit BPM argument.
// "BPM88" MUST always be specified in Q8.8 format.
//
// Originally designed to make an entire animation project pulse with brightness.
// For that effect, add this line just above your existing call to "FastLED.show()":
//
// uint8_t bright = beatsin8( 60 /*BPM*/, 192 /*dimmest*/, 255 /*brightest*/ ));
// FastLED.setBrightness( bright );
// FastLED.show();
//
// The entire animation will now pulse between brightness 192 and 255 once per second.
// The beat generators need access to a millisecond counter.
// On Arduino, this is "millis()". On other platforms, you'll
// need to provide a function with this signature:
// uint32_t get_millisecond_timer();
// that provides similar functionality.
// You can also force use of the get_millisecond_timer function
// by #defining USE_GET_MILLISECOND_TIMER.
#if (defined(ARDUINO) || defined(SPARK) || defined(FASTLED_HAS_MILLIS)) && !defined(USE_GET_MILLISECOND_TIMER)
// Forward declaration of Arduino function 'millis'.
//uint32_t millis();
#define GET_MILLIS millis
#else
uint32_t get_millisecond_timer(void);
#define GET_MILLIS get_millisecond_timer
#endif
// beat16 generates a 16-bit 'sawtooth' wave at a given BPM,
/// with BPM specified in Q8.8 fixed-point format; e.g.
/// for this function, 120 BPM MUST BE specified as
/// 120*256 = 30720.
/// If you just want to specify "120", use beat16 or beat8.
LIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase)
{
// BPM is 'beats per minute', or 'beats per 60000ms'.
// To avoid using the (slower) division operator, we
// want to convert 'beats per 60000ms' to 'beats per 65536ms',
// and then use a simple, fast bit-shift to divide by 65536.
//
// The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256.
// The conversion is accurate to about 0.05%, more or less,
// e.g. if you ask for "120 BPM", you'll get about "119.93".
return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16;
}
/// beat16 generates a 16-bit 'sawtooth' wave at a given BPM
LIB8STATIC uint16_t beat16( accum88 beats_per_minute, uint32_t timebase)
{
// Convert simple 8-bit BPM's to full Q8.8 accum88's if needed
if( beats_per_minute < 256) beats_per_minute <<= 8;
return beat88(beats_per_minute, timebase);
}
/// beat8 generates an 8-bit 'sawtooth' wave at a given BPM
LIB8STATIC uint8_t beat8( accum88 beats_per_minute, uint32_t timebase)
{
return beat16( beats_per_minute, timebase) >> 8;
}
/// beatsin88 generates a 16-bit sine wave at a given BPM,
/// that oscillates within a given range.
/// For this function, BPM MUST BE SPECIFIED as
/// a Q8.8 fixed-point value; e.g. 120BPM must be
/// specified as 120*256 = 30720.
/// If you just want to specify "120", use beatsin16 or beatsin8.
LIB8STATIC uint16_t beatsin88( accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat88( beats_per_minute_88, timebase);
uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
/// beatsin16 generates a 16-bit sine wave at a given BPM,
/// that oscillates within a given range.
LIB8STATIC uint16_t beatsin16(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)
{
uint16_t beat = beat16( beats_per_minute, timebase);
uint16_t beatsin = (sin16( beat + phase_offset) + 32768);
uint16_t rangewidth = highest - lowest;
uint16_t scaledbeat = scale16( beatsin, rangewidth);
uint16_t result = lowest + scaledbeat;
return result;
}
/// beatsin8 generates an 8-bit sine wave at a given BPM,
/// that oscillates within a given range.
LIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset)
{
uint8_t beat = beat8( beats_per_minute, timebase);
uint8_t beatsin = sin8( beat + phase_offset);
uint8_t rangewidth = highest - lowest;
uint8_t scaledbeat = scale8( beatsin, rangewidth);
uint8_t result = lowest + scaledbeat;
return result;
}
/// Return the current seconds since boot in a 16-bit value. Used as part of the
/// "every N time-periods" mechanism
LIB8STATIC uint16_t seconds16(void)
{
uint32_t ms = GET_MILLIS();
uint16_t s16;
s16 = ms / 1000;
return s16;
}
/// Return the current minutes since boot in a 16-bit value. Used as part of the
/// "every N time-periods" mechanism
LIB8STATIC uint16_t minutes16(void)
{
uint32_t ms = GET_MILLIS();
uint16_t m16;
m16 = (ms / (60000L)) & 0xFFFF;
return m16;
}
/// Return the current hours since boot in an 8-bit value. Used as part of the
/// "every N time-periods" mechanism
LIB8STATIC uint8_t hours8(void)
{
uint32_t ms = GET_MILLIS();
uint8_t h8;
h8 = (ms / (3600000L)) & 0xFF;
return h8;
}
///@}
#endif

@ -0,0 +1,552 @@
#ifndef __INC_LIB8TION_MATH_H
#define __INC_LIB8TION_MATH_H
#include "scale8.h"
///@ingroup lib8tion
///@defgroup Math Basic math operations
/// Fast, efficient 8-bit math functions specifically
/// designed for high-performance LED programming.
///
/// Because of the AVR(Arduino) and ARM assembly language
/// implementations provided, using these functions often
/// results in smaller and faster code than the equivalent
/// program using plain "C" arithmetic and logic.
///@{
/// add one byte to another, saturating at 0xFF
/// @param i - first byte to add
/// @param j - second byte to add
/// @returns the sum of i & j, capped at 0xFF
LIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j)
{
#if QADD8_C == 1
uint16_t t = i + j;
if (t > 255) t = 255;
return t;
#elif QADD8_AVRASM == 1
asm volatile(
/* First, add j to i, conditioning the C flag */
"add %0, %1 \n\t"
/* Now test the C flag.
If C is clear, we branch around a load of 0xFF into i.
If C is set, we go ahead and load 0xFF into i.
*/
"brcc L_%= \n\t"
"ldi %0, 0xFF \n\t"
"L_%=: "
: "+a" (i)
: "a" (j) );
return i;
#elif QADD8_ARM_DSP_ASM == 1
asm volatile( "uqadd8 %0, %0, %1" : "+r" (i) : "r" (j));
return i;
#else
#error "No implementation for qadd8 available."
#endif
}
/// Add one byte to another, saturating at 0x7F
/// @param i - first byte to add
/// @param j - second byte to add
/// @returns the sum of i & j, capped at 0xFF
LIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j)
{
#if QADD7_C == 1
int16_t t = i + j;
if (t > 127) t = 127;
return t;
#elif QADD7_AVRASM == 1
asm volatile(
/* First, add j to i, conditioning the V flag */
"add %0, %1 \n\t"
/* Now test the V flag.
If V is clear, we branch around a load of 0x7F into i.
If V is set, we go ahead and load 0x7F into i.
*/
"brvc L_%= \n\t"
"ldi %0, 0x7F \n\t"
"L_%=: "
: "+a" (i)
: "a" (j) );
return i;
#elif QADD7_ARM_DSP_ASM == 1
asm volatile( "qadd8 %0, %0, %1" : "+r" (i) : "r" (j));
return i;
#else
#error "No implementation for qadd7 available."
#endif
}
/// subtract one byte from another, saturating at 0x00
/// @returns i - j with a floor of 0
LIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j)
{
#if QSUB8_C == 1
int16_t t = i - j;
if (t < 0) t = 0;
return t;
#elif QSUB8_AVRASM == 1
asm volatile(
/* First, subtract j from i, conditioning the C flag */
"sub %0, %1 \n\t"
/* Now test the C flag.
If C is clear, we branch around a load of 0x00 into i.
If C is set, we go ahead and load 0x00 into i.
*/
"brcc L_%= \n\t"
"ldi %0, 0x00 \n\t"
"L_%=: "
: "+a" (i)
: "a" (j) );
return i;
#else
#error "No implementation for qsub8 available."
#endif
}
/// add one byte to another, with one byte result
LIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j)
{
#if ADD8_C == 1
uint16_t t = i + j;
return t;
#elif ADD8_AVRASM == 1
// Add j to i, period.
asm volatile( "add %0, %1" : "+a" (i) : "a" (j));
return i;
#else
#error "No implementation for add8 available."
#endif
}
/// add one byte to another, with one byte result
LIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j)
{
#if ADD8_C == 1
uint16_t t = i + j;
return t;
#elif ADD8_AVRASM == 1
// Add i(one byte) to j(two bytes)
asm volatile( "add %A[j], %[i] \n\t"
"adc %B[j], __zero_reg__ \n\t"
: [j] "+a" (j)
: [i] "a" (i)
);
return i;
#else
#error "No implementation for add8to16 available."
#endif
}
/// subtract one byte from another, 8-bit result
LIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j)
{
#if SUB8_C == 1
int16_t t = i - j;
return t;
#elif SUB8_AVRASM == 1
// Subtract j from i, period.
asm volatile( "sub %0, %1" : "+a" (i) : "a" (j));
return i;
#else
#error "No implementation for sub8 available."
#endif
}
/// Calculate an integer average of two unsigned
/// 8-bit integer values (uint8_t).
/// Fractional results are rounded down, e.g. avg8(20,41) = 30
LIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j)
{
#if AVG8_C == 1
return (i + j) >> 1;
#elif AVG8_AVRASM == 1
asm volatile(
/* First, add j to i, 9th bit overflows into C flag */
"add %0, %1 \n\t"
/* Divide by two, moving C flag into high 8th bit */
"ror %0 \n\t"
: "+a" (i)
: "a" (j) );
return i;
#else
#error "No implementation for avg8 available."
#endif
}
/// Calculate an integer average of two unsigned
/// 16-bit integer values (uint16_t).
/// Fractional results are rounded down, e.g. avg16(20,41) = 30
LIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j)
{
#if AVG16_C == 1
return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;
#elif AVG16_AVRASM == 1
asm volatile(
/* First, add jLo (heh) to iLo, 9th bit overflows into C flag */
"add %A[i], %A[j] \n\t"
/* Now, add C + jHi to iHi, 17th bit overflows into C flag */
"adc %B[i], %B[j] \n\t"
/* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */
"ror %B[i] \n\t"
/* Divide iLo by two, moving C flag into high 8th bit */
"ror %A[i] \n\t"
: [i] "+a" (i)
: [j] "a" (j) );
return i;
#else
#error "No implementation for avg16 available."
#endif
}
/// Calculate an integer average of two signed 7-bit
/// integers (int8_t)
/// If the first argument is even, result is rounded down.
/// If the first argument is odd, result is result up.
LIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j)
{
#if AVG7_C == 1
return ((i + j) >> 1) + (i & 0x1);
#elif AVG7_AVRASM == 1
asm volatile(
"asr %1 \n\t"
"asr %0 \n\t"
"adc %0, %1 \n\t"
: "+a" (i)
: "a" (j) );
return i;
#else
#error "No implementation for avg7 available."
#endif
}
/// Calculate an integer average of two signed 15-bit
/// integers (int16_t)
/// If the first argument is even, result is rounded down.
/// If the first argument is odd, result is result up.
LIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j)
{
#if AVG15_C == 1
return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1);
#elif AVG15_AVRASM == 1
asm volatile(
/* first divide j by 2, throwing away lowest bit */
"asr %B[j] \n\t"
"ror %A[j] \n\t"
/* now divide i by 2, with lowest bit going into C */
"asr %B[i] \n\t"
"ror %A[i] \n\t"
/* add j + C to i */
"adc %A[i], %A[j] \n\t"
"adc %B[i], %B[j] \n\t"
: [i] "+a" (i)
: [j] "a" (j) );
return i;
#else
#error "No implementation for avg15 available."
#endif
}
/// Calculate the remainder of one unsigned 8-bit
/// value divided by anoter, aka A % M.
/// Implemented by repeated subtraction, which is
/// very compact, and very fast if A is 'probably'
/// less than M. If A is a large multiple of M,
/// the loop has to execute multiple times. However,
/// even in that case, the loop is only two
/// instructions long on AVR, i.e., quick.
LIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [m] "r" (m)
);
#else
while( a >= m) a -= m;
#endif
return a;
}
/// Add two numbers, and calculate the modulo
/// of the sum and a third number, M.
/// In other words, it returns (A+B) % M.
/// It is designed as a compact mechanism for
/// incrementing a 'mode' switch and wrapping
/// around back to 'mode 0' when the switch
/// goes past the end of the available range.
/// e.g. if you have seven modes, this switches
/// to the next one and wraps around if needed:
/// mode = addmod8( mode, 1, 7);
///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
LIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
" add %[a],%[b] \n\t"
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [b] "r" (b), [m] "r" (m)
);
#else
a += b;
while( a >= m) a -= m;
#endif
return a;
}
/// Subtract two numbers, and calculate the modulo
/// of the difference and a third number, M.
/// In other words, it returns (A-B) % M.
/// It is designed as a compact mechanism for
/// incrementing a 'mode' switch and wrapping
/// around back to 'mode 0' when the switch
/// goes past the end of the available range.
/// e.g. if you have seven modes, this switches
/// to the next one and wraps around if needed:
/// mode = addmod8( mode, 1, 7);
///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.
LIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m)
{
#if defined(__AVR__)
asm volatile (
" sub %[a],%[b] \n\t"
"L_%=: sub %[a],%[m] \n\t"
" brcc L_%= \n\t"
" add %[a],%[m] \n\t"
: [a] "+r" (a)
: [b] "r" (b), [m] "r" (m)
);
#else
a -= b;
while( a >= m) a -= m;
#endif
return a;
}
/// 8x8 bit multiplication, with 8 bit result
LIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j)
{
#if MUL8_C == 1
return ((uint16_t)i * (uint16_t)(j) ) & 0xFF;
#elif MUL8_AVRASM == 1
asm volatile(
/* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Extract the LOW 8-bits (r0) */
"mov %0, r0 \n\t"
/* Restore r1 to "0"; it's expected to always be that */
"clr __zero_reg__ \n\t"
: "+a" (i)
: "a" (j)
: "r0", "r1");
return i;
#else
#error "No implementation for mul8 available."
#endif
}
/// saturating 8x8 bit multiplication, with 8 bit result
/// @returns the product of i * j, capping at 0xFF
LIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j)
{
#if QMUL8_C == 1
int p = ((uint16_t)i * (uint16_t)(j) );
if( p > 255) p = 255;
return p;
#elif QMUL8_AVRASM == 1
asm volatile(
/* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */
" mul %0, %1 \n\t"
/* If high byte of result is zero, all is well. */
" tst r1 \n\t"
" breq Lnospill_%= \n\t"
/* If high byte of result > 0, saturate low byte to 0xFF */
" ldi %0,0xFF \n\t"
" rjmp Ldone_%= \n\t"
"Lnospill_%=: \n\t"
/* Extract the LOW 8-bits (r0) */
" mov %0, r0 \n\t"
"Ldone_%=: \n\t"
/* Restore r1 to "0"; it's expected to always be that */
" clr __zero_reg__ \n\t"
: "+a" (i)
: "a" (j)
: "r0", "r1");
return i;
#else
#error "No implementation for qmul8 available."
#endif
}
/// take abs() of a signed 8-bit uint8_t
LIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i)
{
#if ABS8_C == 1
if( i < 0) i = -i;
return i;
#elif ABS8_AVRASM == 1
asm volatile(
/* First, check the high bit, and prepare to skip if it's clear */
"sbrc %0, 7 \n"
/* Negate the value */
"neg %0 \n"
: "+r" (i) : "r" (i) );
return i;
#else
#error "No implementation for abs8 available."
#endif
}
/// square root for 16-bit integers
/// About three times faster and five times smaller
/// than Arduino's general sqrt on AVR.
LIB8STATIC uint8_t sqrt16(uint16_t x)
{
if( x <= 1) {
return x;
}
uint8_t low = 1; // lower bound
uint8_t hi, mid;
if( x > 7904) {
hi = 255;
} else {
hi = (x >> 5) + 8; // initial estimate for upper bound
}
do {
mid = (low + hi) >> 1;
if ((uint16_t)(mid * mid) > x) {
hi = mid - 1;
} else {
if( mid == 255) {
return 255;
}
low = mid + 1;
}
} while (hi >= low);
return low - 1;
}
/// blend a variable proproportion(0-255) of one byte to another
/// @param a - the starting byte value
/// @param b - the byte value to blend toward
/// @param amountOfB - the proportion (0-255) of b to blend
/// @returns a byte value between a and b, inclusive
#if (FASTLED_BLEND_FIXED == 1)
LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
{
#if BLEND8_C == 1
uint16_t partial;
uint8_t result;
uint8_t amountOfA = 255 - amountOfB;
partial = (a * amountOfA);
#if (FASTLED_SCALE8_FIXED == 1)
partial += a;
//partial = add8to16( a, partial);
#endif
partial += (b * amountOfB);
#if (FASTLED_SCALE8_FIXED == 1)
partial += b;
//partial = add8to16( b, partial);
#endif
result = partial >> 8;
return result;
#elif BLEND8_AVRASM == 1
uint16_t partial;
uint8_t result;
asm volatile (
/* partial = b * amountOfB */
" mul %[b], %[amountOfB] \n\t"
" movw %A[partial], r0 \n\t"
/* amountOfB (aka amountOfA) = 255 - amountOfB */
" com %[amountOfB] \n\t"
/* partial += a * amountOfB (aka amountOfA) */
" mul %[a], %[amountOfB] \n\t"
" add %A[partial], r0 \n\t"
" adc %B[partial], r1 \n\t"
" clr __zero_reg__ \n\t"
#if (FASTLED_SCALE8_FIXED == 1)
/* partial += a */
" add %A[partial], %[a] \n\t"
" adc %B[partial], __zero_reg__ \n\t"
// partial += b
" add %A[partial], %[b] \n\t"
" adc %B[partial], __zero_reg__ \n\t"
#endif
: [partial] "=r" (partial),
[amountOfB] "+a" (amountOfB)
: [a] "a" (a),
[b] "a" (b)
: "r0", "r1"
);
result = partial >> 8;
return result;
#else
#error "No implementation for blend8 available."
#endif
}
#else
LIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)
{
// This version loses precision in the integer math
// and can actually return results outside of the range
// from a to b. Its use is not recommended.
uint8_t result;
uint8_t amountOfA = 255 - amountOfB;
result = scale8_LEAVING_R1_DIRTY( a, amountOfA)
+ scale8_LEAVING_R1_DIRTY( b, amountOfB);
cleanup_R1();
return result;
}
#endif
///@}
#endif

@ -0,0 +1,94 @@
#ifndef __INC_LIB8TION_RANDOM_H
#define __INC_LIB8TION_RANDOM_H
///@ingroup lib8tion
///@defgroup Random Fast random number generators
/// Fast 8- and 16- bit unsigned random numbers.
/// Significantly faster than Arduino random(), but
/// also somewhat less random. You can add entropy.
///@{
// X(n+1) = (2053 * X(n)) + 13849)
#define FASTLED_RAND16_2053 ((uint16_t)(2053))
#define FASTLED_RAND16_13849 ((uint16_t)(13849))
/// random number seed
extern uint16_t rand16seed;// = RAND16_SEED;
/// Generate an 8-bit random number
LIB8STATIC uint8_t random8(void)
{
rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
// return the sum of the high and low bytes, for better
// mixing and non-sequential correlation
return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +
((uint8_t)(rand16seed >> 8)));
}
/// Generate a 16 bit random number
LIB8STATIC uint16_t random16(void)
{
rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;
return rand16seed;
}
/// Generate an 8-bit random number between 0 and lim
/// @param lim the upper bound for the result
LIB8STATIC uint8_t random8_max(uint8_t lim)
{
uint8_t r = random8();
r = (r*lim) >> 8;
return r;
}
/// Generate an 8-bit random number in the given range
/// @param min the lower bound for the random number
/// @param lim the upper bound for the random number
LIB8STATIC uint8_t random8_min_max(uint8_t min, uint8_t lim)
{
uint8_t delta = lim - min;
uint8_t r = random8_max(delta) + min;
return r;
}
/// Generate an 16-bit random number between 0 and lim
/// @param lim the upper bound for the result
LIB8STATIC uint16_t random16_max(uint16_t lim)
{
uint16_t r = random16();
uint32_t p = (uint32_t)lim * (uint32_t)r;
r = p >> 16;
return r;
}
/// Generate an 16-bit random number in the given range
/// @param min the lower bound for the random number
/// @param lim the upper bound for the random number
LIB8STATIC uint16_t random16_min_max( uint16_t min, uint16_t lim)
{
uint16_t delta = lim - min;
uint16_t r = random16_max(delta) + min;
return r;
}
/// Set the 16-bit seed used for the random number generator
LIB8STATIC void random16_set_seed(uint16_t seed)
{
rand16seed = seed;
}
/// Get the current seed value for the random number generator
LIB8STATIC uint16_t random16_get_seed(void)
{
return rand16seed;
}
/// Add entropy into the random number generator
LIB8STATIC void random16_add_entropy(uint16_t entropy)
{
rand16seed += entropy;
}
///@}
#endif

@ -0,0 +1,542 @@
#ifndef __INC_LIB8TION_SCALE_H
#define __INC_LIB8TION_SCALE_H
///@ingroup lib8tion
///@defgroup Scaling Scaling functions
/// Fast, efficient 8-bit scaling functions specifically
/// designed for high-performance LED programming.
///
/// Because of the AVR(Arduino) and ARM assembly language
/// implementations provided, using these functions often
/// results in smaller and faster code than the equivalent
/// program using plain "C" arithmetic and logic.
///@{
/// scale one byte by a second one, which is treated as
/// the numerator of a fraction whose denominator is 256
/// In other words, it computes i * (scale / 256)
/// 4 clocks AVR with MUL, 2 clocks ARM
LIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;
#else
return ((uint16_t)i * (uint16_t)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
#if defined(LIB8_ATTINY)
#if (FASTLED_SCALE8_FIXED == 1)
uint8_t work=i;
#else
uint8_t work=0;
#endif
uint8_t cnt=0x80;
asm volatile(
#if (FASTLED_SCALE8_FIXED == 1)
" inc %[scale] \n\t"
" breq DONE_%= \n\t"
" clr %[work] \n\t"
#endif
"LOOP_%=: \n\t"
/*" sbrc %[scale], 0 \n\t"
" add %[work], %[i] \n\t"
" ror %[work] \n\t"
" lsr %[scale] \n\t"
" clc \n\t"*/
" sbrc %[scale], 0 \n\t"
" add %[work], %[i] \n\t"
" ror %[work] \n\t"
" lsr %[scale] \n\t"
" lsr %[cnt] \n\t"
"brcc LOOP_%= \n\t"
"DONE_%=: \n\t"
: [work] "+r" (work), [cnt] "+r" (cnt)
: [scale] "r" (scale), [i] "r" (i)
:
);
return work;
#else
asm volatile(
#if (FASTLED_SCALE8_FIXED==1)
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
"mul %0, %1 \n\t"
// Add i to r0, possibly setting the carry flag
"add r0, %0 \n\t"
// load the immediate 0 into i (note, this does _not_ touch any flags)
"ldi %0, 0x00 \n\t"
// walk and chew gum at the same time
"adc %0, r1 \n\t"
#else
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Move the high 8-bits of the product (r1) back to i */
"mov %0, r1 \n\t"
/* Restore r1 to "0"; it's expected to always be that */
#endif
"clr __zero_reg__ \n\t"
: "+a" (i) /* writes to i */
: "a" (scale) /* uses scale */
: "r0", "r1" /* clobbers r0, r1 */ );
/* Return the result */
return i;
#endif
#else
#error "No implementation for scale8 available."
#endif
}
/// The "video" version of scale8 guarantees that the output will
/// be only be zero if one or both of the inputs are zero. If both
/// inputs are non-zero, the output is guaranteed to be non-zero.
/// This makes for better 'video'/LED dimming, at the cost of
/// several additional cycles.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
return j;
#elif SCALE8_AVRASM == 1
uint8_t j=0;
asm volatile(
" tst %[i]\n\t"
" breq L_%=\n\t"
" mul %[i], %[scale]\n\t"
" mov %[j], r1\n\t"
" clr __zero_reg__\n\t"
" cpse %[scale], r1\n\t"
" subi %[j], 0xFF\n\t"
"L_%=: \n\t"
: [j] "+a" (j)
: [i] "a" (i), [scale] "a" (scale)
: "r0", "r1");
return j;
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// asm volatile(
// " tst %0 \n"
// " breq L_%= \n"
// " mul %0, %1 \n"
// " mov %0, r1 \n"
// " add %0, %2 \n"
// " clr __zero_reg__ \n"
// "L_%=: \n"
// : "+a" (i)
// : "a" (scale), "a" (nonzeroscale)
// : "r0", "r1");
// // Return the result
// return i;
#else
#error "No implementation for scale8_video available."
#endif
}
/// This version of scale8 does not clean up the R1 register on AVR
/// If you are doing several 'scale8's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1
#if (FASTLED_SCALE8_FIXED == 1)
return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;
#else
return ((int)i * (int)(scale) ) >> 8;
#endif
#elif SCALE8_AVRASM == 1
asm volatile(
#if (FASTLED_SCALE8_FIXED==1)
// Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0
"mul %0, %1 \n\t"
// Add i to r0, possibly setting the carry flag
"add r0, %0 \n\t"
// load the immediate 0 into i (note, this does _not_ touch any flags)
"ldi %0, 0x00 \n\t"
// walk and chew gum at the same time
"adc %0, r1 \n\t"
#else
/* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */
"mul %0, %1 \n\t"
/* Move the high 8-bits of the product (r1) back to i */
"mov %0, r1 \n\t"
#endif
/* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF */
/* "clr __zero_reg__ \n\t" */
: "+a" (i) /* writes to i */
: "a" (scale) /* uses scale */
: "r0", "r1" /* clobbers r0, r1 */ );
// Return the result
return i;
#else
#error "No implementation for scale8_LEAVING_R1_DIRTY available."
#endif
}
/// This version of scale8_video does not clean up the R1 register on AVR
/// If you are doing several 'scale8_video's in a row, use this, and
/// then explicitly call cleanup_R1.
LIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)
{
#if SCALE8_C == 1 || defined(LIB8_ATTINY)
uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;
return j;
#elif SCALE8_AVRASM == 1
uint8_t j=0;
asm volatile(
" tst %[i]\n\t"
" breq L_%=\n\t"
" mul %[i], %[scale]\n\t"
" mov %[j], r1\n\t"
" breq L_%=\n\t"
" subi %[j], 0xFF\n\t"
"L_%=: \n\t"
: [j] "+a" (j)
: [i] "a" (i), [scale] "a" (scale)
: "r0", "r1");
return j;
// uint8_t nonzeroscale = (scale != 0) ? 1 : 0;
// asm volatile(
// " tst %0 \n"
// " breq L_%= \n"
// " mul %0, %1 \n"
// " mov %0, r1 \n"
// " add %0, %2 \n"
// " clr __zero_reg__ \n"
// "L_%=: \n"
// : "+a" (i)
// : "a" (scale), "a" (nonzeroscale)
// : "r0", "r1");
// // Return the result
// return i;
#else
#error "No implementation for scale8_video_LEAVING_R1_DIRTY available."
#endif
}
/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls
LIB8STATIC_ALWAYS_INLINE void cleanup_R1(void)
{
#if CLEANUP_R1_AVRASM == 1
// Restore r1 to "0"; it's expected to always be that
asm volatile( "clr __zero_reg__ \n\t" : : : "r1" );
#endif
}
/// scale a 16-bit unsigned value by an 8-bit value,
/// considered as numerator of a fraction whose denominator
/// is 256. In other words, it computes i * (scale / 256)
LIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )
{
#if SCALE16BY8_C == 1
uint16_t result;
#if FASTLED_SCALE8_FIXED == 1
result = (i * (1+((uint16_t)scale))) >> 8;
#else
result = (i * scale) / 256;
#endif
return result;
#elif SCALE16BY8_AVRASM == 1
#if FASTLED_SCALE8_FIXED == 1
uint16_t result = 0;
asm volatile(
// result.A = HighByte( (i.A x scale) + i.A )
" mul %A[i], %[scale] \n\t"
" add r0, %A[i] \n\t"
// " adc r1, [zero] \n\t"
// " mov %A[result], r1 \n\t"
" adc %A[result], r1 \n\t"
// result.A-B += i.B x scale
" mul %B[i], %[scale] \n\t"
" add %A[result], r0 \n\t"
" adc %B[result], r1 \n\t"
// cleanup r1
" clr __zero_reg__ \n\t"
// result.A-B += i.B
" add %A[result], %B[i] \n\t"
" adc %B[result], __zero_reg__ \n\t"
: [result] "+r" (result)
: [i] "r" (i), [scale] "r" (scale)
: "r0", "r1"
);
return result;
#else
uint16_t result = 0;
asm volatile(
// result.A = HighByte(i.A x j )
" mul %A[i], %[scale] \n\t"
" mov %A[result], r1 \n\t"
//" clr %B[result] \n\t"
// result.A-B += i.B x j
" mul %B[i], %[scale] \n\t"
" add %A[result], r0 \n\t"
" adc %B[result], r1 \n\t"
// cleanup r1
" clr __zero_reg__ \n\t"
: [result] "+r" (result)
: [i] "r" (i), [scale] "r" (scale)
: "r0", "r1"
);
return result;
#endif
#else
#error "No implementation for scale16by8 available."
#endif
}
/// scale a 16-bit unsigned value by a 16-bit value,
/// considered as numerator of a fraction whose denominator
/// is 65536. In other words, it computes i * (scale / 65536)
LIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )
{
#if SCALE16_C == 1
uint16_t result;
#if FASTLED_SCALE8_FIXED == 1
result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;
#else
result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;
#endif
return result;
#elif SCALE16_AVRASM == 1
#if FASTLED_SCALE8_FIXED == 1
// implemented sort of like
// result = ((i * scale) + i ) / 65536
//
// why not like this, you may ask?
// result = (i * (scale+1)) / 65536
// the answer is that if scale is 65535, then scale+1
// will be zero, which is not what we want.
uint32_t result;
asm volatile(
// result.A-B = i.A x scale.A
" mul %A[i], %A[scale] \n\t"
// save results...
// basic idea:
//" mov %A[result], r0 \n\t"
//" mov %B[result], r1 \n\t"
// which can be written as...
" movw %A[result], r0 \n\t"
// Because we're going to add i.A-B to
// result.A-D, we DO need to keep both
// the r0 and r1 portions of the product
// UNlike in the 'unfixed scale8' version.
// So the movw here is needed.
: [result] "=r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
asm volatile(
// result.C-D = i.B x scale.B
" mul %B[i], %B[scale] \n\t"
//" mov %C[result], r0 \n\t"
//" mov %D[result], r1 \n\t"
" movw %C[result], r0 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
const uint8_t zero = 0;
asm volatile(
// result.B-D += i.B x scale.A
" mul %B[i], %A[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// result.B-D += i.A x scale.B
" mul %A[i], %B[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// cleanup r1
" clr r1 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale),
[zero] "r" (zero)
: "r0", "r1"
);
asm volatile(
// result.A-D += i.A-B
" add %A[result], %A[i] \n\t"
" adc %B[result], %B[i] \n\t"
" adc %C[result], %[zero] \n\t"
" adc %D[result], %[zero] \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[zero] "r" (zero)
);
result = result >> 16;
return result;
#else
uint32_t result;
asm volatile(
// result.A-B = i.A x scale.A
" mul %A[i], %A[scale] \n\t"
// save results...
// basic idea:
//" mov %A[result], r0 \n\t"
//" mov %B[result], r1 \n\t"
// which can be written as...
" movw %A[result], r0 \n\t"
// We actually don't need to do anything with r0,
// as result.A is never used again here, so we
// could just move the high byte, but movw is
// one clock cycle, just like mov, so might as
// well, in case we want to use this code for
// a generic 16x16 multiply somewhere.
: [result] "=r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
asm volatile(
// result.C-D = i.B x scale.B
" mul %B[i], %B[scale] \n\t"
//" mov %C[result], r0 \n\t"
//" mov %D[result], r1 \n\t"
" movw %C[result], r0 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale)
: "r0", "r1"
);
const uint8_t zero = 0;
asm volatile(
// result.B-D += i.B x scale.A
" mul %B[i], %A[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// result.B-D += i.A x scale.B
" mul %A[i], %B[scale] \n\t"
" add %B[result], r0 \n\t"
" adc %C[result], r1 \n\t"
" adc %D[result], %[zero] \n\t"
// cleanup r1
" clr r1 \n\t"
: [result] "+r" (result)
: [i] "r" (i),
[scale] "r" (scale),
[zero] "r" (zero)
: "r0", "r1"
);
result = result >> 16;
return result;
#endif
#else
#error "No implementation for scale16 available."
#endif
}
///@}
///@defgroup Dimming Dimming and brightening functions
///
/// Dimming and brightening functions
///
/// The eye does not respond in a linear way to light.
/// High speed PWM'd LEDs at 50% duty cycle appear far
/// brighter then the 'half as bright' you might expect.
///
/// If you want your midpoint brightness leve (128) to
/// appear half as bright as 'full' brightness (255), you
/// have to apply a 'dimming function'.
///@{
/// Adjust a scaling value for dimming
LIB8STATIC uint8_t dim8_raw( uint8_t x)
{
return scale8( x, x);
}
/// Adjust a scaling value for dimming for video (value will never go below 1)
LIB8STATIC uint8_t dim8_video( uint8_t x)
{
return scale8_video( x, x);
}
/// Linear version of the dimming function that halves for values < 128
LIB8STATIC uint8_t dim8_lin( uint8_t x )
{
if( x & 0x80 ) {
x = scale8( x, x);
} else {
x += 1;
x /= 2;
}
return x;
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_raw( uint8_t x)
{
uint8_t ix = 255 - x;
return 255 - scale8( ix, ix);
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_video( uint8_t x)
{
uint8_t ix = 255 - x;
return 255 - scale8_video( ix, ix);
}
/// inverse of the dimming function, brighten a value
LIB8STATIC uint8_t brighten8_lin( uint8_t x )
{
uint8_t ix = 255 - x;
if( ix & 0x80 ) {
ix = scale8( ix, ix);
} else {
ix += 1;
ix /= 2;
}
return 255 - ix;
}
///@}
#endif

@ -0,0 +1,259 @@
#ifndef __INC_LIB8TION_TRIG_H
#define __INC_LIB8TION_TRIG_H
///@ingroup lib8tion
///@defgroup Trig Fast trig functions
/// Fast 8 and 16-bit approximations of sin(x) and cos(x).
/// Don't use these approximations for calculating the
/// trajectory of a rocket to Mars, but they're great
/// for art projects and LED displays.
///
/// On Arduino/AVR, the 16-bit approximation is more than
/// 10X faster than floating point sin(x) and cos(x), while
/// the 8-bit approximation is more than 20X faster.
///@{
#if defined(__AVR__)
#define sin16 sin16_avr
#else
#define sin16 sin16_C
#endif
/// Fast 16-bit approximation of sin(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = sin(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t sin16_avr( uint16_t theta )
{
static const uint8_t data[] =
{ 0, 0, 49, 0, 6393%256, 6393/256, 48, 0,
12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,
23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,
30273%256, 30273/256, 14, 0, 32137%256, 32137/256, 4 /*,0*/ };
uint16_t offset = (theta & 0x3FFF);
// AVR doesn't have a multi-bit shift instruction,
// so if we say "offset >>= 3", gcc makes a tiny loop.
// Inserting empty volatile statements between each
// bit shift forces gcc to unroll the loop.
offset >>= 1; // 0..8191
asm volatile("");
offset >>= 1; // 0..4095
asm volatile("");
offset >>= 1; // 0..2047
if( theta & 0x4000 ) offset = 2047 - offset;
uint8_t sectionX4;
sectionX4 = offset / 256;
sectionX4 *= 4;
uint8_t m;
union {
uint16_t b;
struct {
uint8_t blo;
uint8_t bhi;
};
} u;
//in effect u.b = blo + (256 * bhi);
u.blo = data[ sectionX4 ];
u.bhi = data[ sectionX4 + 1];
m = data[ sectionX4 + 2];
uint8_t secoffset8 = (uint8_t)(offset) / 2;
uint16_t mx = m * secoffset8;
int16_t y = mx + u.b;
if( theta & 0x8000 ) y = -y;
return y;
}
/// Fast 16-bit approximation of sin(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = sin(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t sin16_C( uint16_t theta )
{
static const uint16_t base[] =
{ 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };
static const uint8_t slope[] =
{ 49, 48, 44, 38, 31, 23, 14, 4 };
uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047
if( theta & 0x4000 ) offset = 2047 - offset;
uint8_t section = offset / 256; // 0..7
uint16_t b = base[section];
uint8_t m = slope[section];
uint8_t secoffset8 = (uint8_t)(offset) / 2;
uint16_t mx = m * secoffset8;
int16_t y = mx + b;
if( theta & 0x8000 ) y = -y;
return y;
}
/// Fast 16-bit approximation of cos(x). This approximation never varies more than
/// 0.69% from the floating point value you'd get by doing
///
/// float s = cos(x) * 32767.0;
///
/// @param theta input angle from 0-65535
/// @returns sin of theta, value between -32767 to 32767.
LIB8STATIC int16_t cos16( uint16_t theta)
{
return sin16( theta + 16384);
}
///////////////////////////////////////////////////////////////////////
// sin8 & cos8
// Fast 8-bit approximations of sin(x) & cos(x).
// Input angle is an unsigned int from 0-255.
// Output is an unsigned int from 0 to 255.
//
// This approximation can vary to to 2%
// from the floating point value you'd get by doing
// float s = (sin( x ) * 128.0) + 128;
//
// Don't use this approximation for calculating the
// "real" trigonometric calculations, but it's great
// for art projects and LED displays.
//
// On Arduino/AVR, this approximation is more than
// 20X faster than floating point sin(x) and cos(x)
#if defined(__AVR__) && !defined(LIB8_ATTINY)
#define sin8 sin8_avr
#else
#define sin8 sin8_C
#endif
const uint8_t b_m16_interleave[] = { 0, 49, 49, 41, 90, 27, 117, 10 };
/// Fast 8-bit approximation of sin(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (sin(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t sin8_avr( uint8_t theta)
{
uint8_t offset = theta;
asm volatile(
"sbrc %[theta],6 \n\t"
"com %[offset] \n\t"
: [theta] "+r" (theta), [offset] "+r" (offset)
);
offset &= 0x3F; // 0..63
uint8_t secoffset = offset & 0x0F; // 0..15
if( theta & 0x40) secoffset++;
uint8_t m16; uint8_t b;
uint8_t section = offset >> 4; // 0..3
uint8_t s2 = section * 2;
const uint8_t* p = b_m16_interleave;
p += s2;
b = *p;
p++;
m16 = *p;
uint8_t mx;
uint8_t xr1;
asm volatile(
"mul %[m16],%[secoffset] \n\t"
"mov %[mx],r0 \n\t"
"mov %[xr1],r1 \n\t"
"eor r1, r1 \n\t"
"swap %[mx] \n\t"
"andi %[mx],0x0F \n\t"
"swap %[xr1] \n\t"
"andi %[xr1], 0xF0 \n\t"
"or %[mx], %[xr1] \n\t"
: [mx] "=d" (mx), [xr1] "=d" (xr1)
: [m16] "d" (m16), [secoffset] "d" (secoffset)
);
int8_t y = mx + b;
if( theta & 0x80 ) y = -y;
y += 128;
return y;
}
/// Fast 8-bit approximation of sin(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (sin(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t sin8_C( uint8_t theta)
{
uint8_t offset = theta;
if( theta & 0x40 ) {
offset = (uint8_t)255 - offset;
}
offset &= 0x3F; // 0..63
uint8_t secoffset = offset & 0x0F; // 0..15
if( theta & 0x40) secoffset++;
uint8_t section = offset >> 4; // 0..3
uint8_t s2 = section * 2;
const uint8_t* p = b_m16_interleave;
p += s2;
uint8_t b = *p;
p++;
uint8_t m16 = *p;
uint8_t mx = (m16 * secoffset) >> 4;
int8_t y = mx + b;
if( theta & 0x80 ) y = -y;
y += 128;
return y;
}
/// Fast 8-bit approximation of cos(x). This approximation never varies more than
/// 2% from the floating point value you'd get by doing
///
/// float s = (cos(x) * 128.0) + 128;
///
/// @param theta input angle from 0-255
/// @returns sin of theta, value between 0 and 255
LIB8STATIC uint8_t cos8( uint8_t theta)
{
return sin8( theta + 64);
}
///@}
#endif

@ -79,7 +79,7 @@ float startup_song[][2] = STARTUP_SONG;
static void gpt_cb8(GPTDriver *gptp);
#define DAC_BUFFER_SIZE 720
#define DAC_BUFFER_SIZE 100
#ifndef DAC_SAMPLE_MAX
#define DAC_SAMPLE_MAX 65535U
#endif
@ -98,8 +98,8 @@ static void gpt_cb8(GPTDriver *gptp);
RESTART_CHANNEL_1()
#define UPDATE_CHANNEL_2_FREQ(freq) gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \
RESTART_CHANNEL_2()
#define GET_CHANNEL_1_FREQ gpt6cfg1.frequency
#define GET_CHANNEL_2_FREQ gpt7cfg1.frequency
#define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE)
#define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE)
/*

@ -713,4 +713,83 @@
H__NOTE(_B5), H__NOTE(_C6), H__NOTE(_E6), H__NOTE(_G6), WD_NOTE(_G6), Q__NOTE(_C6), B__NOTE(_C6), H__NOTE(_B6), \
Q__NOTE(_C7), BD_NOTE(_C7),
#define ISABELLAS_LULLABY \
W__NOTE(_BF4), B__NOTE(_D5), W__NOTE(_EF5), B__NOTE(_F5), W__NOTE(_BF5), B__NOTE(_AF5), W__NOTE(_GF5), BD_NOTE(_F5), B__NOTE(_CS5), \
W__NOTE(_F5), B__NOTE(_C5), W__NOTE(_EF5), BD_NOTE(_BF4), W__NOTE(_AF4), W__NOTE(_BF4), W__NOTE(_F5), W__NOTE(_GF5), \
WD_NOTE(_AF5), H__NOTE(_FS5), W__NOTE(_F5), B__NOTE(_EF5), W__NOTE(_C6), B__NOTE(_AF5), W__NOTE(_F5), WD_NOTE(_AF5), \
H__NOTE(_BF5), W__NOTE(_F5), WD_NOTE(_AF5), H__NOTE(_BF5), W__NOTE(_F5), W__NOTE(_EF5), W__NOTE(_BF4), W__NOTE(_AF5), \
WD_NOTE(_F5), H__NOTE(_F5), H__NOTE(_BF5), H__NOTE(_C6), WD_NOTE(_CS6), H__NOTE(_C6), W__NOTE(_BF5), W__NOTE(_AF5), \
W__NOTE(_F5), W__NOTE(_EF5), WD_NOTE(_EF5), H__NOTE(_DF5), W__NOTE(_AF5), BD_NOTE(_F5), WD_NOTE(_BF4), H__NOTE(_C5), \
W__NOTE(_CS5), W__NOTE(_EF5), W__NOTE(_AF4), W__NOTE(_EF5), WD_NOTE(_GF5), H__NOTE(_F5), W__NOTE(_EF5), WD_NOTE(_F5), \
H__NOTE(_F5), H__NOTE(_BF5), H__NOTE(_C6), WD_NOTE(_CS6), H__NOTE(_C6), W__NOTE(_CS6), W__NOTE(_EF6), W__NOTE(_AF5), \
W__NOTE(_EF6), WD_NOTE(_GF6), H__NOTE(_F6), W__NOTE(_EF6), B__NOTE(_DF6), H__NOTE(_GF6), H__NOTE(_AF6), BD_NOTE(_DF6), \
B__NOTE(_BF5), W__NOTE(_F6), BD_NOTE(_C6), W__NOTE(_AF5), WD_NOTE(_EF6), H__NOTE(_DF6), W__NOTE(_C6), B__NOTE(_BF5),
#define FANTASIE_IMPROMPTU \
E__NOTE(_GS4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), \
E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_GS4), E__NOTE(_A4), \
E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), \
E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_A4), E__NOTE(_CS5), E__NOTE(_DS5), \
E__NOTE(_FS5), E__NOTE(_A5), E__NOTE(_CS6), E__NOTE(_DS6), E__NOTE(_B6), E__NOTE(_A6), E__NOTE(_GS6), E__NOTE(_FS6), \
E__NOTE(_E6), E__NOTE(_DS6), E__NOTE(_FS6), E__NOTE(_CS6), E__NOTE(_C5), E__NOTE(_DS6), E__NOTE(_A5), E__NOTE(_GS5), \
E__NOTE(_FS5), E__NOTE(_A5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_FS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_DS5), \
E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_B4), E__NOTE(_A4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_A4), E__NOTE(_GS4), \
E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), \
E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_GS4), E__NOTE(_AS4), E__NOTE(_GS4), E__NOTE(_REST), \
E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), \
E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_DS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_REST), E__NOTE(_DS5), \
E__NOTE(_B5), E__NOTE(_AS5), E__NOTE(_GS5), E__NOTE(_REST), E__NOTE(_E6), E__NOTE(_DS6), E__NOTE(_CS6), E__NOTE(_B5), \
E__NOTE(_AS5), E__NOTE(_GS5), E__NOTE(_REST), E__NOTE(_AS5), WD_NOTE(_GS5),
#define TERRAS_THEME \
Q__NOTE(_GS5), Q__NOTE(_AS5), Q__NOTE(_B5), Q__NOTE(_EF6), BD_NOTE(_B5), Q__NOTE(_AS5), Q__NOTE(_GS5), W__NOTE(_AS5), \
BD_NOTE(_DS5), Q__NOTE(_AF5), Q__NOTE(_BF5), Q__NOTE(_B5), Q__NOTE(_DS6), BD_NOTE(_B5), \
Q__NOTE(_BF5), Q__NOTE(_AF5), W__NOTE(_AS5), BD_NOTE(_DS6), Q__NOTE(_B5), Q__NOTE(_CS6), Q__NOTE(_DS6), \
Q__NOTE(_FS6), BD_NOTE(_DS6), Q__NOTE(_CS6), Q__NOTE(_B5), W__NOTE(_CS6), BD_NOTE(_FS5), \
Q__NOTE(_B5), Q__NOTE(_AS5), BD_NOTE(_GS5), Q__NOTE(_B5), Q__NOTE(_AS5), BD_NOTE(_GS5),
#define RENAI_CIRCULATION \
Q__NOTE(_E6), Q__NOTE(_B5), HD_NOTE(_CS6), HD_NOTE(_CS6), H__NOTE(_B5), HD_NOTE(_E6), HD_NOTE(_E6), Q__NOTE(_E6), Q__NOTE(_B5), \
HD_NOTE(_CS6), HD_NOTE(_CS6), H__NOTE(_B5), HD_NOTE(_E6), HD_NOTE(_GS6), Q__NOTE(_E6), Q__NOTE(_B5), HD_NOTE(_CS6), \
H__NOTE(_CS6), Q__NOTE(_CS6), H__NOTE(_B5), HD_NOTE(_E6), H__NOTE(_E6), Q__NOTE(_E6), H__NOTE(_FS6), HD_NOTE(_E6), \
H__NOTE(_E6), Q__NOTE(_E6), H__NOTE(_CS6), WD_NOTE(_GS6), HD_NOTE(_E6), H__NOTE(_E6), Q__NOTE(_FS6), H__NOTE(_G6), \
HD_NOTE(_GS6), HD_NOTE(_E6), Q__NOTE(_B5), Q__NOTE(_CS6), HD_NOTE(_E6), H__NOTE(_E6), Q__NOTE(_FS6), H__NOTE(_G6), \
HD_NOTE(_GS6), HD_NOTE(_E6), H__NOTE(_CS6), H__NOTE(_E6), Q__NOTE(_CS6), HD_NOTE(_E6), H__NOTE(_CS6), H__NOTE(_E6), \
Q__NOTE(_CS6), HD_NOTE(_E6), H__NOTE(_E6), Q__NOTE(_A6), H__NOTE(_GS6), HD_NOTE(_E6), H__NOTE(_FS6), WD_NOTE(_E6), \
H__NOTE(_GS6), H__NOTE(_A6), H__NOTE(_GS6), H__NOTE(_A6), W__NOTE(_B6), H__NOTE(_GS6), H__NOTE(_A6), H__NOTE(_GS6), \
H__NOTE(_A6), W__NOTE(_B6), H__NOTE(_B6), H__NOTE(_A6), H__NOTE(_GS6), H__NOTE(_A6), Q__NOTE(_GS6), H__NOTE(_E6), \
H__NOTE(_E6), Q__NOTE(_E6), H__NOTE(_CS6), Q__NOTE(_GS6), H__NOTE(_E6), H__NOTE(_E6), Q__NOTE(_E6), H__NOTE(_CS6), \
Q__NOTE(_E6), H__NOTE(_E6), H__NOTE(_E6), Q__NOTE(_E6), H__NOTE(_FS6), WD_NOTE(_E6), W__NOTE(_B6), W__NOTE(_GS6), \
W__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_GS6), H__NOTE(_FS6), H__NOTE(_E6), H__NOTE(_FS6), B__NOTE(_GS6), H__NOTE(_GS6), \
W__NOTE(_CS7), W__NOTE(_GS6), W__NOTE(_E6), H__NOTE(_GS6), H__NOTE(_GS6), HD_NOTE(_E6), H__NOTE(_E6), Q__NOTE(_E6), \
H__NOTE(_FS6), WD_NOTE(_E6),
#define PLATINUM_DISCO \
H__NOTE(_DS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_DS6), H__NOTE(_FS6), W__NOTE(_GS6), H__NOTE(_DS6), H__NOTE(_FS6), \
H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_CS6), H__NOTE(_FS6), WD_NOTE(_FS6), H__NOTE(_CS6), W__NOTE(_DS6), H__NOTE(_FS6), \
H__NOTE(_AS6), W__NOTE(_GS6), H__NOTE(_FS6), H__NOTE(_GS6), Q__NOTE(_AS6), Q__NOTE(_CS7), Q__NOTE(_GS6), Q__NOTE(_AS6), \
Q__NOTE(_FS6), Q__NOTE(_GS6), Q__NOTE(_DS6), Q__NOTE(_FS6), Q__NOTE(_CS6), Q__NOTE(_DS6), Q__NOTE(_AS5), Q__NOTE(_CS6), \
H__NOTE(_DS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_DS6), H__NOTE(_FS6), W__NOTE(_GS6), H__NOTE(_DS6), \
H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_CS7), H__NOTE(_GS6), WD_NOTE(_FS6), H__NOTE(_CS6), W__NOTE(_DS6), \
H__NOTE(_FS6), H__NOTE(_AS6), WD_NOTE(_GS6), H__NOTE(_FS6), Q__NOTE(_FS6), Q__NOTE(_GS5), Q__NOTE(_AS5), Q__NOTE(_CS6), \
Q__NOTE(_FS6), Q__NOTE(_GS6), Q__NOTE(_AS6), Q__NOTE(_CS7), WD_NOTE(_FS7), H__NOTE(_CS6), WD_NOTE(_DS6), H__NOTE(_CS6), \
WD_NOTE(_DS6), H__NOTE(_CS6), H__NOTE(_DS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_AS6), WD_NOTE(_GS6), H__NOTE(_FS6), \
WD_NOTE(_GS6), H__NOTE(_FS6), WD_NOTE(_GS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_DS6), H__NOTE(_FS6), \
WD_NOTE(_FS6), H__NOTE(_CS6), WD_NOTE(_DS6), H__NOTE(_CS6), WD_NOTE(_DS6), H__NOTE(_CS6), H__NOTE(_DS6), H__NOTE(_FS6), \
H__NOTE(_GS6), H__NOTE(_AS6), H__NOTE(_CS7), H__NOTE(_AS6), H__NOTE(_GS6), H__NOTE(_FS6), H__NOTE(_DS6), W__NOTE(_FS6), \
H__NOTE(_CS6), H__NOTE(_DS6), W__NOTE(_FS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_FS6), H__NOTE(_GS6), H__NOTE(_FS6), \
B__NOTE(_FS6),
#define NOCTURNE_OP_9_NO_1 \
H__NOTE(_BF5), H__NOTE(_C6), H__NOTE(_DF6), H__NOTE(_A5), H__NOTE(_BF5), H__NOTE(_GF5), W__NOTE(_F5), W__NOTE(_F5), W__NOTE(_F5), \
W__NOTE(_F5), H__NOTE(_GF5), H__NOTE(_F5), H__NOTE(_EF5), H__NOTE(_C5), B__NOTE(_DF5), W__NOTE(_BF4), Q__NOTE(_BF5), \
Q__NOTE(_C6), Q__NOTE(_DF6), Q__NOTE(_A5), Q__NOTE(_BF5), Q__NOTE(_A5), Q__NOTE(_GS5), Q__NOTE(_A5), Q__NOTE(_C6), \
Q__NOTE(_BF5), Q__NOTE(_GF5), Q__NOTE(_F5), Q__NOTE(_GF5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_BF5), Q__NOTE(_A5), \
Q__NOTE(_AF5), Q__NOTE(_G5), Q__NOTE(_GF5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_EF5), Q__NOTE(_D5), Q__NOTE(_DF5), \
Q__NOTE(_C5), Q__NOTE(_DF5), Q__NOTE(_C5), Q__NOTE(_B4), Q__NOTE(_C5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_EF5), \
B__NOTE(_DF5), W__NOTE(_BF4), W__NOTE(_BF5), W__NOTE(_BF5), W__NOTE(_BF5), BD_NOTE(_AF5), W__NOTE(_DF5), H__NOTE(_BF4), \
H__NOTE(_C5), H__NOTE(_DF5), H__NOTE(_GF5), H__NOTE(_GF5), BD_NOTE(_F5), W__NOTE(_EF5), H__NOTE(_F5), H__NOTE(_EF5), \
H__NOTE(_DF5), H__NOTE(_A4), B__NOTE(_AF4), W__NOTE(_DF5), W__NOTE(_EF5), H__NOTE(_F5), H__NOTE(_EF5), H__NOTE(_DF5), \
H__NOTE(_EF5), BD_NOTE(_F5),
#endif

@ -78,9 +78,11 @@ RGB hsv_to_rgb( HSV hsv )
break;
}
#ifdef USE_CIE1931_CURVE
rgb.r = pgm_read_byte( &CIE1931_CURVE[rgb.r] );
rgb.g = pgm_read_byte( &CIE1931_CURVE[rgb.g] );
rgb.b = pgm_read_byte( &CIE1931_CURVE[rgb.b] );
#endif
return rgb;
}

@ -0,0 +1,100 @@
/*
Copyright 2019 Alex Ong<the.onga@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
/*
Basic per-row algorithm. Uses an 8-bit counter per row.
After pressing a key, it immediately changes state, and sets a counter.
No further inputs are accepted until DEBOUNCE milliseconds have occurred.
*/
#include "matrix.h"
#include "timer.h"
#include "quantum.h"
#include <stdlib.h>
#ifndef DEBOUNCE
#define DEBOUNCE 5
#endif
#define debounce_counter_t uint8_t
static debounce_counter_t *debounce_counters;
#define DEBOUNCE_ELAPSED 251
#define MAX_DEBOUNCE (DEBOUNCE_ELAPSED - 1)
void update_debounce_counters(uint8_t num_rows, uint8_t current_time);
void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time);
//we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows)
{
debounce_counters = (debounce_counter_t*)malloc(num_rows*sizeof(debounce_counter_t));
for (uint8_t r = 0; r < num_rows; r++)
{
debounce_counters[r] = DEBOUNCE_ELAPSED;
}
}
void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed)
{
uint8_t current_time = timer_read() % MAX_DEBOUNCE;
update_debounce_counters(num_rows, current_time);
transfer_matrix_values(raw, cooked, num_rows, current_time);
}
//If the current time is > debounce counter, set the counter to enable input.
void update_debounce_counters(uint8_t num_rows, uint8_t current_time)
{
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++)
{
if (*debounce_pointer != DEBOUNCE_ELAPSED)
{
if (TIMER_DIFF(current_time, *debounce_pointer, MAX_DEBOUNCE) >= DEBOUNCE) {
*debounce_pointer = DEBOUNCE_ELAPSED;
}
}
debounce_pointer++;
}
}
// upload from raw_matrix to final matrix;
void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t current_time)
{
debounce_counter_t *debounce_pointer = debounce_counters;
for (uint8_t row = 0; row < num_rows; row++)
{
matrix_row_t existing_row = cooked[row];
matrix_row_t raw_row = raw[row];
//determine new value basd on debounce pointer + raw value
if (*debounce_pointer == DEBOUNCE_ELAPSED &&
(existing_row != raw_row))
{
*debounce_pointer = current_time;
existing_row = raw_row;
}
cooked[row] = existing_row;
debounce_pointer++;
}
}
bool debounce_active(void)
{
return true;
}

@ -22,7 +22,7 @@ Here are a few that could be implemented:
sym_g.c
sym_pk.c
sym_pr.c
sym_pr_cycles.c //currently used in ergo-dox
sym_pr_cycles.c
eager_g.c
eager_pk.c
eager_pr.c //could be used in ergo-dox!

@ -210,19 +210,27 @@ void dynamic_keymap_macro_send( uint8_t id )
++p;
}
// Send the macro string one char at a time
// by making temporary 1 char strings
char data[2] = { 0, 0 };
// Send the macro string one or two chars at a time
// by making temporary 1 or 2 char strings
char data[3] = { 0, 0, 0 };
// We already checked there was a null at the end of
// the buffer, so this cannot go past the end
while ( 1 ) {
data[0] = eeprom_read_byte(p);
data[0] = eeprom_read_byte(p++);
data[1] = 0;
// Stop at the null terminator of this macro string
if ( data[0] == 0 ) {
break;
}
// If the char is magic (tap, down, up),
// add the next char (key to use) and send a 2 char string.
if ( data[0] == SS_TAP_CODE || data[0] == SS_DOWN_CODE || data[0] == SS_UP_CODE ) {
data[1] = eeprom_read_byte(p++);
if ( data[1] == 0 ) {
break;
}
}
send_string(data);
++p;
}
}

@ -17,6 +17,10 @@
#include "encoder.h"
// for memcpy
#include <string.h>
#ifndef ENCODER_RESOLUTION
#define ENCODER_RESOLUTION 4
#endif
@ -35,7 +39,13 @@ static pin_t encoders_pad_b[NUMBER_OF_ENCODERS] = ENCODERS_PAD_B;
static int8_t encoder_LUT[] = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 };
static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
#ifdef SPLIT_KEYBOARD
// slave half encoders come over as second set of encoders
static int8_t encoder_value[NUMBER_OF_ENCODERS * 2] = {0};
#else
static int8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
#endif
__attribute__ ((weak))
void encoder_update_user(int8_t index, bool clockwise) { }
@ -60,11 +70,30 @@ void encoder_read(void) {
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
encoder_value[i] += encoder_LUT[encoder_state[i] & 0xF];
if (encoder_value[i] >= ENCODER_RESOLUTION) {
encoder_update_kb(i, COUNTRECLOCKWISE);
encoder_update_kb(i, false);
}
if (encoder_value[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
encoder_update_kb(i, CLOCKWISE);
encoder_update_kb(i, true);
}
encoder_value[i] %= ENCODER_RESOLUTION;
}
}
#ifdef SPLIT_KEYBOARD
void encoder_state_raw(uint8_t* slave_state) {
memcpy(slave_state, encoder_state, sizeof(encoder_state));
}
void encoder_update_raw(uint8_t* slave_state) {
for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_value[NUMBER_OF_ENCODERS + i] += encoder_LUT[slave_state[i] & 0xF];
if (encoder_value[NUMBER_OF_ENCODERS + i] >= ENCODER_RESOLUTION) {
encoder_update_kb(NUMBER_OF_ENCODERS + i, false);
}
if (encoder_value[NUMBER_OF_ENCODERS + i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
encoder_update_kb(NUMBER_OF_ENCODERS + i, true);
}
encoder_value[NUMBER_OF_ENCODERS + i] %= ENCODER_RESOLUTION;
}
}
#endif

@ -19,11 +19,13 @@
#include "quantum.h"
#define COUNTRECLOCKWISE 0
#define CLOCKWISE 1
void encoder_init(void);
void encoder_read(void);
void encoder_update_kb(int8_t index, bool clockwise);
void encoder_update_user(int8_t index, bool clockwise);
#ifdef SPLIT_KEYBOARD
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
#endif

@ -14,24 +14,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "process_combo.h"
#include "print.h"
#include "process_combo.h"
__attribute__ ((weak))
combo_t key_combos[COMBO_COUNT] = {
__attribute__((weak)) combo_t key_combos[COMBO_COUNT] = {
};
__attribute__ ((weak))
void process_combo_event(uint8_t combo_index, bool pressed) {
}
__attribute__((weak)) void process_combo_event(uint8_t combo_index,
bool pressed) {}
static uint16_t timer = 0;
static uint8_t current_combo_index = 0;
static bool drop_buffer = false;
static bool is_active = false;
static inline void send_combo(uint16_t action, bool pressed)
{
static uint8_t buffer_size = 0;
#ifdef COMBO_ALLOW_ACTION_KEYS
static keyrecord_t key_buffer[MAX_COMBO_LENGTH];
#else
static uint16_t key_buffer[MAX_COMBO_LENGTH];
#endif
static inline void send_combo(uint16_t action, bool pressed) {
if (action) {
if (pressed) {
register_code16(action);
@ -43,26 +48,55 @@ static inline void send_combo(uint16_t action, bool pressed)
}
}
static inline void dump_key_buffer(bool emit) {
if (buffer_size == 0) {
return;
}
if (emit) {
for (uint8_t i = 0; i < buffer_size; i++) {
#ifdef COMBO_ALLOW_ACTION_KEYS
const action_t action = store_or_get_action(key_buffer[i].event.pressed,
key_buffer[i].event.key);
process_action(&(key_buffer[i]), action);
#else
register_code16(key_buffer[i]);
send_keyboard_report();
#endif
}
}
buffer_size = 0;
}
#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state)
#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
#define KEY_STATE_DOWN(key) do{ combo->state |= (1<<key); } while(0)
#define KEY_STATE_UP(key) do{ combo->state &= ~(1<<key); } while(0)
static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record)
{
#define KEY_STATE_DOWN(key) \
do { \
combo->state |= (1 << key); \
} while (0)
#define KEY_STATE_UP(key) \
do { \
combo->state &= ~(1 << key); \
} while (0)
static bool process_single_combo(combo_t *combo, uint16_t keycode,
keyrecord_t *record) {
uint8_t count = 0;
uint8_t index = -1;
/* Find index of keycode and number of combo keys */
for (const uint16_t *keys = combo->keys;; ++count) {
uint16_t key = pgm_read_word(&keys[count]);
if (keycode == key) index = count;
if (COMBO_END == key) break;
if (keycode == key)
index = count;
if (COMBO_END == key)
break;
}
/* Return if not a combo key */
if (-1 == (int8_t)index) return false;
/* Continue processing if not a combo key */
if (-1 == (int8_t)index)
return false;
/* The combos timer is used to signal whether the combo is active */
bool is_combo_active = combo->is_active;
bool is_combo_active = is_active;
if (record->event.pressed) {
KEY_STATE_DOWN(index);
@ -70,85 +104,74 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
if (is_combo_active) {
if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
send_combo(combo->keycode, true);
combo->is_active = false;
} else { /* Combo key was pressed */
combo->timer = timer_read();
combo->is_active = true;
#ifdef COMBO_ALLOW_ACTION_KEYS
combo->prev_record = *record;
#else
combo->prev_key = keycode;
#endif
drop_buffer = true;
}
}
} else {
if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
send_combo(combo->keycode, false);
}
if (is_combo_active) { /* Combo key was tapped */
#ifdef COMBO_ALLOW_ACTION_KEYS
record->event.pressed = true;
process_action(record, store_or_get_action(record->event.pressed, record->event.key));
record->event.pressed = false;
process_action(record, store_or_get_action(record->event.pressed, record->event.key));
#else
register_code16(keycode);
send_keyboard_report();
unregister_code16(keycode);
#endif
combo->is_active = false;
combo->timer = 0;
} else {
/* continue processing without immediately returning */
is_combo_active = false;
}
KEY_STATE_UP(index);
}
if (NO_COMBO_KEYS_ARE_DOWN) {
combo->is_active = true;
combo->timer = 0;
}
return is_combo_active;
}
bool process_combo(uint16_t keycode, keyrecord_t *record)
{
#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
bool process_combo(uint16_t keycode, keyrecord_t *record) {
bool is_combo_key = false;
drop_buffer = false;
bool no_combo_keys_pressed = false;
for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
for (current_combo_index = 0; current_combo_index < COMBO_COUNT;
++current_combo_index) {
combo_t *combo = &key_combos[current_combo_index];
is_combo_key |= process_single_combo(combo, keycode, record);
no_combo_keys_pressed |= NO_COMBO_KEYS_ARE_DOWN;
}
if (drop_buffer) {
/* buffer is only dropped when we complete a combo, so we refresh the timer
* here */
timer = timer_read();
dump_key_buffer(false);
} else if (!is_combo_key) {
/* if no combos claim the key we need to emit the keybuffer */
dump_key_buffer(true);
// reset state if there are no combo keys pressed at all
if (no_combo_keys_pressed) {
timer = 0;
is_active = true;
}
} else if (record->event.pressed && is_active) {
/* otherwise the key is consumed and placed in the buffer */
timer = timer_read();
if (buffer_size < MAX_COMBO_LENGTH) {
#ifdef COMBO_ALLOW_ACTION_KEYS
key_buffer[buffer_size++] = *record;
#else
key_buffer[buffer_size++] = keycode;
#endif
}
}
return !is_combo_key;
}
void matrix_scan_combo(void)
{
for (int i = 0; i < COMBO_COUNT; ++i) {
// Do not treat the (weak) key_combos too strict.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
combo_t *combo = &key_combos[i];
#pragma GCC diagnostic pop
if (combo->is_active &&
combo->timer &&
timer_elapsed(combo->timer) > COMBO_TERM) {
void matrix_scan_combo(void) {
if (is_active && timer && timer_elapsed(timer) > COMBO_TERM) {
/* This disables the combo, meaning key events for this
* combo will be handled by the next processors in the chain
*/
combo->is_active = false;
#ifdef COMBO_ALLOW_ACTION_KEYS
process_action(&combo->prev_record,
store_or_get_action(combo->prev_record.event.pressed,
combo->prev_record.event.key));
#else
unregister_code16(combo->prev_key);
register_code16(combo->prev_key);
#endif
}
is_active = false;
dump_key_buffer(true);
}
}

@ -17,12 +17,19 @@
#ifndef PROCESS_COMBO_H
#define PROCESS_COMBO_H
#include <stdint.h>
#include "progmem.h"
#include "quantum.h"
#include <stdint.h>
#ifdef EXTRA_EXTRA_LONG_COMBOS
#define MAX_COMBO_LENGTH 32
#elif EXTRA_LONG_COMBOS
#define MAX_COMBO_LENGTH 16
#else
#define MAX_COMBO_LENGTH 8
#endif
typedef struct
{
typedef struct {
const uint16_t *keys;
uint16_t keycode;
#ifdef EXTRA_EXTRA_LONG_COMBOS
@ -31,19 +38,13 @@ typedef struct
uint16_t state;
#else
uint8_t state;
#endif
uint16_t timer;
bool is_active;
#ifdef COMBO_ALLOW_ACTION_KEYS
keyrecord_t prev_record;
#else
uint16_t prev_key;
#endif
} combo_t;
#define COMBO(ck, ca) {.keys = &(ck)[0], .keycode = (ca)}
#define COMBO_ACTION(ck) {.keys = &(ck)[0]}
#define COMBO(ck, ca) \
{ .keys = &(ck)[0], .keycode = (ca) }
#define COMBO_ACTION(ck) \
{ .keys = &(ck)[0] }
#define COMBO_END 0
#ifndef COMBO_COUNT

@ -225,27 +225,39 @@ static uint16_t scs_timer[2] = {0, 0};
*/
static bool grave_esc_was_shifted = false;
bool process_record_quantum(keyrecord_t *record) {
/* Convert record into usable keycode via the contained event. */
uint16_t get_record_keycode(keyrecord_t *record) {
return get_event_keycode(record->event);
}
/* This gets the keycode from the key pressed */
keypos_t key = record->event.key;
uint16_t keycode;
/* Convert event into usable keycode. Checks the layer cache to ensure that it
* retains the correct keycode after a layer change, if the key is still pressed.
*/
uint16_t get_event_keycode(keyevent_t event) {
#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
/* TODO: Use store_or_get_action() or a similar function. */
if (!disable_action_cache) {
uint8_t layer;
if (record->event.pressed) {
layer = layer_switch_get_layer(key);
update_source_layers_cache(key, layer);
if (event.pressed) {
layer = layer_switch_get_layer(event.key);
update_source_layers_cache(event.key, layer);
} else {
layer = read_source_layers_cache(key);
layer = read_source_layers_cache(event.key);
}
keycode = keymap_key_to_keycode(layer, key);
return keymap_key_to_keycode(layer, event.key);
} else
#endif
keycode = keymap_key_to_keycode(layer_switch_get_layer(key), key);
return keymap_key_to_keycode(layer_switch_get_layer(event.key), event.key);
}
/* Main keycode processing function. Hands off handling to other functions,
* then processes internal Quantum keycodes, then processes ACTIONs.
*/
bool process_record_quantum(keyrecord_t *record) {
uint16_t keycode = get_record_keycode(record);
// This is how you use actions here
// if (keycode == KC_LEAD) {
@ -274,10 +286,10 @@ bool process_record_quantum(keyrecord_t *record) {
#ifdef HAPTIC_ENABLE
process_haptic(keycode, record) &&
#endif //HAPTIC_ENABLE
process_record_kb(keycode, record) &&
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYPRESSES)
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
process_rgb_matrix(keycode, record) &&
#endif
process_record_kb(keycode, record) &&
#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
process_midi(keycode, record) &&
#endif
@ -870,16 +882,16 @@ void send_string_with_delay(const char *str, uint8_t interval) {
while (1) {
char ascii_code = *str;
if (!ascii_code) break;
if (ascii_code == 1) {
if (ascii_code == SS_TAP_CODE) {
// tap
uint8_t keycode = *(++str);
register_code(keycode);
unregister_code(keycode);
} else if (ascii_code == 2) {
} else if (ascii_code == SS_DOWN_CODE) {
// down
uint8_t keycode = *(++str);
register_code(keycode);
} else if (ascii_code == 3) {
} else if (ascii_code == SS_UP_CODE) {
// up
uint8_t keycode = *(++str);
unregister_code(keycode);
@ -896,16 +908,16 @@ void send_string_with_delay_P(const char *str, uint8_t interval) {
while (1) {
char ascii_code = pgm_read_byte(str);
if (!ascii_code) break;
if (ascii_code == 1) {
if (ascii_code == SS_TAP_CODE) {
// tap
uint8_t keycode = pgm_read_byte(++str);
register_code(keycode);
unregister_code(keycode);
} else if (ascii_code == 2) {
} else if (ascii_code == SS_DOWN_CODE) {
// down
uint8_t keycode = pgm_read_byte(++str);
register_code(keycode);
} else if (ascii_code == 3) {
} else if (ascii_code == SS_UP_CODE) {
// up
uint8_t keycode = pgm_read_byte(++str);
unregister_code(keycode);
@ -1049,12 +1061,6 @@ void matrix_init_quantum() {
matrix_init_kb();
}
uint8_t rgb_matrix_task_counter = 0;
#ifndef RGB_MATRIX_SKIP_FRAMES
#define RGB_MATRIX_SKIP_FRAMES 1
#endif
void matrix_scan_quantum() {
#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)
matrix_scan_music();
@ -1078,10 +1084,6 @@ void matrix_scan_quantum() {
#ifdef RGB_MATRIX_ENABLE
rgb_matrix_task();
if (rgb_matrix_task_counter == 0) {
rgb_matrix_update_pwm_buffers();
}
rgb_matrix_task_counter = ((rgb_matrix_task_counter + 1) % (RGB_MATRIX_SKIP_FRAMES + 1));
#endif
#ifdef ENCODER_ENABLE

@ -187,6 +187,10 @@ extern uint32_t default_layer_state;
#define ADD_SLASH_X(y) STRINGIZE(\x ## y)
#define SYMBOL_STR(x) ADD_SLASH_X(x)
#define SS_TAP_CODE 1
#define SS_DOWN_CODE 2
#define SS_UP_CODE 3
#define SS_TAP(keycode) "\1" SYMBOL_STR(keycode)
#define SS_DOWN(keycode) "\2" SYMBOL_STR(keycode)
#define SS_UP(keycode) "\3" SYMBOL_STR(keycode)
@ -224,6 +228,8 @@ void matrix_init_kb(void);
void matrix_scan_kb(void);
void matrix_init_user(void);
void matrix_scan_user(void);
uint16_t get_record_keycode(keyrecord_t *record);
uint16_t get_event_keycode(keyevent_t event);
bool process_action_kb(keyrecord_t *record);
bool process_record_kb(uint16_t keycode, keyrecord_t *record);
bool process_record_user(uint16_t keycode, keyrecord_t *record);

@ -24,15 +24,26 @@
#include <string.h>
#include <math.h>
rgb_config_t rgb_matrix_config;
#ifndef MAX
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b)? (a): (b))
#endif
#include "lib/lib8tion/lib8tion.h"
#include "rgb_matrix_animations/solid_color_anim.h"
#include "rgb_matrix_animations/alpha_mods_anim.h"
#include "rgb_matrix_animations/dual_beacon_anim.h"
#include "rgb_matrix_animations/gradient_up_down_anim.h"
#include "rgb_matrix_animations/raindrops_anim.h"
#include "rgb_matrix_animations/cycle_all_anim.h"
#include "rgb_matrix_animations/cycle_left_right_anim.h"
#include "rgb_matrix_animations/cycle_up_down_anim.h"
#include "rgb_matrix_animations/rainbow_beacon_anim.h"
#include "rgb_matrix_animations/rainbow_pinwheels_anim.h"
#include "rgb_matrix_animations/rainbow_moving_chevron_anim.h"
#include "rgb_matrix_animations/jellybean_raindrops_anim.h"
#include "rgb_matrix_animations/digital_rain_anim.h"
#include "rgb_matrix_animations/solid_reactive_simple_anim.h"
#include "rgb_matrix_animations/solid_reactive_anim.h"
#include "rgb_matrix_animations/splash_anim.h"
#include "rgb_matrix_animations/solid_splash_anim.h"
#include "rgb_matrix_animations/breathing_anim.h"
#ifndef RGB_DISABLE_AFTER_TIMEOUT
#define RGB_DISABLE_AFTER_TIMEOUT 0
@ -46,55 +57,63 @@ rgb_config_t rgb_matrix_config;
#define EECONFIG_RGB_MATRIX EECONFIG_RGBLIGHT
#endif
#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > 255
#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 255
#if !defined(RGB_MATRIX_MAXIMUM_BRIGHTNESS) || RGB_MATRIX_MAXIMUM_BRIGHTNESS > UINT8_MAX
#undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
#define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX
#endif
#ifndef RGB_DIGITAL_RAIN_DROPS
// lower the number for denser effect/wider keyboard
#define RGB_DIGITAL_RAIN_DROPS 24
#if !defined(RGB_MATRIX_HUE_STEP)
#define RGB_MATRIX_HUE_STEP 8
#endif
#if !defined(DISABLE_RGB_MATRIX_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_DIGITAL_RAIN)
#define TRACK_PREVIOUS_EFFECT
#if !defined(RGB_MATRIX_SAT_STEP)
#define RGB_MATRIX_SAT_STEP 16
#endif
bool g_suspend_state = false;
#if !defined(RGB_MATRIX_VAL_STEP)
#define RGB_MATRIX_VAL_STEP 16
#endif
#if !defined(RGB_MATRIX_SPD_STEP)
#define RGB_MATRIX_SPD_STEP 16
#endif
// Global tick at 20 Hz
uint32_t g_tick = 0;
bool g_suspend_state = false;
// Ticks since this key was last hit.
uint8_t g_key_hit[DRIVER_LED_TOTAL];
rgb_config_t rgb_matrix_config;
// Ticks since any key was last hit.
uint32_t g_any_key_hit = 0;
rgb_counters_t g_rgb_counters;
static uint32_t rgb_counters_buffer;
#ifndef PI
#define PI 3.14159265
#endif
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
last_hit_t g_last_hit_tracker;
static last_hit_t last_hit_buffer;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
uint32_t eeconfig_read_rgb_matrix(void) {
return eeprom_read_dword(EECONFIG_RGB_MATRIX);
}
void eeconfig_update_rgb_matrix(uint32_t val) {
eeprom_update_dword(EECONFIG_RGB_MATRIX, val);
}
void eeconfig_update_rgb_matrix_default(void) {
dprintf("eeconfig_update_rgb_matrix_default\n");
rgb_matrix_config.enable = 1;
#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
rgb_matrix_config.mode = RGB_MATRIX_CYCLE_LEFT_RIGHT;
#else
// fallback to solid colors if RGB_MATRIX_CYCLE_LEFT_RIGHT is disabled in userspace
rgb_matrix_config.mode = RGB_MATRIX_SOLID_COLOR;
#endif
rgb_matrix_config.hue = 0;
rgb_matrix_config.sat = 255;
rgb_matrix_config.sat = UINT8_MAX;
rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
rgb_matrix_config.speed = 0;
rgb_matrix_config.speed = UINT8_MAX / 2;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void eeconfig_debug_rgb_matrix(void) {
dprintf("rgb_matrix_config eprom\n");
dprintf("rgb_matrix_config.enable = %d\n", rgb_matrix_config.enable);
@ -105,23 +124,17 @@ void eeconfig_debug_rgb_matrix(void) {
dprintf("rgb_matrix_config.speed = %d\n", rgb_matrix_config.speed);
}
// Last led hit
#define LED_HITS_TO_REMEMBER 8
uint8_t g_last_led_hit[LED_HITS_TO_REMEMBER] = {255};
uint8_t g_last_led_count = 0;
void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t *led_count) {
rgb_led led;
*led_count = 0;
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
// map_index_to_led(i, &led);
led = g_rgb_leds[i];
if (row == led.matrix_co.row && column == led.matrix_co.col) {
led_i[*led_count] = i;
(*led_count)++;
uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {
// TODO: This is kinda expensive, fix this soonish
uint8_t led_count = 0;
for (uint8_t i = 0; i < DRIVER_LED_TOTAL && led_count < LED_HITS_TO_REMEMBER; i++) {
matrix_co_t matrix_co = g_rgb_leds[i].matrix_co;
if (row == matrix_co.row && column == matrix_co.col) {
led_i[led_count] = i;
led_count++;
}
}
return led_count;
}
void rgb_matrix_update_pwm_buffers(void) {
@ -129,681 +142,324 @@ void rgb_matrix_update_pwm_buffers(void) {
}
void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) {
#ifdef RGB_MATRIX_EXTRA_TOG
const bool is_key = g_rgb_leds[index].matrix_co.raw != 0xff;
if (
(rgb_matrix_config.enable == RGB_ZONE_KEYS && !is_key) ||
(rgb_matrix_config.enable == RGB_ZONE_UNDER && is_key)
) {
rgb_matrix_driver.set_color(index, 0, 0, 0);
return;
}
#endif
rgb_matrix_driver.set_color(index, red, green, blue);
}
void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
#ifdef RGB_MATRIX_EXTRA_TOG
for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
rgb_matrix_set_color(i, red, green, blue);
}
#else
rgb_matrix_driver.set_color_all(red, green, blue);
#endif
}
bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
if ( record->event.pressed ) {
uint8_t led[8], led_count;
map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
if (led_count > 0) {
for (uint8_t i = LED_HITS_TO_REMEMBER; i > 1; i--) {
g_last_led_hit[i - 1] = g_last_led_hit[i - 2];
}
g_last_led_hit[0] = led[0];
g_last_led_count = MIN(LED_HITS_TO_REMEMBER, g_last_led_count + 1);
}
for(uint8_t i = 0; i < led_count; i++)
g_key_hit[led[i]] = 0;
g_any_key_hit = 0;
} else {
#ifdef RGB_MATRIX_KEYRELEASES
uint8_t led[8], led_count;
map_row_column_to_led(record->event.key.row, record->event.key.col, led, &led_count);
for(uint8_t i = 0; i < led_count; i++)
g_key_hit[led[i]] = 255;
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
uint8_t led[LED_HITS_TO_REMEMBER];
uint8_t led_count = 0;
g_any_key_hit = 255;
#endif
#if defined(RGB_MATRIX_KEYRELEASES)
if (!record->event.pressed) {
led_count = rgb_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led);
g_rgb_counters.any_key_hit = 0;
}
return true;
#elif defined(RGB_MATRIX_KEYPRESSES)
if (record->event.pressed) {
led_count = rgb_matrix_map_row_column_to_led(record->event.key.row, record->event.key.col, led);
g_rgb_counters.any_key_hit = 0;
}
#endif // defined(RGB_MATRIX_KEYRELEASES)
void rgb_matrix_set_suspend_state(bool state) {
g_suspend_state = state;
if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {
memcpy(&last_hit_buffer.x[0], &last_hit_buffer.x[led_count], LED_HITS_TO_REMEMBER - led_count);
memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);
memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit
memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);
last_hit_buffer.count--;
}
for(uint8_t i = 0; i < led_count; i++) {
uint8_t index = last_hit_buffer.count;
last_hit_buffer.x[index] = g_rgb_leds[led[i]].point.x;
last_hit_buffer.y[index] = g_rgb_leds[led[i]].point.y;
last_hit_buffer.index[index] = led[i];
last_hit_buffer.tick[index] = 0;
last_hit_buffer.count++;
}
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
return true;
}
void rgb_matrix_test(void) {
// Mask out bits 4 and 5
// Increase the factor to make the test animation slower (and reduce to make it faster)
uint8_t factor = 10;
switch ( (g_tick & (0b11 << factor)) >> factor )
{
case 0:
switch ( (g_rgb_counters.tick & (0b11 << factor)) >> factor )
{
case 0: {
rgb_matrix_set_color_all( 20, 0, 0 );
break;
}
case 1:
{
case 1: {
rgb_matrix_set_color_all( 0, 20, 0 );
break;
}
case 2:
{
case 2: {
rgb_matrix_set_color_all( 0, 0, 20 );
break;
}
case 3:
{
case 3: {
rgb_matrix_set_color_all( 20, 20, 20 );
break;
}
}
}
// All LEDs off
void rgb_matrix_all_off(void) {
rgb_matrix_set_color_all( 0, 0, 0 );
}
// Solid color
void rgb_matrix_solid_color(void) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color_all( rgb.r, rgb.g, rgb.b );
}
void rgb_matrix_solid_reactive(void) {
// Relies on hue being 8-bit and wrapping
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
uint16_t offset2 = g_key_hit[i]<<2;
offset2 = (offset2<=130) ? (130-offset2) : 0;
HSV hsv = { .h = rgb_matrix_config.hue+offset2, .s = 255, .v = rgb_matrix_config.val };
RGB rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_solid_reactive_simple(void)
{
HSV hsv = {.h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val};
RGB rgb;
for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
uint16_t offset2 = g_key_hit[i] << 2;
offset2 = (offset2 <= 255) ? (255 - offset2) : 0;
hsv.v = offset2 * rgb_matrix_config.val / RGB_MATRIX_MAXIMUM_BRIGHTNESS;
rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
static bool rgb_matrix_none(effect_params_t* params) {
if (!params->init) {
return false;
}
}
// alphas = color1, mods = color2
void rgb_matrix_alphas_mods(void) {
RGB rgb1 = hsv_to_rgb( (HSV){ .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val } );
RGB rgb2 = hsv_to_rgb( (HSV){ .h = (rgb_matrix_config.hue + 180) % 360, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val } );
rgb_led led;
for (int i = 0; i < DRIVER_LED_TOTAL; i++) {
led = g_rgb_leds[i];
if ( led.matrix_co.raw < 0xFF ) {
if ( led.modifier )
{
rgb_matrix_set_color( i, rgb2.r, rgb2.g, rgb2.b );
}
else
{
rgb_matrix_set_color( i, rgb1.r, rgb1.g, rgb1.b );
}
}
RGB_MATRIX_USE_LIMITS(led_min, led_max);
for (uint8_t i = led_min; i < led_max; i++) {
rgb_matrix_set_color(i, 0, 0, 0);
}
return led_max < DRIVER_LED_TOTAL;
}
void rgb_matrix_gradient_up_down(void) {
int16_t h1 = rgb_matrix_config.hue;
int16_t h2 = (rgb_matrix_config.hue + 180) % 360;
int16_t deltaH = h2 - h1;
static uint8_t rgb_last_enable = UINT8_MAX;
static uint8_t rgb_last_effect = UINT8_MAX;
static effect_params_t rgb_effect_params = { 0, 0 };
static rgb_task_states rgb_task_state = SYNCING;
// Take the shortest path between hues
if ( deltaH > 127 )
{
deltaH -= 256;
}
else if ( deltaH < -127 )
{
deltaH += 256;
}
// Divide delta by 4, this gives the delta per row
deltaH /= 4;
int16_t s1 = rgb_matrix_config.sat;
int16_t s2 = rgb_matrix_config.hue;
int16_t deltaS = ( s2 - s1 ) / 4;
HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// map_led_to_point( i, &point );
point = g_rgb_leds[i].point;
// The y range will be 0..64, map this to 0..4
uint8_t y = (point.y>>4);
// Relies on hue being 8-bit and wrapping
hsv.h = rgb_matrix_config.hue + ( deltaH * y );
hsv.s = rgb_matrix_config.sat + ( deltaS * y );
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_raindrops(bool initialize) {
int16_t h1 = rgb_matrix_config.hue;
int16_t h2 = (rgb_matrix_config.hue + 180) % 360;
int16_t deltaH = h2 - h1;
deltaH /= 4;
// Take the shortest path between hues
if ( deltaH > 127 )
{
deltaH -= 256;
}
else if ( deltaH < -127 )
{
deltaH += 256;
}
int16_t s1 = rgb_matrix_config.sat;
int16_t s2 = rgb_matrix_config.sat;
int16_t deltaS = ( s2 - s1 ) / 4;
HSV hsv;
RGB rgb;
// Change one LED every tick, make sure speed is not 0
uint8_t led_to_change = ( g_tick & ( 0x0A / (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed) ) ) == 0 ? rand() % (DRIVER_LED_TOTAL) : 255;
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// If initialize, all get set to random colors
// If not, all but one will stay the same as before.
if ( initialize || i == led_to_change )
{
hsv.h = h1 + ( deltaH * ( rand() & 0x03 ) );
hsv.s = s1 + ( deltaS * ( rand() & 0x03 ) );
// Override brightness with global brightness control
hsv.v = rgb_matrix_config.val;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
}
void rgb_matrix_cycle_all(void) {
uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
rgb_led led;
// Relies on hue being 8-bit and wrapping
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// map_index_to_led(i, &led);
led = g_rgb_leds[i];
if (led.matrix_co.raw < 0xFF) {
uint16_t offset2 = g_key_hit[i]<<2;
offset2 = (offset2<=63) ? (63-offset2) : 0;
HSV hsv = { .h = offset+offset2, .s = 255, .v = rgb_matrix_config.val };
RGB rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
}
void rgb_matrix_cycle_left_right(void) {
uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
rgb_led led;
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// map_index_to_led(i, &led);
led = g_rgb_leds[i];
if (led.matrix_co.raw < 0xFF) {
uint16_t offset2 = g_key_hit[i]<<2;
offset2 = (offset2<=63) ? (63-offset2) : 0;
// map_led_to_point( i, &point );
point = g_rgb_leds[i].point;
// Relies on hue being 8-bit and wrapping
hsv.h = point.x + offset + offset2;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
}
void rgb_matrix_cycle_up_down(void) {
uint8_t offset = ( g_tick << rgb_matrix_config.speed ) & 0xFF;
HSV hsv = { .h = 0, .s = 255, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
rgb_led led;
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// map_index_to_led(i, &led);
led = g_rgb_leds[i];
if (led.matrix_co.raw < 0xFF) {
uint16_t offset2 = g_key_hit[i]<<2;
offset2 = (offset2<=63) ? (63-offset2) : 0;
// map_led_to_point( i, &point );
point = g_rgb_leds[i].point;
// Relies on hue being 8-bit and wrapping
hsv.h = point.y + offset + offset2;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
}
void rgb_matrix_dual_beacon(void) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
double cos_value = cos(g_tick * PI / 128) / 32;
double sin_value = sin(g_tick * PI / 128) / 112;
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
point = g_rgb_leds[i].point;
hsv.h = ((point.y - 32.0)* cos_value + (point.x - 112.0) * sin_value) * (180) + rgb_matrix_config.hue;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_rainbow_beacon(void) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
double cos_value = cos(g_tick * PI / 128);
double sin_value = sin(g_tick * PI / 128);
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
point = g_rgb_leds[i].point;
hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - 112.0) * sin_value + rgb_matrix_config.hue;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_rainbow_pinwheels(void) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
double cos_value = cos(g_tick * PI / 128);
double sin_value = sin(g_tick * PI / 128);
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
point = g_rgb_leds[i].point;
hsv.h = (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (66 - abs(point.x - 112.0)) * sin_value + rgb_matrix_config.hue;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_rainbow_moving_chevron(void) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
Point point;
uint8_t r = 128;
double cos_value = cos(r * PI / 128);
double sin_value = sin(r * PI / 128);
double multiplier = (g_tick / 256.0 * 224);
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
point = g_rgb_leds[i].point;
hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * abs(point.y - 32.0)* sin_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - multiplier) * cos_value + rgb_matrix_config.hue;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
void rgb_matrix_jellybean_raindrops( bool initialize ) {
HSV hsv;
RGB rgb;
// Change one LED every tick, make sure speed is not 0
uint8_t led_to_change = ( g_tick & ( 0x0A / (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed) ) ) == 0 ? rand() % (DRIVER_LED_TOTAL) : 255;
for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
{
// If initialize, all get set to random colors
// If not, all but one will stay the same as before.
if ( initialize || i == led_to_change )
{
hsv.h = rand() & 0xFF;
hsv.s = rand() & 0xFF;
// Override brightness with global brightness control
hsv.v = rgb_matrix_config.val;
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
}
}
void rgb_matrix_digital_rain( const bool initialize ) {
// algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain
const uint8_t drop_ticks = 28;
const uint8_t pure_green_intensity = 0xd0;
const uint8_t max_brightness_boost = 0xc0;
const uint8_t max_intensity = 0xff;
static uint8_t map[MATRIX_COLS][MATRIX_ROWS] = {{0}};
static uint8_t drop = 0;
if (initialize) {
rgb_matrix_set_color_all(0, 0, 0);
memset(map, 0, sizeof map);
drop = 0;
}
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {
// top row, pixels have just fallen and we're
// making a new rain drop in this column
map[col][row] = max_intensity;
}
else if (map[col][row] > 0 && map[col][row] < max_intensity) {
// neither fully bright nor dark, decay it
map[col][row]--;
}
// set the pixel colour
uint8_t led, led_count;
map_row_column_to_led(row, col, &led, &led_count);
if (map[col][row] > pure_green_intensity) {
const uint8_t boost = (uint8_t) ((uint16_t) max_brightness_boost
* (map[col][row] - pure_green_intensity) / (max_intensity - pure_green_intensity));
rgb_matrix_set_color(led, boost, max_intensity, boost);
}
else {
const uint8_t green = (uint8_t) ((uint16_t) max_intensity * map[col][row] / pure_green_intensity);
rgb_matrix_set_color(led, 0, green, 0);
}
}
}
if (++drop > drop_ticks) {
// reset drop timer
drop = 0;
for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
// if ths is on the bottom row and bright allow decay
if (row == MATRIX_ROWS - 1 && map[col][row] == max_intensity) {
map[col][row]--;
}
// check if the pixel above is bright
if (map[col][row - 1] == max_intensity) {
// allow old bright pixel to decay
map[col][row - 1]--;
// make this pixel bright
map[col][row] = max_intensity;
}
}
}
}
}
void rgb_matrix_multisplash(void) {
// if (g_any_key_hit < 0xFF) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
rgb_led led;
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
led = g_rgb_leds[i];
uint16_t c = 0, d = 0;
rgb_led last_led;
// if (g_last_led_count) {
for (uint8_t last_i = 0; last_i < g_last_led_count; last_i++) {
last_led = g_rgb_leds[g_last_led_hit[last_i]];
uint16_t dist = (uint16_t)sqrt(pow(led.point.x - last_led.point.x, 2) + pow(led.point.y - last_led.point.y, 2));
uint16_t effect = (g_key_hit[g_last_led_hit[last_i]] << 2) - dist;
c += MIN(MAX(effect, 0), 255);
d += 255 - MIN(MAX(effect, 0), 255);
}
// } else {
// d = 255;
// }
hsv.h = (rgb_matrix_config.hue + c) % 256;
hsv.v = MAX(MIN(d, 255), 0);
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
}
// } else {
// rgb_matrix_set_color_all( 0, 0, 0 );
// }
static void rgb_task_timers(void) {
// Update double buffer timers
uint16_t deltaTime = timer_elapsed32(rgb_counters_buffer);
rgb_counters_buffer = timer_read32();
if (g_rgb_counters.any_key_hit < UINT32_MAX) {
if (UINT32_MAX - deltaTime < g_rgb_counters.any_key_hit) {
g_rgb_counters.any_key_hit = UINT32_MAX;
} else {
g_rgb_counters.any_key_hit += deltaTime;
}
void rgb_matrix_splash(void) {
g_last_led_count = MIN(g_last_led_count, 1);
rgb_matrix_multisplash();
}
void rgb_matrix_solid_multisplash(void) {
// if (g_any_key_hit < 0xFF) {
HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
RGB rgb;
rgb_led led;
for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
led = g_rgb_leds[i];
uint16_t d = 0;
rgb_led last_led;
// if (g_last_led_count) {
for (uint8_t last_i = 0; last_i < g_last_led_count; last_i++) {
last_led = g_rgb_leds[g_last_led_hit[last_i]];
uint16_t dist = (uint16_t)sqrt(pow(led.point.x - last_led.point.x, 2) + pow(led.point.y - last_led.point.y, 2));
uint16_t effect = (g_key_hit[g_last_led_hit[last_i]] << 2) - dist;
d += 255 - MIN(MAX(effect, 0), 255);
// Update double buffer last hit timers
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
uint8_t count = last_hit_buffer.count;
for (uint8_t i = 0; i < count; ++i) {
if (UINT16_MAX - deltaTime < last_hit_buffer.tick[i]) {
last_hit_buffer.count--;
continue;
}
// } else {
// d = 255;
// }
hsv.v = MAX(MIN(d, 255), 0);
rgb = hsv_to_rgb( hsv );
rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
last_hit_buffer.tick[i] += deltaTime;
}
// } else {
// rgb_matrix_set_color_all( 0, 0, 0 );
// }
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
}
void rgb_matrix_solid_splash(void) {
g_last_led_count = MIN(g_last_led_count, 1);
rgb_matrix_solid_multisplash();
static void rgb_task_sync(void) {
// next task
if (timer_elapsed32(g_rgb_counters.tick) >= RGB_MATRIX_LED_FLUSH_LIMIT)
rgb_task_state = STARTING;
}
static void rgb_task_start(void) {
// reset iter
rgb_effect_params.iter = 0;
// Needs eeprom access that we don't have setup currently
// update double buffers
g_rgb_counters.tick = rgb_counters_buffer;
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
g_last_hit_tracker = last_hit_buffer;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
void rgb_matrix_custom(void) {
// HSV hsv;
// RGB rgb;
// for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
// {
// backlight_get_key_color(i, &hsv);
// // Override brightness with global brightness control
// hsv.v = rgb_matrix_config.val;
// rgb = hsv_to_rgb( hsv );
// rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
// }
// next task
rgb_task_state = RENDERING;
}
void rgb_matrix_task(void) {
#ifdef TRACK_PREVIOUS_EFFECT
static uint8_t toggle_enable_last = 255;
#endif
if (!rgb_matrix_config.enable) {
rgb_matrix_all_off();
rgb_matrix_indicators();
#ifdef TRACK_PREVIOUS_EFFECT
toggle_enable_last = rgb_matrix_config.enable;
#endif
return;
}
// delay 1 second before driving LEDs or doing anything else
static uint8_t startup_tick = 0;
if ( startup_tick < 20 ) {
startup_tick++;
return;
}
static void rgb_task_render(uint8_t effect) {
bool rendering = false;
rgb_effect_params.init = (effect != rgb_last_effect) || (rgb_matrix_config.enable != rgb_last_enable);
g_tick++;
if ( g_any_key_hit < 0xFFFFFFFF ) {
g_any_key_hit++;
}
for ( int led = 0; led < DRIVER_LED_TOTAL; led++ ) {
if ( g_key_hit[led] < 255 ) {
if (g_key_hit[led] == 254)
g_last_led_count = MAX(g_last_led_count - 1, 0);
g_key_hit[led]++;
}
}
// Factory default magic value
if ( rgb_matrix_config.mode == 255 ) {
rgb_matrix_test();
return;
}
// Ideally we would also stop sending zeros to the LED driver PWM buffers
// while suspended and just do a software shutdown. This is a cheap hack for now.
bool suspend_backlight = ((g_suspend_state && RGB_DISABLE_WHEN_USB_SUSPENDED) ||
(RGB_DISABLE_AFTER_TIMEOUT > 0 && g_any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20));
uint8_t effect = suspend_backlight ? 0 : rgb_matrix_config.mode;
#ifdef TRACK_PREVIOUS_EFFECT
// Keep track of the effect used last time,
// detect change in effect, so each effect can
// have an optional initialization.
static uint8_t effect_last = 255;
bool initialize = (effect != effect_last) || (rgb_matrix_config.enable != toggle_enable_last);
effect_last = effect;
toggle_enable_last = rgb_matrix_config.enable;
#endif
// this gets ticked at 20 Hz.
// each effect can opt to do calculations
// and/or request PWM buffer updates.
switch (effect) {
case RGB_MATRIX_NONE:
rendering = rgb_matrix_none(&rgb_effect_params);
break;
case RGB_MATRIX_SOLID_COLOR:
rgb_matrix_solid_color();
rendering = rgb_matrix_solid_color(&rgb_effect_params); // Max 1ms Avg 0ms
break;
#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
case RGB_MATRIX_ALPHAS_MODS:
rgb_matrix_alphas_mods();
break;
#endif
#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
case RGB_MATRIX_DUAL_BEACON:
rgb_matrix_dual_beacon();
rendering = rgb_matrix_alphas_mods(&rgb_effect_params); // Max 2ms Avg 1ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS
#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
case RGB_MATRIX_GRADIENT_UP_DOWN:
rgb_matrix_gradient_up_down();
rendering = rgb_matrix_gradient_up_down(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#ifndef DISABLE_RGB_MATRIX_RAINDROPS
case RGB_MATRIX_RAINDROPS:
rgb_matrix_raindrops( initialize );
#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
#ifndef DISABLE_RGB_MATRIX_BREATHING
case RGB_MATRIX_BREATHING:
rendering = rgb_matrix_breathing(&rgb_effect_params); // Max 1ms Avg 0ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_BREATHING
#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
case RGB_MATRIX_CYCLE_ALL:
rgb_matrix_cycle_all();
rendering = rgb_matrix_cycle_all(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_ALL
#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
case RGB_MATRIX_CYCLE_LEFT_RIGHT:
rgb_matrix_cycle_left_right();
rendering = rgb_matrix_cycle_left_right(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
case RGB_MATRIX_CYCLE_UP_DOWN:
rgb_matrix_cycle_up_down();
rendering = rgb_matrix_cycle_up_down(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
rendering = rgb_matrix_rainbow_moving_chevron(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
case RGB_MATRIX_DUAL_BEACON:
rendering = rgb_matrix_dual_beacon(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif // DISABLE_RGB_MATRIX_DUAL_BEACON
#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
case RGB_MATRIX_RAINBOW_BEACON:
rgb_matrix_rainbow_beacon();
rendering = rgb_matrix_rainbow_beacon(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON
#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
case RGB_MATRIX_RAINBOW_PINWHEELS:
rgb_matrix_rainbow_pinwheels();
rendering = rgb_matrix_rainbow_pinwheels(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif
#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
rgb_matrix_rainbow_moving_chevron();
#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
#ifndef DISABLE_RGB_MATRIX_RAINDROPS
case RGB_MATRIX_RAINDROPS:
rendering = rgb_matrix_raindrops(&rgb_effect_params); // Max 1ms Avg 0ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_RAINDROPS
#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
case RGB_MATRIX_JELLYBEAN_RAINDROPS:
rgb_matrix_jellybean_raindrops( initialize );
rendering = rgb_matrix_jellybean_raindrops(&rgb_effect_params); // Max 1ms Avg 0ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
case RGB_MATRIX_DIGITAL_RAIN:
rgb_matrix_digital_rain( initialize );
break;
#endif
#ifdef RGB_MATRIX_KEYPRESSES
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
case RGB_MATRIX_SOLID_REACTIVE:
rgb_matrix_solid_reactive();
rendering = rgb_matrix_digital_rain(&rgb_effect_params); // Max 9ms Avg 8ms | this is expensive, fix it
break;
#endif
#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
case RGB_MATRIX_SOLID_REACTIVE_SIMPLE:
rgb_matrix_solid_reactive_simple();
rendering = rgb_matrix_solid_reactive_simple(&rgb_effect_params);// Max 4ms Avg 3ms
break;
#endif
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
case RGB_MATRIX_SOLID_REACTIVE:
rendering = rgb_matrix_solid_reactive(&rgb_effect_params); // Max 4ms Avg 3ms
break;
#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE
#ifndef DISABLE_RGB_MATRIX_SPLASH
case RGB_MATRIX_SPLASH:
rgb_matrix_splash();
rendering = rgb_matrix_splash(&rgb_effect_params); // Max 5ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_SPLASH
#ifndef DISABLE_RGB_MATRIX_MULTISPLASH
case RGB_MATRIX_MULTISPLASH:
rgb_matrix_multisplash();
rendering = rgb_matrix_multisplash(&rgb_effect_params); // Max 10ms Avg 5ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_MULTISPLASH
#ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
case RGB_MATRIX_SOLID_SPLASH:
rgb_matrix_solid_splash();
rendering = rgb_matrix_solid_splash(&rgb_effect_params); // Max 5ms Avg 3ms
break;
#endif
#endif // DISABLE_RGB_MATRIX_SOLID_SPLASH
#ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
case RGB_MATRIX_SOLID_MULTISPLASH:
rgb_matrix_solid_multisplash();
rendering = rgb_matrix_solid_multisplash(&rgb_effect_params); // Max 10ms Avg 5ms
break;
#endif
#endif
default:
rgb_matrix_custom();
#endif // DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
// Factory default magic value
case UINT8_MAX: {
rgb_matrix_test();
rgb_task_state = FLUSHING;
}
return;
}
rgb_effect_params.iter++;
// next task
if (!rendering) {
rgb_task_state = FLUSHING;
if (!rgb_effect_params.init && effect == RGB_MATRIX_NONE) {
// We only need to flush once if we are RGB_MATRIX_NONE
rgb_task_state = SYNCING;
}
}
}
static void rgb_task_flush(uint8_t effect) {
// update last trackers after the first full render so we can init over several frames
rgb_last_effect = effect;
rgb_last_enable = rgb_matrix_config.enable;
// update pwm buffers
rgb_matrix_update_pwm_buffers();
// next task
rgb_task_state = SYNCING;
}
void rgb_matrix_task(void) {
rgb_task_timers();
// Ideally we would also stop sending zeros to the LED driver PWM buffers
// while suspended and just do a software shutdown. This is a cheap hack for now.
bool suspend_backlight = ((g_suspend_state && RGB_DISABLE_WHEN_USB_SUSPENDED) || (RGB_DISABLE_AFTER_TIMEOUT > 0 && g_rgb_counters.any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20));
uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;
switch (rgb_task_state) {
case STARTING:
rgb_task_start();
break;
case RENDERING:
rgb_task_render(effect);
break;
case FLUSHING:
rgb_task_flush(effect);
break;
case SYNCING:
rgb_task_sync();
break;
}
if (!suspend_backlight) {
rgb_matrix_indicators();
}
}
void rgb_matrix_indicators(void) {
@ -817,41 +473,31 @@ void rgb_matrix_indicators_kb(void) {}
__attribute__((weak))
void rgb_matrix_indicators_user(void) {}
// void rgb_matrix_set_indicator_index( uint8_t *index, uint8_t row, uint8_t column )
// {
// if ( row >= MATRIX_ROWS )
// {
// // Special value, 255=none, 254=all
// *index = row;
// }
// else
// {
// // This needs updated to something like
// // uint8_t led[8], led_count;
// // map_row_column_to_led(row,column,led,&led_count);
// // for(uint8_t i = 0; i < led_count; i++)
// map_row_column_to_led( row, column, index );
// }
// }
void rgb_matrix_init(void) {
rgb_matrix_driver.init();
// TODO: put the 1 second startup delay here?
// clear the key hits
for ( int led=0; led<DRIVER_LED_TOTAL; led++ ) {
g_key_hit[led] = 255;
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
g_last_hit_tracker.count = 0;
for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
g_last_hit_tracker.tick[i] = UINT16_MAX;
}
last_hit_buffer.count = 0;
for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {
last_hit_buffer.tick[i] = UINT16_MAX;
}
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
if (!eeconfig_is_enabled()) {
dprintf("rgb_matrix_init_drivers eeconfig is not enabled.\n");
eeconfig_init();
eeconfig_update_rgb_matrix_default();
}
rgb_matrix_config.raw = eeconfig_read_rgb_matrix();
rgb_matrix_config.speed = UINT8_MAX / 2; //EECONFIG needs to be increased to support this
if (!rgb_matrix_config.mode) {
dprintf("rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\n");
eeconfig_update_rgb_matrix_default();
@ -860,54 +506,15 @@ void rgb_matrix_init(void) {
eeconfig_debug_rgb_matrix(); // display current eeprom values
}
// Deals with the messy details of incrementing an integer
static uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
int16_t new_value = value;
new_value += step;
return MIN( MAX( new_value, min ), max );
}
static uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
int16_t new_value = value;
new_value -= step;
return MIN( MAX( new_value, min ), max );
}
// void *backlight_get_custom_key_color_eeprom_address( uint8_t led )
// {
// // 3 bytes per color
// return EECONFIG_RGB_MATRIX + ( led * 3 );
// }
// void backlight_get_key_color( uint8_t led, HSV *hsv )
// {
// void *address = backlight_get_custom_key_color_eeprom_address( led );
// hsv->h = eeprom_read_byte(address);
// hsv->s = eeprom_read_byte(address+1);
// hsv->v = eeprom_read_byte(address+2);
// }
// void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv )
// {
// uint8_t led[8], led_count;
// map_row_column_to_led(row,column,led,&led_count);
// for(uint8_t i = 0; i < led_count; i++) {
// if ( led[i] < DRIVER_LED_TOTAL )
// {
// void *address = backlight_get_custom_key_color_eeprom_address(led[i]);
// eeprom_update_byte(address, hsv.h);
// eeprom_update_byte(address+1, hsv.s);
// eeprom_update_byte(address+2, hsv.v);
// }
// }
// }
uint32_t rgb_matrix_get_tick(void) {
return g_tick;
void rgb_matrix_set_suspend_state(bool state) {
g_suspend_state = state;
}
void rgb_matrix_toggle(void) {
rgb_matrix_config.enable ^= 1;
rgb_matrix_config.enable++;
if (!rgb_matrix_config.enable) {
rgb_task_state = STARTING;
}
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
@ -933,6 +540,7 @@ void rgb_matrix_step(void) {
rgb_matrix_config.mode++;
if (rgb_matrix_config.mode >= RGB_MATRIX_EFFECT_MAX)
rgb_matrix_config.mode = 1;
rgb_task_state = STARTING;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
@ -940,51 +548,55 @@ void rgb_matrix_step_reverse(void) {
rgb_matrix_config.mode--;
if (rgb_matrix_config.mode < 1)
rgb_matrix_config.mode = RGB_MATRIX_EFFECT_MAX - 1;
rgb_task_state = STARTING;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_increase_hue(void) {
rgb_matrix_config.hue = increment( rgb_matrix_config.hue, 8, 0, 255 );
rgb_matrix_config.hue += RGB_MATRIX_HUE_STEP;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_decrease_hue(void) {
rgb_matrix_config.hue = decrement( rgb_matrix_config.hue, 8, 0, 255 );
rgb_matrix_config.hue -= RGB_MATRIX_HUE_STEP;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_increase_sat(void) {
rgb_matrix_config.sat = increment( rgb_matrix_config.sat, 8, 0, 255 );
rgb_matrix_config.sat = qadd8(rgb_matrix_config.sat, RGB_MATRIX_SAT_STEP);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_decrease_sat(void) {
rgb_matrix_config.sat = decrement( rgb_matrix_config.sat, 8, 0, 255 );
rgb_matrix_config.sat = qsub8(rgb_matrix_config.sat, RGB_MATRIX_SAT_STEP);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_increase_val(void) {
rgb_matrix_config.val = increment( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
rgb_matrix_config.val = qadd8(rgb_matrix_config.val, RGB_MATRIX_VAL_STEP);
if (rgb_matrix_config.val > RGB_MATRIX_MAXIMUM_BRIGHTNESS)
rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_decrease_val(void) {
rgb_matrix_config.val = decrement( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
rgb_matrix_config.val = qsub8(rgb_matrix_config.val, RGB_MATRIX_VAL_STEP);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
void rgb_matrix_increase_speed(void) {
rgb_matrix_config.speed = increment( rgb_matrix_config.speed, 1, 0, 3 );
rgb_matrix_config.speed = qadd8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
}
void rgb_matrix_decrease_speed(void) {
rgb_matrix_config.speed = decrement( rgb_matrix_config.speed, 1, 0, 3 );
rgb_matrix_config.speed = qsub8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
}
void rgb_matrix_mode(uint8_t mode) {
rgb_matrix_config.mode = mode;
rgb_task_state = STARTING;
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
@ -997,9 +609,7 @@ uint8_t rgb_matrix_get_mode(void) {
}
void rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {
rgb_matrix_config.hue = hue;
rgb_matrix_config.sat = sat;
rgb_matrix_config.val = val;
rgb_matrix_sethsv_noeeprom(hue, sat, val);
eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
}
@ -1007,4 +617,6 @@ void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {
rgb_matrix_config.hue = hue;
rgb_matrix_config.sat = sat;
rgb_matrix_config.val = val;
if (rgb_matrix_config.val > RGB_MATRIX_MAXIMUM_BRIGHTNESS)
rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
}

@ -21,6 +21,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "rgb_matrix_types.h"
#include "color.h"
#include "quantum.h"
@ -28,25 +29,27 @@
#include "is31fl3731.h"
#elif defined (IS31FL3733)
#include "is31fl3733.h"
#elif defined (IS31FL3737)
#include "is31fl3737.h"
#endif
typedef struct Point {
uint8_t x;
uint8_t y;
} __attribute__((packed)) Point;
typedef struct rgb_led {
union {
uint8_t raw;
struct {
uint8_t row:4; // 16 max
uint8_t col:4; // 16 max
};
} matrix_co;
Point point;
uint8_t modifier:1;
} __attribute__((packed)) rgb_led;
#ifndef RGB_MATRIX_LED_FLUSH_LIMIT
#define RGB_MATRIX_LED_FLUSH_LIMIT 16
#endif
#ifndef RGB_MATRIX_LED_PROCESS_LIMIT
#define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5
#endif
#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
#define RGB_MATRIX_USE_LIMITS(min, max) uint8_t min = RGB_MATRIX_LED_PROCESS_LIMIT * params->iter; \
uint8_t max = min + RGB_MATRIX_LED_PROCESS_LIMIT; \
if (max > DRIVER_LED_TOTAL) \
max = DRIVER_LED_TOTAL;
#else
#define RGB_MATRIX_USE_LIMITS(min, max) uint8_t min = 0; \
uint8_t max = DRIVER_LED_TOTAL;
#endif
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
@ -56,79 +59,73 @@ typedef struct
uint8_t index;
} rgb_indicator;
typedef union {
uint32_t raw;
struct {
bool enable :1;
uint8_t mode :6;
uint16_t hue :9;
uint8_t sat :8;
uint8_t val :8;
uint8_t speed :8;//EECONFIG needs to be increased to support this
};
} rgb_config_t;
enum rgb_matrix_effects {
RGB_MATRIX_NONE = 0,
RGB_MATRIX_SOLID_COLOR = 1,
#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
RGB_MATRIX_ALPHAS_MODS,
#endif
#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
RGB_MATRIX_DUAL_BEACON,
#endif
#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS
#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
RGB_MATRIX_GRADIENT_UP_DOWN,
#endif
#ifndef DISABLE_RGB_MATRIX_RAINDROPS
RGB_MATRIX_RAINDROPS,
#endif
#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
#ifndef DISABLE_RGB_MATRIX_BREATHING
RGB_MATRIX_BREATHING,
#endif // DISABLE_RGB_MATRIX_BREATHING
#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
RGB_MATRIX_CYCLE_ALL,
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_ALL
#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
RGB_MATRIX_CYCLE_LEFT_RIGHT,
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
RGB_MATRIX_CYCLE_UP_DOWN,
#endif
#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
RGB_MATRIX_DUAL_BEACON,
#endif // DISABLE_RGB_MATRIX_DUAL_BEACON
#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
RGB_MATRIX_RAINBOW_BEACON,
#endif
#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON
#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
RGB_MATRIX_RAINBOW_PINWHEELS,
#endif
#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
#endif
#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
#ifndef DISABLE_RGB_MATRIX_RAINDROPS
RGB_MATRIX_RAINDROPS,
#endif // DISABLE_RGB_MATRIX_RAINDROPS
#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
RGB_MATRIX_JELLYBEAN_RAINDROPS,
#endif
#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
RGB_MATRIX_DIGITAL_RAIN,
#endif
#ifdef RGB_MATRIX_KEYPRESSES
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
RGB_MATRIX_SOLID_REACTIVE,
#endif
#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
RGB_MATRIX_SOLID_REACTIVE_SIMPLE,
#endif
#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
RGB_MATRIX_SOLID_REACTIVE,
#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE
#ifndef DISABLE_RGB_MATRIX_SPLASH
RGB_MATRIX_SPLASH,
#endif
#endif // DISABLE_RGB_MATRIX_SPLASH
#ifndef DISABLE_RGB_MATRIX_MULTISPLASH
RGB_MATRIX_MULTISPLASH,
#endif
#endif // DISABLE_RGB_MATRIX_MULTISPLASH
#ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
RGB_MATRIX_SOLID_SPLASH,
#endif
#endif // DISABLE_RGB_MATRIX_SOLID_SPLASH
#ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
RGB_MATRIX_SOLID_MULTISPLASH,
#endif
#endif
#endif // DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
RGB_MATRIX_EFFECT_MAX
};
uint8_t rgb_matrix_map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i);
void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue );
void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue );
@ -162,8 +159,6 @@ void rgb_matrix_decrease(void);
// void backlight_get_key_color( uint8_t led, HSV *hsv );
// void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv );
uint32_t rgb_matrix_get_tick(void);
void rgb_matrix_toggle(void);
void rgb_matrix_enable(void);
void rgb_matrix_enable_noeeprom(void);
@ -212,7 +207,6 @@ uint8_t rgb_matrix_get_mode(void);
typedef struct {
/* Perform any initialisation required for the other driver functions to work. */
void (*init)(void);
/* Set the colour of a single LED in the buffer. */
void (*set_color)(int index, uint8_t r, uint8_t g, uint8_t b);
/* Set the colour of all LEDS on the keyboard in the buffer. */

@ -0,0 +1,26 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
// alphas = color1, mods = color2
bool rgb_matrix_alphas_mods(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, rgb_matrix_config.val };
RGB rgb1 = hsv_to_rgb(hsv);
hsv.h += rgb_matrix_config.speed;
RGB rgb2 = hsv_to_rgb(hsv);
for (uint8_t i = led_min; i < led_max; i++) {
if (g_rgb_leds[i].modifier) {
rgb_matrix_set_color(i, rgb2.r, rgb2.g, rgb2.b);
} else {
rgb_matrix_set_color(i, rgb1.r, rgb1.g, rgb1.b);
}
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_ALPHAS_MODS

@ -0,0 +1,20 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_BREATHING
extern rgb_counters_t g_rgb_counters;
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_breathing(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 8);
uint8_t val = scale8(abs8(sin8(time) - 128) * 2, rgb_matrix_config.val);
HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, val };
RGB rgb = hsv_to_rgb(hsv);
for (uint8_t i = led_min; i < led_max; i++) {
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_BREATHING

@ -0,0 +1,21 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_cycle_all(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
for (uint8_t i = led_min; i < led_max; i++) {
hsv.h = time;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_CYCLE_ALL

@ -0,0 +1,22 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_cycle_left_right(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = point.x - time;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT

@ -0,0 +1,22 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_cycle_up_down(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = point.y - time;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_CYCLE_UP_DOWN

@ -0,0 +1,74 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
#ifndef RGB_DIGITAL_RAIN_DROPS
// lower the number for denser effect/wider keyboard
#define RGB_DIGITAL_RAIN_DROPS 24
#endif
bool rgb_matrix_digital_rain(effect_params_t* params) {
// algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain
const uint8_t drop_ticks = 28;
const uint8_t pure_green_intensity = 0xd0;
const uint8_t max_brightness_boost = 0xc0;
const uint8_t max_intensity = 0xff;
static uint8_t map[MATRIX_COLS][MATRIX_ROWS] = {{0}};
static uint8_t drop = 0;
if (params->init) {
rgb_matrix_set_color_all(0, 0, 0);
memset(map, 0, sizeof map);
drop = 0;
}
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {
// top row, pixels have just fallen and we're
// making a new rain drop in this column
map[col][row] = max_intensity;
}
else if (map[col][row] > 0 && map[col][row] < max_intensity) {
// neither fully bright nor dark, decay it
map[col][row]--;
}
// 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 (map[col][row] > pure_green_intensity) {
const uint8_t boost = (uint8_t) ((uint16_t) max_brightness_boost * (map[col][row] - pure_green_intensity) / (max_intensity - pure_green_intensity));
rgb_matrix_set_color(led[0], boost, max_intensity, boost);
}
else {
const uint8_t green = (uint8_t) ((uint16_t) max_intensity * map[col][row] / pure_green_intensity);
rgb_matrix_set_color(led[0], 0, green, 0);
}
}
}
}
if (++drop > drop_ticks) {
// reset drop timer
drop = 0;
for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
// if ths is on the bottom row and bright allow decay
if (row == MATRIX_ROWS - 1 && map[col][row] == max_intensity) {
map[col][row]--;
}
// check if the pixel above is bright
if (map[col][row - 1] == max_intensity) {
// allow old bright pixel to decay
map[col][row - 1]--;
// make this pixel bright
map[col][row] = max_intensity;
}
}
}
}
return false;
}
#endif // DISABLE_RGB_MATRIX_DIGITAL_RAIN

@ -0,0 +1,24 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_dual_beacon(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
int8_t cos_value = cos8(time) - 128;
int8_t sin_value = sin8(time) - 128;
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = ((point.y - 32) * cos_value + (point.x - 112) * sin_value) / 128 + rgb_matrix_config.hue;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_DUAL_BEACON

@ -0,0 +1,22 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_gradient_up_down(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint8_t scale = scale8(64, rgb_matrix_config.speed);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
// The y range will be 0..64, map this to 0..4
// Relies on hue being 8-bit and wrapping
hsv.h = rgb_matrix_config.hue + scale * (point.y >> 4);
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN

@ -0,0 +1,30 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
static void jellybean_raindrops_set_color(int i) {
HSV hsv = { rand() & 0xFF , rand() & 0xFF, rgb_matrix_config.val };
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
bool rgb_matrix_jellybean_raindrops(effect_params_t* params) {
if (!params->init) {
// Change one LED every tick, make sure speed is not 0
if (scale16by8(g_rgb_counters.tick, qadd8(rgb_matrix_config.speed, 16)) % 5 == 0) {
jellybean_raindrops_set_color(rand() % DRIVER_LED_TOTAL);
}
return false;
}
RGB_MATRIX_USE_LIMITS(led_min, led_max);
for (int i = led_min; i < led_max; i++) {
jellybean_raindrops_set_color(i);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS

@ -0,0 +1,24 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_rainbow_beacon(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
int16_t cos_value = 2 * (cos8(time) - 128);
int16_t sin_value = 2 * (sin8(time) - 128);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = ((point.y - 32) * cos_value + (point.x - 112) * sin_value) / 128 + rgb_matrix_config.hue;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_RAINBOW_BEACON

@ -0,0 +1,22 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_rainbow_moving_chevron(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint8_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = abs8(point.y - 32) + (point.x - time) + rgb_matrix_config.hue;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON

@ -0,0 +1,24 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
extern rgb_counters_t g_rgb_counters;
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_rainbow_pinwheels(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, rgb_matrix_config.val };
uint16_t time = scale16by8(g_rgb_counters.tick, rgb_matrix_config.speed / 4);
int16_t cos_value = 3 * (cos8(time) - 128);
int16_t sin_value = 3 * (sin8(time) - 128);
for (uint8_t i = led_min; i < led_max; i++) {
point_t point = g_rgb_leds[i].point;
hsv.h = ((point.y - 32) * cos_value + (56 - abs8(point.x - 112)) * sin_value) / 128 + rgb_matrix_config.hue;
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS

@ -0,0 +1,40 @@
#pragma once
#ifndef DISABLE_RGB_MATRIX_RAINDROPS
#include "rgb_matrix_types.h"
extern rgb_counters_t g_rgb_counters;
extern rgb_config_t rgb_matrix_config;
static void raindrops_set_color(int i) {
HSV hsv = { 0 , rgb_matrix_config.sat, rgb_matrix_config.val };
// Take the shortest path between hues
int16_t deltaH = ((rgb_matrix_config.hue + 180) % 360 - rgb_matrix_config.hue) / 4;
if (deltaH > 127) {
deltaH -= 256;
} else if (deltaH < -127) {
deltaH += 256;
}
hsv.h = rgb_matrix_config.hue + (deltaH * (rand() & 0x03));
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
bool rgb_matrix_raindrops(effect_params_t* params) {
if (!params->init) {
// Change one LED every tick, make sure speed is not 0
if (scale16by8(g_rgb_counters.tick, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) {
raindrops_set_color(rand() % DRIVER_LED_TOTAL);
}
return false;
}
RGB_MATRIX_USE_LIMITS(led_min, led_max);
for (int i = led_min; i < led_max; i++) {
raindrops_set_color(i);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_RAINDROPS

@ -0,0 +1,14 @@
#pragma once
extern rgb_config_t rgb_matrix_config;
bool rgb_matrix_solid_color(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, rgb_matrix_config.val };
RGB rgb = hsv_to_rgb(hsv);
for (uint8_t i = led_min; i < led_max; i++) {
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}

@ -0,0 +1,33 @@
#pragma once
#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED)
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
extern rgb_config_t rgb_matrix_config;
extern last_hit_t g_last_hit_tracker;
bool rgb_matrix_solid_reactive(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { rgb_matrix_config.hue, 255, rgb_matrix_config.val };
// Max tick based on speed scale ensures results from scale16by8 with rgb_matrix_config.speed are no greater than 255
uint16_t max_tick = 65535 / rgb_matrix_config.speed;
// Relies on hue being 8-bit and wrapping
for (uint8_t i = led_min; i < led_max; i++) {
uint16_t tick = max_tick;
for(uint8_t j = 0; j < g_last_hit_tracker.count; j++) {
if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {
tick = g_last_hit_tracker.tick[j];
break;
}
}
uint16_t offset = scale16by8(tick, rgb_matrix_config.speed);
hsv.h = rgb_matrix_config.hue + qsub8(130, offset);
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
#endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED)

@ -0,0 +1,32 @@
#pragma once
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
#ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
extern rgb_config_t rgb_matrix_config;
extern last_hit_t g_last_hit_tracker;
bool rgb_matrix_solid_reactive_simple(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, 0 };
// Max tick based on speed scale ensures results from scale16by8 with rgb_matrix_config.speed are no greater than 255
uint16_t max_tick = 65535 / rgb_matrix_config.speed;
for (uint8_t i = led_min; i < led_max; i++) {
uint16_t tick = max_tick;
for(uint8_t j = 0; j < g_last_hit_tracker.count; j++) {
if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {
tick = g_last_hit_tracker.tick[j];
break;
}
}
uint16_t offset = scale16by8(tick, rgb_matrix_config.speed);
hsv.v = scale8(255 - offset, rgb_matrix_config.val);
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
#endif // DISABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED

@ -0,0 +1,42 @@
#pragma once
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
#if !defined(DISABLE_RGB_MATRIX_SOLID_SPLASH) || !defined(DISABLE_RGB_MATRIX_SOLID_MULTISPLASH)
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
extern last_hit_t g_last_hit_tracker;
static bool rgb_matrix_solid_multisplash_range(uint8_t start, effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { rgb_matrix_config.hue, rgb_matrix_config.sat, 0 };
uint8_t count = g_last_hit_tracker.count;
for (uint8_t i = led_min; i < led_max; i++) {
hsv.v = 0;
point_t point = g_rgb_leds[i].point;
for (uint8_t j = start; j < count; j++) {
int16_t dx = point.x - g_last_hit_tracker.x[j];
int16_t dy = point.y - g_last_hit_tracker.y[j];
uint8_t dist = sqrt16(dx * dx + dy * dy);
uint16_t effect = scale16by8(g_last_hit_tracker.tick[j], rgb_matrix_config.speed) - dist;
if (effect > 255)
effect = 255;
hsv.v = qadd8(hsv.v, 255 - effect);
}
hsv.v = scale8(hsv.v, rgb_matrix_config.val);
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
bool rgb_matrix_solid_multisplash(effect_params_t* params) {
return rgb_matrix_solid_multisplash_range(0, params);
}
bool rgb_matrix_solid_splash(effect_params_t* params) {
return rgb_matrix_solid_multisplash_range(qsub8(g_last_hit_tracker.count, 1), params);
}
#endif // !defined(DISABLE_RGB_MATRIX_SPLASH) && !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED

@ -0,0 +1,44 @@
#pragma once
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
#if !defined(DISABLE_RGB_MATRIX_SPLASH) || !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
extern const rgb_led g_rgb_leds[DRIVER_LED_TOTAL];
extern rgb_config_t rgb_matrix_config;
extern last_hit_t g_last_hit_tracker;
static bool rgb_matrix_multisplash_range(uint8_t start, effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
HSV hsv = { 0, rgb_matrix_config.sat, 0 };
uint8_t count = g_last_hit_tracker.count;
for (uint8_t i = led_min; i < led_max; i++) {
hsv.h = rgb_matrix_config.hue;
hsv.v = 0;
point_t point = g_rgb_leds[i].point;
for (uint8_t j = start; j < count; j++) {
int16_t dx = point.x - g_last_hit_tracker.x[j];
int16_t dy = point.y - g_last_hit_tracker.y[j];
uint8_t dist = sqrt16(dx * dx + dy * dy);
uint16_t effect = scale16by8(g_last_hit_tracker.tick[j], rgb_matrix_config.speed) - dist;
if (effect > 255)
effect = 255;
hsv.h += effect;
hsv.v = qadd8(hsv.v, 255 - effect);
}
hsv.v = scale8(hsv.v, rgb_matrix_config.val);
RGB rgb = hsv_to_rgb(hsv);
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
return led_max < DRIVER_LED_TOTAL;
}
bool rgb_matrix_multisplash(effect_params_t* params) {
return rgb_matrix_multisplash_range(0, params);
}
bool rgb_matrix_splash(effect_params_t* params) {
return rgb_matrix_multisplash_range(qsub8(g_last_hit_tracker.count, 1), params);
}
#endif // !defined(DISABLE_RGB_MATRIX_SPLASH) || !defined(DISABLE_RGB_MATRIX_MULTISPLASH)
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED

@ -23,7 +23,7 @@
* be here if shared between boards.
*/
#if defined(IS31FL3731) || defined(IS31FL3733)
#if defined(IS31FL3731) || defined(IS31FL3733) || defined(IS31FL3737)
#include "i2c_master.h"
@ -33,23 +33,29 @@ static void init( void )
#ifdef IS31FL3731
IS31FL3731_init( DRIVER_ADDR_1 );
IS31FL3731_init( DRIVER_ADDR_2 );
#else
#elif defined(IS31FL3733)
IS31FL3733_init( DRIVER_ADDR_1 );
#else
IS31FL3737_init( DRIVER_ADDR_1 );
#endif
for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) {
bool enabled = true;
// This only caches it for later
#ifdef IS31FL3731
IS31FL3731_set_led_control_register( index, enabled, enabled, enabled );
#else
#elif defined(IS31FL3733)
IS31FL3733_set_led_control_register( index, enabled, enabled, enabled );
#else
IS31FL3737_set_led_control_register( index, enabled, enabled, enabled );
#endif
}
// This actually updates the LED drivers
#ifdef IS31FL3731
IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
#else
#elif defined(IS31FL3733)
IS31FL3733_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
#else
IS31FL3737_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
#endif
}
@ -65,7 +71,7 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
.set_color = IS31FL3731_set_color,
.set_color_all = IS31FL3731_set_color_all,
};
#else
#elif defined(IS31FL3733)
static void flush( void )
{
IS31FL3733_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
@ -77,6 +83,18 @@ const rgb_matrix_driver_t rgb_matrix_driver = {
.set_color = IS31FL3733_set_color,
.set_color_all = IS31FL3733_set_color_all,
};
#else
static void flush( void )
{
IS31FL3737_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
}
const rgb_matrix_driver_t rgb_matrix_driver = {
.init = init,
.flush = flush,
.set_color = IS31FL3737_set_color,
.set_color_all = IS31FL3737_set_color_all,
};
#endif
#endif

@ -0,0 +1,97 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if defined(__GNUC__)
#define PACKED __attribute__ ((__packed__))
#else
#define PACKED
#endif
#if defined(_MSC_VER)
#pragma pack( push, 1 )
#endif
#if defined(RGB_MATRIX_KEYPRESSES) || defined(RGB_MATRIX_KEYRELEASES)
#define RGB_MATRIX_KEYREACTIVE_ENABLED
#endif
// Last led hit
#ifndef LED_HITS_TO_REMEMBER
#define LED_HITS_TO_REMEMBER 8
#endif // LED_HITS_TO_REMEMBER
#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED
typedef struct PACKED {
uint8_t count;
uint8_t x[LED_HITS_TO_REMEMBER];
uint8_t y[LED_HITS_TO_REMEMBER];
uint8_t index[LED_HITS_TO_REMEMBER];
uint16_t tick[LED_HITS_TO_REMEMBER];
} last_hit_t;
#endif // RGB_MATRIX_KEYREACTIVE_ENABLED
typedef enum rgb_task_states {
STARTING,
RENDERING,
FLUSHING,
SYNCING
} rgb_task_states;
typedef uint8_t led_flags_t;
typedef struct PACKED {
uint8_t iter;
led_flags_t flags;
bool init;
} effect_params_t;
typedef struct PACKED {
// Global tick at 20 Hz
uint32_t tick;
// Ticks since this key was last hit.
uint32_t any_key_hit;
} rgb_counters_t;
typedef struct PACKED {
uint8_t x;
uint8_t y;
} point_t;
typedef union {
uint8_t raw;
struct {
uint8_t row:4; // 16 max
uint8_t col:4; // 16 max
};
} matrix_co_t;
typedef struct PACKED {
matrix_co_t matrix_co;
point_t point;
uint8_t modifier:1;
} rgb_led;
typedef enum {
RGB_ZONE_OFF = 0,
RGB_ZONE_ALL,
RGB_ZONE_KEYS,
RGB_ZONE_UNDER,
} rgb_zone_t;
typedef union {
uint32_t raw;
struct PACKED {
uint8_t enable :2;
uint8_t mode :6;
uint8_t hue :8;
uint8_t sat :8;
uint8_t val :8;
uint8_t speed :8;//EECONFIG needs to be increased to support this
};
} rgb_config_t;
#if defined(_MSC_VER)
#pragma pack( pop )
#endif

@ -29,6 +29,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "debounce.h"
#include "transport.h"
#ifdef ENCODER_ENABLE
#include "encoder.h"
#endif
#if (MATRIX_COLS <= 8)
# define print_matrix_header() print("\nr/c 01234567\n")
# define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))
@ -320,6 +324,9 @@ uint8_t matrix_scan(void) {
matrix_scan_quantum();
} else {
transport_slave(matrix + thisHand);
#ifdef ENCODER_ENABLE
encoder_read();
#endif
matrix_slave_scan_user();
}

@ -1,4 +1,5 @@
#include <string.h>
#include <stddef.h>
#include "config.h"
#include "matrix.h"
@ -15,15 +16,45 @@
extern backlight_config_t backlight_config;
#endif
#ifdef ENCODER_ENABLE
# include "encoder.h"
#endif
#if defined(USE_I2C) || defined(EH)
# include "i2c_master.h"
# include "i2c_slave.h"
# define I2C_BACKLIT_START 0x00
// Need 4 bytes for RGB (32 bit)
# define I2C_RGB_START 0x01
# define I2C_KEYMAP_START 0x05
typedef struct __attribute__ ((__packed__)) {
#ifdef BACKLIGHT_ENABLE
uint8_t backlight_level;
#endif
#ifdef RGBLIGHT_ENABLE
uint32_t rgb_settings;
#endif
#ifdef ENCODER_ENABLE
uint8_t encoder_state[NUMBER_OF_ENCODERS];
#endif
// Keep matrix last, we are only using this for it's offset
uint8_t matrix_start[0];
} transport_values_t;
__attribute__ ((unused))
static transport_values_t transport_values;
#ifdef BACKLIGHT_ENABLE
# define I2C_BACKLIT_START (uint8_t)offsetof(transport_values_t, backlight_level)
#endif
#ifdef RGBLIGHT_ENABLE
# define I2C_RGB_START (uint8_t)offsetof(transport_values_t, rgb_settings)
#endif
#ifdef ENCODER_ENABLE
# define I2C_ENCODER_START (uint8_t)offsetof(transport_values_t, encoder_state)
#endif
#define I2C_KEYMAP_START (uint8_t)offsetof(transport_values_t, matrix_start)
# define TIMEOUT 100
@ -37,25 +68,28 @@ bool transport_master(matrix_row_t matrix[]) {
// write backlight info
# ifdef BACKLIGHT_ENABLE
static uint8_t prev_level = ~0;
uint8_t level = get_backlight_level();
if (level != prev_level) {
if (level != transport_values.backlight_level) {
if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
prev_level = level;
transport_values.backlight_level = level;
}
}
# endif
# ifdef RGBLIGHT_ENABLE
static uint32_t prev_rgb = ~0;
uint32_t rgb = rgblight_read_dword();
if (rgb != prev_rgb) {
if (rgb != transport_values.rgb_settings) {
if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgb, sizeof(rgb), TIMEOUT) >= 0) {
prev_rgb = rgb;
transport_values.rgb_settings = rgb;
}
}
# endif
# ifdef ENCODER_ENABLE
i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)transport_values.encoder_state, sizeof(transport_values.encoder_state), TIMEOUT);
encoder_update_raw(&transport_values.encoder_state[0]);
# endif
return true;
}
@ -73,6 +107,10 @@ void transport_slave(matrix_row_t matrix[]) {
// Update the RGB with the new data
rgblight_update_dword(rgb);
# endif
# ifdef ENCODER_ENABLE
encoder_state_raw((uint8_t*)(i2c_slave_reg + I2C_ENCODER_START));
# endif
}
void transport_master_init(void) { i2c_init(); }
@ -83,12 +121,15 @@ void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }
# include "serial.h"
typedef struct _Serial_s2m_buffer_t {
typedef struct __attribute__ ((__packed__)) {
# ifdef ENCODER_ENABLE
uint8_t encoder_state[NUMBER_OF_ENCODERS];
# endif
// TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
matrix_row_t smatrix[ROWS_PER_HAND];
} Serial_s2m_buffer_t;
typedef struct _Serial_m2s_buffer_t {
typedef struct __attribute__ ((__packed__)) {
# ifdef BACKLIGHT_ENABLE
uint8_t backlight_level;
# endif
@ -147,6 +188,10 @@ bool transport_master(matrix_row_t matrix[]) {
}
# endif
# ifdef ENCODER_ENABLE
encoder_update_raw((uint8_t*)&serial_s2m_buffer.encoder_state);
# endif
return true;
}
@ -162,6 +207,10 @@ void transport_slave(matrix_row_t matrix[]) {
// Update RGB config with the new data
rgblight_update_dword(serial_m2s_buffer.rgblight_config.raw);
# endif
# ifdef ENCODER_ENABLE
encoder_state_raw((uint8_t*)&serial_s2m_buffer.encoder_state);
# endif
}
#endif

@ -76,7 +76,7 @@
* @brief Enables the I2C subsystem.
*/
#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
#define HAL_USE_I2C FALSE
#define HAL_USE_I2C TRUE
#endif
/**

@ -154,7 +154,7 @@
/*
* I2C driver system settings.
*/
#define STM32_I2C_USE_I2C1 FALSE
#define STM32_I2C_USE_I2C1 TRUE
#define STM32_I2C_USE_I2C2 FALSE
#define STM32_I2C_BUSY_TIMEOUT 50
#define STM32_I2C_I2C1_IRQ_PRIORITY 10

@ -18,8 +18,17 @@
#define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
#define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
#define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
#define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
__attribute__ ((weak))
uint16_t get_tapping_term(uint16_t keycode) {
return TAPPING_TERM;
}
#ifdef TAPPING_TERM_PER_KEY
#define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_event_keycode(tapping_key.event)))
#else
#define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
#endif
static keyrecord_t tapping_key = {};
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
@ -100,12 +109,17 @@ bool process_tapping(keyrecord_t *keyp)
// enqueue
return false;
}
#if TAPPING_TERM >= 500 || defined PERMISSIVE_HOLD
/* Process a key typed within TAPPING_TERM
* This can register the key before settlement of tapping,
* useful for long TAPPING_TERM but may prevent fast typing.
*/
else if (IS_RELEASED(event) && waiting_buffer_typed(event)) {
#if defined(TAPPING_TERM_PER_KEY) || (!defined(PER_KEY_TAPPING_TERM) && TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD)
#ifdef TAPPING_TERM_PER_KEY
else if ( ( get_tapping_term(get_event_keycode(tapping_key.event)) >= 500) && IS_RELEASED(event) && waiting_buffer_typed(event))
#else
else if ( IS_RELEASED(event) && waiting_buffer_typed(event))
#endif
{
debug("Tapping: End. No tap. Interfered by typing key\n");
process_record(&tapping_key);
tapping_key = (keyrecord_t){};

@ -35,6 +35,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef NO_ACTION_TAPPING
uint16_t get_event_keycode(keyevent_t event);
uint16_t get_tapping_term(uint16_t keycode);
void action_tapping_process(keyrecord_t record);
#endif

@ -35,7 +35,9 @@ void suspend_power_down_kb(void) {
*/
void suspend_power_down(void)
{
#ifdef RGB_MATRIX_ENABLE
I2C3733_Control_Set(0); //Disable LED driver
#endif
suspend_power_down_kb();
}
@ -75,10 +77,9 @@ void suspend_wakeup_init_kb(void) {
* FIXME: needs doc
*/
void suspend_wakeup_init(void) {
/* If LEDs are set to enabled, enable the hardware */
if (led_enabled) {
#ifdef RGB_MATRIX_ENABLE
I2C3733_Control_Set(1);
}
#endif
suspend_wakeup_init_kb();
}

@ -4,7 +4,10 @@ SRC += $(ARM_ATSAM_DIR)/adc.c
SRC += $(ARM_ATSAM_DIR)/clks.c
SRC += $(ARM_ATSAM_DIR)/d51_util.c
SRC += $(ARM_ATSAM_DIR)/i2c_master.c
ifeq ($(RGB_MATRIX_ENABLE),custom)
SRC += $(ARM_ATSAM_DIR)/led_matrix_programs.c
SRC += $(ARM_ATSAM_DIR)/led_matrix.c
endif
SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c
SRC += $(ARM_ATSAM_DIR)/spi.c
SRC += $(ARM_ATSAM_DIR)/startup.c

@ -34,7 +34,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef MD_BOOTLOADER
#include "main_arm_atsam.h"
#ifdef RGB_MATRIX_ENABLE
#include "led_matrix.h"
#include "rgb_matrix.h"
#endif
#include "issi3733_driver.h"
#include "./usb/compiler.h"
#include "./usb/udc.h"

@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "arm_atsam_protocol.h"
#ifndef MD_BOOTLOADER
#if !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
#include <string.h>
@ -37,7 +37,7 @@ static uint8_t dma_sendbuf[I2C_DMA_MAX_SEND]; //Data being written to I2C
volatile uint8_t i2c_led_q_running;
#endif //MD_BOOTLOADER
#endif // !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
void i2c0_init(void)
{
@ -112,7 +112,7 @@ void i2c0_stop(void)
}
}
#ifndef MD_BOOTLOADER
#if !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)
void i2c1_init(void)
{
DBGC(DC_I2C1_INIT_BEGIN);
@ -583,4 +583,4 @@ uint8_t i2c_led_q_run(void)
return 1;
}
#endif //MD_BOOTLOADER
#endif // !defined(MD_BOOTLOADER) && defined(RGB_MATRIX_ENABLE)

@ -17,9 +17,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "arm_atsam_protocol.h"
#include "tmk_core/common/led.h"
#include "rgb_matrix.h"
#include <string.h>
#include <math.h>
#ifdef USE_MASSDROP_CONFIGURATOR
__attribute__((weak))
led_instruction_t led_instructions[] = { { .end = 1 } };
static void led_matrix_massdrop_config_override(int i);
#endif // USE_MASSDROP_CONFIGURATOR
extern rgb_config_t rgb_matrix_config;
extern rgb_counters_t g_rgb_counters;
void SERCOM1_0_Handler( void )
{
if (SERCOM1->I2CM.INTFLAG.bit.ERROR)
@ -51,14 +61,17 @@ void DMAC_0_Handler( void )
issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
issi3733_led_t led_map[ISSI3733_LED_COUNT+1] = ISSI3733_LED_MAP;
issi3733_led_t *lede = led_map + ISSI3733_LED_COUNT; //End pointer of mapping
issi3733_led_t led_map[ISSI3733_LED_COUNT] = ISSI3733_LED_MAP;
RGB led_buffer[ISSI3733_LED_COUNT];
uint8_t gcr_desired;
uint8_t gcr_breathe;
uint8_t gcr_use;
uint8_t gcr_actual;
uint8_t gcr_actual_last;
#ifdef USE_MASSDROP_CONFIGURATOR
uint8_t gcr_breathe;
float breathe_mult;
float pomod;
#endif
#define ACT_GCR_NONE 0
#define ACT_GCR_INC 1
@ -73,11 +86,14 @@ static uint8_t v_5v_cat_hit;
void gcr_compute(void)
{
uint8_t action = ACT_GCR_NONE;
uint8_t gcr_use = gcr_desired;
#ifdef USE_MASSDROP_CONFIGURATOR
if (led_animation_breathing)
{
gcr_use = gcr_breathe;
else
gcr_use = gcr_desired;
}
#endif
//If the 5v takes a catastrophic hit, disable the LED drivers briefly, assert auto gcr mode, min gcr and let the auto take over
if (v_5v < V5_CAT)
@ -151,6 +167,7 @@ void gcr_compute(void)
gcr_actual -= LED_GCR_STEP_AUTO;
gcr_min_counter = 0;
#ifdef USE_MASSDROP_CONFIGURATOR
//If breathe mode is active, the top end can fluctuate if the host can not supply enough current
//So set the breathe GCR to where it becomes stable
if (led_animation_breathing == 1)
@ -160,12 +177,11 @@ void gcr_compute(void)
// and the same would happen maybe one or two more times. Therefore I'm favoring
// powering through one full breathe and letting gcr settle completely
}
#endif
}
}
}
led_disp_t disp;
void issi3733_prepare_arrays(void)
{
memset(issidrv,0,sizeof(issi3733_driver_t) * ISSI3733_DRIVER_COUNT);
@ -178,111 +194,93 @@ void issi3733_prepare_arrays(void)
issidrv[i].addr = addrs[i];
}
issi3733_led_t *cur = led_map;
while (cur < lede)
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
{
//BYTE: 1 + (SW-1)*16 + (CS-1)
cur->rgb.g = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swg-1)*16 + (cur->adr.cs-1));
cur->rgb.r = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swr-1)*16 + (cur->adr.cs-1));
cur->rgb.b = issidrv[cur->adr.drv-1].pwm + 1 + ((cur->adr.swb-1)*16 + (cur->adr.cs-1));
led_map[i].rgb.g = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swg-1)*16 + (led_map[i].adr.cs-1));
led_map[i].rgb.r = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swr-1)*16 + (led_map[i].adr.cs-1));
led_map[i].rgb.b = issidrv[led_map[i].adr.drv-1].pwm + 1 + ((led_map[i].adr.swb-1)*16 + (led_map[i].adr.cs-1));
//BYTE: 1 + (SW-1)*2 + (CS-1)/8
//BIT: (CS-1)%8
*(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swg-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
*(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swr-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
*(issidrv[cur->adr.drv-1].onoff + 1 + (cur->adr.swb-1)*2+(cur->adr.cs-1)/8) |= (1<<((cur->adr.cs-1)%8));
cur++;
*(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swg-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
*(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swr-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
*(issidrv[led_map[i].adr.drv-1].onoff + 1 + (led_map[i].adr.swb-1)*2+(led_map[i].adr.cs-1)/8) |= (1<<((led_map[i].adr.cs-1)%8));
}
}
void disp_calc_extents(void)
void led_matrix_prepare(void)
{
issi3733_led_t *cur = led_map;
disp.left = 1e10;
disp.right = -1e10;
disp.top = -1e10;
disp.bottom = 1e10;
while (cur < lede)
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
{
if (cur->x < disp.left) disp.left = cur->x;
if (cur->x > disp.right) disp.right = cur->x;
if (cur->y < disp.bottom) disp.bottom = cur->y;
if (cur->y > disp.top) disp.top = cur->y;
cur++;
*led_map[i].rgb.r = 0;
*led_map[i].rgb.g = 0;
*led_map[i].rgb.b = 0;
}
disp.width = disp.right - disp.left;
disp.height = disp.top - disp.bottom;
disp.max_distance = sqrtf(powf(disp.width, 2) + powf(disp.height, 2));
}
void disp_pixel_setup(void)
void led_set_one(int i, uint8_t r, uint8_t g, uint8_t b)
{
issi3733_led_t *cur = led_map;
while (cur < lede)
if (i < ISSI3733_LED_COUNT)
{
cur->px = (cur->x - disp.left) / disp.width * 100;
cur->py = (cur->y - disp.bottom) / disp.height * 100;
*cur->rgb.r = 0;
*cur->rgb.g = 0;
*cur->rgb.b = 0;
cur++;
#ifdef USE_MASSDROP_CONFIGURATOR
led_matrix_massdrop_config_override(i);
#else
led_buffer[i].r = r;
led_buffer[i].g = g;
led_buffer[i].b = b;
#endif
}
}
void led_matrix_prepare(void)
void led_set_all(uint8_t r, uint8_t g, uint8_t b)
{
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
{
disp_calc_extents();
disp_pixel_setup();
led_set_one(i, r, g, b);
}
}
uint8_t led_enabled;
float led_animation_speed;
uint8_t led_animation_direction;
uint8_t led_animation_orientation;
uint8_t led_animation_breathing;
uint8_t led_animation_breathe_cur;
uint8_t breathe_step;
uint8_t breathe_dir;
uint8_t led_animation_circular;
uint64_t led_next_run;
void init(void)
{
DBGC(DC_LED_MATRIX_INIT_BEGIN);
uint8_t led_animation_id;
uint8_t led_lighting_mode;
issi3733_prepare_arrays();
issi3733_led_t *led_cur;
uint8_t led_per_run = 15;
float breathe_mult;
led_matrix_prepare();
__attribute__ ((weak))
void led_matrix_run(void)
{
float ro;
float go;
float bo;
float po;
gcr_min_counter = 0;
v_5v_cat_hit = 0;
uint8_t led_this_run = 0;
led_setup_t *f = (led_setup_t*)led_setups[led_animation_id];
DBGC(DC_LED_MATRIX_INIT_COMPLETE);
}
if (led_cur == 0) //Denotes start of new processing cycle in the case of chunked processing
void flush(void)
{
led_cur = led_map;
#ifdef USE_MASSDROP_CONFIGURATOR
if (!led_enabled) { return; } //Prevent calculations and I2C traffic if LED drivers are not enabled
#else
if (!sr_exp_data.bit.SDB_N) { return; } //Prevent calculations and I2C traffic if LED drivers are not enabled
#endif
// Wait for previous transfer to complete
while (i2c_led_q_running) {}
disp.frame += 1;
// Copy buffer to live DMA region
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
{
*led_map[i].rgb.r = led_buffer[i].r;
*led_map[i].rgb.g = led_buffer[i].g;
*led_map[i].rgb.b = led_buffer[i].b;
}
#ifdef USE_MASSDROP_CONFIGURATOR
breathe_mult = 1;
if (led_animation_breathing)
{
led_animation_breathe_cur += breathe_step * breathe_dir;
//+60us 119 LED
led_animation_breathe_cur += BREATHE_STEP * breathe_dir;
if (led_animation_breathe_cur >= BREATHE_MAX_STEP)
breathe_dir = -1;
@ -294,77 +292,107 @@ void led_matrix_run(void)
if (breathe_mult > 1) breathe_mult = 1;
else if (breathe_mult < 0) breathe_mult = 0;
}
}
uint8_t fcur = 0;
uint8_t fmax = 0;
//This should only be performed once per frame
pomod = (float)((g_rgb_counters.tick / 10) % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed;
pomod *= 100.0f;
pomod = (uint32_t)pomod % 10000;
pomod /= 100.0f;
//Frames setup
while (f[fcur].end != 1)
{
fcur++; //Count frames
}
#endif // USE_MASSDROP_CONFIGURATOR
fmax = fcur; //Store total frames count
uint8_t drvid;
while (led_cur < lede && led_this_run < led_per_run)
//NOTE: GCR does not need to be timed with LED processing, but there is really no harm
if (gcr_actual != gcr_actual_last)
{
ro = 0;
go = 0;
bo = 0;
for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
I2C_LED_Q_GCR(drvid); //Queue data
gcr_actual_last = gcr_actual;
}
if (led_lighting_mode == LED_MODE_KEYS_ONLY && led_cur->scan == 255)
{
//Do not act on this LED
for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
I2C_LED_Q_PWM(drvid); //Queue data
i2c_led_q_run();
}
else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && led_cur->scan != 255)
void led_matrix_indicators(void)
{
//Do not act on this LED
}
else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY)
uint8_t kbled = keyboard_leds();
if (kbled && rgb_matrix_config.enable)
{
//Do not act on this LED (Only show indicators)
}
else
for (uint8_t i = 0; i < ISSI3733_LED_COUNT; i++)
{
//Act on LED
for (fcur = 0; fcur < fmax; fcur++)
if (
#if USB_LED_NUM_LOCK_SCANCODE != 255
(led_map[i].scan == USB_LED_NUM_LOCK_SCANCODE && (kbled & (1<<USB_LED_NUM_LOCK))) ||
#endif //NUM LOCK
#if USB_LED_CAPS_LOCK_SCANCODE != 255
(led_map[i].scan == USB_LED_CAPS_LOCK_SCANCODE && (kbled & (1<<USB_LED_CAPS_LOCK))) ||
#endif //CAPS LOCK
#if USB_LED_SCROLL_LOCK_SCANCODE != 255
(led_map[i].scan == USB_LED_SCROLL_LOCK_SCANCODE && (kbled & (1<<USB_LED_SCROLL_LOCK))) ||
#endif //SCROLL LOCK
#if USB_LED_COMPOSE_SCANCODE != 255
(led_map[i].scan == USB_LED_COMPOSE_SCANCODE && (kbled & (1<<USB_LED_COMPOSE))) ||
#endif //COMPOSE
#if USB_LED_KANA_SCANCODE != 255
(led_map[i].scan == USB_LED_KANA_SCANCODE && (kbled & (1<<USB_LED_KANA))) ||
#endif //KANA
(0))
{
if (led_animation_circular) {
po = sqrtf((powf(fabsf((disp.width / 2) - (led_cur->x - disp.left)), 2) + powf(fabsf((disp.height / 2) - (led_cur->y - disp.bottom)), 2))) / disp.max_distance * 100;
led_buffer[i].r = 255 - led_buffer[i].r;
led_buffer[i].g = 255 - led_buffer[i].g;
led_buffer[i].b = 255 - led_buffer[i].b;
}
else {
if (led_animation_orientation)
{
po = led_cur->py;
}
else
{
po = led_cur->px;
}
}
float pomod;
pomod = (float)(disp.frame % (uint32_t)(1000.0f / led_animation_speed)) / 10.0f * led_animation_speed;
const rgb_matrix_driver_t rgb_matrix_driver = {
.init = init,
.flush = flush,
.set_color = led_set_one,
.set_color_all = led_set_all
};
//Add in any moving effects
if ((!led_animation_direction && f[fcur].ef & EF_SCR_R) || (led_animation_direction && (f[fcur].ef & EF_SCR_L)))
/*==============================================================================
= Legacy Lighting Support =
==============================================================================*/
#ifdef USE_MASSDROP_CONFIGURATOR
// Ported from Massdrop QMK Github Repo
// TODO?: wire these up to keymap.c
uint8_t led_animation_orientation = 0;
uint8_t led_animation_direction = 0;
uint8_t led_animation_breathing = 0;
uint8_t led_animation_id = 0;
float led_animation_speed = 4.0f;
uint8_t led_lighting_mode = LED_MODE_NORMAL;
uint8_t led_enabled = 1;
uint8_t led_animation_breathe_cur = BREATHE_MIN_STEP;
uint8_t breathe_dir = 1;
static void led_run_pattern(led_setup_t *f, float* ro, float* go, float* bo, float pos) {
float po;
while (f->end != 1)
{
pomod *= 100.0f;
pomod = (uint32_t)pomod % 10000;
pomod /= 100.0f;
po = pos; //Reset po for new frame
//Add in any moving effects
if ((!led_animation_direction && f->ef & EF_SCR_R) || (led_animation_direction && (f->ef & EF_SCR_L)))
{
po -= pomod;
if (po > 100) po -= 100;
else if (po < 0) po += 100;
}
else if ((!led_animation_direction && f[fcur].ef & EF_SCR_L) || (led_animation_direction && (f[fcur].ef & EF_SCR_R)))
else if ((!led_animation_direction && f->ef & EF_SCR_L) || (led_animation_direction && (f->ef & EF_SCR_R)))
{
pomod *= 100.0f;
pomod = (uint32_t)pomod % 10000;
pomod /= 100.0f;
po += pomod;
if (po > 100) po -= 100;
@ -372,167 +400,103 @@ void led_matrix_run(void)
}
//Check if LED's po is in current frame
if (po < f[fcur].hs) continue;
if (po > f[fcur].he) continue;
if (po < f->hs) { f++; continue; }
if (po > f->he) { f++; continue; }
//note: < 0 or > 100 continue
//Calculate the po within the start-stop percentage for color blending
po = (po - f[fcur].hs) / (f[fcur].he - f[fcur].hs);
po = (po - f->hs) / (f->he - f->hs);
//Add in any color effects
if (f[fcur].ef & EF_OVER)
if (f->ef & EF_OVER)
{
ro = (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
go = (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
bo = (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
*ro = (po * (f->re - f->rs)) + f->rs;// + 0.5;
*go = (po * (f->ge - f->gs)) + f->gs;// + 0.5;
*bo = (po * (f->be - f->bs)) + f->bs;// + 0.5;
}
else if (f[fcur].ef & EF_SUBTRACT)
else if (f->ef & EF_SUBTRACT)
{
ro -= (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
go -= (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
bo -= (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
*ro -= (po * (f->re - f->rs)) + f->rs;// + 0.5;
*go -= (po * (f->ge - f->gs)) + f->gs;// + 0.5;
*bo -= (po * (f->be - f->bs)) + f->bs;// + 0.5;
}
else
{
ro += (po * (f[fcur].re - f[fcur].rs)) + f[fcur].rs;// + 0.5;
go += (po * (f[fcur].ge - f[fcur].gs)) + f[fcur].gs;// + 0.5;
bo += (po * (f[fcur].be - f[fcur].bs)) + f[fcur].bs;// + 0.5;
*ro += (po * (f->re - f->rs)) + f->rs;// + 0.5;
*go += (po * (f->ge - f->gs)) + f->gs;// + 0.5;
*bo += (po * (f->be - f->bs)) + f->bs;// + 0.5;
}
f++;
}
}
//Clamp values 0-255
if (ro > 255) ro = 255; else if (ro < 0) ro = 0;
if (go > 255) go = 255; else if (go < 0) go = 0;
if (bo > 255) bo = 255; else if (bo < 0) bo = 0;
if (led_animation_breathing)
static void led_matrix_massdrop_config_override(int i)
{
ro *= breathe_mult;
go *= breathe_mult;
bo *= breathe_mult;
}
float ro = 0;
float go = 0;
float bo = 0;
*led_cur->rgb.r = (uint8_t)ro;
*led_cur->rgb.g = (uint8_t)go;
*led_cur->rgb.b = (uint8_t)bo;
float po = (led_animation_orientation)
? (float)g_rgb_leds[i].point.y / 64.f * 100
: (float)g_rgb_leds[i].point.x / 224.f * 100;
#ifdef USB_LED_INDICATOR_ENABLE
if (keyboard_leds())
{
uint8_t kbled = keyboard_leds();
if (
#if USB_LED_NUM_LOCK_SCANCODE != 255
(led_cur->scan == USB_LED_NUM_LOCK_SCANCODE && kbled & (1<<USB_LED_NUM_LOCK)) ||
#endif //NUM LOCK
#if USB_LED_CAPS_LOCK_SCANCODE != 255
(led_cur->scan == USB_LED_CAPS_LOCK_SCANCODE && kbled & (1<<USB_LED_CAPS_LOCK)) ||
#endif //CAPS LOCK
#if USB_LED_SCROLL_LOCK_SCANCODE != 255
(led_cur->scan == USB_LED_SCROLL_LOCK_SCANCODE && kbled & (1<<USB_LED_SCROLL_LOCK)) ||
#endif //SCROLL LOCK
#if USB_LED_COMPOSE_SCANCODE != 255
(led_cur->scan == USB_LED_COMPOSE_SCANCODE && kbled & (1<<USB_LED_COMPOSE)) ||
#endif //COMPOSE
#if USB_LED_KANA_SCANCODE != 255
(led_cur->scan == USB_LED_KANA_SCANCODE && kbled & (1<<USB_LED_KANA)) ||
#endif //KANA
(0))
{
if (*led_cur->rgb.r > 127) *led_cur->rgb.r = 0;
else *led_cur->rgb.r = 255;
if (*led_cur->rgb.g > 127) *led_cur->rgb.g = 0;
else *led_cur->rgb.g = 255;
if (*led_cur->rgb.b > 127) *led_cur->rgb.b = 0;
else *led_cur->rgb.b = 255;
}
uint8_t highest_active_layer = biton32(layer_state);
if (led_lighting_mode == LED_MODE_KEYS_ONLY && g_rgb_leds[i].matrix_co.raw == 0xff) {
//Do not act on this LED
} else if (led_lighting_mode == LED_MODE_NON_KEYS_ONLY && g_rgb_leds[i].matrix_co.raw != 0xff) {
//Do not act on this LED
} else if (led_lighting_mode == LED_MODE_INDICATORS_ONLY) {
//Do not act on this LED (Only show indicators)
} else {
led_instruction_t* led_cur_instruction = led_instructions;
while (!led_cur_instruction->end) {
// Check if this applies to current layer
if ((led_cur_instruction->flags & LED_FLAG_MATCH_LAYER) &&
(led_cur_instruction->layer != highest_active_layer)) {
goto next_iter;
}
#endif //USB_LED_INDICATOR_ENABLE
led_cur++;
led_this_run++;
// Check if this applies to current index
if (led_cur_instruction->flags & LED_FLAG_MATCH_ID) {
uint8_t modid = i / 32; //Calculate which id# contains the led bit
uint32_t modidbit = 1 << (i % 32); //Calculate the bit within the id#
uint32_t *bitfield = &led_cur_instruction->id0 + modid; //Add modid as offset to id0 address. *bitfield is now idX of the led id
if (~(*bitfield) & modidbit) { //Check if led bit is not set in idX
goto next_iter;
}
}
uint8_t led_matrix_init(void)
{
DBGC(DC_LED_MATRIX_INIT_BEGIN);
issi3733_prepare_arrays();
led_matrix_prepare();
disp.frame = 0;
led_next_run = 0;
led_enabled = 1;
led_animation_id = 0;
led_lighting_mode = LED_MODE_NORMAL;
led_animation_speed = 4.0f;
led_animation_direction = 0;
led_animation_orientation = 0;
led_animation_breathing = 0;
led_animation_breathe_cur = BREATHE_MIN_STEP;
breathe_step = 1;
breathe_dir = 1;
led_animation_circular = 0;
gcr_min_counter = 0;
v_5v_cat_hit = 0;
//Run led matrix code once for initial LED coloring
led_cur = 0;
rgb_matrix_init_user();
led_matrix_run();
DBGC(DC_LED_MATRIX_INIT_COMPLETE);
return 0;
if (led_cur_instruction->flags & LED_FLAG_USE_RGB) {
ro = led_cur_instruction->r;
go = led_cur_instruction->g;
bo = led_cur_instruction->b;
} else if (led_cur_instruction->flags & LED_FLAG_USE_PATTERN) {
led_run_pattern(led_setups[led_cur_instruction->pattern_id], &ro, &go, &bo, po);
} else if (led_cur_instruction->flags & LED_FLAG_USE_ROTATE_PATTERN) {
led_run_pattern(led_setups[led_animation_id], &ro, &go, &bo, po);
}
__attribute__ ((weak))
void rgb_matrix_init_user(void) {
next_iter:
led_cur_instruction++;
}
#define LED_UPDATE_RATE 10 //ms
//led data processing can take time, so process data in chunks to free up the processor
//this is done through led_cur and lede
void led_matrix_task(void)
{
if (led_enabled)
{
//If an update may run and frame processing has completed
if (timer_read64() >= led_next_run && led_cur == lede)
{
uint8_t drvid;
led_next_run = timer_read64() + LED_UPDATE_RATE; //Set next frame update time
if (ro > 255) ro = 255; else if (ro < 0) ro = 0;
if (go > 255) go = 255; else if (go < 0) go = 0;
if (bo > 255) bo = 255; else if (bo < 0) bo = 0;
//NOTE: GCR does not need to be timed with LED processing, but there is really no harm
if (gcr_actual != gcr_actual_last)
if (led_animation_breathing)
{
for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
I2C_LED_Q_GCR(drvid); //Queue data
gcr_actual_last = gcr_actual;
}
for (drvid=0;drvid<ISSI3733_DRIVER_COUNT;drvid++)
I2C_LED_Q_PWM(drvid); //Queue data
i2c_led_q_run();
led_cur = 0; //Signal next frame calculations may begin
ro *= breathe_mult;
go *= breathe_mult;
bo *= breathe_mult;
}
}
//Process more data if not finished
if (led_cur != lede)
{
//DBG_1_OFF; //debug profiling
led_matrix_run();
//DBG_1_ON; //debug profiling
}
led_buffer[i].r = (uint8_t)ro;
led_buffer[i].g = (uint8_t)go;
led_buffer[i].b = (uint8_t)bo;
}
#endif // USE_MASSDROP_CONFIGURATOR

@ -18,6 +18,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef _LED_MATRIX_H_
#define _LED_MATRIX_H_
#include "quantum.h"
//From keyboard
#include "config_led.h"
@ -75,25 +77,20 @@ typedef struct issi3733_led_s {
uint8_t scan; //Key scan code from wiring (set 0xFF if no key)
} issi3733_led_t;
typedef struct led_disp_s {
uint64_t frame;
float left;
float right;
float top;
float bottom;
float width;
float height;
float max_distance;
} led_disp_t;
extern issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
uint8_t led_matrix_init(void);
void rgb_matrix_init_user(void);
extern uint8_t gcr_desired;
extern uint8_t gcr_breathe;
extern uint8_t gcr_actual;
extern uint8_t gcr_actual_last;
#define LED_MODE_NORMAL 0 //Must be 0
#define LED_MODE_KEYS_ONLY 1
#define LED_MODE_NON_KEYS_ONLY 2
#define LED_MODE_INDICATORS_ONLY 3
#define LED_MODE_MAX_INDEX LED_MODE_INDICATORS_ONLY //Must be highest value
void gcr_compute(void);
void led_matrix_indicators(void);
/*------------------------- Legacy Lighting Support ------------------------*/
#ifdef USE_MASSDROP_CONFIGURATOR
#define EF_NONE 0x00000000 //No effect
#define EF_OVER 0x00000001 //Overwrite any previous color information with new
@ -114,33 +111,48 @@ typedef struct led_setup_s {
uint8_t end; //Set to signal end of the setup
} led_setup_t;
extern issi3733_driver_t issidrv[ISSI3733_DRIVER_COUNT];
extern const uint8_t led_setups_count;
extern void *led_setups[];
extern uint8_t gcr_desired;
extern uint8_t gcr_breathe;
extern uint8_t gcr_actual;
extern uint8_t gcr_actual_last;
//LED Extra Instructions
#define LED_FLAG_NULL 0x00 //Matching and coloring not used (default)
#define LED_FLAG_MATCH_ID 0x01 //Match on the ID of the LED (set id#'s to desired bit pattern, first LED is id 1)
#define LED_FLAG_MATCH_LAYER 0x02 //Match on the current active layer (set layer to desired match layer)
#define LED_FLAG_USE_RGB 0x10 //Use a specific RGB value (set r, g, b to desired output color values)
#define LED_FLAG_USE_PATTERN 0x20 //Use a specific pattern ID (set pattern_id to desired output pattern)
#define LED_FLAG_USE_ROTATE_PATTERN 0x40 //Use pattern the user has cycled to manually
typedef struct led_instruction_s {
uint16_t flags; // Bitfield for LED instructions
uint32_t id0; // Bitwise id, IDs 0-31
uint32_t id1; // Bitwise id, IDs 32-63
uint32_t id2; // Bitwise id, IDs 64-95
uint32_t id3; // Bitwise id, IDs 96-127
uint8_t layer;
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t pattern_id;
uint8_t end;
} led_instruction_t;
extern led_instruction_t led_instructions[];
extern uint8_t led_animation_breathing;
extern uint8_t led_animation_id;
extern uint8_t led_enabled;
extern float led_animation_speed;
extern uint8_t led_lighting_mode;
extern uint8_t led_animation_direction;
extern uint8_t led_animation_orientation;
extern uint8_t led_animation_breathing;
extern uint8_t led_enabled;
extern uint8_t led_animation_breathe_cur;
extern uint8_t led_animation_direction;
extern uint8_t breathe_dir;
extern uint8_t led_animation_circular;
extern const uint8_t led_setups_count;
extern void *led_setups[];
extern issi3733_led_t *led_cur;
extern issi3733_led_t *lede;
void led_matrix_run(void);
void led_matrix_task(void);
#define LED_MODE_NORMAL 0 //Must be 0
#define LED_MODE_KEYS_ONLY 1
#define LED_MODE_NON_KEYS_ONLY 2
#define LED_MODE_INDICATORS_ONLY 3
#define LED_MODE_MAX_INDEX LED_MODE_INDICATORS_ONLY //Must be highest value
void gcr_compute(void);
#endif // USE_MASSDROP_CONFIGURATOR
#endif //_LED_MATRIX_H_

@ -0,0 +1,123 @@
/*
Copyright 2018 Massdrop Inc.
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 <http://www.gnu.org/licenses/>.
*/
#ifdef USE_MASSDROP_CONFIGURATOR
#include "led_matrix.h"
//Teal <-> Salmon
led_setup_t leds_teal_salmon[] = {
{ .hs = 0, .he = 33, .rs = 24, .re = 24, .gs = 215, .ge = 215, .bs = 204, .be = 204, .ef = EF_NONE },
{ .hs = 33, .he = 66, .rs = 24, .re = 255, .gs = 215, .ge = 114, .bs = 204, .be = 118, .ef = EF_NONE },
{ .hs = 66, .he = 100, .rs = 255, .re = 255, .gs = 114, .ge = 114, .bs = 118, .be = 118, .ef = EF_NONE },
{ .end = 1 },
};
//Yellow
led_setup_t leds_yellow[] = {
{ .hs = 0, .he = 100, .rs = 255, .re = 255, .gs = 255, .ge = 255, .bs = 0, .be = 0, .ef = EF_NONE },
{ .end = 1 },
};
//Off
led_setup_t leds_off[] = {
{ .hs = 0, .he = 100, .rs = 0, .re = 0, .gs = 0, .ge = 0, .bs = 0, .be = 0, .ef = EF_NONE },
{ .end = 1 },
};
//Red
led_setup_t leds_red[] = {
{ .hs = 0, .he = 100, .rs = 255, .re = 255, .gs = 0, .ge = 0, .bs = 0, .be = 0, .ef = EF_NONE },
{ .end = 1 },
};
//Green
led_setup_t leds_green[] = {
{ .hs = 0, .he = 100, .rs = 0, .re = 0, .gs = 255, .ge = 255, .bs = 0, .be = 0, .ef = EF_NONE },
{ .end = 1 },
};
//Blue
led_setup_t leds_blue[] = {
{ .hs = 0, .he = 100, .rs = 0, .re = 0, .gs = 0, .ge = 0, .bs = 255, .be = 255, .ef = EF_NONE },
{ .end = 1 },
};
//White
led_setup_t leds_white[] = {
{ .hs = 0, .he = 100, .rs = 255, .re = 255, .gs = 255, .ge = 255, .bs = 255, .be = 255, .ef = EF_NONE },
{ .end = 1 },
};
//White with moving red stripe
led_setup_t leds_white_with_red_stripe[] = {
{ .hs = 0, .he = 100, .rs = 255, .re = 255, .gs = 255, .ge = 255, .bs = 255, .be = 255, .ef = EF_NONE },
{ .hs = 0, .he = 15, .rs = 0, .re = 0, .gs = 0, .ge = 255, .bs = 0, .be = 255, .ef = EF_SCR_R | EF_SUBTRACT },
{ .hs = 15, .he = 30, .rs = 0, .re = 0, .gs = 255, .ge = 0, .bs = 255, .be = 0, .ef = EF_SCR_R | EF_SUBTRACT },
{ .end = 1 },
};
//Black with moving red stripe
led_setup_t leds_black_with_red_stripe[] = {
{ .hs = 0, .he = 15, .rs = 0, .re = 255, .gs = 0, .ge = 0, .bs = 0, .be = 0, .ef = EF_SCR_R },
{ .hs = 15, .he = 30, .rs = 255, .re = 0, .gs = 0, .ge = 0, .bs = 0, .be = 0, .ef = EF_SCR_R },
{ .end = 1 },
};
//Rainbow no scrolling
led_setup_t leds_rainbow_ns[] = {
{ .hs = 0, .he = 16.67, .rs = 255, .re = 255, .gs = 0, .ge = 255, .bs = 0, .be = 0, .ef = EF_OVER },
{ .hs = 16.67, .he = 33.33, .rs = 255, .re = 0, .gs = 255, .ge = 255, .bs = 0, .be = 0, .ef = EF_OVER },
{ .hs = 33.33, .he = 50, .rs = 0, .re = 0, .gs = 255, .ge = 255, .bs = 0, .be = 255, .ef = EF_OVER },
{ .hs = 50, .he = 66.67, .rs = 0, .re = 0, .gs = 255, .ge = 0, .bs = 255, .be = 255, .ef = EF_OVER },
{ .hs = 66.67, .he = 83.33, .rs = 0, .re = 255, .gs = 0, .ge = 0, .bs = 255, .be = 255, .ef = EF_OVER },
{ .hs = 83.33, .he = 100, .rs = 255, .re = 255, .gs = 0, .ge = 0, .bs = 255, .be = 0, .ef = EF_OVER },
{ .end = 1 },
};
//Rainbow scrolling
led_setup_t leds_rainbow_s[] = {
{ .hs = 0, .he = 16.67, .rs = 255, .re = 255, .gs = 0, .ge = 255, .bs = 0, .be = 0, .ef = EF_OVER | EF_SCR_R },
{ .hs = 16.67, .he = 33.33, .rs = 255, .re = 0, .gs = 255, .ge = 255, .bs = 0, .be = 0, .ef = EF_OVER | EF_SCR_R },
{ .hs = 33.33, .he = 50, .rs = 0, .re = 0, .gs = 255, .ge = 255, .bs = 0, .be = 255, .ef = EF_OVER | EF_SCR_R },
{ .hs = 50, .he = 66.67, .rs = 0, .re = 0, .gs = 255, .ge = 0, .bs = 255, .be = 255, .ef = EF_OVER | EF_SCR_R },
{ .hs = 66.67, .he = 83.33, .rs = 0, .re = 255, .gs = 0, .ge = 0, .bs = 255, .be = 255, .ef = EF_OVER | EF_SCR_R },
{ .hs = 83.33, .he = 100, .rs = 255, .re = 255, .gs = 0, .ge = 0, .bs = 255, .be = 0, .ef = EF_OVER | EF_SCR_R },
{ .end = 1 },
};
//Add new LED animations here using one from above as example
//The last entry must be { .end = 1 }
//Add the new animation name to the list below following its format
void *led_setups[] = {
leds_rainbow_s,
leds_rainbow_ns,
leds_teal_salmon,
leds_yellow,
leds_red,
leds_green,
leds_blue,
leds_white,
leds_white_with_red_stripe,
leds_black_with_red_stripe,
leds_off
};
const uint8_t led_setups_count = sizeof(led_setups) / sizeof(led_setups[0]);
#endif

@ -203,13 +203,6 @@ void main_subtask_usb_state(void)
}
}
void main_subtask_led(void)
{
if (g_usb_state != USB_FSMSTATUS_FSMSTATE_ON_Val) return; //Only run LED tasks if USB is operating
led_matrix_task();
}
void main_subtask_power_check(void)
{
static uint64_t next_5v_checkup = 0;
@ -221,7 +214,9 @@ void main_subtask_power_check(void)
v_5v = adc_get(ADC_5V);
v_5v_avg = 0.9 * v_5v_avg + 0.1 * v_5v;
#ifdef RGB_MATRIX_ENABLE
gcr_compute();
#endif
}
}
@ -240,7 +235,6 @@ void main_subtask_usb_extra_device(void)
void main_subtasks(void)
{
main_subtask_usb_state();
main_subtask_led();
main_subtask_power_check();
main_subtask_usb_extra_device();
}
@ -263,7 +257,9 @@ int main(void)
SR_EXP_Init();
#ifdef RGB_MATRIX_ENABLE
i2c1_init();
#endif // RGB_MATRIX_ENABLE
matrix_init();
@ -281,8 +277,7 @@ int main(void)
DBG_LED_OFF;
led_matrix_init();
#ifdef RGB_MATRIX_ENABLE
while (I2C3733_Init_Control() != 1) {}
while (I2C3733_Init_Drivers() != 1) {}
@ -292,6 +287,7 @@ int main(void)
for (uint8_t drvid = 0; drvid < ISSI3733_DRIVER_COUNT; drvid++)
I2C_LED_Q_ONOFF(drvid); //Queue data
#endif // RGB_MATRIX_ENABLE
keyboard_setup();

@ -366,7 +366,9 @@ void USB_ExtraSetState(uint8_t state)
else if (usb_extra_state == USB_EXTRA_STATE_DISABLED)
{
CDC_print("USB: Extra disabled\r\n");
#ifdef USE_MASSDROP_CONFIGURATOR
if (led_animation_breathing) gcr_breathe = gcr_desired;
#endif
}
else if (usb_extra_state == USB_EXTRA_STATE_DISABLED_UNTIL_REPLUG) CDC_print("USB: Extra disabled until replug\r\n");
else CDC_print("USB: Extra state unknown\r\n");

@ -47,4 +47,4 @@ dir=$(pwd -W 2>/dev/null) || dir=$PWD # Use Windows path if on Windows
# Run container and build firmware
docker run --rm -it $usb_args -v "$dir":/qmk_firmware qmkfm/qmk_firmware \
make "$keyboard${keymap:+:$keymap}${target:+:$target}"
/bin/bash -c "make git-submodule; make \"$keyboard${keymap:+:$keymap}${target:+:$target}\""

Loading…
Cancel
Save