~ruther/qmk_firmware

f5209aa4e941c7f3a942ef4a7e256ab432ec98ce — fauxpark 5 years ago eb5d267
Remove custom backlight code for PS2AVRGB boards (#7775)

* Remove custom backlight code for PS2AVRGB boards

* Remove custom driver setting

* BACKLIGHT_BREATHING goes in config.h, not here

* Don't need to include backlight.c again here either

* Turn on backlight for Canoe

* Disable console on a few boards due to oversize
101 files changed, 474 insertions(+), 2802 deletions(-)

M keyboards/ares/ares.c
M keyboards/ares/config.h
M keyboards/ares/rules.mk
M keyboards/bfake/bfake.c
M keyboards/bfake/config.h
M keyboards/bfake/rules.mk
M keyboards/donutcables/budget96/budget96.c
M keyboards/donutcables/budget96/config.h
M keyboards/donutcables/budget96/rules.mk
M keyboards/eve/meteor/config.h
M keyboards/eve/meteor/meteor.c
M keyboards/exclusive/e6v2/le_bmc/config.h
M keyboards/exclusive/e6v2/le_bmc/le_bmc.c
M keyboards/exclusive/e6v2/le_bmc/rules.mk
M keyboards/exclusive/e6v2/oe_bmc/config.h
M keyboards/exclusive/e6v2/oe_bmc/oe_bmc.c
M keyboards/exclusive/e6v2/oe_bmc/rules.mk
M keyboards/exent/config.h
M keyboards/exent/exent.c
M keyboards/exent/rules.mk
M keyboards/facew/config.h
M keyboards/facew/facew.c
M keyboards/facew/rules.mk
M keyboards/ft/mars80/config.h
M keyboards/ft/mars80/mars80.c
M keyboards/gray_studio/hb85/hb85.c
M keyboards/gray_studio/hb85/rules.mk
M keyboards/jc65/v32a/config.h
M keyboards/jc65/v32a/v32a.c
M keyboards/jj40/config.h
M keyboards/jj4x4/config.h
D keyboards/jj50/backlight.c
D keyboards/jj50/backlight_custom.h
D keyboards/jj50/breathing_custom.h
M keyboards/jj50/config.h
M keyboards/jj50/jj50.c
M keyboards/jj50/rules.mk
M keyboards/kbdfans/kbdpad/mk1/mk1.c
M keyboards/mechmini/v1/config.h
M keyboards/mechmini/v1/rules.mk
M keyboards/mehkee96/config.h
M keyboards/mehkee96/mehkee96.c
M keyboards/mehkee96/rules.mk
M keyboards/panc60/config.h
M keyboards/panc60/panc60.c
M keyboards/pearl/config.h
M keyboards/pearl/pearl.c
M keyboards/percent/canoe/canoe.c
M keyboards/percent/canoe/config.h
M keyboards/percent/canoe/rules.mk
D keyboards/percent/skog/backlight.c
D keyboards/percent/skog/backlight_custom.h
D keyboards/percent/skog/breathing_custom.h
M keyboards/percent/skog/config.h
M keyboards/percent/skog/rules.mk
M keyboards/percent/skog/skog.c
M keyboards/percent/skog_lite/config.h
M keyboards/percent/skog_lite/skog_lite.c
M keyboards/singa/config.h
M keyboards/singa/rules.mk
M keyboards/singa/singa.c
M keyboards/tgr/alice/alice.c
M keyboards/tgr/alice/config.h
M keyboards/tgr/alice/rules.mk
M keyboards/tgr/jane/config.h
M keyboards/tgr/jane/jane.c
M keyboards/unikorn/config.h
M keyboards/unikorn/unikorn.c
M keyboards/winkeyless/bface/bface.c
M keyboards/winkeyless/bface/config.h
M keyboards/winkeyless/bface/rules.mk
M keyboards/winkeyless/bmini/bmini.c
M keyboards/winkeyless/bmini/config.h
M keyboards/winkeyless/bmini/rules.mk
D keyboards/winkeyless/bminiex/backlight.c
D keyboards/winkeyless/bminiex/backlight_custom.h
M keyboards/winkeyless/bminiex/bminiex.c
D keyboards/winkeyless/bminiex/breathing_custom.h
M keyboards/winkeyless/bminiex/config.h
M keyboards/winkeyless/bminiex/rules.mk
D keyboards/ymd75/backlight.c
D keyboards/ymd75/backlight_custom.h
D keyboards/ymd75/breathing_custom.h
M keyboards/ymd75/config.h
M keyboards/ymd75/rules.mk
M keyboards/ymd75/ymd75.c
D keyboards/ymd96/backlight.c
D keyboards/ymd96/backlight_custom.h
D keyboards/ymd96/breathing_custom.h
M keyboards/ymd96/config.h
M keyboards/ymd96/rules.mk
M keyboards/ymd96/ymd96.c
M keyboards/ymdk/bface/bface.c
M keyboards/ymdk/bface/config.h
M keyboards/ymdk/bface/rules.mk
D keyboards/ymdk_np21/backlight.c
D keyboards/ymdk_np21/backlight_custom.h
D keyboards/ymdk_np21/breathing_custom.h
M keyboards/ymdk_np21/config.h
M keyboards/ymdk_np21/rules.mk
M keyboards/ymdk_np21/ymdk_np21.c
M keyboards/ares/ares.c => keyboards/ares/ares.c +13 -21
@@ 17,27 17,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ares.h"

#ifdef BACKLIGHT_ENABLE
void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}
#endif

M keyboards/ares/config.h => keyboards/ares/config.h +2 -1
@@ 37,7 37,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

#define NO_UART 1

M keyboards/ares/rules.mk => keyboards/ares/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = no
WS2812_DRIVER = i2c


M keyboards/bfake/bfake.c => keyboards/bfake/bfake.c +12 -23
@@ 17,30 17,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "bfake.h"

