backlight breathing overhaul (#2187)

* add breathing to bananasplit

* backlight breathing overhaul

* fix the backlight_tick thing.

* fix for vision_division backlight

* fix a few keymaps and probably break breathing for some weirdly set-up boards.

* remove BL_x keycodes because they made unreasonable assumptions

* some fixes for BL keycodes

* integer cie lightness scaling

* use cie lightness for non-breathing backlight and make breathing able to reach true max brightness
pull/2211/head 0.5.204
Balz Guenat 7 years ago committed by Jack Humbert
parent d6215ad6af
commit 4931510ad3

@ -70,6 +70,10 @@ This is a C header file that is one of the first things included, and will persi
* pin of the backlight - B5, B6, B7 use PWM, others use softPWM * pin of the backlight - B5, B6, B7 use PWM, others use softPWM
* `#define BACKLIGHT_LEVELS 3` * `#define BACKLIGHT_LEVELS 3`
* number of levels your backlight will have (not including off) * number of levels your backlight will have (not including off)
* `#define BACKLIGHT_BREATHING`
* enables backlight breathing (only works with backlight pins B5, B6 and B7)
* `#define BREATHING_PERIOD 6`
* the length of one backlight "breath" in seconds
* `#define DEBOUNCING_DELAY 5` * `#define DEBOUNCING_DELAY 5`
* the delay when reading the value of the pin (5 is default) * the delay when reading the value of the pin (5 is default)
* `#define LOCKING_SUPPORT_ENABLE` * `#define LOCKING_SUPPORT_ENABLE`

@ -127,7 +127,7 @@ https://github.com/tekezo/Karabiner/issues/403
## Esc and <code>&#96;</code> on a Single Key ## Esc and <code>&#96;</code> on a Single Key
See the [Grave Escape](feature_grave_escape.md) feature. See the [Grave Escape](feature_grave_esc.md) feature.
## Arrow on Right Modifier Keys with Dual-Role ## Arrow on Right Modifier Keys with Dual-Role
This turns right modifier keys into arrow keys when the keys are tapped while still modifiers when the keys are hold. In TMK the dual-role function is dubbed **TAP**. This turns right modifier keys into arrow keys when the keys are tapped while still modifiers when the keys are hold. In TMK the dual-role function is dubbed **TAP**.

@ -10,8 +10,30 @@ These keycodes control the backlight. Most keyboards use this for single color i
|---------|------------------------------------------| |---------|------------------------------------------|
|`BL_TOGG`|Turn the backlight on or off | |`BL_TOGG`|Turn the backlight on or off |
|`BL_STEP`|Cycle through backlight levels | |`BL_STEP`|Cycle through backlight levels |
|`BL_x` |Set a specific backlight level between 0-9| |`BL_ON` |Set backlight to max brightness |
|`BL_ON` |An alias for `BL_9` | |`BL_OFF` |Turn backlight off |
|`BL_OFF` |An alias for `BL_0` |
|`BL_INC` |Increase backlight level | |`BL_INC` |Increase backlight level |
|`BL_DEC` |Decrease backlight level | |`BL_DEC` |Decrease backlight level |
|`BL_BRTG`|Toggle backlight breathing |
Note that for backlight breathing, you need to have `#define BACKLIGHT_BREATHING` in your config.h.
## Configuration Options in `config.h`
* `BACKLIGHT_PIN B7` defines the pin that controlls the LEDs. Unless you design your own keyboard, you don't need to set this.
* `BACKLIGHT_LEVELS 3` defines the number of brightness levels (excluding OFF).
* `BACKLIGHT_BREATHING` if defined, enables backlight breathing. Note that this is only available if `BACKLIGHT_PIN` is B5, B6 or B7.
* `BREATHING_PERIOD 6` defines the length of one backlight "breath" in seconds.
## Notes on Implementation
To change the brightness when using pins B5, B6 or B7, the PWM (Pulse Width Modulation) functionality of the on-chip timer is used.
The timer is a counter that counts up to a certain TOP value (`0xFFFF` set in ICR1) before resetting to 0.
We also set an OCR1x register.
When the counter reaches the value stored in that register, the PWM pin drops to low.
The PWM pin is pulled high again when the counter resets to 0.
Therefore, OCR1x basically sets the duty cycle of the LEDs and as such the brightness where `0` is the darkest and `0xFFFF` the brightest setting.
To enable the breathing effect, we register an interrupt handler to be called whenever the counter resets (with `ISR(TIMER1_OVF_vect)`).
In this handler, which gets called roughly 244 times per second, we compute the desired brightness using a precomputed brightness curve.
To disable breathing, we can just disable the respective interrupt vector and reset the brightness to the desired level.

@ -101,8 +101,9 @@ By default QMK supports backlighting on pins `B5`, `B6`, and `B7`. If you are us
``` ```
#define BACKLIGHT_PIN B7 #define BACKLIGHT_PIN B7
#define BACKLIGHT_BREATHING
#define BACKLIGHT_LEVELS 3 #define BACKLIGHT_LEVELS 3
#define BACKLIGHT_BREATHING
#define BREATHING_PERIOD 6
``` ```
{% hint style='info' %} {% hint style='info' %}

@ -345,14 +345,14 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_BREATH_SPEED_INC: case MACRO_BREATH_SPEED_INC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_inc(1); breathing_period_inc();
} }
break; break;
case MACRO_BREATH_SPEED_DEC: case MACRO_BREATH_SPEED_DEC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_dec(1); breathing_period_dec();
} }
break; break;
@ -374,7 +374,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_UPPER); layer_on(LAYER_UPPER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -389,7 +389,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_LOWER); layer_on(LAYER_LOWER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -403,13 +403,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_FUNCTION: case MACRO_FUNCTION:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(3); breathing_period_set(3);
breathing_enable(); breathing_enable();
layer_on(LAYER_FUNCTION); layer_on(LAYER_FUNCTION);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(LAYER_FUNCTION); layer_off(LAYER_FUNCTION);
} }

@ -42,7 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
//#define MATRIX_HAS_GHOST //#define MATRIX_HAS_GHOST
/* number of backlight levels */ /* number of backlight levels */
#define BACKLIGHT_LEVELS 1 #define BACKLIGHT_LEVELS 3
/* mapping backlight LEDs to correct Pin */ /* mapping backlight LEDs to correct Pin */
#define BACKLIGHT_PIN B7 #define BACKLIGHT_PIN B7

@ -21,5 +21,7 @@
// place overrides here // place overrides here
#define GRAVE_ESC_CTRL_OVERRIDE #define GRAVE_ESC_CTRL_OVERRIDE
#define BACKLIGHT_BREATHING
#define BREATHING_PERIOD 8
#endif #endif

@ -16,7 +16,7 @@
#include "bananasplit.h" #include "bananasplit.h"
enum custom_keycodes { enum custom_keycodes {
WIN_SWITCH_LAYOUT = SAFE_RANGE WIN_SWITCH_LAYOUT = SAFE_RANGE,
}; };
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
@ -57,7 +57,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, \ _______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, \
KC_CAPS, KC_MPRV, KC_VOLU, KC_MNXT, KC_PGUP, KC_INS, KC_HOME, LCTL(KC_LEFT), LCTL(KC_RGHT), KC_END, _______, _______, _______, KC_PSCR, \ KC_CAPS, KC_MPRV, KC_VOLU, KC_MNXT, KC_PGUP, KC_INS, KC_HOME, LCTL(KC_LEFT), LCTL(KC_RGHT), KC_END, _______, _______, _______, KC_PSCR, \
_______, KC_MUTE, KC_VOLD, KC_MPLY, KC_PGDN, KC_DEL, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, _______, _______, _______, \ _______, KC_MUTE, KC_VOLD, KC_MPLY, KC_PGDN, KC_DEL, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, _______, _______, _______, \
_______, _______, _______, _______, _______,_______, LCTL(KC_BSPC), LCTL(KC_DEL), _______, _______, _______, _______, _______, \ _______, BL_STEP, BL_BRTG, _______, _______,_______, LCTL(KC_BSPC), LCTL(KC_DEL), _______, _______, _______, _______, _______, \
_______, _______, _______, _______, _______,_______, _______, _______, _______, _______, RESET \ _______, _______, _______, _______, _______,_______, _______, _______, _______, _______, RESET \
), ),
}; };
@ -76,19 +76,19 @@ void matrix_scan_user(void) {
bool process_record_user(uint16_t keycode, keyrecord_t *record) { bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) { switch (keycode) {
case WIN_SWITCH_LAYOUT: { case WIN_SWITCH_LAYOUT: {
// Sends Alt+Shift on both key down and key up. // Sends Alt+Shift on both key down and key up.
// Designed to switch between two keyboard layouts on Windows using a locking switch. // Designed to switch between two keyboard layouts on Windows using a locking switch.
// Does nothing if right shift is pressed for easy resync. // Does nothing if right shift is pressed for easy resync.
if (!(get_mods() & MOD_BIT(KC_RSFT))) { if (!(get_mods() & MOD_BIT(KC_RSFT)))
SEND_STRING(SS_DOWN(X_LALT)SS_TAP(X_LSHIFT)SS_UP(X_LALT)); SEND_STRING(SS_DOWN(X_LALT)SS_TAP(X_LSHIFT)SS_UP(X_LALT));
return false; return false;
}
else
return false;
} }
default:
return true;
} }
return true;
} }
void led_set_user(uint8_t usb_led) { void led_set_user(uint8_t usb_led) {

@ -50,13 +50,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
// action_function_tap may also handle this... // action_function_tap may also handle this...
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(3); breathing_period_set(3);
breathing_enable(); breathing_enable();
layer_on(1); layer_on(1);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(1); layer_off(1);
} }
@ -64,13 +64,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case M_LAYER2: case M_LAYER2:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
layer_on(2); layer_on(2);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(2); layer_off(2);
} }