void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

	writePinLow(D0);
	writePinLow(D1);
	writePinLow(D4);
	writePinLow(D6);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/bfake/config.h => keyboards/bfake/config.h +3 -5
@@ 15,8 15,7 @@ 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 BFAKE_CONFIG_H
#define BFAKE_CONFIG_H
#pragma once

#include "config_common.h"



@@ 38,9 37,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

#define NO_UART 1

#endif

M keyboards/bfake/rules.mk => keyboards/bfake/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = no
WS2812_DRIVER = i2c


M keyboards/donutcables/budget96/budget96.c => keyboards/donutcables/budget96/budget96.c +11 -21
@@ 16,32 16,22 @@

#include "budget96.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/donutcables/budget96/config.h => keyboards/donutcables/budget96/config.h +2 -1
@@ 37,5 37,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

M keyboards/donutcables/budget96/rules.mk => keyboards/donutcables/budget96/rules.mk +1 -1
@@ 15,7 15,7 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = no
MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes

M keyboards/eve/meteor/config.h => keyboards/eve/meteor/config.h +2 -1
@@ 36,4 36,5 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

M keyboards/eve/meteor/meteor.c => keyboards/eve/meteor/meteor.c +10 -23
@@ 16,32 16,19 @@

#include "meteor.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

    // turn backlight LEDs on
    writePinHigh(D0);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/exclusive/e6v2/le_bmc/config.h => keyboards/exclusive/e6v2/le_bmc/config.h +3 -0
@@ 45,5 45,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, C2, C3, C4, C5, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLED_NUM 6
#define RGBLIGHT_ANIMATIONS

M keyboards/exclusive/e6v2/le_bmc/le_bmc.c => keyboards/exclusive/e6v2/le_bmc/le_bmc.c +10 -23
@@ 16,32 16,19 @@

#include "le_bmc.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

    // turn backlight LEDs on
    writePinHigh(D0);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/exclusive/e6v2/le_bmc/rules.mk => keyboards/exclusive/e6v2/le_bmc/rules.mk +2 -2
@@ 17,13 17,13 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = no       # Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = yes       # Mouse keys(+4700)
EXTRAKEY_ENABLE = yes       # Audio control and System control(+450)
CONSOLE_ENABLE = yes        # Console for debug(+400)
CONSOLE_ENABLE = no         # Console for debug(+400)
COMMAND_ENABLE = yes        # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no            # USB Nkey Rollover
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality on B7 by default
BACKLIGHT_ENABLE = yes      # Enable keyboard backlight functionality on B7 by default
WS2812_DRIVER = i2c
RGBLIGHT_ENABLE = yes        # Enable keyboard RGB underglow
MIDI_ENABLE = no            # MIDI support (+2400 to 4200, depending on config)

M keyboards/exclusive/e6v2/oe_bmc/config.h => keyboards/exclusive/e6v2/oe_bmc/config.h +3 -0
@@ 45,5 45,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, C2, C3, C4, C5, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLED_NUM 6
#define RGBLIGHT_ANIMATIONS

M keyboards/exclusive/e6v2/oe_bmc/oe_bmc.c => keyboards/exclusive/e6v2/oe_bmc/oe_bmc.c +10 -23
@@ 16,32 16,19 @@

#include "oe_bmc.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

    // turn backlight LEDs on
    writePinHigh(D0);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/exclusive/e6v2/oe_bmc/rules.mk => keyboards/exclusive/e6v2/oe_bmc/rules.mk +2 -2
@@ 17,13 17,13 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = no       # Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = yes       # Mouse keys(+4700)
EXTRAKEY_ENABLE = yes       # Audio control and System control(+450)
CONSOLE_ENABLE = yes        # Console for debug(+400)
CONSOLE_ENABLE = no         # Console for debug(+400)
COMMAND_ENABLE = yes        # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no            # USB Nkey Rollover
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality on B7 by default
BACKLIGHT_ENABLE = yes      # Enable keyboard backlight functionality on B7 by default
WS2812_DRIVER = i2c
RGBLIGHT_ENABLE = yes        # Enable keyboard RGB underglow
MIDI_ENABLE = no            # MIDI support (+2400 to 4200, depending on config)

M keyboards/exent/config.h => keyboards/exent/config.h +2 -1
@@ 39,7 39,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

#define NO_UART 1

M keyboards/exent/exent.c => keyboards/exent/exent.c +23 -0
@@ 15,3 15,26 @@
 */

#include "exent.h"

void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D6);
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D6);
}

bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/exent/rules.mk => keyboards/exent/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/facew/config.h => keyboards/facew/config.h +2 -1
@@ 37,5 37,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

M keyboards/facew/facew.c => keyboards/facew/facew.c +14 -23
@@ 17,30 17,21 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "facew.h"

void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

	writePinLow(D0);
	writePinLow(D1);
	writePinLow(D4);
	writePinLow(D6);
void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    writePinHigh(D0);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/facew/rules.mk => keyboards/facew/rules.mk +1 -1
@@ 15,7 15,7 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = no
MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes

M keyboards/ft/mars80/config.h => keyboards/ft/mars80/config.h +2 -1
@@ 38,7 38,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3
#define RGBLIGHT_ANIMATIONS

#define NO_UART 1

M keyboards/ft/mars80/mars80.c => keyboards/ft/mars80/mars80.c +11 -21
@@ 16,32 16,22 @@

#include "mars80.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/gray_studio/hb85/hb85.c => keyboards/gray_studio/hb85/hb85.c +12 -19
@@ 18,32 18,25 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "hb85.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/gray_studio/hb85/rules.mk => keyboards/gray_studio/hb85/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/jc65/v32a/config.h => keyboards/jc65/v32a/config.h +3 -1
@@ 32,7 32,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, A6, A7, C7, C6, C5, C4, C3, C2, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLED_NUM 16
#define RGBLIGHT_ANIMATIONS
#define RGBLIGHT_HUE_STEP 8