@ -220,7 +220,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{ {
layer_on(_RAISE); layer_on(_RAISE);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);
@ -236,7 +236,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{ {
layer_on(_LOWER); layer_on(_LOWER);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);

@ -242,7 +242,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) { if (record->event.pressed) {
layer_on(_LOWER); layer_on(_LOWER);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);
@ -256,7 +256,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) { if (record->event.pressed) {
layer_on(_RAISE); layer_on(_RAISE);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);

@ -128,12 +128,12 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) {
break; break;
case MACRO_BREATH_SPEED_INC: case MACRO_BREATH_SPEED_INC:
if (record->event.pressed) { if (record->event.pressed) {
breathing_speed_inc(1); breathing_period_inc();
} }
break; break;
case MACRO_BREATH_SPEED_DEC: case MACRO_BREATH_SPEED_DEC:
if (record->event.pressed) { if (record->event.pressed) {
breathing_speed_dec(1); breathing_period_dec();
} }
break; break;
case MACRO_BREATH_DEFAULT: case MACRO_BREATH_DEFAULT:

@ -323,14 +323,14 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_BREATH_SPEED_INC: case MACRO_BREATH_SPEED_INC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_inc(1); breathing_period_inc();
} }
break; break;
case MACRO_BREATH_SPEED_DEC: case MACRO_BREATH_SPEED_DEC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_dec(1); breathing_period_dec();
} }
break; break;
@ -352,7 +352,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_UPPER); layer_on(LAYER_UPPER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -367,7 +367,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_LOWER); layer_on(LAYER_LOWER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -381,13 +381,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_FUNCTION: case MACRO_FUNCTION:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(3); breathing_period_set(3);
breathing_enable(); breathing_enable();
layer_on(LAYER_FUNCTION); layer_on(LAYER_FUNCTION);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(LAYER_FUNCTION); layer_off(LAYER_FUNCTION);
} }

@ -270,7 +270,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
PLAY_SONG(tone_ctrl_mod); PLAY_SONG(tone_ctrl_mod);
#endif #endif
#ifdef BACKLIGHT_BREATHING #ifdef BACKLIGHT_BREATHING
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
} }

@ -332,14 +332,14 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_BREATH_SPEED_INC: case MACRO_BREATH_SPEED_INC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_inc(1); breathing_period_inc();
} }
break; break;
case MACRO_BREATH_SPEED_DEC: case MACRO_BREATH_SPEED_DEC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_dec(1); breathing_period_dec();
} }
break; break;
@ -361,7 +361,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_UPPER); layer_on(LAYER_UPPER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -376,7 +376,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_LOWER); layer_on(LAYER_LOWER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -390,13 +390,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_FUNCTION: case MACRO_FUNCTION:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(3); breathing_period_set(3);
breathing_enable(); breathing_enable();
layer_on(LAYER_FUNCTION); layer_on(LAYER_FUNCTION);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(LAYER_FUNCTION); layer_off(LAYER_FUNCTION);
} }

@ -270,7 +270,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
PLAY_SONG(tone_ctrl_mod); PLAY_SONG(tone_ctrl_mod);
#endif #endif
#ifdef BACKLIGHT_BREATHING #ifdef BACKLIGHT_BREATHING
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
} }