M keyboards/jc65/v32a/v32a.c => keyboards/jc65/v32a/v32a.c +12 -25
@@ 17,32 17,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "v32a.h"

#ifdef BACKLIGHT_ENABLE
void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

	writePinLow(D0);
	writePinLow(D1);
	writePinLow(D4);
	writePinLow(D6);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}
#endif

M keyboards/jj40/config.h => keyboards/jj40/config.h +1 -1
@@ 35,7 35,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12
// #define BACKLIGHT_BREATHING  // Requires #4324 to enable hardware pwm for atmega32a
#define BACKLIGHT_BREATHING

/* RGB underglow */
// NOTE: for PS2AVRGB boards, underglow commands are sent via I2C to 0xB0.

M keyboards/jj4x4/config.h => keyboards/jj4x4/config.h +1 -1
@@ 38,7 38,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12
// #define BACKLIGHT_BREATHING  // Requires #4324 to enable hardware pwm for atmega32a
#define BACKLIGHT_BREATHING

/* RGB underglow */
// NOTE: for PS2AVRGB boards, underglow commands are sent via I2C to 0xB0.

D keyboards/jj50/backlight.c => keyboards/jj50/backlight.c +0 -213
@@ 1,213 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // D0
#define CAPSLOCK_PORT   (1 << 1)  // D1
#define BACKLIGHT_PORT  (1 << 4)  // D4
#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif

  // TODO call user defined function
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/jj50/backlight_custom.h => keyboards/jj50/backlight_custom.h +0 -15
@@ 1,15 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifndef BACKLIGHT_CUSTOM_H
#define BACKLIGHT_CUSTOM_H

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);

#endif  // BACKLIGHT_CUSTOM_H

D keyboards/jj50/breathing_custom.h => keyboards/jj50/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/jj50/config.h => keyboards/jj50/config.h +1 -1
@@ 38,7 38,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, A6, A7, C7, C6, C5, C4 }
#define DIODE_DIRECTION COL2ROW

//#define BACKLIGHT_PIN D4
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12

#define RGB_DI_PIN E2

M keyboards/jj50/jj50.c => keyboards/jj50/jj50.c +0 -17
@@ 16,20 16,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "jj50.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
void backlight_init_ports(void) {
    b_led_init_ports();
}

void backlight_task(void) {
    b_led_task();
}

void backlight_set(uint8_t level) {
    b_led_set(level);
}
#endif

M keyboards/jj50/rules.mk => keyboards/jj50/rules.mk +0 -2
@@ 29,6 29,4 @@ SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

#OPT_DEFS = -DDEBUG_LEVEL=0

SRC = backlight.c

LAYOUTS = ortho_5x12

M keyboards/kbdfans/kbdpad/mk1/mk1.c => keyboards/kbdfans/kbdpad/mk1/mk1.c +12 -7
@@ 16,14 16,19 @@

#include "mk1.h"

#define NUMLOCK_PIN D0
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void matrix_init_kb(void) {
    setPinOutput(NUMLOCK_PIN);
    matrix_init_user();
void led_init_ports(void) {
    setPinOutput(D0);
    writePinHigh(D0);
}

void led_set_kb(uint8_t usb_led) {
    writePin(NUMLOCK_PIN, IS_LED_ON(usb_led, USB_LED_NUM_LOCK));
    led_set_user(usb_led);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
    }
    return true;
}

M keyboards/mechmini/v1/config.h => keyboards/mechmini/v1/config.h +4 -4
@@ 15,8 15,7 @@ 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 V1_CONFIG_H
#define V1_CONFIG_H
#pragma once

#include "config_common.h"



@@ 38,11 37,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define NO_UART 1

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

/* RGB underglow */
// The RGB_DI_PIN value seems to be shared between all PS2AVRGB boards.
// The same pin is used on the JJ40, at least.
#define RGBLED_NUM 16
#define RGBLIGHT_ANIMATIONS
#define RGB_DI_PIN E2

#endif

M keyboards/mechmini/v1/rules.mk => keyboards/mechmini/v1/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/mehkee96/config.h => keyboards/mehkee96/config.h +3 -0
@@ 16,6 16,9 @@
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, A6, A7, C7, C6, C5, C4, C3, C2, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLED_NUM 16
#define RGBLIGHT_ANIMATIONS
/* key combination for command */

M keyboards/mehkee96/mehkee96.c => keyboards/mehkee96/mehkee96.c +20 -0
@@ 16,3 16,23 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "mehkee96.h"

void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    writePinHigh(D0);
    writePinHigh(D1);
}

bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/mehkee96/rules.mk => keyboards/mehkee96/rules.mk +2 -2
@@ 17,9 17,9 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = yes	# Virtual DIP switch configuration(+1000)
MOUSEKEY_ENABLE = yes	# Mouse keys(+4700)
EXTRAKEY_ENABLE = yes	# Audio control and System control(+450)
CONSOLE_ENABLE = yes	# Console for debug(+400)
CONSOLE_ENABLE = no	# Console for debug(+400)
COMMAND_ENABLE = yes    # Commands for debug and configuration
BACKLIGHT_ENABLE = no  # Enable keyboard backlight functionality
BACKLIGHT_ENABLE = yes  # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/panc60/config.h => keyboards/panc60/config.h +3 -1
@@ 37,5 37,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLIGHT_ANIMATIONS

M keyboards/panc60/panc60.c => keyboards/panc60/panc60.c +12 -23
@@ 16,30 16,19 @@

#include "panc60.h"

void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

	writePinLow(D0);
	writePinLow(D1);
	writePinLow(D4);
	writePinLow(D6);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/pearl/config.h => keyboards/pearl/config.h +2 -1
@@ 31,7 31,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define RGBLIGHT_ANIMATIONS
#define RGBLED_NUM 12

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define MATRIX_ROWS 4
#define MATRIX_COLS 13