@ -406,21 +406,21 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_BREATH_SPEED_INC: case MACRO_BREATH_SPEED_INC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_inc(1); breathing_period_inc();
} }
break; break;
case MACRO_BREATH_SPEED_DEC: case MACRO_BREATH_SPEED_DEC:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_dec(1); breathing_period_dec();
} }
break; break;
case MACRO_BREATH_DEFAULT: case MACRO_BREATH_DEFAULT:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_defaults(); breathing_period_default();
} }
break; break;
@ -435,7 +435,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_UPPER); layer_on(LAYER_UPPER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -450,7 +450,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
if (record->event.pressed) if (record->event.pressed)
{ {
layer_on(LAYER_LOWER); layer_on(LAYER_LOWER);
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST); update_tri_layer(LAYER_LOWER, LAYER_UPPER, LAYER_ADJUST);
} }
@ -464,13 +464,13 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
case MACRO_FUNCTION: case MACRO_FUNCTION:
if (record->event.pressed) if (record->event.pressed)
{ {
breathing_speed_set(3); breathing_period_set(3);
breathing_enable(); breathing_enable();
layer_on(LAYER_FUNCTION); layer_on(LAYER_FUNCTION);
} }
else else
{ {
breathing_speed_set(1); breathing_period_set(1);
breathing_self_disable(); breathing_self_disable();
layer_off(LAYER_FUNCTION); layer_off(LAYER_FUNCTION);
} }