M keyboards/pearl/pearl.c => keyboards/pearl/pearl.c +12 -19
@@ 17,32 17,25 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "pearl.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/percent/canoe/canoe.c => keyboards/percent/canoe/canoe.c +12 -25
@@ 17,32 17,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "canoe.h"

#ifdef BACKLIGHT_ENABLE
void backlight_set(uint8_t level) {
	if (level == 0) {
		// Turn out the lights
		writePinLow(D0);
		writePinLow(D1);
		writePinLow(D4);
		writePinLow(D6);
	} else {
		// Turn on the lights
		writePinHigh(D0);
		writePinHigh(D1);
		writePinHigh(D4);
		writePinHigh(D6);
	}
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_init_ports(void) {
	setPinOutput(D0);
	setPinOutput(D1);
	setPinOutput(D4);
	setPinOutput(D6);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

	writePinLow(D0);
	writePinLow(D1);
	writePinLow(D4);
	writePinLow(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}
#endif

M keyboards/percent/canoe/config.h => keyboards/percent/canoe/config.h +3 -1
@@ 38,7 38,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLIGHT_ANIMATIONS

#define NO_UART 1

M keyboards/percent/canoe/rules.mk => keyboards/percent/canoe/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


D keyboards/percent/skog/backlight.c => keyboards/percent/skog/backlight.c +0 -211
@@ 1,211 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // D0
#define CAPSLOCK_PORT   (1 << 1)  // D1
#define BACKLIGHT_PORT  (1 << 4)  // D4
#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/percent/skog/backlight_custom.h => keyboards/percent/skog/backlight_custom.h +0 -13
@@ 1,13 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#pragma once

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);


D keyboards/percent/skog/breathing_custom.h => keyboards/percent/skog/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/percent/skog/config.h => keyboards/percent/skog/config.h +2 -4
@@ 15,8 15,7 @@ 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 CONFIG_H
#define CONFIG_H
#pragma once

#define VENDOR_ID       0x20A0
#define PRODUCT_ID      0x422D


@@ 36,8 35,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define RGBLED_NUM 2
#define RGBLIGHT_ANIMATIONS

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 5

#define NO_UART 1

#endif

M keyboards/percent/skog/rules.mk => keyboards/percent/skog/rules.mk +0 -4
@@ 21,11 21,7 @@ BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c

BACKLIGHT_CUSTOM_DRIVER = yes

OPT_DEFS = -DDEBUG_LEVEL=0

# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

SRC = backlight.c

M keyboards/percent/skog/skog.c => keyboards/percent/skog/skog.c +14 -12
@@ 19,20 19,22 @@ ps2avrGB support code by Kenneth A. (bminiex/.[ch])

#include "skog.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
/// Overrides functions in `quantum.c`
void backlight_init_ports(void) {
  b_led_init_ports();
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_task(void) {
  b_led_task();
void led_init_ports(void) {
    setPinOutput(D1);
    setPinOutput(D6);
    writePinHigh(D1);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
  b_led_set(level);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}
#endif

M keyboards/percent/skog_lite/config.h => keyboards/percent/skog_lite/config.h +3 -1
@@ 38,5 38,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLIGHT_ANIMATIONS

M keyboards/percent/skog_lite/skog_lite.c => keyboards/percent/skog_lite/skog_lite.c +11 -21
@@ 16,32 16,22 @@

#include "skog_lite.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/singa/config.h => keyboards/singa/config.h +3 -1
@@ 37,5 37,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLIGHT_ANIMATIONS

M keyboards/singa/rules.mk => keyboards/singa/rules.mk +1 -1
@@ 15,7 15,7 @@ BOOTLOADER = bootloadHID
BOOTMAGIC_ENABLE = no
MOUSEKEY_ENABLE = yes
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes

M keyboards/singa/singa.c => keyboards/singa/singa.c +10 -23
@@ 16,32 16,19 @@

#include "singa.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

    // turn backlight LEDs on
    writePinHigh(D0);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/tgr/alice/alice.c => keyboards/tgr/alice/alice.c +23 -0
@@ 16,3 16,26 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "alice.h"

void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D6);
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D6);
}

bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/tgr/alice/config.h => keyboards/tgr/alice/config.h +3 -0
@@ 33,6 33,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, A6, A7, C7, C6, C5, C4, C3, C2, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#define RGBLED_NUM 18
#define RGBLIGHT_ANIMATIONS


M keyboards/tgr/alice/rules.mk => keyboards/tgr/alice/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/tgr/jane/config.h => keyboards/tgr/jane/config.h +2 -1
@@ 36,4 36,5 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

M keyboards/tgr/jane/jane.c => keyboards/tgr/jane/jane.c +11 -21
@@ 16,32 16,22 @@

#include "jane.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/unikorn/config.h => keyboards/unikorn/config.h +3 -1
@@ 36,7 36,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW
#define DEBOUNCE 5

#define BACKLIGHT_LEVELS 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#ifdef RGBLIGHT_ENABLE
#define RGBLED_NUM 17
#define RGBLIGHT_ANIMATIONS

M keyboards/unikorn/unikorn.c => keyboards/unikorn/unikorn.c +10 -23
@@ 16,32 16,19 @@

#include "unikorn.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

    // turn backlight LEDs on
    writePinHigh(D0);
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}

M keyboards/winkeyless/bface/bface.c => keyboards/winkeyless/bface/bface.c +12 -19
@@ 16,32 16,25 @@

#include "bface.h"

void backlight_init_ports(void) {
    // initialize pins D0, D1, D4 and D6 as output
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D4);
    setPinOutput(D6);

    // turn backlight LEDs on
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D4);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
    if (level == 0) {
        // turn backlight LEDs off
        writePinLow(D0);
        writePinLow(D1);
        writePinLow(D4);
        writePinLow(D6);
    } else {
        // turn backlight LEDs on
        writePinHigh(D0);
        writePinHigh(D1);
        writePinHigh(D4);
        writePinHigh(D6);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/winkeyless/bface/config.h => keyboards/winkeyless/bface/config.h +2 -7
@@ 42,10 42,5 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define NO_UART 1

#ifdef BACKLIGHT_ENABLE
	// the backlight PWM does not work (yet). Therefore, we only have two backlight levels (on/off)
	#define BACKLIGHT_LEVELS 1
	#define LED_PIN PORTD
	#define BACKLIGHT_PORT_NUM (1 << 4)
#endif

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

M keyboards/winkeyless/bface/rules.mk => keyboards/winkeyless/bface/rules.mk +0 -2
@@ 18,8 18,6 @@ EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = yes
BACKLIGHT_CUSTOM_DRIVER = yes
BACKLIGHT_BREATHING = no
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


M keyboards/winkeyless/bmini/bmini.c => keyboards/winkeyless/bmini/bmini.c +23 -0
@@ 16,3 16,26 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "bmini.h"

void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D6);
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D6);
}

bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}

M keyboards/winkeyless/bmini/config.h => keyboards/winkeyless/bmini/config.h +4 -4
@@ 15,8 15,7 @@ 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 BMINI_CONFIG_H
#define BMINI_CONFIG_H
#pragma once

#include "config_common.h"



@@ 37,6 36,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define RGBLIGHT_ANIMATIONS

#define NO_UART 1
#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 3

#endif
#define NO_UART 1

M keyboards/winkeyless/bmini/rules.mk => keyboards/winkeyless/bmini/rules.mk +1 -1
@@ 17,7 17,7 @@ MOUSEKEY_ENABLE = no
EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = yes
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = no
BACKLIGHT_ENABLE = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


D keyboards/winkeyless/bminiex/backlight.c => keyboards/winkeyless/bminiex/backlight.c +0 -211
@@ 1,211 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // D0
#define CAPSLOCK_PORT   (1 << 1)  // D1
#define BACKLIGHT_PORT  (1 << 4)  // D4
#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/winkeyless/bminiex/backlight_custom.h => keyboards/winkeyless/bminiex/backlight_custom.h +0 -13
@@ 1,13 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#pragma once

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);


M keyboards/winkeyless/bminiex/bminiex.c => keyboards/winkeyless/bminiex/bminiex.c +17 -12
@@ 17,20 17,25 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "bminiex.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
/// Overrides functions in `quantum.c`
void backlight_init_ports(void) {
  b_led_init_ports();
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_task(void) {
  b_led_task();
void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    setPinOutput(D6);
    writePinHigh(D0);
    writePinHigh(D1);
    writePinHigh(D6);
}

void backlight_set(uint8_t level) {
  b_led_set(level);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
        writePin(D6, !led_state.scroll_lock);
    }
    return true;
}
#endif

D keyboards/winkeyless/bminiex/breathing_custom.h => keyboards/winkeyless/bminiex/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/winkeyless/bminiex/config.h => keyboards/winkeyless/bminiex/config.h +1 -0
@@ 36,6 36,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define RGBLIGHT_ANIMATIONS

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 5

#define NO_UART 1

M keyboards/winkeyless/bminiex/rules.mk => keyboards/winkeyless/bminiex/rules.mk +0 -3
@@ 19,7 19,6 @@ CONSOLE_ENABLE = no
DEBUG_ENABLE = no
COMMAND_ENABLE = no
BACKLIGHT_ENABLE = yes
BACKLIGHT_CUSTOM_DRIVER = yes
RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c
TAP_DANCE_ENABLE = no


@@ 28,5 27,3 @@ TAP_DANCE_ENABLE = no
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

OPT_DEFS = -DDEBUG_LEVEL=0

SRC += backlight.c

D keyboards/ymd75/backlight.c => keyboards/ymd75/backlight.c +0 -216
@@ 1,216 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 Modified by Wayne K Jones (github.com/WarmCatUK) 2018
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
//#define NUMLOCK_PORT    (1 << 2)  // 2nd pin of Port D (digital)
#define CAPSLOCK_PORT   (1 << 1)  // 1st pin
#define BACKLIGHT_PORT  (1 << 4)  // D4
//#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    /*
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }
     */
    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }
    /*
    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
     */
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif

  // TODO call user defined function
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/ymd75/backlight_custom.h => keyboards/ymd75/backlight_custom.h +0 -15
@@ 1,15 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifndef BACKLIGHT_CUSTOM_H
#define BACKLIGHT_CUSTOM_H

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);

#endif  // BACKLIGHT_CUSTOM_H

D keyboards/ymd75/breathing_custom.h => keyboards/ymd75/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/ymd75/config.h => keyboards/ymd75/config.h +2 -4
@@ 17,8 17,7 @@ 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 CONFIG_H
#define CONFIG_H
#pragma once

#include "config_common.h"



@@ 39,6 38,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define MATRIX_COL_PINS { A0, A1, A2, A3, A4, A5, A6, A7, C7, C6, C5, C4, C3, C2, D7 }
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12

#define RGB_DI_PIN E2


@@ 49,5 49,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define RGBLIGHT_VAL_STEP 18

#define NO_UART 1

#endif

M keyboards/ymd75/rules.mk => keyboards/ymd75/rules.mk +0 -6
@@ 23,14 23,8 @@ WS2812_DRIVER = i2c
NKRO_ENABLE = no
# Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work


DISABLE_WS2812 = no

KEY_LOCK_ENABLE = yes
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend


#OPT_DEFS = -DDEBUG_LEVEL=0

SRC = backlight.c