@ -265,7 +265,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{ {
layer_on(_RAISE); layer_on(_RAISE);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);
@ -281,7 +281,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
{ {
layer_on(_LOWER); layer_on(_LOWER);
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
breathing_speed_set(2); breathing_period_set(2);
breathing_pulse(); breathing_pulse();
#endif #endif
update_tri_layer(_LOWER, _RAISE, _ADJUST); update_tri_layer(_LOWER, _RAISE, _ADJUST);

@ -127,8 +127,11 @@ action_t action_for_key(uint8_t layer, keypos_t key)
action.code = ACTION_MODS_TAP_KEY(mod, keycode & 0xFF); action.code = ACTION_MODS_TAP_KEY(mod, keycode & 0xFF);
break; break;
#ifdef BACKLIGHT_ENABLE #ifdef BACKLIGHT_ENABLE
case BL_0 ... BL_15: case BL_ON:
action.code = ACTION_BACKLIGHT_LEVEL(keycode - BL_0); action.code = ACTION_BACKLIGHT_ON();
break;
case BL_OFF:
action.code = ACTION_BACKLIGHT_OFF();
break; break;
case BL_DEC: case BL_DEC:
action.code = ACTION_BACKLIGHT_DECREASE(); action.code = ACTION_BACKLIGHT_DECREASE();

@ -23,6 +23,10 @@
#define TAPPING_TERM 200 #define TAPPING_TERM 200
#endif #endif
#ifndef BREATHING_PERIOD
#define BREATHING_PERIOD 6
#endif
#include "backlight.h" #include "backlight.h"
extern backlight_config_t backlight_config; extern backlight_config_t backlight_config;
@ -618,7 +622,17 @@ bool process_record_quantum(keyrecord_t *record) {
} }
send_keyboard_report(); send_keyboard_report();
return false;
} }
#if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_BREATHING)
case BL_BRTG: {
if (record->event.pressed)
breathing_toggle();
return false;
}
#endif
default: { default: {
shift_interrupted[0] = true; shift_interrupted[0] = true;
shift_interrupted[1] = true; shift_interrupted[1] = true;
@ -831,6 +845,7 @@ void matrix_scan_quantum() {
static const uint8_t backlight_pin = BACKLIGHT_PIN; static const uint8_t backlight_pin = BACKLIGHT_PIN;
// depending on the pin, we use a different output compare unit
#if BACKLIGHT_PIN == B7 #if BACKLIGHT_PIN == B7
# define COM1x1 COM1C1 # define COM1x1 COM1C1
# define OCR1x OCR1C # define OCR1x OCR1C
@ -841,17 +856,18 @@ static const uint8_t backlight_pin = BACKLIGHT_PIN;
# define COM1x1 COM1A1 # define COM1x1 COM1A1
# define OCR1x OCR1A # define OCR1x OCR1A
#else #else
# define NO_BACKLIGHT_CLOCK # define NO_HARDWARE_PWM
#endif #endif
#ifndef BACKLIGHT_ON_STATE #ifndef BACKLIGHT_ON_STATE
#define BACKLIGHT_ON_STATE 0 #define BACKLIGHT_ON_STATE 0
#endif #endif
#ifdef NO_HARDWARE_PWM // pwm through software
__attribute__ ((weak)) __attribute__ ((weak))
void backlight_init_ports(void) void backlight_init_ports(void)
{ {
// Setup backlight pin as output and output to on state. // Setup backlight pin as output and output to on state.
// DDRx |= n // DDRx |= n
_SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF); _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
@ -862,83 +878,15 @@ void backlight_init_ports(void)
// PORTx |= n // PORTx |= n
_SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF); _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
#endif #endif
#ifndef NO_BACKLIGHT_CLOCK
// Use full 16-bit resolution.
ICR1 = 0xFFFF;
// I could write a wall of text here to explain... but TL;DW
// Go read the ATmega32u4 datasheet.
// And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
// Pin PB7 = OCR1C (Timer 1, Channel C)
// Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
// (i.e. start high, go low when counter matches.)
// WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
// Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
#endif
backlight_init();
#ifdef BACKLIGHT_BREATHING
breathing_defaults();
#endif
} }
__attribute__ ((weak)) __attribute__ ((weak))
void backlight_set(uint8_t level) void backlight_set(uint8_t level) {}
{
// Prevent backlight blink on lowest level
// #if BACKLIGHT_ON_STATE == 0
// // PORTx &= ~n
// _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
// #else
// // PORTx |= n
// _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
// #endif
if ( level == 0 ) {
#ifndef NO_BACKLIGHT_CLOCK
// Turn off PWM control on backlight pin, revert to output low.
TCCR1A &= ~(_BV(COM1x1));
OCR1x = 0x0;
#else
// #if BACKLIGHT_ON_STATE == 0
// // PORTx |= n
// _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
// #else
// // PORTx &= ~n
// _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
// #endif
#endif
}
#ifndef NO_BACKLIGHT_CLOCK
else if ( level == BACKLIGHT_LEVELS ) {
// Turn on PWM control of backlight pin
TCCR1A |= _BV(COM1x1);
// Set the brightness
OCR1x = 0xFFFF;
}
else {
// Turn on PWM control of backlight pin
TCCR1A |= _BV(COM1x1);
// Set the brightness
OCR1x = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2));
}
#endif
#ifdef BACKLIGHT_BREATHING
breathing_intensity_default();
#endif
}
uint8_t backlight_tick = 0; uint8_t backlight_tick = 0;
void backlight_task(void) { void backlight_task(void) {
#ifdef NO_BACKLIGHT_CLOCK if ((0xFFFF >> ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
if ((0xFFFF >> ((BACKLIGHT_LEVELS - backlight_config.level) * ((BACKLIGHT_LEVELS + 1) / 2))) & (1 << backlight_tick)) {
#if BACKLIGHT_ON_STATE == 0 #if BACKLIGHT_ON_STATE == 0
// PORTx &= ~n // PORTx &= ~n
_SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
@ -955,232 +903,216 @@ void backlight_task(void) {
_SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF); _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
#endif #endif
} }
backlight_tick = (backlight_tick + 1) % 16; backlight_tick = backlight_tick + 1 % 16;
#endif
} }
#ifdef BACKLIGHT_BREATHING #ifdef BACKLIGHT_BREATHING
#error "Backlight breathing only available with hardware PWM. Please disable."
#endif
#ifdef NO_BACKLIGHT_CLOCK #else // pwm through timer
void breathing_defaults(void) {}
void breathing_intensity_default(void) {} #define TIMER_TOP 0xFFFFU
#else
// 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;
}
}
// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
static inline void set_pwm(uint16_t val) {
OCR1x = val;
}
__attribute__ ((weak))
void backlight_set(uint8_t level) {
if (level > BACKLIGHT_LEVELS)
level = BACKLIGHT_LEVELS;
if (level == 0) {
// Turn off PWM control on backlight pin
TCCR1A &= ~(_BV(COM1x1));
} else {
// Turn on PWM control of backlight pin
TCCR1A |= _BV(COM1x1);
}
// Set the brightness
set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS));
}
void backlight_task(void) {}
#ifdef BACKLIGHT_BREATHING
#define BREATHING_NO_HALT 0 #define BREATHING_NO_HALT 0
#define BREATHING_HALT_OFF 1 #define BREATHING_HALT_OFF 1
#define BREATHING_HALT_ON 2 #define BREATHING_HALT_ON 2
#define BREATHING_STEPS 128
static uint8_t breath_intensity; static uint8_t breathing_period = BREATHING_PERIOD;
static uint8_t breath_speed; static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_index; static uint16_t breathing_counter = 0;
static uint8_t breathing_halt;
void breathing_enable(void) bool is_breathing(void) {
{ return !!(TIMSK1 & _BV(TOIE1));
if (get_backlight_level() == 0) }
{
breathing_index = 0;
}
else
{
// Set breathing_index to be at the midpoint (brightest point)
breathing_index = 0x20 << breath_speed;
}
breathing_halt = BREATHING_NO_HALT; #define breathing_interrupt_enable() do {TIMSK1 |= _BV(TOIE1);} while (0)
#define breathing_interrupt_disable() do {TIMSK1 &= ~_BV(TOIE1);} while (0)
#define breathing_min() do {breathing_counter = 0;} while (0)
#define breathing_max() do {breathing_counter = breathing_period * 244 / 2;} while (0)
// Enable breathing interrupt void breathing_enable(void)
TIMSK1 |= _BV(OCIE1A); {
breathing_counter = 0;
breathing_halt = BREATHING_NO_HALT;
breathing_interrupt_enable();
} }
void breathing_pulse(void) void breathing_pulse(void)
{ {
if (get_backlight_level() == 0) if (get_backlight_level() == 0)
{ breathing_min();
breathing_index = 0;
}
else else
{ breathing_max();
// Set breathing_index to be at the midpoint + 1 (brightest point)
breathing_index = 0x21 << breath_speed;
}
breathing_halt = BREATHING_HALT_ON; breathing_halt = BREATHING_HALT_ON;
breathing_interrupt_enable();
// Enable breathing interrupt
TIMSK1 |= _BV(OCIE1A);
} }
void breathing_disable(void) void breathing_disable(void)
{ {
// Disable breathing interrupt breathing_interrupt_disable();
TIMSK1 &= ~_BV(OCIE1A); // Restore backlight level
backlight_set(get_backlight_level()); backlight_set(get_backlight_level());
} }
void breathing_self_disable(void) void breathing_self_disable(void)
{ {
if (get_backlight_level() == 0) if (get_backlight_level() == 0)
{ breathing_halt = BREATHING_HALT_OFF;
breathing_halt = BREATHING_HALT_OFF; else
} breathing_halt = BREATHING_HALT_ON;
else
{
breathing_halt = BREATHING_HALT_ON;
}
//backlight_set(get_backlight_level());
} }
void breathing_toggle(void) void breathing_toggle(void) {
{ if (is_breathing())
if (!is_breathing()) breathing_disable();
{ else
if (get_backlight_level() == 0) breathing_enable();
{
breathing_index = 0;
}
else
{
// Set breathing_index to be at the midpoint + 1 (brightest point)
breathing_index = 0x21 << breath_speed;
}
breathing_halt = BREATHING_NO_HALT;
}
// Toggle breathing interrupt
TIMSK1 ^= _BV(OCIE1A);
// Restore backlight level
if (!is_breathing())
{
backlight_set(get_backlight_level());
}
} }
bool is_breathing(void) void breathing_period_set(uint8_t value)
{ {
return (TIMSK1 && _BV(OCIE1A)); if (!value)
value = 1;
breathing_period = value;
} }
void breathing_intensity_default(void) void breathing_period_default(void) {
{ breathing_period_set(BREATHING_PERIOD);
//breath_intensity = (uint8_t)((uint16_t)100 * (uint16_t)get_backlight_level() / (uint16_t)BACKLIGHT_LEVELS);
breath_intensity = ((BACKLIGHT_LEVELS - get_backlight_level()) * ((BACKLIGHT_LEVELS + 1) / 2));
} }
void breathing_intensity_set(uint8_t value) void breathing_period_inc(void)
{ {
breath_intensity = value; breathing_period_set(breathing_period+1);
} }
void breathing_speed_default(void) void breathing_period_dec(void)
{ {
breath_speed = 4; breathing_period_set(breathing_period-1);
} }
void breathing_speed_set(uint8_t value) /* To generate breathing curve in python:
{ * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]
bool is_breathing_now = is_breathing(); */
uint8_t old_breath_speed = breath_speed; 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};
if (is_breathing_now)
{
// Disable breathing interrupt
TIMSK1 &= ~_BV(OCIE1A);
}
breath_speed = value;
if (is_breathing_now)
{
// Adjust index to account for new speed
breathing_index = (( (uint8_t)( (breathing_index) >> old_breath_speed ) ) & 0x3F) << breath_speed;
// Enable breathing interrupt
TIMSK1 |= _BV(OCIE1A);
}
}
void breathing_speed_inc(uint8_t value) // Use this before the cie_lightness function.
{ static inline uint16_t scale_backlight(uint16_t v) {
if ((uint16_t)(breath_speed - value) > 10 ) return v / BACKLIGHT_LEVELS * get_backlight_level();
{
breathing_speed_set(0);
}
else
{
breathing_speed_set(breath_speed - value);
}
} }
void breathing_speed_dec(uint8_t value) /* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
* about 244 times per second.
*/
ISR(TIMER1_OVF_vect)
{ {
if ((uint16_t)(breath_speed + value) > 10 ) uint16_t interval = (uint16_t) breathing_period * 244 / BREATHING_STEPS;
{ // resetting after one period to prevent ugly reset at overflow.
breathing_speed_set(10); breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
} uint8_t index = breathing_counter / interval % BREATHING_STEPS;
else
{ if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) ||
breathing_speed_set(breath_speed + value); ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1)))
} {
} breathing_interrupt_disable();
}
void breathing_defaults(void) set_pwm(cie_lightness(scale_backlight((uint16_t) pgm_read_byte(&breathing_table[index]) * 0x0101U)));
{
breathing_intensity_default();
breathing_speed_default();
breathing_halt = BREATHING_NO_HALT;
} }
/* Breathing Sleep LED brighness(PWM On period) table #endif // BACKLIGHT_BREATHING
* (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle
*
* http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63
* (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }
*/
static const uint8_t breathing_table[64] PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10,
15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252,
255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23,
15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
ISR(TIMER1_COMPA_vect) __attribute__ ((weak))
void backlight_init_ports(void)
{ {
// OCR1x = (pgm_read_byte(&breathing_table[ ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F ] )) * breath_intensity; // Setup backlight pin as output and output to on state.
// DDRx |= n
_SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
uint8_t local_index = ( (uint8_t)( (breathing_index++) >> breath_speed ) ) & 0x3F; #if BACKLIGHT_ON_STATE == 0
// PORTx &= ~n
if (((breathing_halt == BREATHING_HALT_ON) && (local_index == 0x20)) || ((breathing_halt == BREATHING_HALT_OFF) && (local_index == 0x3F))) _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
{ #else
// Disable breathing interrupt // PORTx |= n
TIMSK1 &= ~_BV(OCIE1A); _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
} #endif
// I could write a wall of text here to explain... but TL;DW
OCR1x = (uint16_t)(((uint16_t)pgm_read_byte(&breathing_table[local_index]) * 257)) >> breath_intensity; // Go read the ATmega32u4 datasheet.
// And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
// Pin PB7 = OCR1C (Timer 1, Channel C)
// Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
// (i.e. start high, go low when counter matches.)
// WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
// Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
/*
14.8.3:
"In fast PWM mode, the compare units allow generation of PWM waveforms on the OCnx pins. Setting the COMnx1:0 bits to two will produce a non-inverted PWM [..]."
"In fast PWM mode the counter is incremented until the counter value matches either one of the fixed values 0x00FF, 0x01FF, or 0x03FF (WGMn3:0 = 5, 6, or 7), the value in ICRn (WGMn3:0 = 14), or the value in OCRnA (WGMn3:0 = 15)."
*/
TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
// Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
ICR1 = TIMER_TOP;
backlight_init();
#ifdef BACKLIGHT_BREATHING
breathing_enable();
#endif
} }
#endif // NO_BACKLIGHT_CLOCK #endif // NO_HARDWARE_PWM
#endif // breathing
#else // backlight #else // backlight
__attribute__ ((weak)) __attribute__ ((weak))
void backlight_init_ports(void) void backlight_init_ports(void) {}
{
}
__attribute__ ((weak)) __attribute__ ((weak))
void backlight_set(uint8_t level) void backlight_set(uint8_t level) {}
{
}
#endif // backlight #endif // backlight