M keyboards/ymd75/ymd75.c => keyboards/ymd75/ymd75.c +11 -12
@@ 17,20 17,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ymd75.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
/// Overrides functions in `quantum.c`
void backlight_init_ports(void) {
    b_led_init_ports();
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_task(void) {
    b_led_task();
void led_init_ports(void) {
    setPinOutput(D1);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
    b_led_set(level);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}
#endif

D keyboards/ymd96/backlight.c => keyboards/ymd96/backlight.c +0 -214
@@ 1,214 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // 0th pin of Port D (digital)
#define CAPSLOCK_PORT   (1 << 1)  // 1st pin
#define BACKLIGHT_PORT  (1 << 4)  // D4
//#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

  /* YMD96 does not have scroll lock led  
   if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }*/
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif

  // TODO call user defined function
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/ymd96/backlight_custom.h => keyboards/ymd96/backlight_custom.h +0 -15
@@ 1,15 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifndef BACKLIGHT_CUSTOM_H
#define BACKLIGHT_CUSTOM_H

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);

#endif  // BACKLIGHT_CUSTOM_H

D keyboards/ymd96/breathing_custom.h => keyboards/ymd96/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/ymd96/config.h => keyboards/ymd96/config.h +3 -5
@@ 18,8 18,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "config_common.h"

#ifndef CONFIG_H
#define CONFIG_H
#pragma once

#define VENDOR_ID       0x20A0
#define PRODUCT_ID      0x422D


@@ 38,8 37,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/* COL2ROW or ROW2COL */
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12
// #define BACKLIGHT_BREATHING  // works, but BL_TOGG might not work
#define BACKLIGHT_BREATHING

#define TAPPING_TOGGLE 3



@@ 55,5 55,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/*#define RGBLIGHT_VAL_STEP 20

#define NO_UART 1*/

#endif

M keyboards/ymd96/rules.mk => keyboards/ymd96/rules.mk +0 -3
@@ 19,7 19,6 @@ CONSOLE_ENABLE = no
COMMAND_ENABLE = no

BACKLIGHT_ENABLE = yes
BACKLIGHT_CUSTOM_DRIVER = yes

RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


@@ 30,5 29,3 @@ KEY_LOCK_ENABLE = yes
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

OPT_DEFS = -DDEBUG_LEVEL=0

SRC = backlight.c

M keyboards/ymd96/ymd96.c => keyboards/ymd96/ymd96.c +14 -12
@@ 18,20 18,22 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ymd96.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
/// Overrides functions in `quantum.c`
void backlight_init_ports(void) {
  b_led_init_ports();
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_task(void) {
  b_led_task();
void led_init_ports(void) {
    setPinOutput(D0);
    setPinOutput(D1);
    writePinHigh(D0);
    writePinHigh(D1);
}

void backlight_set(uint8_t level) {
  b_led_set(level);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
        writePin(D1, !led_state.caps_lock);
    }
    return true;
}
#endif

M keyboards/ymdk/bface/bface.c => keyboards/ymdk/bface/bface.c +10 -17
@@ 16,26 16,19 @@
 */
#include "quantum.h"

void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void keyboard_pre_init_kb(void){
    //init the CAPS LOCK LED pin as an output
void led_init_ports(void) {
    setPinOutput(D1);
    //init the Backlight Pin as an output
    setPinOutput(D4);
    //call any user initialization code
    keyboard_pre_init_user();
    writePinHigh(D1);
}

void led_set_kb(uint8_t usb_led){
    //control the caps lock LED
    if(IS_LED_ON(usb_led, USB_LED_CAPS_LOCK)){
        //set led pin to high
        writePinHigh(D1);
    } else {
        //set to low
        writePinLow(D1);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D1, !led_state.caps_lock);
    }
    //call any user LED functions
    led_set_user(usb_led);
    return true;
}


M keyboards/ymdk/bface/config.h => keyboards/ymdk/bface/config.h +0 -2
@@ 40,5 40,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#define BACKLIGHT_PIN       D4
#define BACKLIGHT_LEVELS    6



M keyboards/ymdk/bface/rules.mk => keyboards/ymdk/bface/rules.mk +0 -3
@@ 18,9 18,6 @@ EXTRAKEY_ENABLE = yes
CONSOLE_ENABLE = no
COMMAND_ENABLE = yes
BACKLIGHT_ENABLE = yes
BACKLIGHT_CUSTOM_DRIVER = no
BACKLIGHT_BREATHING = no
RGBLIGHT_ENABLE = no
RGBLIGHT_CUSTOM_DRIVER = no

OPT_DEFS = -DDEBUG_LEVEL=0

D keyboards/ymdk_np21/backlight.c => keyboards/ymdk_np21/backlight.c +0 -213
@@ 1,213 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#include "backlight.h"
#include "quantum.h"

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include "backlight_custom.h"
#include "breathing_custom.h"

// DEBUG
#include <stdlib.h>
#include <stdio.h>

// Port D: digital pins of the AVR chipset
#define NUMLOCK_PORT    (1 << 0)  // D0
#define CAPSLOCK_PORT   (1 << 1)  // D1
#define BACKLIGHT_PORT  (1 << 4)  // D4
#define SCROLLLOCK_PORT (1 << 6)  // D6

#define TIMER_CLK_DIV64			  0x03	///< Timer clocked at F_CPU/64
#define TIMER1PRESCALE	TIMER_CLK_DIV64 ///< timer 1 prescaler default

#define TIMER_PRESCALE_MASK		0x07	///< Timer Prescaler Bit-Mask

#define PWM_MAX 0xFF
#define TIMER_TOP 255 // 8 bit PWM

extern backlight_config_t backlight_config;

/**
 * References
 * Port Registers: https://www.arduino.cc/en/Reference/PortManipulation
 * TCCR1A: https://electronics.stackexchange.com/questions/92350/what-is-the-difference-between-tccr1a-and-tccr1b
 * Timers: http://www.avrbeginners.net/architecture/timers/timers.html
 * 16-bit timer setup: http://sculland.com/ATmega168/Interrupts-And-Timers/16-Bit-Timer-Setup/
 * PS2AVRGB firmware: https://github.com/showjean/ps2avrU/tree/master/firmware
 */

// @Override
// turn LEDs on and off depending on USB caps/num/scroll lock states.
__attribute__ ((weak))
void led_set_user(uint8_t usb_led) {
    if (usb_led & (1 << USB_LED_NUM_LOCK)) {
      // turn on
      DDRD  |= NUMLOCK_PORT;
      PORTD |= NUMLOCK_PORT;
    } else {
      // turn off
      DDRD  &= ~NUMLOCK_PORT;
      PORTD &= ~NUMLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_CAPS_LOCK)) {
      DDRD  |= CAPSLOCK_PORT;
      PORTD |= CAPSLOCK_PORT;
    } else {
      DDRD  &= ~CAPSLOCK_PORT;
      PORTD &= ~CAPSLOCK_PORT;
    }

    if (usb_led & (1 << USB_LED_SCROLL_LOCK)) {
      DDRD  |= SCROLLLOCK_PORT;
      PORTD |= SCROLLLOCK_PORT;
    } else {
      DDRD  &= ~SCROLLLOCK_PORT;
      PORTD &= ~SCROLLLOCK_PORT;
    }
}

#ifdef BACKLIGHT_ENABLE

// sets up Timer 1 for 8-bit PWM
void timer1PWMSetup(void) { // NOTE ONLY CALL THIS ONCE
  // default 8 bit mode
  TCCR1A &= ~(1 << 1); // cbi(TCCR1A,PWM11); <- set PWM11 bit to HIGH
  TCCR1A |= (1 << 0);  // sbi(TCCR1A,PWM10); <- set PWM10 bit to LOW

  // clear output compare value A
  // outb(OCR1AH, 0);
  // outb(OCR1AL, 0);

  // clear output comparator registers for B
	OCR1BH = 0; // outb(OCR1BH, 0);
	OCR1BL = 0; // outb(OCR1BL, 0);
}

bool is_init = false;
void timer1Init(void) {
  // timer1SetPrescaler(TIMER1PRESCALE)
  // set to DIV/64
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | TIMER1PRESCALE;

  // reset TCNT1
  TCNT1H = 0;  // outb(TCNT1H, 0);
	TCNT1L = 0;  // outb(TCNT1L, 0);

  // TOIE1: Timer Overflow Interrupt Enable (Timer 1);
	TIMSK |= _BV(TOIE1); // sbi(TIMSK, TOIE1);

  is_init = true;
}

void timer1UnInit(void) {
  // set prescaler back to NONE
  (TCCR1B) = ((TCCR1B) & ~TIMER_PRESCALE_MASK) | 0x00;  // TIMERRTC_CLK_STOP

  // disable timer overflow interrupt
  TIMSK &= ~_BV(TOIE1); // overflow bit?

  setPWM(0);

  is_init = false;
}


// handle TCNT1 overflow
//! Interrupt handler for tcnt1 overflow interrupt
ISR(TIMER1_OVF_vect, ISR_NOBLOCK)
{
	// sei();
  // handle breathing here
  #ifdef BACKLIGHT_BREATHING
  if (is_breathing()) {
    custom_breathing_handler();
  }
  #endif

  // TODO call user defined function
}

// enable timer 1 PWM
// timer1PWMBOn()
void timer1PWMBEnable(void) {
  // turn on channel B (OC1B) PWM output
  // set OC1B as non-inverted PWM
  TCCR1A |= _BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

// disable timer 1 PWM
// timer1PWMBOff()
void timer1PWMBDisable(void) {
  TCCR1A &= ~_BV(COM1B1);
  TCCR1A &= ~_BV(COM1B0);
}

void enableBacklight(void) {
  DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
  PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
}

void disableBacklight(void) {
  // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
  PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
}

void startPWM(void) {
  timer1Init();
  timer1PWMBEnable();
  enableBacklight();
}

void stopPWM(void) {
  timer1UnInit();
  disableBacklight();
  timer1PWMBDisable();
}

void b_led_init_ports(void) {
  /* turn backlight on/off depending on user preference */
  #if BACKLIGHT_ON_STATE == 0
    // DDRx register: sets the direction of Port D
    // DDRD  &= ~BACKLIGHT_PORT;  // set digital pin 4 as input
    PORTD &= ~BACKLIGHT_PORT;  // set digital pin 4 to low
  #else
    DDRD  |= BACKLIGHT_PORT;  // set digital pin 4 as output
    PORTD |= BACKLIGHT_PORT;  // set digital pin 4 to high
  #endif

  timer1PWMSetup();
  startPWM();

  #ifdef BACKLIGHT_BREATHING
  breathing_enable();
  #endif
}

void b_led_set(uint8_t level) {
  if (level > BACKLIGHT_LEVELS) {
    level = BACKLIGHT_LEVELS;
  }

  setPWM((int)(TIMER_TOP * (float) level / BACKLIGHT_LEVELS));
}

// called every matrix scan
void b_led_task(void) {
  // do nothing for now
}

void setPWM(uint16_t xValue) {
  if (xValue > TIMER_TOP) {
    xValue = TIMER_TOP;
  }
  OCR1B = xValue; // timer1PWMBSet(xValue);
}

#endif  // BACKLIGHT_ENABLE

D keyboards/ymdk_np21/backlight_custom.h => keyboards/ymdk_np21/backlight_custom.h +0 -15
@@ 1,15 0,0 @@
/**
 * Backlighting code for PS2AVRGB boards (ATMEGA32A)
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifndef BACKLIGHT_CUSTOM_H
#define BACKLIGHT_CUSTOM_H

#include <avr/pgmspace.h>
void b_led_init_ports(void);
void b_led_set(uint8_t level);
void b_led_task(void);
void setPWM(uint16_t xValue);

#endif  // BACKLIGHT_CUSTOM_H

D keyboards/ymdk_np21/breathing_custom.h => keyboards/ymdk_np21/breathing_custom.h +0 -140
@@ 1,140 0,0 @@
/**
 * Breathing effect code for PS2AVRGB boards (ATMEGA32A)
 * Works in conjunction with `backlight.c`.
 *
 * Code adapted from `quantum.c` to register with the existing TIMER1 overflow
 * handler in `backlight.c` instead of setting up its own timer.
 * Kenneth A. (github.com/krusli | krusli.me)
 */

#ifdef BACKLIGHT_ENABLE
#ifdef BACKLIGHT_BREATHING

#include "backlight_custom.h"

#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif

#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)

// TODO make this share code with quantum.c

#define BREATHING_NO_HALT  0
#define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON  2
#define BREATHING_STEPS 128

static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;

static bool breathing = false;

bool is_breathing(void) {
  return breathing;
}

// See http://jared.geek.nz/2013/feb/linear-led-pwm
static uint16_t cie_lightness(uint16_t v) {
  if (v <= 5243) // if below 8% of max
    return v / 9; // same as dividing by 900%
  else {
    uint32_t y = (((uint32_t) v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare
    // to get a useful result with integer division, we shift left in the expression above
    // and revert what we've done again after squaring.
    y = y * y * y >> 8;
    if (y > 0xFFFFUL) // prevent overflow
      return 0xFFFFU;
    else
      return (uint16_t) y;
  }
}

void breathing_enable(void) {
  breathing = true;
  breathing_counter = 0;
  breathing_halt = BREATHING_NO_HALT;
  // interrupt already registered
}

void breathing_pulse(void) {
  if (get_backlight_level() == 0)
    breathing_min();
  else
    breathing_max();
  breathing_halt = BREATHING_HALT_ON;
  // breathing_interrupt_enable();
  breathing = true;
}

void breathing_disable(void) {
  breathing = false;
  // backlight_set(get_backlight_level());
  b_led_set(get_backlight_level()); // custom implementation of backlight_set()
}

void breathing_self_disable(void)
{
  if (get_backlight_level() == 0)
    breathing_halt = BREATHING_HALT_OFF;
  else
    breathing_halt = BREATHING_HALT_ON;
}

void breathing_toggle(void) {
  if (is_breathing())
    breathing_disable();
  else
    breathing_enable();
}

void breathing_period_set(uint8_t value)
{
  if (!value)
    value = 1;
  breathing_period = value;
}

void breathing_period_default(void) {
  breathing_period_set(BREATHING_PERIOD);
}

void breathing_period_inc(void)
{
  breathing_period_set(breathing_period+1);
}

void breathing_period_dec(void)
{
  breathing_period_set(breathing_period-1);
}

/* To generate breathing curve in python:
 * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
 */
static const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// Use this before the cie_lightness function.
static inline uint16_t scale_backlight(uint16_t v) {
  return v / BACKLIGHT_LEVELS * get_backlight_level();
}

void custom_breathing_handler(void) {
  uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
  // resetting after one period to prevent ugly reset at overflow.
  breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
  uint8_t index = breathing_counter / interval % BREATHING_STEPS;

  if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
      ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
  {
      // breathing_interrupt_disable();
  }

  setPWM(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
}

#endif // BACKLIGHT_BREATHING
#endif  // BACKLIGHT_ENABLE

M keyboards/ymdk_np21/config.h => keyboards/ymdk_np21/config.h +4 -6
@@ 15,10 15,9 @@ 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 "config_common.h"
#pragma once

#ifndef CONFIG_H
#define CONFIG_H
#include "config_common.h"

#define VENDOR_ID       0x20A0
#define PRODUCT_ID      0x422D


@@ 37,8 36,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
/* COL2ROW or ROW2COL */
#define DIODE_DIRECTION COL2ROW

#define BACKLIGHT_PIN D4
#define BACKLIGHT_LEVELS 12
// #define BACKLIGHT_BREATHING  // works, but BL_TOGG might not work
#define BACKLIGHT_BREATHING

#define TAPPING_TOGGLE 3



@@ 52,5 52,3 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define RGBLED_NUM 5
#define RGB_DI_PIN E2 // NOTE: for PS2AVRGB boards, underglow commands are sent via I2C to 0xB0.
#define RGBLIGHT_ANIMATIONS

#endif

M keyboards/ymdk_np21/rules.mk => keyboards/ymdk_np21/rules.mk +0 -3
@@ 19,7 19,6 @@ CONSOLE_ENABLE = no
COMMAND_ENABLE = yes

BACKLIGHT_ENABLE = yes
BACKLIGHT_CUSTOM_DRIVER = yes

RGBLIGHT_ENABLE = yes
WS2812_DRIVER = i2c


@@ 30,5 29,3 @@ KEY_LOCK_ENABLE = yes
SLEEP_LED_ENABLE = no    # Breathing sleep LED during USB suspend

OPT_DEFS = -DDEBUG_LEVEL=0

SRC = backlight.c

M keyboards/ymdk_np21/ymdk_np21.c => keyboards/ymdk_np21/ymdk_np21.c +11 -12
@@ 18,20 18,19 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "ymdk_np21.h"

#include "backlight.h"
#include "backlight_custom.h"

#ifdef BACKLIGHT_ENABLE
/// Overrides functions in `quantum.c`
void backlight_init_ports(void) {
  b_led_init_ports();
void keyboard_pre_init_kb(void) {
    led_init_ports();
    keyboard_pre_init_user();
}

void backlight_task(void) {
  b_led_task();
void led_init_ports(void) {
    setPinOutput(D0);
    writePinHigh(D0);
}

void backlight_set(uint8_t level) {
  b_led_set(level);
bool led_update_kb(led_t led_state) {
    if (led_update_user(led_state)) {
        writePin(D0, !led_state.num_lock);
    }
    return true;
}
#endif