@ -173,12 +173,11 @@ void breathing_self_disable(void);
void breathing_toggle(void); void breathing_toggle(void);
bool is_breathing(void); bool is_breathing(void);
void breathing_defaults(void);
void breathing_intensity_default(void); void breathing_intensity_default(void);
void breathing_speed_default(void); void breathing_period_default(void);
void breathing_speed_set(uint8_t value); void breathing_period_set(uint8_t value);
void breathing_speed_inc(uint8_t value); void breathing_period_inc(void);
void breathing_speed_dec(uint8_t value); void breathing_period_dec(void);
#endif #endif
#endif #endif

@ -380,26 +380,13 @@ enum quantum_keycodes {
#endif // MIDI_ADVANCED #endif // MIDI_ADVANCED
// Backlight functionality // Backlight functionality
BL_0, BL_ON,
BL_1, BL_OFF,
BL_2,
BL_3,
BL_4,
BL_5,
BL_6,
BL_7,
BL_8,
BL_9,
BL_10,
BL_11,
BL_12,
BL_13,
BL_14,
BL_15,
BL_DEC, BL_DEC,
BL_INC, BL_INC,
BL_TOGG, BL_TOGG,
BL_STEP, BL_STEP,
BL_BRTG,
// RGB functionality // RGB functionality
RGB_TOG, RGB_TOG,
@ -579,9 +566,6 @@ enum quantum_keycodes {
#define AG_SWAP MAGIC_SWAP_ALT_GUI #define AG_SWAP MAGIC_SWAP_ALT_GUI
#define AG_NORM MAGIC_UNSWAP_ALT_GUI #define AG_NORM MAGIC_UNSWAP_ALT_GUI
#define BL_ON BL_9
#define BL_OFF BL_0
// GOTO layer - 16 layers max // GOTO layer - 16 layers max
// when: // when:
// ON_PRESS = 1 // ON_PRESS = 1

@ -512,8 +512,11 @@ void process_action(keyrecord_t *record, action_t action)
case BACKLIGHT_STEP: case BACKLIGHT_STEP:
backlight_step(); backlight_step();
break; break;
case BACKLIGHT_LEVEL: case BACKLIGHT_ON:
backlight_level(action.backlight.level); backlight_level(BACKLIGHT_LEVELS);
break;
case BACKLIGHT_OFF:
backlight_level(0);
break; break;
} }
} }

@ -304,7 +304,8 @@ enum backlight_opt {
BACKLIGHT_DECREASE = 1, BACKLIGHT_DECREASE = 1,
BACKLIGHT_TOGGLE = 2, BACKLIGHT_TOGGLE = 2,
BACKLIGHT_STEP = 3, BACKLIGHT_STEP = 3,
BACKLIGHT_LEVEL = 4, BACKLIGHT_ON = 4,
BACKLIGHT_OFF = 5,
}; };
/* Macro */ /* Macro */
@ -316,7 +317,8 @@ enum backlight_opt {
#define ACTION_BACKLIGHT_DECREASE() ACTION(ACT_BACKLIGHT, BACKLIGHT_DECREASE << 8) #define ACTION_BACKLIGHT_DECREASE() ACTION(ACT_BACKLIGHT, BACKLIGHT_DECREASE << 8)
#define ACTION_BACKLIGHT_TOGGLE() ACTION(ACT_BACKLIGHT, BACKLIGHT_TOGGLE << 8) #define ACTION_BACKLIGHT_TOGGLE() ACTION(ACT_BACKLIGHT, BACKLIGHT_TOGGLE << 8)
#define ACTION_BACKLIGHT_STEP() ACTION(ACT_BACKLIGHT, BACKLIGHT_STEP << 8) #define ACTION_BACKLIGHT_STEP() ACTION(ACT_BACKLIGHT, BACKLIGHT_STEP << 8)
#define ACTION_BACKLIGHT_LEVEL(level) ACTION(ACT_BACKLIGHT, BACKLIGHT_LEVEL << 8 | (level)) #define ACTION_BACKLIGHT_ON() ACTION(ACT_BACKLIGHT, BACKLIGHT_ON << 8)
#define ACTION_BACKLIGHT_OFF() ACTION(ACT_BACKLIGHT, BACKLIGHT_OFF << 8)
/* Command */ /* Command */
#define ACTION_COMMAND(id, opt) ACTION(ACT_COMMAND, (opt)<<8 | (id)) #define ACTION_COMMAND(id, opt) ACTION(ACT_COMMAND, (opt)<<8 | (id))
/* Function */ /* Function */

@ -61,6 +61,8 @@ void backlight_decrease(void)
void backlight_toggle(void) void backlight_toggle(void)
{ {
backlight_config.enable ^= 1; backlight_config.enable ^= 1;
if (backlight_config.raw == 1) // enabled but level = 0
backlight_config.level = 1;
eeconfig_update_backlight(backlight_config.raw); eeconfig_update_backlight(backlight_config.raw);
dprintf("backlight toggle: %u\n", backlight_config.enable); dprintf("backlight toggle: %u\n", backlight_config.enable);
backlight_set(backlight_config.enable ? backlight_config.level : 0); backlight_set(backlight_config.enable ? backlight_config.level : 0);
@ -81,7 +83,9 @@ void backlight_step(void)
void backlight_level(uint8_t level) void backlight_level(uint8_t level)
{ {
backlight_config.level ^= level; if (level > BACKLIGHT_LEVELS)
level = BACKLIGHT_LEVELS;
backlight_config.level = level;
backlight_config.enable = !!backlight_config.level; backlight_config.enable = !!backlight_config.level;
eeconfig_update_backlight(backlight_config.raw); eeconfig_update_backlight(backlight_config.raw);
backlight_set(backlight_config.level); backlight_set(backlight_config.level);

Loading…
Cancel
Save