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

This commit is contained in:
Nick Brassel 2021-11-28 12:56:26 +11:00
commit bf66b91433
5591 changed files with 131128 additions and 54530 deletions

View file

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "action_util.h"
#include "action.h"
#include "wait.h"
#include "keycode_config.h"
#ifdef BACKLIGHT_ENABLE
# include "backlight.h"
@ -44,10 +45,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
int tp_buttons;
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
int retro_tapping_counter = 0;
#endif
#if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
# include "process_auto_shift.h"
#endif
#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; }
#endif
@ -68,7 +73,7 @@ void action_exec(keyevent_t event) {
dprint("EVENT: ");
debug_event(event);
dprintln();
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
retro_tapping_counter++;
#endif
}
@ -87,22 +92,29 @@ void action_exec(keyevent_t event) {
keyrecord_t record = {.event = event};
#ifndef NO_ACTION_ONESHOT
if (!keymap_config.oneshot_disable) {
# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))
if (has_oneshot_layer_timed_out()) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
if (has_oneshot_mods_timed_out()) {
clear_oneshot_mods();
}
if (has_oneshot_layer_timed_out()) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
if (has_oneshot_mods_timed_out()) {
clear_oneshot_mods();
}
# ifdef SWAP_HANDS_ENABLE
if (has_oneshot_swaphands_timed_out()) {
clear_oneshot_swaphands();
}
if (has_oneshot_swaphands_timed_out()) {
clear_oneshot_swaphands();
}
# endif
# endif
}
#endif
#ifndef NO_ACTION_TAPPING
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
if (event.pressed) {
retroshift_poll_time(&event);
}
# endif
if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {
action_tapping_process(record);
}
@ -195,7 +207,7 @@ void process_record(keyrecord_t *record) {
if (!process_record_quantum(record)) {
#ifndef NO_ACTION_ONESHOT
if (is_oneshot_layer_active() && record->event.pressed) {
if (is_oneshot_layer_active() && record->event.pressed && !keymap_config.oneshot_disable) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
#endif
@ -260,7 +272,7 @@ void process_action(keyrecord_t *record, action_t action) {
# ifdef SWAP_HANDS_ENABLE
&& !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)
# endif
) {
&& !keymap_config.oneshot_disable) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
do_release_oneshot = !is_oneshot_layer_active();
}
@ -274,8 +286,8 @@ void process_action(keyrecord_t *record, action_t action) {
if (event.pressed) {
if (mods) {
if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
// e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
// This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
// e.g. LSFT(KC_LEFT_GUI): we don't want the LSFT to be weak as it would make it useless.
// This also makes LSFT(KC_LEFT_GUI) behave exactly the same as LGUI(KC_LEFT_SHIFT).
// Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
add_mods(mods);
} else {
@ -304,41 +316,68 @@ void process_action(keyrecord_t *record, action_t action) {
# ifndef NO_ACTION_ONESHOT
case MODS_ONESHOT:
// Oneshot modifier
if (event.pressed) {
if (tap_count == 0) {
dprint("MODS_TAP: Oneshot: 0\n");
register_mods(mods | get_oneshot_mods());
} else if (tap_count == 1) {
dprint("MODS_TAP: Oneshot: start\n");
set_oneshot_mods(mods | get_oneshot_mods());
# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
dprint("MODS_TAP: Toggling oneshot");
clear_oneshot_mods();
set_oneshot_locked_mods(mods);
register_mods(mods);
# endif
if (keymap_config.oneshot_disable) {
if (event.pressed) {
if (mods) {
if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
// e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.
// This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).
// Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).
add_mods(mods);
} else {
add_weak_mods(mods);
}
send_keyboard_report();
}
register_code(action.key.code);
} else {
register_mods(mods | get_oneshot_mods());
unregister_code(action.key.code);
if (mods) {
if (IS_MOD(action.key.code) || action.key.code == KC_NO) {
del_mods(mods);
} else {
del_weak_mods(mods);
}
send_keyboard_report();
}
}
} else {
if (tap_count == 0) {
clear_oneshot_mods();
unregister_mods(mods);
} else if (tap_count == 1) {
// Retain Oneshot mods
if (event.pressed) {
if (tap_count == 0) {
dprint("MODS_TAP: Oneshot: 0\n");
register_mods(mods | get_oneshot_mods());
} else if (tap_count == 1) {
dprint("MODS_TAP: Oneshot: start\n");
set_oneshot_mods(mods | get_oneshot_mods());
# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
if (mods & get_mods()) {
clear_oneshot_locked_mods();
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
dprint("MODS_TAP: Toggling oneshot");
clear_oneshot_mods();
set_oneshot_locked_mods(mods);
register_mods(mods);
# endif
} else {
register_mods(mods | get_oneshot_mods());
}
} else {
if (tap_count == 0) {
clear_oneshot_mods();
unregister_mods(mods);
} else if (tap_count == 1) {
// Retain Oneshot mods
# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
if (mods & get_mods()) {
clear_oneshot_locked_mods();
clear_oneshot_mods();
unregister_mods(mods);
}
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
// Toggle Oneshot Layer
# endif
} else {
clear_oneshot_mods();
unregister_mods(mods);
}
} else if (tap_count == ONESHOT_TAP_TOGGLE) {
// Toggle Oneshot Layer
# endif
} else {
clear_oneshot_mods();
unregister_mods(mods);
}
}
break;
@ -380,7 +419,7 @@ void process_action(keyrecord_t *record, action_t action) {
} else {
if (tap_count > 0) {
dprint("MODS_TAP: Tap: unregister_code\n");
if (action.layer_tap.code == KC_CAPS) {
if (action.layer_tap.code == KC_CAPS_LOCK) {
wait_ms(TAP_HOLD_CAPS_DELAY);
} else {
wait_ms(TAP_CODE_DELAY);
@ -523,39 +562,47 @@ void process_action(keyrecord_t *record, action_t action) {
# ifndef NO_ACTION_ONESHOT
case OP_ONESHOT:
// Oneshot modifier
# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
do_release_oneshot = false;
if (event.pressed) {
del_mods(get_oneshot_locked_mods());
if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
reset_oneshot_layer();
if (keymap_config.oneshot_disable) {
if (event.pressed) {
layer_on(action.layer_tap.val);
} else {
layer_off(action.layer_tap.val);
break;
} else if (tap_count < ONESHOT_TAP_TOGGLE) {
}
} else {
# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1
do_release_oneshot = false;
if (event.pressed) {
del_mods(get_oneshot_locked_mods());
if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {
reset_oneshot_layer();
layer_off(action.layer_tap.val);
break;
} else if (tap_count < ONESHOT_TAP_TOGGLE) {
layer_on(action.layer_tap.val);
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
}
} else {
add_mods(get_oneshot_locked_mods());
if (tap_count >= ONESHOT_TAP_TOGGLE) {
reset_oneshot_layer();
clear_oneshot_locked_mods();
set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
} else {
clear_oneshot_layer_state(ONESHOT_PRESSED);
}
}
# else
if (event.pressed) {
layer_on(action.layer_tap.val);
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
}
} else {
add_mods(get_oneshot_locked_mods());
if (tap_count >= ONESHOT_TAP_TOGGLE) {
reset_oneshot_layer();
clear_oneshot_locked_mods();
set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);
} else {
clear_oneshot_layer_state(ONESHOT_PRESSED);
if (tap_count > 1) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
}
}
# else
if (event.pressed) {
layer_on(action.layer_tap.val);
set_oneshot_layer(action.layer_tap.val, ONESHOT_START);
} else {
clear_oneshot_layer_state(ONESHOT_PRESSED);
if (tap_count > 1) {
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
}
}
# endif
}
break;
# endif
default:
@ -571,7 +618,7 @@ void process_action(keyrecord_t *record, action_t action) {
} else {
if (tap_count > 0) {
dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n");
if (action.layer_tap.code == KC_CAPS) {
if (action.layer_tap.code == KC_CAPS_LOCK) {
wait_ms(TAP_HOLD_CAPS_DELAY);
} else {
wait_ms(TAP_CODE_DELAY);
@ -692,7 +739,7 @@ void process_action(keyrecord_t *record, action_t action) {
#endif
#ifndef NO_ACTION_TAPPING
# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY)
# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
if (!is_tap_action(action)) {
retro_tapping_counter = 0;
} else {
@ -709,7 +756,11 @@ void process_action(keyrecord_t *record, action_t action) {
get_retro_tapping(get_event_keycode(record->event, false), record) &&
# endif
retro_tapping_counter == 2) {
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
process_auto_shift(action.layer_tap.code, record);
# else
tap_code(action.layer_tap.code);
# endif
}
retro_tapping_counter = 0;
}
@ -748,44 +799,45 @@ void register_code(uint8_t code) {
return;
}
#ifdef LOCKING_SUPPORT_ENABLE
else if (KC_LOCKING_CAPS == code) {
else if (KC_LOCKING_CAPS_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
// Resync: ignore if caps lock already is on
if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return;
# endif
add_key(KC_CAPSLOCK);
add_key(KC_CAPS_LOCK);
send_keyboard_report();
wait_ms(100);
del_key(KC_CAPSLOCK);
del_key(KC_CAPS_LOCK);
send_keyboard_report();
}
else if (KC_LOCKING_NUM == code) {
else if (KC_LOCKING_NUM_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return;
# endif
add_key(KC_NUMLOCK);
add_key(KC_NUM_LOCK);
send_keyboard_report();
wait_ms(100);
del_key(KC_NUMLOCK);
del_key(KC_NUM_LOCK);
send_keyboard_report();
}
else if (KC_LOCKING_SCROLL == code) {
else if (KC_LOCKING_SCROLL_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return;
# endif
add_key(KC_SCROLLLOCK);
add_key(KC_SCROLL_LOCK);
send_keyboard_report();
wait_ms(100);
del_key(KC_SCROLLLOCK);
del_key(KC_SCROLL_LOCK);
send_keyboard_report();
}
#endif
else if IS_KEY (code) {
// TODO: should push command_proc out of this block?
if (command_proc(code)) return;
else if
IS_KEY(code) {
// TODO: should push command_proc out of this block?
if (command_proc(code)) return;
#ifndef NO_ACTION_ONESHOT
/* TODO: remove
@ -802,33 +854,35 @@ void register_code(uint8_t code) {
} else
*/
#endif
{
// Force a new key press if the key is already pressed
// without this, keys with the same keycode, but different
// modifiers will be reported incorrectly, see issue #1708
if (is_key_pressed(keyboard_report, code)) {
del_key(code);
{
// Force a new key press if the key is already pressed
// without this, keys with the same keycode, but different
// modifiers will be reported incorrectly, see issue #1708
if (is_key_pressed(keyboard_report, code)) {
del_key(code);
send_keyboard_report();
}
add_key(code);
send_keyboard_report();
}
add_key(code);
}
else if
IS_MOD(code) {
add_mods(MOD_BIT(code));
send_keyboard_report();
}
} else if IS_MOD (code) {
add_mods(MOD_BIT(code));
send_keyboard_report();
}
#ifdef EXTRAKEY_ENABLE
else if IS_SYSTEM (code) {
host_system_send(KEYCODE2SYSTEM(code));
} else if IS_CONSUMER (code) {
host_consumer_send(KEYCODE2CONSUMER(code));
}
else if
IS_SYSTEM(code) { host_system_send(KEYCODE2SYSTEM(code)); }
else if
IS_CONSUMER(code) { host_consumer_send(KEYCODE2CONSUMER(code)); }
#endif
#ifdef MOUSEKEY_ENABLE
else if IS_MOUSEKEY (code) {
mousekey_on(code);
mousekey_send();
}
else if
IS_MOUSEKEY(code) {
mousekey_on(code);
mousekey_send();
}
#endif
}
@ -841,54 +895,58 @@ void unregister_code(uint8_t code) {
return;
}
#ifdef LOCKING_SUPPORT_ENABLE
else if (KC_LOCKING_CAPS == code) {
else if (KC_LOCKING_CAPS_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
// Resync: ignore if caps lock already is off
if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return;
# endif
add_key(KC_CAPSLOCK);
add_key(KC_CAPS_LOCK);
send_keyboard_report();
del_key(KC_CAPSLOCK);
del_key(KC_CAPS_LOCK);
send_keyboard_report();
}
else if (KC_LOCKING_NUM == code) {
else if (KC_LOCKING_NUM_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return;
# endif
add_key(KC_NUMLOCK);
add_key(KC_NUM_LOCK);
send_keyboard_report();
del_key(KC_NUMLOCK);
del_key(KC_NUM_LOCK);
send_keyboard_report();
}
else if (KC_LOCKING_SCROLL == code) {
else if (KC_LOCKING_SCROLL_LOCK == code) {
# ifdef LOCKING_RESYNC_ENABLE
if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return;
# endif
add_key(KC_SCROLLLOCK);
add_key(KC_SCROLL_LOCK);
send_keyboard_report();
del_key(KC_SCROLLLOCK);
del_key(KC_SCROLL_LOCK);
send_keyboard_report();
}
#endif
else if IS_KEY (code) {
del_key(code);
send_keyboard_report();
} else if IS_MOD (code) {
del_mods(MOD_BIT(code));
send_keyboard_report();
} else if IS_SYSTEM (code) {
host_system_send(0);
} else if IS_CONSUMER (code) {
host_consumer_send(0);
}
else if
IS_KEY(code) {
del_key(code);
send_keyboard_report();
}
else if
IS_MOD(code) {
del_mods(MOD_BIT(code));
send_keyboard_report();
}
else if
IS_SYSTEM(code) { host_system_send(0); }
else if
IS_CONSUMER(code) { host_consumer_send(0); }
#ifdef MOUSEKEY_ENABLE
else if IS_MOUSEKEY (code) {
mousekey_off(code);
mousekey_send();
}
else if
IS_MOUSEKEY(code) {
mousekey_off(code);
mousekey_send();
}
#endif
}
@ -907,9 +965,9 @@ void tap_code_delay(uint8_t code, uint16_t delay) {
/** \brief Tap a keycode with the default delay.
*
* \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
* \param code The basic keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.
*/
void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); }
/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately.
*
@ -1033,7 +1091,7 @@ bool is_tap_action(action_t action) {
case ACT_LAYER_TAP:
case ACT_LAYER_TAP_EXT:
switch (action.layer_tap.code) {
case KC_NO ... KC_RGUI:
case KC_NO ... KC_RIGHT_GUI:
case OP_TAP_TOGGLE:
case OP_ONESHOT:
return true;
@ -1041,7 +1099,7 @@ bool is_tap_action(action_t action) {
return false;
case ACT_SWAP_HANDS:
switch (action.swap.code) {
case KC_NO ... KC_RGUI:
case KC_NO ... KC_RIGHT_GUI:
case OP_SH_TAP_TOGGLE:
return true;
}

View file

@ -88,7 +88,7 @@ extern bool disable_action_cache;
/* Code for handling one-handed key modifiers. */
#ifdef SWAP_HANDS_ENABLE
extern bool swap_hands;
extern bool swap_hands;
extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];
# if (MATRIX_COLS <= 8)
typedef uint8_t swap_state_row_t;

View file

@ -18,18 +18,20 @@
# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed)
# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed)
# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k)))
#ifndef COMBO_ENABLE
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
#else
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
#endif
# ifndef COMBO_ENABLE
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)))
# else
# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)
# endif
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; }
uint16_t g_tapping_term = TAPPING_TERM;
__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return g_tapping_term; }
# ifdef TAPPING_TERM_PER_KEY
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key))
# else
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM)
# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < g_tapping_term)
# endif
# ifdef TAPPING_FORCE_HOLD_PER_KEY
@ -44,6 +46,10 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; }
# endif
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
# include "process_auto_shift.h"
# endif
static keyrecord_t tapping_key = {};
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
static uint8_t waiting_buffer_head = 0;
@ -107,12 +113,29 @@ void action_tapping_process(keyrecord_t record) {
/* return true when key event is processed or consumed. */
bool process_tapping(keyrecord_t *keyp) {
keyevent_t event = keyp->event;
# if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(TAPPING_TERM_PER_KEY) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(TAPPING_FORCE_HOLD_PER_KEY) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
uint16_t tapping_keycode = get_record_keycode(&tapping_key, false);
# endif
// if tapping
if (IS_TAPPING_PRESSED()) {
if (WITHIN_TAPPING_TERM(event)) {
// clang-format off
if (WITHIN_TAPPING_TERM(event)
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|| (
# ifdef RETRO_TAPPING_PER_KEY
get_retro_tapping(tapping_keycode, keyp) &&
# endif
(RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
)
# endif
) {
// clang-format on
if (tapping_key.tap.count == 0) {
if (IS_TAPPING_RECORD(keyp) && !event.pressed) {
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
retroshift_swap_times();
# endif
// first tap!
debug("Tapping: First tap(0->1).\n");
tapping_key.tap.count = 1;
@ -128,22 +151,70 @@ bool process_tapping(keyrecord_t *keyp) {
* This can register the key before settlement of tapping,
* useful for long TAPPING_TERM but may prevent fast typing.
*/
# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY)
else if (((
// clang-format off
# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))
else if (
(
(
(
# ifdef TAPPING_TERM_PER_KEY
get_tapping_term(get_record_keycode(&tapping_key, false), keyp)
get_tapping_term(tapping_keycode, keyp)
# else
TAPPING_TERM
g_tapping_term
# endif
>= 500)
>= 500
)
# ifdef PERMISSIVE_HOLD_PER_KEY
|| get_permissive_hold(get_record_keycode(&tapping_key, false), keyp)
|| get_permissive_hold(tapping_keycode, keyp)
# elif defined(PERMISSIVE_HOLD)
|| true
|| true
# endif
) &&
IS_RELEASED(event) && waiting_buffer_typed(event)) {
) && IS_RELEASED(event) && waiting_buffer_typed(event)
)
// Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT
// unnecessarily and fixes them for Layer Taps.
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|| (
# ifdef RETRO_TAPPING_PER_KEY
get_retro_tapping(tapping_keycode, keyp) &&
# endif
(
// Rolled over the two keys.
(
(
false
# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
|| (
IS_LT(tapping_keycode)
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
&& get_hold_on_other_key_press(tapping_keycode, keyp)
# endif
)
# endif
# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
|| (
IS_MT(tapping_keycode)
# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
&& !get_ignore_mod_tap_interrupt(tapping_keycode, keyp)
# endif
)
# endif
) && tapping_key.tap.interrupted == true
)
// Makes Retro Shift ignore [IGNORE_MOD_TAP_INTERRUPT's
// effects on nested taps for MTs and the default
// behavior of LTs] below TAPPING_TERM or RETRO_SHIFT.
|| (
IS_RETRO(tapping_keycode)
&& (event.key.col != tapping_key.event.key.col || event.key.row != tapping_key.event.key.row)
&& IS_RELEASED(event) && waiting_buffer_typed(event)
)
)
)
# endif
) {
// clang-format on
debug("Tapping: End. No tap. Interfered by typing key\n");
process_record(&tapping_key);
tapping_key = (keyrecord_t){};
@ -181,7 +252,7 @@ bool process_tapping(keyrecord_t *keyp) {
tapping_key.tap.interrupted = true;
# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp))
if (get_hold_on_other_key_press(tapping_keycode, keyp))
# endif
{
debug("Tapping: End. No tap. Interfered by pressed key\n");
@ -212,11 +283,15 @@ bool process_tapping(keyrecord_t *keyp) {
if (tapping_key.tap.count > 1) {
debug("Tapping: Start new tap with releasing last tap(>1).\n");
// unregister key
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
#ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
#endif
});
process_record(&(keyrecord_t){
.tap = tapping_key.tap,
.event.key = tapping_key.event.key,
.event.time = event.time,
.event.pressed = false,
# ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
# endif
});
} else {
debug("Tapping: Start while last tap(1).\n");
}
@ -254,11 +329,15 @@ bool process_tapping(keyrecord_t *keyp) {
if (tapping_key.tap.count > 1) {
debug("Tapping: Start new tap with releasing last timeout tap(>1).\n");
// unregister key
process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false,
#ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
#endif
});
process_record(&(keyrecord_t){
.tap = tapping_key.tap,
.event.key = tapping_key.event.key,
.event.time = event.time,
.event.pressed = false,
# ifdef COMBO_ENABLE
.keycode = tapping_key.keycode,
# endif
});
} else {
debug("Tapping: Start while last timeout tap(1).\n");
}
@ -276,14 +355,25 @@ bool process_tapping(keyrecord_t *keyp) {
}
}
} else if (IS_TAPPING_RELEASED()) {
if (WITHIN_TAPPING_TERM(event)) {
// clang-format off
if (WITHIN_TAPPING_TERM(event)
# if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)
|| (
# ifdef RETRO_TAPPING_PER_KEY
get_retro_tapping(tapping_keycode, keyp) &&
# endif
(RETRO_SHIFT + 0) != 0 && TIMER_DIFF_16(event.time, tapping_key.event.time) < (RETRO_SHIFT + 0)
)
# endif
) {
// clang-format on
if (event.pressed) {
if (IS_TAPPING_RECORD(keyp)) {
//# ifndef TAPPING_FORCE_HOLD
# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY)
if (
# ifdef TAPPING_FORCE_HOLD_PER_KEY
!get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) &&
!get_tapping_force_hold(tapping_keycode, keyp) &&
# endif
!tapping_key.tap.interrupted && tapping_key.tap.count > 0) {
// sequential tap.

View file

@ -33,10 +33,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);
uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
void action_tapping_process(keyrecord_t record);
#endif
uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);
bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record);
bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record);
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
extern uint16_t g_tapping_term;
#endif

View file

@ -32,16 +32,6 @@ static uint8_t weak_override_mods = 0;
static uint8_t suppressed_mods = 0;
#endif
#ifdef USB_6KRO_ENABLE
# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS)
# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS)
# define RO_INC(a) RO_ADD(a, 1)
# define RO_DEC(a) RO_SUB(a, 1)
static int8_t cb_head = 0;
static int8_t cb_tail = 0;
static int8_t cb_count = 0;
#endif
// TODO: pointer variable is not needed
// report_keyboard_t keyboard_report = {};
report_keyboard_t *keyboard_report = &(report_keyboard_t){};
@ -180,7 +170,7 @@ void reset_oneshot_layer(void) {
void clear_oneshot_layer_state(oneshot_fullfillment_t state) {
uint8_t start_state = oneshot_layer_data;
oneshot_layer_data &= ~state;
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) {
if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && !keymap_config.oneshot_disable) {
layer_off(get_oneshot_layer());
reset_oneshot_layer();
}
@ -199,6 +189,7 @@ void oneshot_set(bool active) {
if (keymap_config.oneshot_disable != active) {
keymap_config.oneshot_disable = active;
eeconfig_update_keymap(keymap_config.raw);
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
dprintf("Oneshot: active: %d\n", active);
}
}

View file

@ -1,182 +0,0 @@
/* Copyright 2016 Jack Humbert
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "api.h"
#include "quantum.h"
void dword_to_bytes(uint32_t dword, uint8_t* bytes) {
bytes[0] = (dword >> 24) & 0xFF;
bytes[1] = (dword >> 16) & 0xFF;
bytes[2] = (dword >> 8) & 0xFF;
bytes[3] = (dword >> 0) & 0xFF;
}
uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index) { return ((uint32_t)bytes[index + 0] << 24) | ((uint32_t)bytes[index + 1] << 16) | ((uint32_t)bytes[index + 2] << 8) | (uint32_t)bytes[index + 3]; }
__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data) { return process_api_keyboard(length, data); }
__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data) { return process_api_user(length, data); }
__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data) { return true; }
void process_api(uint16_t length, uint8_t* data) {
// SEND_STRING("\nRX: ");
// for (uint8_t i = 0; i < length; i++) {
// send_byte(data[i]);
// SEND_STRING(" ");
// }
if (!process_api_quantum(length, data)) return;
switch (data[0]) {
case MT_SET_DATA:
switch (data[1]) {
case DT_DEFAULT_LAYER: {
eeconfig_update_default_layer(data[2]);
default_layer_set((uint32_t)(data[2]));
break;
}
case DT_KEYMAP_OPTIONS: {
eeconfig_update_keymap(data[2]);
break;
}
case DT_RGBLIGHT: {
#ifdef RGBLIGHT_ENABLE
uint32_t rgblight = bytes_to_dword(data, 2);
eeconfig_update_rgblight(rgblight);
#endif
break;
}
}
case MT_GET_DATA:
switch (data[1]) {
case DT_HANDSHAKE: {
MT_GET_DATA_ACK(DT_HANDSHAKE, NULL, 0);
break;
}
case DT_DEBUG: {
uint8_t debug_bytes[1] = {eeprom_read_byte(EECONFIG_DEBUG)};
MT_GET_DATA_ACK(DT_DEBUG, debug_bytes, 1);
break;
}
case DT_DEFAULT_LAYER: {
uint8_t default_bytes[1] = {eeprom_read_byte(EECONFIG_DEFAULT_LAYER)};
MT_GET_DATA_ACK(DT_DEFAULT_LAYER, default_bytes, 1);
break;
}
case DT_CURRENT_LAYER: {
uint8_t layer_state_bytes[4];
dword_to_bytes(layer_state, layer_state_bytes);
MT_GET_DATA_ACK(DT_CURRENT_LAYER, layer_state_bytes, 4);
break;
}
case DT_AUDIO: {
#ifdef AUDIO_ENABLE
uint8_t audio_bytes[1] = {eeprom_read_byte(EECONFIG_AUDIO)};
MT_GET_DATA_ACK(DT_AUDIO, audio_bytes, 1);
#else
MT_GET_DATA_ACK(DT_AUDIO, NULL, 0);
#endif
break;
}
case DT_BACKLIGHT: {
#ifdef BACKLIGHT_ENABLE
uint8_t backlight_bytes[1] = {eeprom_read_byte(EECONFIG_BACKLIGHT)};
MT_GET_DATA_ACK(DT_BACKLIGHT, backlight_bytes, 1);
#else
MT_GET_DATA_ACK(DT_BACKLIGHT, NULL, 0);
#endif
break;
}
case DT_RGBLIGHT: {
#ifdef RGBLIGHT_ENABLE
uint8_t rgblight_bytes[4];
dword_to_bytes(eeconfig_read_rgblight(), rgblight_bytes);
MT_GET_DATA_ACK(DT_RGBLIGHT, rgblight_bytes, 4);
#else
MT_GET_DATA_ACK(DT_RGBLIGHT, NULL, 0);
#endif
break;
}
case DT_KEYMAP_OPTIONS: {
uint8_t keymap_bytes[1] = {eeconfig_read_keymap()};
MT_GET_DATA_ACK(DT_KEYMAP_OPTIONS, keymap_bytes, 1);
break;
}
case DT_KEYMAP_SIZE: {
uint8_t keymap_size[2] = {MATRIX_ROWS, MATRIX_COLS};
MT_GET_DATA_ACK(DT_KEYMAP_SIZE, keymap_size, 2);
break;
}
// This may be too much
// case DT_KEYMAP: {
// uint8_t keymap_data[MATRIX_ROWS * MATRIX_COLS * 4 + 3];
// keymap_data[0] = data[2];
// keymap_data[1] = MATRIX_ROWS;
// keymap_data[2] = MATRIX_COLS;
// for (int i = 0; i < MATRIX_ROWS; i++) {
// for (int j = 0; j < MATRIX_COLS; j++) {
// keymap_data[3 + (i*MATRIX_COLS*2) + (j*2)] = pgm_read_word(&keymaps[data[2]][i][j]) >> 8;
// keymap_data[3 + (i*MATRIX_COLS*2) + (j*2) + 1] = pgm_read_word(&keymaps[data[2]][i][j]) & 0xFF;
// }
// }
// MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, MATRIX_ROWS * MATRIX_COLS * 4 + 3);
// // uint8_t keymap_data[5];
// // keymap_data[0] = data[2];
// // keymap_data[1] = data[3];
// // keymap_data[2] = data[4];
// // keymap_data[3] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) >> 8;
// // keymap_data[4] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) & 0xFF;
// // MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, 5);
// break;
// }
default:
break;
}
break;
case MT_SET_DATA_ACK:
case MT_GET_DATA_ACK:
break;
case MT_SEND_DATA:
break;
case MT_SEND_DATA_ACK:
break;
case MT_EXE_ACTION:
break;
case MT_EXE_ACTION_ACK:
break;
case MT_TYPE_ERROR:
break;
default:; // command not recognised
SEND_BYTES(MT_TYPE_ERROR, DT_NONE, data, length);
break;
// #ifdef RGBLIGHT_ENABLE
// case 0x27: ; // RGB LED functions
// switch (*data++) {
// case 0x00: ; // Update HSV
// rgblight_sethsv((data[0] << 8 | data[1]) % 360, data[2], data[3]);
// break;
// case 0x01: ; // Update RGB
// break;
// case 0x02: ; // Update mode
// rgblight_mode(data[0]);
// break;
// }
// break;
// #endif
}
}

View file

@ -1,55 +0,0 @@
/* Copyright 2016 Jack Humbert
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef __AVR__
# include "lufa.h"
#endif
enum MESSAGE_TYPE {
MT_GET_DATA = 0x10, // Get data from keyboard
MT_GET_DATA_ACK = 0x11, // returned data to process (ACK)
MT_SET_DATA = 0x20, // Set data on keyboard
MT_SET_DATA_ACK = 0x21, // returned data to confirm (ACK)
MT_SEND_DATA = 0x30, // Sending data/action from keyboard
MT_SEND_DATA_ACK = 0x31, // returned data/action confirmation (ACK)
MT_EXE_ACTION = 0x40, // executing actions on keyboard
MT_EXE_ACTION_ACK = 0x41, // return confirmation/value (ACK)
MT_TYPE_ERROR = 0x80 // type not recognised (ACK)
};
enum DATA_TYPE { DT_NONE = 0x00, DT_HANDSHAKE, DT_DEFAULT_LAYER, DT_CURRENT_LAYER, DT_KEYMAP_OPTIONS, DT_BACKLIGHT, DT_RGBLIGHT, DT_UNICODE, DT_DEBUG, DT_AUDIO, DT_QUANTUM_ACTION, DT_KEYBOARD_ACTION, DT_USER_ACTION, DT_KEYMAP_SIZE, DT_KEYMAP };
void dword_to_bytes(uint32_t dword, uint8_t* bytes);
uint32_t bytes_to_dword(uint8_t* bytes, uint8_t index);
#define MT_GET_DATA(data_type, data, length) SEND_BYTES(MT_GET_DATA, data_type, data, length)
#define MT_GET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_GET_DATA_ACK, data_type, data, length)
#define MT_SET_DATA(data_type, data, length) SEND_BYTES(MT_SET_DATA, data_type, data, length)
#define MT_SET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SET_DATA_ACK, data_type, data, length)
#define MT_SEND_DATA(data_type, data, length) SEND_BYTES(MT_SEND_DATA, data_type, data, length)
#define MT_SEND_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SEND_DATA_ACK, data_type, data, length)
#define MT_EXE_ACTION(data_type, data, length) SEND_BYTES(MT_EXE_ACTION, data_type, data, length)
#define MT_EXE_ACTION_ACK(data_type, data, length) SEND_BYTES(MT_EXE_ACTION_ACK, data_type, data, length)
void process_api(uint16_t length, uint8_t* data);
__attribute__((weak)) bool process_api_quantum(uint8_t length, uint8_t* data);
__attribute__((weak)) bool process_api_keyboard(uint8_t length, uint8_t* data);
__attribute__((weak)) bool process_api_user(uint8_t length, uint8_t* data);

View file

@ -1,72 +0,0 @@
/* Copyright 2016 Jack Humbert, Fred Sundvik
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "api_sysex.h"
#include "sysex_tools.h"
#include "print.h"
#include "qmk_midi.h"
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length) {
// SEND_STRING("\nTX: ");
// for (uint8_t i = 0; i < length; i++) {
// send_byte(bytes[i]);
// SEND_STRING(" ");
// }
if (length > API_SYSEX_MAX_SIZE) {
xprintf("Sysex msg too big %d %d %d", message_type, data_type, length);
return;
}
// The buffer size required is calculated as the following
// API_SYSEX_MAX_SIZE is the maximum length
// In addition to that we have a two byte message header consisting of the message_type and data_type
// This has to be encoded with an additional overhead of one byte for every starting 7 bytes
// We just add one extra byte in case it's not divisible by 7
// Then we have an unencoded header consisting of 4 bytes
// Plus a one byte terminator
const unsigned message_header = 2;
const unsigned unencoded_message = API_SYSEX_MAX_SIZE + message_header;
const unsigned encoding_overhead = unencoded_message / 7 + 1;
const unsigned encoded_size = unencoded_message + encoding_overhead;
const unsigned unencoded_header = 4;
const unsigned terminator = 1;
const unsigned buffer_size = encoded_size + unencoded_header + terminator;
uint8_t buffer[encoded_size + unencoded_header + terminator];
// The unencoded header
buffer[0] = 0xF0;
buffer[1] = 0x00;
buffer[2] = 0x00;
buffer[3] = 0x00;
// We copy the message to the end of the array, this way we can do an inplace encoding, using the same
// buffer for both input and output
const unsigned message_size = length + message_header;
uint8_t* unencoded_start = buffer + buffer_size - message_size;
uint8_t* ptr = unencoded_start;
*(ptr++) = message_type;
*(ptr++) = data_type;
memcpy(ptr, bytes, length);
unsigned encoded_length = sysex_encode(buffer + unencoded_header, unencoded_start, message_size);
unsigned final_size = unencoded_header + encoded_length + terminator;
buffer[final_size - 1] = 0xF7;
midi_send_array(&midi_device, final_size, buffer);
// SEND_STRING("\nTD: ");
// for (uint8_t i = 0; i < encoded_length + 5; i++) {
// send_byte(buffer[i]);
// SEND_STRING(" ");
// }
}

View file

@ -26,17 +26,12 @@
#if defined(__AVR__)
# include <avr/io.h>
# if defined(AUDIO_DRIVER_PWM)
# include "driver_avr_pwm.h"
# endif
#endif
#if defined(PROTOCOL_CHIBIOS)
# if defined(AUDIO_DRIVER_PWM)
# include "driver_chibios_pwm.h"
# elif defined(AUDIO_DRIVER_DAC)
# include "driver_chibios_dac.h"
# endif
#if defined(AUDIO_DRIVER_PWM)
# include "audio_pwm.h"
#elif defined(AUDIO_DRIVER_DAC)
# include "audio_dac.h"
#endif
typedef union {

View file

@ -1,17 +0,0 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

View file

@ -1,332 +0,0 @@
/* Copyright 2016 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(__AVR__)
# include <avr/pgmspace.h>
# include <avr/interrupt.h>
# include <avr/io.h>
#endif
#include "audio.h"
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
#define CPU_PRESCALER 8
/*
Audio Driver: PWM
drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3
and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
alternatively, the PWM pins on PORTB can be used as only/primary speaker
*/
#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)
# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
#endif
#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
# define AUDIO1_PIN_SET
# define AUDIO1_TIMSKx TIMSK3
# define AUDIO1_TCCRxA TCCR3A
# define AUDIO1_TCCRxB TCCR3B
# define AUDIO1_ICRx ICR3
# define AUDIO1_WGMx0 WGM30
# define AUDIO1_WGMx1 WGM31
# define AUDIO1_WGMx2 WGM32
# define AUDIO1_WGMx3 WGM33
# define AUDIO1_CSx0 CS30
# define AUDIO1_CSx1 CS31
# define AUDIO1_CSx2 CS32
# if (AUDIO_PIN == C6)
# define AUDIO1_COMxy0 COM3A0
# define AUDIO1_COMxy1 COM3A1
# define AUDIO1_OCIExy OCIE3A
# define AUDIO1_OCRxy OCR3A
# define AUDIO1_PIN C6
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
# elif (AUDIO_PIN == C5)
# define AUDIO1_COMxy0 COM3B0
# define AUDIO1_COMxy1 COM3B1
# define AUDIO1_OCIExy OCIE3B
# define AUDIO1_OCRxy OCR3B
# define AUDIO1_PIN C5
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
# elif (AUDIO_PIN == C4)
# define AUDIO1_COMxy0 COM3C0
# define AUDIO1_COMxy1 COM3C1
# define AUDIO1_OCIExy OCIE3C
# define AUDIO1_OCRxy OCR3C
# define AUDIO1_PIN C4
# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
# endif
#endif
#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
#endif
#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))
# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
#endif
#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
#endif
#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)
# define AUDIO2_PIN_SET
# define AUDIO2_TIMSKx TIMSK1
# define AUDIO2_TCCRxA TCCR1A
# define AUDIO2_TCCRxB TCCR1B
# define AUDIO2_ICRx ICR1
# define AUDIO2_WGMx0 WGM10
# define AUDIO2_WGMx1 WGM11
# define AUDIO2_WGMx2 WGM12
# define AUDIO2_WGMx3 WGM13
# define AUDIO2_CSx0 CS10
# define AUDIO2_CSx1 CS11
# define AUDIO2_CSx2 CS12
# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
# define AUDIO2_COMxy0 COM1A0
# define AUDIO2_COMxy1 COM1A1
# define AUDIO2_OCIExy OCIE1A
# define AUDIO2_OCRxy OCR1A
# define AUDIO2_PIN B5
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
# define AUDIO2_COMxy0 COM1B0
# define AUDIO2_COMxy1 COM1B1
# define AUDIO2_OCIExy OCIE1B
# define AUDIO2_OCRxy OCR1B
# define AUDIO2_PIN B6
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
# define AUDIO2_COMxy0 COM1C0
# define AUDIO2_COMxy1 COM1C1
# define AUDIO2_OCIExy OCIE1C
# define AUDIO2_OCRxy OCR1C
# define AUDIO2_PIN B7
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
# elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)
# pragma message "Audio support for ATmega32A is experimental and can cause crashes."
# undef AUDIO2_TIMSKx
# define AUDIO2_TIMSKx TIMSK
# define AUDIO2_COMxy0 COM1A0
# define AUDIO2_COMxy1 COM1A1
# define AUDIO2_OCIExy OCIE1A
# define AUDIO2_OCRxy OCR1A
# define AUDIO2_PIN D5
# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
# endif
#endif
// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
# pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)"
// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
#endif
// -----------------------------------------------------------------------------
#ifdef AUDIO1_PIN_SET
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
{
// disable the output, but keep the pwm-ISR going (with the previous
// frequency) so the audio-state keeps getting updated
// Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
return;
} else {
AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
}
channel_1_frequency = freq;
// set pwm period
AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
// and duty cycle
AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
}
void channel_1_start(void) {
// enable timer-counter ISR
AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
// enable timer-counter output
AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
}
void channel_1_stop(void) {
// disable timer-counter ISR
AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
// disable timer-counter output
AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
}
#endif
#ifdef AUDIO2_PIN_SET
static float channel_2_frequency = 0.0f;
void channel_2_set_frequency(float freq) {
if (freq == 0.0f) {
AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
return;
} else {
AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
}
channel_2_frequency = freq;
AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
}
float channel_2_get_frequency(void) { return channel_2_frequency; }
void channel_2_start(void) {
AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
}
void channel_2_stop(void) {
AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
}
#endif
void audio_driver_initialize() {
#ifdef AUDIO1_PIN_SET
channel_1_stop();
setPinOutput(AUDIO1_PIN);
#endif
#ifdef AUDIO2_PIN_SET
channel_2_stop();
setPinOutput(AUDIO2_PIN);
#endif
// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
// OC3A -- PC6
// OC3B -- PC5
// OC3C -- PC4
// OC1A -- PB5
// OC1B -- PB6
// OC1C -- PB7
// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
// OCR3A - PC6
// OCR3B - PC5
// OCR3C - PC4
// OCR1A - PB5
// OCR1B - PB6
// OCR1C - PB7
// Clock Select (CS3n) = 0b010 = Clock / 8
#ifdef AUDIO1_PIN_SET
// initialize timer-counter
AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
#endif
#ifdef AUDIO2_PIN_SET
AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
#endif
}
void audio_driver_stop() {
#ifdef AUDIO1_PIN_SET
channel_1_stop();
#endif
#ifdef AUDIO2_PIN_SET
channel_2_stop();
#endif
}
void audio_driver_start(void) {
#ifdef AUDIO1_PIN_SET
channel_1_start();
if (playing_note) {
channel_1_set_frequency(audio_get_processed_frequency(0));
}
#endif
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
channel_2_start();
if (playing_note) {
channel_2_set_frequency(audio_get_processed_frequency(0));
}
#endif
}
static volatile uint32_t isr_counter = 0;
#ifdef AUDIO1_PIN_SET
ISR(AUDIO1_TIMERx_COMPy_vect) {
isr_counter++;
if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
isr_counter = 0;
bool state_changed = audio_update_state();
if (!playing_note && !playing_melody) {
channel_1_stop();
# ifdef AUDIO2_PIN_SET
channel_2_stop();
# endif
return;
}
if (state_changed) {
channel_1_set_frequency(audio_get_processed_frequency(0));
# ifdef AUDIO2_PIN_SET
if (audio_get_number_of_active_tones() > 1) {
channel_2_set_frequency(audio_get_processed_frequency(1));
} else {
channel_2_stop();
}
# endif
}
}
#endif
#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
ISR(AUDIO2_TIMERx_COMPy_vect) {
isr_counter++;
if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
isr_counter = 0;
bool state_changed = audio_update_state();
if (!playing_note && !playing_melody) {
channel_2_stop();
return;
}
if (state_changed) {
channel_2_set_frequency(audio_get_processed_frequency(0));
}
}
#endif

View file

@ -1,126 +0,0 @@
/* Copyright 2019 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef A4
# define A4 PAL_LINE(GPIOA, 4)
#endif
#ifndef A5
# define A5 PAL_LINE(GPIOA, 5)
#endif
/**
* Size of the dac_buffer arrays. All must be the same size.
*/
#define AUDIO_DAC_BUFFER_SIZE 256U
/**
* Highest value allowed sample value.
* since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;
* lower values adjust the peak-voltage aka volume down.
* adjusting this value has only an effect on a sample-buffer whose values are
* are NOT pregenerated - see square-wave
*/
#ifndef AUDIO_DAC_SAMPLE_MAX
# define AUDIO_DAC_SAMPLE_MAX 4095U
#endif
#if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH)
# define AUDIO_DAC_QUALITY_SANE_MINIMUM
#endif
/**
* These presets allow you to quickly switch between quality settings for
* the DAC. The sample rate and maximum number of simultaneous tones roughly
* has an inverse relationship - slightly higher sample rates may be possible.
*
* NOTE: a high sample-rate results in a higher cpu-load, which might lead to
* (audible) discontinuities and/or starve other processes of cpu-time
* (like RGB-led back-lighting, ...)
*/
#ifdef AUDIO_DAC_QUALITY_VERY_LOW
# define AUDIO_DAC_SAMPLE_RATE 11025U
# define AUDIO_MAX_SIMULTANEOUS_TONES 8
#endif
#ifdef AUDIO_DAC_QUALITY_LOW
# define AUDIO_DAC_SAMPLE_RATE 22050U
# define AUDIO_MAX_SIMULTANEOUS_TONES 4
#endif
#ifdef AUDIO_DAC_QUALITY_HIGH
# define AUDIO_DAC_SAMPLE_RATE 44100U
# define AUDIO_MAX_SIMULTANEOUS_TONES 2
#endif
#ifdef AUDIO_DAC_QUALITY_VERY_HIGH
# define AUDIO_DAC_SAMPLE_RATE 88200U
# define AUDIO_MAX_SIMULTANEOUS_TONES 1
#endif
#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM
/* a sane-minimum config: with a trade-off between cpu-load and tone-range
*
* the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now
* aim for an even even multiple of the buffer-size, we end up with:
* ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)
* 7902/256 = 30.867 * 2 * 256 ~= 16384
* which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)
*/
# define AUDIO_DAC_SAMPLE_RATE 16384U
# define AUDIO_MAX_SIMULTANEOUS_TONES 8
#endif
/**
* Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any
* lower will sacrifice perceptible audio quality. Any higher will limit the
* number of simultaneous tones. In most situations, a tenth (1/10) of the
* sample rate is where notes become unbearable.
*/
#ifndef AUDIO_DAC_SAMPLE_RATE
# define AUDIO_DAC_SAMPLE_RATE 44100U
#endif
/**
* The number of tones that can be played simultaneously. If too high a value
* is used here, the keyboard will freeze and glitch-out when that many tones
* are being played.
*/
#ifndef AUDIO_MAX_SIMULTANEOUS_TONES
# define AUDIO_MAX_SIMULTANEOUS_TONES 2
#endif
/**
* The default value of the DAC when not playing anything. Certain hardware
* setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.
* Since multiple added sine waves tend to oscillate around the midpoint,
* and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a
* reasonable default value.
*/
#ifndef AUDIO_DAC_OFF_VALUE
# define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2
#endif
#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX
# error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX"
#endif
/**
*user overridable sample generation/processing
*/
uint16_t dac_value_generate(void);

View file

@ -1,335 +0,0 @@
/* Copyright 2016-2019 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "audio.h"
#include <ch.h>
#include <hal.h>
/*
Audio Driver: DAC
which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA
it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
*/
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
#endif
#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
# pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though."
#endif
#if !defined(AUDIO_PIN_ALT)
// no ALT pin defined is valid, but the c-ifs below need some value set
# define AUDIO_PIN_ALT PAL_NOLINE
#endif
#if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
# define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
#endif
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
*/
static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = {
// 256 values, max 4095
0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, 0x27, 0x32, 0x3d, 0x4a, 0x58, 0x67, 0x78, 0x89, 0x9c, 0xb0, 0xc5, 0xdb, 0xf2, 0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe,
0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2, 0xdb, 0xc5, 0xb0, 0x9c, 0x89, 0x78, 0x67, 0x58, 0x4a, 0x3d, 0x32, 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = {
// 256 values, max 4095
0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf,
0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0, 0xc0, 0xa0, 0x80, 0x60, 0x40, 0x20};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = {
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, // first and
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half
};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
/*
// four steps: 0, 1/3, 2/3 and 1
static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = {
[0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0,
[AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3,
[AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3,
[3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX,
}
*/
#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,
0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf, 0x7f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE};
/* keep track of the sample position for for each frequency */
static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0};
static uint8_t active_tones_snapshot_length = 0;
typedef enum {
OUTPUT_SHOULD_START,
OUTPUT_RUN_NORMALLY,
// path 1: wait for zero, then change/update active tones
OUTPUT_TONES_CHANGED,
OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
// path 2: hardware should stop, wait for zero then turn output off = stop the timer
OUTPUT_SHOULD_STOP,
OUTPUT_REACHED_ZERO_BEFORE_OFF,
OUTPUT_OFF,
OUTPUT_OFF_1,
OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level
number_of_output_states
} output_states_t;
output_states_t state = OUTPUT_OFF_2;
/**
* Generation of the waveform being passed to the callback. Declared weak so users
* can override it with their own wave-forms/noises.
*/
__attribute__((weak)) uint16_t dac_value_generate(void) {
// DAC is running/asking for values but snapshot length is zero -> must be playing a pause
if (active_tones_snapshot_length == 0) {
return AUDIO_DAC_OFF_VALUE;
}
/* doing additive wave synthesis over all currently playing tones = adding up
* sine-wave-samples for each frequency, scaled by the number of active tones
*/
uint16_t value = 0;
float frequency = 0.0f;
for (uint8_t i = 0; i < active_tones_snapshot_length; i++) {
/* Note: a user implementation does not have to rely on the active_tones_snapshot, but
* could directly query the active frequencies through audio_get_processed_frequency */
frequency = active_tones_snapshot[i];
dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3;
/*Note: the 2/3 are necessary to get the correct frequencies on the
* DAC output (as measured with an oscilloscope), since the gpt
* timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
* is called twice per conversion.*/
dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE);
// Wavetable generation/lookup
uint16_t dac_i = (uint16_t)dac_if[i];
#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
#endif
/*
// SINE
value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
// TRIANGLE
value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
// SQUARE
value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
//NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
*/
// STAIRS (mostly usefully as test-pattern)
// value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
}
return value;
}
/**
* DAC streaming callback. Does all of the main computing for playing songs.
*
* Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
*/
static void dac_end(DACDriver *dacp) {
dacsample_t *sample_p = (dacp)->samples;
// work on the other half of the buffer
if (dacIsBufferComplete(dacp)) {
sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'
}
for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
if (OUTPUT_OFF <= state) {
sample_p[s] = AUDIO_DAC_OFF_VALUE;
continue;
} else {
sample_p[s] = dac_value_generate();
}
/* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
* ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
* * *
* * *
* ---------------------------------------------------------
* * * } AUDIO_DAC_SAMPLE_MAX/100
* --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
* * * } AUDIO_DAC_SAMPLE_MAX/100
* ---------------------------------------------------------
* *
* * *
* * *
* =====*=*================================================= 0x0
*/
if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below
(sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above
) {
if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
state = OUTPUT_RUN_NORMALLY;
} else if (OUTPUT_TONES_CHANGED == state) {
state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
} else if (OUTPUT_SHOULD_STOP == state) {
state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
}
}
// still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
if (OUTPUT_SHOULD_START == state) {
sample_p[s] = AUDIO_DAC_OFF_VALUE;
}
if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
active_tones_snapshot_length = 0;
// update the snapshot - once, and only on occasion that something changed;
// -> saves cpu cycles (?)
for (uint8_t i = 0; i < active_tones; i++) {
float freq = audio_get_processed_frequency(i);
if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
active_tones_snapshot[active_tones_snapshot_length++] = freq;
}
}
if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
state = OUTPUT_OFF;
}
if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
state = OUTPUT_RUN_NORMALLY;
}
}
}
// update audio internal state (note position, current_note, ...)
if (audio_update_state()) {
if (OUTPUT_SHOULD_STOP != state) {
state = OUTPUT_TONES_CHANGED;
}
}
if (OUTPUT_OFF <= state) {
if (OUTPUT_OFF_2 == state) {
// stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
gptStopTimer(&GPTD6);
} else {
state++;
}
}
}
static void dac_error(DACDriver *dacp, dacerror_t err) {
(void)dacp;
(void)err;
chSysHalt("DAC failure. halp");
}
static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
/**
* @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
* on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
* to be a third of what we expect.
*
* Here are all the values for DAC_TRG (TSEL in the ref manual)
* TIM15_TRGO 0b011
* TIM2_TRGO 0b100
* TIM3_TRGO 0b001
* TIM6_TRGO 0b000
* TIM7_TRGO 0b010
* EXTI9 0b110
* SWTRIG 0b111
*/
static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
void audio_driver_initialize() {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD1, &dac_conf);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD2, &dac_conf);
}
/* enable the output buffer, to directly drive external loads with no additional circuitry
*
* see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
* Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
* Note: enabling the output buffer imparts an additional dc-offset of a couple mV
*
* this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
* (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
*/
DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
if (AUDIO_PIN == A4) {
dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
} else if (AUDIO_PIN == A5) {
dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
}
// no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
if (AUDIO_PIN_ALT == A4) {
dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
} else if (AUDIO_PIN_ALT == A5) {
dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
}
#endif
gptStart(&GPTD6, &gpt6cfg1);
}
void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; }
void audio_driver_start(void) {
gptStartContinuous(&GPTD6, 2U);
for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
dac_if[i] = 0.0f;
active_tones_snapshot[i] = 0.0f;
}
active_tones_snapshot_length = 0;
state = OUTPUT_SHOULD_START;
}

View file

@ -1,245 +0,0 @@
/* Copyright 2016-2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "audio.h"
#include "ch.h"
#include "hal.h"
/*
Audio Driver: DAC
which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA
this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously
OR
one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio
*/
#if !defined(AUDIO_PIN)
# pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options."
// TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here
# define AUDIO_PIN A5
#endif
// check configuration for ONE speaker, connected to both DAC pins
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)
# error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT"
#endif
#ifndef AUDIO_PIN_ALT
// no ALT pin defined is valid, but the c-ifs below need some value set
# define AUDIO_PIN_ALT -1
#endif
#if !defined(AUDIO_STATE_TIMER)
# define AUDIO_STATE_TIMER GPTD8
#endif
// square-wave
static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {
// First half is max, second half is 0
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX,
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,
};
// square-wave
static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {
// opposite of dac_buffer above
[0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0,
[AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,
};
GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
.callback = NULL,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static void gpt_audio_state_cb(GPTDriver *gptp);
GPTConfig gptStateUpdateCfg = {.frequency = 10,
.callback = gpt_audio_state_cb,
.cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
.dier = 0U};
static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
/**
* @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
* on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
* to be a third of what we expect.
*
* Here are all the values for DAC_TRG (TSEL in the ref manual)
* TIM15_TRGO 0b011
* TIM2_TRGO 0b100
* TIM3_TRGO 0b001
* TIM6_TRGO 0b000
* TIM7_TRGO 0b010
* EXTI9 0b110
* SWTRIG 0b111
*/
static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};
static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};
void channel_1_start(void) {
gptStart(&GPTD6, &gpt6cfg1);
gptStartContinuous(&GPTD6, 2U);
palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
}
void channel_1_stop(void) {
gptStopTimer(&GPTD6);
palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);
palSetPad(GPIOA, 4);
}
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
channel_1_stop();
if (freq <= 0.0) // a pause/rest has freq=0
return;
gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
channel_1_start();
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_2_start(void) {
gptStart(&GPTD7, &gpt7cfg1);
gptStartContinuous(&GPTD7, 2U);
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
}
void channel_2_stop(void) {
gptStopTimer(&GPTD7);
palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL);
palSetPad(GPIOA, 5);
}
static float channel_2_frequency = 0.0f;
void channel_2_set_frequency(float freq) {
channel_2_frequency = freq;
channel_2_stop();
if (freq <= 0.0) // a pause/rest has freq=0
return;
gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
channel_2_start();
}
float channel_2_get_frequency(void) { return channel_2_frequency; }
static void gpt_audio_state_cb(GPTDriver *gptp) {
if (audio_update_state()) {
#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
// one piezo/speaker connected to both audio pins, the generated square-waves are inverted
channel_1_set_frequency(audio_get_processed_frequency(0));
channel_2_set_frequency(audio_get_processed_frequency(0));
#else // two separate audio outputs/speakers
// primary speaker on A4, optional secondary on A5
if (AUDIO_PIN == A4) {
channel_1_set_frequency(audio_get_processed_frequency(0));
if (AUDIO_PIN_ALT == A5) {
if (audio_get_number_of_active_tones() > 1) {
channel_2_set_frequency(audio_get_processed_frequency(1));
} else {
channel_2_stop();
}
}
}
// primary speaker on A5, optional secondary on A4
if (AUDIO_PIN == A5) {
channel_2_set_frequency(audio_get_processed_frequency(0));
if (AUDIO_PIN_ALT == A4) {
if (audio_get_number_of_active_tones() > 1) {
channel_1_set_frequency(audio_get_processed_frequency(1));
} else {
channel_1_stop();
}
}
}
#endif
}
}
void audio_driver_initialize() {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD1, &dac_conf_ch1);
// initial setup of the dac-triggering timer is still required, even
// though it gets reconfigured and restarted later on
gptStart(&GPTD6, &gpt6cfg1);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
dacStart(&DACD2, &dac_conf_ch2);
gptStart(&GPTD7, &gpt7cfg1);
}
/* enable the output buffer, to directly drive external loads with no additional circuitry
*
* see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
* Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
* Note: enabling the output buffer imparts an additional dc-offset of a couple mV
*
* this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
* (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
*/
DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
// start state-updater
gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);
}
void audio_driver_stop(void) {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
gptStopTimer(&GPTD6);
// stop the ongoing conversion and put the output in a known state
dacStopConversion(&DACD1);
dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
gptStopTimer(&GPTD7);
dacStopConversion(&DACD2);
dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
}
gptStopTimer(&AUDIO_STATE_TIMER);
}
void audio_driver_start(void) {
if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);
}
if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);
}
gptStartContinuous(&AUDIO_STATE_TIMER, 2U);
}

View file

@ -1,40 +0,0 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(AUDIO_PWM_DRIVER)
// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))
# define AUDIO_PWM_DRIVER PWMD1
#endif
#if !defined(AUDIO_PWM_CHANNEL)
// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4
// default: STM32F303CC PA8+TIM1_CH1 -> 1
# define AUDIO_PWM_CHANNEL 1
#endif
#if !defined(AUDIO_PWM_PAL_MODE)
// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy
// default: STM32F303CC PA8+TIM1_CH1 -> 6
# define AUDIO_PWM_PAL_MODE 6
#endif
#if !defined(AUDIO_STATE_TIMER)
// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.
// Tim6 is the default for "larger" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4)
# define AUDIO_STATE_TIMER GPTD6
#endif

View file

@ -1,144 +0,0 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Audio Driver: PWM
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function.
*/
#include "audio.h"
#include "ch.h"
#include "hal.h"
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
static PWMConfig pwmCFG = {
.frequency = 100000, /* PWM clock frequency */
// CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
.period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
.callback = NULL, /* no callback, the hardware directly toggles the pin */
.channels =
{
#if AUDIO_PWM_CHANNEL == 4
{PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
#elif AUDIO_PWM_CHANNEL == 3
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
{PWM_OUTPUT_DISABLED, NULL}
#elif AUDIO_PWM_CHANNEL == 2
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#else /*fallback to CH1 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#endif
},
};
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
if (freq <= 0.0) // a pause/rest has freq=0
return;
pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
// adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_1_start(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
}
void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); }
static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
/* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
the tempo (which might vary!) is in bpm (beats per minute)
therefore: if the timer ticks away at .frequency = (60*64)Hz,
and the .interval counts from 64 downwards - audio_update_state is
called just often enough to not miss any notes
*/
.frequency = 60 * 64,
.callback = gpt_callback,
};
void audio_driver_initialize(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
// connect the AUDIO_PIN to the PWM hardware
#if defined(USE_GPIOV1) // STM32F103C8
palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE(AUDIO_PWM_PAL_MODE));
#endif
gptStart(&AUDIO_STATE_TIMER, &gptCFG);
}
void audio_driver_start(void) {
channel_1_stop();
channel_1_start();
if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
}
}
void audio_driver_stop(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}
/* a regular timer task, that checks the note to be currently played
* and updates the pwm to output that frequency
*/
static void gpt_callback(GPTDriver *gptp) {
float freq; // TODO: freq_alt
if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}
}

View file

@ -1,164 +0,0 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Audio Driver: PWM
the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.
this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software
- a pwm callback is used to set/clear the configured pin.
*/
#include "audio.h"
#include "ch.h"
#include "hal.h"
#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif
extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;
static void pwm_audio_period_callback(PWMDriver *pwmp);
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
static PWMConfig pwmCFG = {
.frequency = 100000, /* PWM clock frequency */
// CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
.period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
.callback = pwm_audio_period_callback,
.channels =
{
// software-PWM just needs another callback on any channel
{PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
{PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */
},
};
static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
if (freq <= 0.0) // a pause/rest has freq=0
return;
pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
// adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
}
float channel_1_get_frequency(void) { return channel_1_frequency; }
void channel_1_start(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
}
void channel_1_stop(void) {
pwmStop(&AUDIO_PWM_DRIVER);
palClearLine(AUDIO_PIN); // leave the line low, after last note was played
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played
#endif
}
// generate a PWM signal on any pin, not necessarily the one connected to the timer
static void pwm_audio_period_callback(PWMDriver *pwmp) {
(void)pwmp;
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLine(AUDIO_PIN_ALT);
#endif
}
static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
(void)pwmp;
if (channel_1_frequency > 0) {
palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palClearLine(AUDIO_PIN_ALT);
#endif
}
}
static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
/* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
the tempo (which might vary!) is in bpm (beats per minute)
therefore: if the timer ticks away at .frequency = (60*64)Hz,
and the .interval counts from 64 downwards - audio_update_state is
called just often enough to not miss anything
*/
.frequency = 60 * 64,
.callback = gpt_callback,
};
void audio_driver_initialize(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN);
#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
palClearLine(AUDIO_PIN_ALT);
#endif
pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks
pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
gptStart(&AUDIO_STATE_TIMER, &gptCFG);
}
void audio_driver_start(void) {
channel_1_stop();
channel_1_start();
if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
}
}
void audio_driver_stop(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}
/* a regular timer task, that checks the note to be currently played
* and updates the pwm to output that frequency
*/
static void gpt_callback(GPTDriver *gptp) {
float freq; // TODO: freq_alt
if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}
}

View file

@ -20,11 +20,9 @@
#include "musical_notes.h"
#if __GNUC__ > 5 // don't use for older gcc compilers since check isn't supported.
# if __has_include("user_song_list.h")
# include "user_song_list.h"
# endif // if file exists
#endif // __GNUC__
#if __has_include("user_song_list.h")
# include "user_song_list.h"
#endif // if file exists
#define NO_SOUND

View file

@ -199,13 +199,14 @@ static inline void disable_pwm(void) {
// reaches the backlight level, where we turn off the LEDs,
// but also an overflow interrupt when the counter rolls back to 0,
// in which we're going to turn on the LEDs.
// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz.
// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz,
// or F_CPU/BACKLIGHT_CUSTOM_RESOLUTION if used.
// Triggered when the counter reaches the OCRx value
ISR(TIMERx_COMPA_vect) { backlight_pins_off(); }
// Triggered when the counter reaches the TOP value
// this one triggers at F_CPU/65536 =~ 244 Hz
// this one triggers at F_CPU/ICRx = 16MHz/65536 =~ 244 Hz
ISR(TIMERx_OVF_vect) {
# ifdef BACKLIGHT_BREATHING
if (is_breathing()) {
@ -220,7 +221,7 @@ ISR(TIMERx_OVF_vect) {
// artifacts (especially while breathing, because breathing_task
// takes many computation cycles).
// so better not turn them on while the counter TOP is very low.
if (OCRxx > 256) {
if (OCRxx > ICRx / 250 + 5) {
backlight_pins_on();
}
}
@ -231,24 +232,26 @@ ISR(TIMERx_OVF_vect) {
// 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;
if (v <= ICRx / 12) // If the value is less than or equal to ~8% of max
{
return v / 9; // Same as dividing by 900%
} else {
// In the next two lines values are bit-shifted. This is to avoid loosing decimals in integer math.
uint32_t y = (((uint32_t)v + ICRx / 6) << 5) / (ICRx / 6 + ICRx); // If above 8%, add ~16% of max, and normalize with (max + ~16% max)
uint32_t out = (y * y * y * ICRx) >> 15; // Cube it and undo the bit-shifting. (which is now three times as much due to the cubing)
if (out > ICRx) // Avoid overflows
{
out = ICRx;
}
return out;
}
}
// rescale the supplied backlight value to be in terms of the value limit
// rescale the supplied backlight value to be in terms of the value limit // range for val is [0..ICRx]. PWM pin is high while the timer count is below val.
static uint32_t rescale_limit_val(uint32_t val) { return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; }
// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.
static inline void set_pwm(uint16_t val) { OCRxx = val; }
void backlight_set(uint8_t level) {
@ -277,7 +280,7 @@ void backlight_set(uint8_t level) {
#endif
}
// Set the brightness
set_pwm(cie_lightness(rescale_limit_val(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)));
set_pwm(cie_lightness(rescale_limit_val(ICRx * (uint32_t)level / BACKLIGHT_LEVELS)));
}
void backlight_task(void) {}
@ -292,6 +295,11 @@ void backlight_task(void) {}
static uint8_t breathing_halt = BREATHING_NO_HALT;
static uint16_t breathing_counter = 0;
static uint8_t breath_scale_counter = 1;
/* Run the breathing loop at ~120Hz*/
const uint8_t breathing_ISR_frequency = 120;
static uint16_t breathing_freq_scale_factor = 2;
# ifdef BACKLIGHT_PWM_TIMER
static bool breathing = false;
@ -323,9 +331,9 @@ bool is_breathing(void) { return !!(TIMSKx & _BV(TOIEx)); }
do { \
breathing_counter = 0; \
} while (0)
# define breathing_max() \
do { \
breathing_counter = get_breathing_period() * 244 / 2; \
# define breathing_max() \
do { \
breathing_counter = get_breathing_period() * breathing_ISR_frequency / 2; \
} while (0)
void breathing_enable(void) {
@ -369,21 +377,30 @@ void breathing_task(void)
# else
/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run
* about 244 times per second.
*
* The following ISR runs at F_CPU/ISRx. With a 16MHz clock and default pwm resolution, that means 244Hz
*/
ISR(TIMERx_OVF_vect)
# endif
{
uint8_t breathing_period = get_breathing_period();
uint16_t interval = (uint16_t)breathing_period * 244 / BREATHING_STEPS;
// Only run this ISR at ~120 Hz
if (breath_scale_counter++ == breathing_freq_scale_factor) {
breath_scale_counter = 1;
} else {
return;
}
uint16_t interval = (uint16_t)get_breathing_period() * breathing_ISR_frequency / BREATHING_STEPS;
// resetting after one period to prevent ugly reset at overflow.
breathing_counter = (breathing_counter + 1) % (breathing_period * 244);
breathing_counter = (breathing_counter + 1) % (get_breathing_period() * breathing_ISR_frequency);
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();
}
set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))));
// Set PWM to a brightnessvalue scaled to the configured resolution
set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));
}
#endif // BACKLIGHT_BREATHING
@ -413,16 +430,23 @@ void backlight_init_ports(void) {
"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)."
*/
# if BACKLIGHT_ON_STATE == 1
TCCRxA = _BV(COMxx1) | _BV(WGM11);
# else
TCCRxA = _BV(COMxx1) | _BV(COMxx0) | _BV(WGM11);
# endif
TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
TCCRxA = _BV(COMxx1) | _BV(WGM11); // = 0b00001010;
TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
#endif
// Use full 16-bit resolution. Counter counts to ICR1 before reset to 0.
#ifdef BACKLIGHT_CUSTOM_RESOLUTION
# if (BACKLIGHT_CUSTOM_RESOLUTION > 0xFFFF || BACKLIGHT_CUSTOM_RESOLUTION < 1)
# error "This out of range of the timer capabilities"
# elif (BACKLIGHT_CUSTOM_RESOLUTION < 0xFF)
# warning "Resolution lower than 0xFF isn't recommended"
# endif
# ifdef BACKLIGHT_BREATHING
breathing_freq_scale_factor = F_CPU / BACKLIGHT_CUSTOM_RESOLUTION / 120;
# endif
ICRx = BACKLIGHT_CUSTOM_RESOLUTION;
#else
ICRx = TIMER_TOP;
#endif
backlight_init();
#ifdef BACKLIGHT_BREATHING

View file

@ -39,7 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "backlight.h"
#endif
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
#if defined(MOUSEKEY_ENABLE)
# include "mousekey.h"
#endif
@ -53,7 +53,7 @@ static void print_version(void);
static void print_status(void);
static bool command_console(uint8_t code);
static void command_console_help(void);
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
#if defined(MOUSEKEY_ENABLE)
static bool mousekey_console(uint8_t code);
#endif
@ -73,7 +73,7 @@ bool command_proc(uint8_t code) {
else
return (command_console_extra(code) || command_console(code));
break;
#if defined(MOUSEKEY_ENABLE) && !defined(MK_3_SPEED)
#if defined(MOUSEKEY_ENABLE)
case MOUSEKEY:
mousekey_console(code);
break;

View file

@ -24,7 +24,4 @@
#define COL2ROW 0
#define ROW2COL 1
// Deprecated alias - avoid using
#define KEYMAP LAYOUT
#include "song_list.h"

View file

@ -46,17 +46,17 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#define ROW_SHIFTER ((matrix_row_t)1)
typedef struct {
bool pressed : 1;
bool pressed : 1;
uint8_t time : 7;
} debounce_counter_t;
#if DEBOUNCE > 0
static debounce_counter_t *debounce_counters;
static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
#define DEBOUNCE_ELAPSED 0
# define DEBOUNCE_ELAPSED 0
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
@ -64,7 +64,7 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
// we use num_rows rather than MATRIX_ROWS to support split keyboards
void debounce_init(uint8_t num_rows) {
debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));
int i = 0;
int i = 0;
for (uint8_t r = 0; r < num_rows; r++) {
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
debounce_counters[i++].time = DEBOUNCE_ELAPSED;
@ -81,10 +81,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool updated_last = false;
if (counters_need_update) {
fast_timer_t now = timer_read_fast();
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
last_time = now;
last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;
@ -108,7 +108,7 @@ static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[],
debounce_counter_t *debounce_pointer = debounce_counters;
counters_need_update = false;
matrix_need_update = false;
matrix_need_update = false;
for (uint8_t row = 0; row < num_rows; row++) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
@ -146,8 +146,8 @@ static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], ui
if (delta & col_mask) {
if (debounce_pointer->time == DEBOUNCE_ELAPSED) {
debounce_pointer->pressed = (raw[row] & col_mask);
debounce_pointer->time = DEBOUNCE;
counters_need_update = true;
debounce_pointer->time = DEBOUNCE;
counters_need_update = true;
if (debounce_pointer->pressed) {
// key-down: eager

View file

@ -25,7 +25,7 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state.
#endif
#if DEBOUNCE > 0
static bool debouncing = false;
static bool debouncing = false;
static fast_timer_t debouncing_time;
void debounce_init(uint8_t num_rows) {}

View file

@ -49,7 +49,7 @@ static debounce_counter_t *debounce_counters;
static fast_timer_t last_time;
static bool counters_need_update;
#define DEBOUNCE_ELAPSED 0
# define DEBOUNCE_ELAPSED 0
static void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);
static void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
@ -74,10 +74,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool updated_last = false;
if (counters_need_update) {
fast_timer_t now = timer_read_fast();
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
last_time = now;
last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;

View file

@ -50,7 +50,7 @@ static fast_timer_t last_time;
static bool counters_need_update;
static bool matrix_need_update;
#define DEBOUNCE_ELAPSED 0
# define DEBOUNCE_ELAPSED 0
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
@ -75,10 +75,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool updated_last = false;
if (counters_need_update) {
fast_timer_t now = timer_read_fast();
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
last_time = now;
last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;
@ -107,7 +107,7 @@ static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
for (uint8_t col = 0; col < MATRIX_COLS; col++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_need_update = true;
} else {
*debounce_pointer -= elapsed_time;

View file

@ -49,7 +49,7 @@ static debounce_counter_t *debounce_counters;
static fast_timer_t last_time;
static bool counters_need_update;
#define DEBOUNCE_ELAPSED 0
# define DEBOUNCE_ELAPSED 0
static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);
static void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);
@ -71,10 +71,10 @@ void debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool
bool updated_last = false;
if (counters_need_update) {
fast_timer_t now = timer_read_fast();
fast_timer_t now = timer_read_fast();
fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);
last_time = now;
last_time = now;
updated_last = true;
if (elapsed_time > UINT8_MAX) {
elapsed_time = UINT8_MAX;
@ -102,7 +102,7 @@ static void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {
for (uint8_t row = 0; row < num_rows; row++) {
if (*debounce_pointer != DEBOUNCE_ELAPSED) {
if (*debounce_pointer <= elapsed_time) {
*debounce_pointer = DEBOUNCE_ELAPSED;
*debounce_pointer = DEBOUNCE_ELAPSED;
matrix_need_update = true;
} else {
*debounce_pointer -= elapsed_time;

View file

@ -19,7 +19,8 @@
#include "debounce_test_common.h"
TEST_F(DebounceTest, OneKeyShort1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 1ms delay */
{1, {{0, 1, UP}}, {}},
@ -43,7 +44,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
}
TEST_F(DebounceTest, OneKeyShort2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 2ms delay */
{2, {{0, 1, UP}}, {}},
@ -58,7 +60,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
}
TEST_F(DebounceTest, OneKeyShort3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 3ms delay */
{3, {{0, 1, UP}}, {}},
@ -73,7 +76,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
}
TEST_F(DebounceTest, OneKeyShort4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 4ms delay */
{4, {{0, 1, UP}}, {}},
@ -88,7 +92,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
}
TEST_F(DebounceTest, OneKeyShort5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 5ms delay */
@ -102,7 +107,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
}
TEST_F(DebounceTest, OneKeyShort6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 6ms delay */
@ -116,7 +122,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
}
TEST_F(DebounceTest, OneKeyShort7) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 7ms delay */
@ -130,7 +137,8 @@ TEST_F(DebounceTest, OneKeyShort7) {
}
TEST_F(DebounceTest, OneKeyShort8) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 1ms delay */
{1, {{0, 1, UP}}, {}},
@ -145,7 +153,8 @@ TEST_F(DebounceTest, OneKeyShort8) {
}
TEST_F(DebounceTest, OneKeyShort9) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Release key after 1ms delay */
{1, {{0, 1, UP}}, {}},
@ -159,7 +168,8 @@ TEST_F(DebounceTest, OneKeyShort9) {
}
TEST_F(DebounceTest, OneKeyBouncing1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 1, DOWN}}, {}},
@ -185,7 +195,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
}
TEST_F(DebounceTest, OneKeyBouncing2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Change twice in the same time period */
{1, {{0, 1, UP}}, {}},
@ -217,7 +228,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
}
TEST_F(DebounceTest, OneKeyLong) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{25, {{0, 1, UP}}, {}},
@ -236,7 +248,8 @@ TEST_F(DebounceTest, OneKeyLong) {
}
TEST_F(DebounceTest, TwoKeysShort) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
/* Release key after 2ms delay */
@ -249,14 +262,14 @@ TEST_F(DebounceTest, TwoKeysShort) {
{10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */
/* Press key again after 1ms delay */
{11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */
{12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */
{12, {{0, 2, DOWN}}, {{0, 2, DOWN}}}, /* 5ms+5ms after DOWN at time 0 */
});
runEvents();
}
TEST_F(DebounceTest, OneKeyDelayedScan1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late, immediately release key */
@ -269,7 +282,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
}
TEST_F(DebounceTest, OneKeyDelayedScan2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late, immediately release key */
@ -283,7 +297,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late */
@ -298,7 +313,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
}
TEST_F(DebounceTest, OneKeyDelayedScan4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late */
@ -314,7 +330,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
}
TEST_F(DebounceTest, OneKeyDelayedScan5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{5, {{0, 1, UP}}, {}},
@ -329,7 +346,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
}
TEST_F(DebounceTest, OneKeyDelayedScan6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{5, {{0, 1, UP}}, {}},
@ -345,7 +363,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan6) {
}
TEST_F(DebounceTest, OneKeyDelayedScan7) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{5, {{0, 1, UP}}, {}},
@ -358,7 +377,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan7) {
}
TEST_F(DebounceTest, OneKeyDelayedScan8) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is a bit late */

View file

@ -31,9 +31,7 @@ void set_time(uint32_t t);
void advance_time(uint32_t ms);
}
void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {
events_.insert(events_.end(), events.begin(), events.end());
}
void DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) { events_.insert(events_.end(), events.begin(), events.end()); }
void DebounceTest::runEvents() {
/* Run the test multiple times, from 1kHz to 10kHz scan rate */
@ -54,7 +52,7 @@ void DebounceTest::runEvents() {
void DebounceTest::runEventsInternal() {
fast_timer_t previous = 0;
bool first = true;
bool first = true;
/* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */
debounce_init(MATRIX_ROWS);
@ -80,7 +78,7 @@ void DebounceTest::runEventsInternal() {
}
}
first = false;
first = false;
previous = event.time_;
/* Prepare input matrix */
@ -98,12 +96,7 @@ void DebounceTest::runEventsInternal() {
/* Check output matrix has expected change events */
for (auto &output : event.outputs_) {
EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_))
<< "Missing event at " << strTime()
<< " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_)
<< "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_)
<< "\nexpected_matrix:\n" << strMatrix(output_matrix_)
<< "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_)) << "Missing event at " << strTime() << " expected key " << output.row_ << "," << output.col_ << " " << directionLabel(output.direction_) << "\ninput_matrix: changed=" << !event.inputs_.empty() << "\n" << strMatrix(input_matrix_) << "\nexpected_matrix:\n" << strMatrix(output_matrix_) << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
}
/* Check output matrix has no other changes */
@ -133,27 +126,20 @@ void DebounceTest::runDebounce(bool changed) {
debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);
if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {
FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime()
<< "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
<< "\nraw_matrix:\n" << strMatrix(raw_matrix_);
FAIL() << "Fatal error: debounce() modified raw matrix at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nraw_matrix:\n" << strMatrix(raw_matrix_);
}
}
void DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) {
if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) {
FAIL() << "Unexpected event: " << error_message << " at " << strTime()
<< "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_)
<< "\nexpected_matrix:\n" << strMatrix(output_matrix_)
<< "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
FAIL() << "Unexpected event: " << error_message << " at " << strTime() << "\ninput_matrix: changed=" << changed << "\n" << strMatrix(input_matrix_) << "\nexpected_matrix:\n" << strMatrix(output_matrix_) << "\nactual_matrix:\n" << strMatrix(cooked_matrix_);
}
}
std::string DebounceTest::strTime() {
std::stringstream text;
text << "time " << (timer_read_fast() - time_offset_)
<< " (extra_iterations=" << extra_iterations_
<< ", auto_advance_time=" << auto_advance_time_ << ")";
text << "time " << (timer_read_fast() - time_offset_) << " (extra_iterations=" << extra_iterations_ << ", auto_advance_time=" << auto_advance_time_ << ")";
return text.str();
}
@ -181,49 +167,39 @@ std::string DebounceTest::strMatrix(matrix_row_t matrix[]) {
bool DebounceTest::directionValue(Direction direction) {
switch (direction) {
case DOWN:
return true;
case DOWN:
return true;
case UP:
return false;
case UP:
return false;
}
}
std::string DebounceTest::directionLabel(Direction direction) {
switch (direction) {
case DOWN:
return "DOWN";
case DOWN:
return "DOWN";
case UP:
return "UP";
case UP:
return "UP";
}
}
/* Modify a matrix and verify that events always specify a change */
void DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) {
ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_))
<< "Test " << name << " at " << strTime()
<< " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_)
<< " but it is already " << directionLabel(event.direction_)
<< "\n" << name << "_matrix:\n" << strMatrix(matrix);
ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_)) << "Test " << name << " at " << strTime() << " sets key " << event.row_ << "," << event.col_ << " " << directionLabel(event.direction_) << " but it is already " << directionLabel(event.direction_) << "\n" << name << "_matrix:\n" << strMatrix(matrix);
switch (event.direction_) {
case DOWN:
matrix[event.row_] |= (1U << event.col_);
break;
case DOWN:
matrix[event.row_] |= (1U << event.col_);
break;
case UP:
matrix[event.row_] &= ~(1U << event.col_);
break;
case UP:
matrix[event.row_] &= ~(1U << event.col_);
break;
}
}
DebounceTestEvent::DebounceTestEvent(fast_timer_t time,
std::initializer_list<MatrixTestEvent> inputs,
std::initializer_list<MatrixTestEvent> outputs)
: time_(time), inputs_(inputs), outputs_(outputs) {
}
DebounceTestEvent::DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs) : time_(time), inputs_(inputs), outputs_(outputs) {}
MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction)
: row_(row), col_(col), direction_(direction) {
}
MatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction) : row_(row), col_(col), direction_(direction) {}

View file

@ -31,36 +31,34 @@ enum Direction {
};
class MatrixTestEvent {
public:
public:
MatrixTestEvent(int row, int col, Direction direction);
const int row_;
const int col_;
const int row_;
const int col_;
const Direction direction_;
};
class DebounceTestEvent {
public:
public:
// 0, {{0, 1, DOWN}}, {{0, 1, DOWN}})
DebounceTestEvent(fast_timer_t time,
std::initializer_list<MatrixTestEvent> inputs,
std::initializer_list<MatrixTestEvent> outputs);
DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs);
const fast_timer_t time_;
const fast_timer_t time_;
const std::list<MatrixTestEvent> inputs_;
const std::list<MatrixTestEvent> outputs_;
};
class DebounceTest : public ::testing::Test {
protected:
protected:
void addEvents(std::initializer_list<DebounceTestEvent> events);
void runEvents();
fast_timer_t time_offset_ = 7777;
bool time_jumps_ = false;
bool time_jumps_ = false;
private:
static bool directionValue(Direction direction);
private:
static bool directionValue(Direction direction);
static std::string directionLabel(Direction direction);
void runEventsInternal();
@ -78,6 +76,6 @@ private:
matrix_row_t cooked_matrix_[MATRIX_ROWS];
matrix_row_t output_matrix_[MATRIX_ROWS];
int extra_iterations_;
int extra_iterations_;
bool auto_advance_time_;
};

View file

@ -16,7 +16,7 @@
DEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5
DEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \
$(TMK_PATH)/common/test/timer.c
$(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c
debounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS)
debounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \

View file

@ -19,7 +19,8 @@
#include "debounce_test_common.h"
TEST_F(DebounceTest, OneKeyShort1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
}
TEST_F(DebounceTest, OneKeyShort2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
}
TEST_F(DebounceTest, OneKeyShort3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
}
TEST_F(DebounceTest, OneKeyTooQuick1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Release key exactly on the debounce time */
{5, {{0, 1, UP}}, {}},
@ -67,7 +71,8 @@ TEST_F(DebounceTest, OneKeyTooQuick1) {
}
TEST_F(DebounceTest, OneKeyTooQuick2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -80,7 +85,8 @@ TEST_F(DebounceTest, OneKeyTooQuick2) {
}
TEST_F(DebounceTest, OneKeyBouncing1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 1, DOWN}}, {}},
@ -94,7 +100,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
}
TEST_F(DebounceTest, OneKeyBouncing2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
{6, {{0, 1, UP}}, {}},
@ -108,7 +115,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
}
TEST_F(DebounceTest, OneKeyLong) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -125,7 +133,8 @@ TEST_F(DebounceTest, OneKeyLong) {
}
TEST_F(DebounceTest, TwoKeysShort) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 2, DOWN}}, {}},
@ -140,7 +149,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
@ -152,7 +162,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 2, DOWN}}, {}},
@ -167,7 +178,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is very late */
@ -182,7 +194,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
}
TEST_F(DebounceTest, OneKeyDelayedScan2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is very late */
@ -197,7 +210,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Release key before debounce expires */
@ -208,7 +222,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
}
TEST_F(DebounceTest, OneKeyDelayedScan4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is a bit late */

View file

@ -19,7 +19,8 @@
#include "debounce_test_common.h"
TEST_F(DebounceTest, OneKeyShort1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
}
TEST_F(DebounceTest, OneKeyShort2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
}
TEST_F(DebounceTest, OneKeyShort3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
}
TEST_F(DebounceTest, OneKeyTooQuick1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Release key exactly on the debounce time */
{5, {{0, 1, UP}}, {}},
@ -67,7 +71,8 @@ TEST_F(DebounceTest, OneKeyTooQuick1) {
}
TEST_F(DebounceTest, OneKeyTooQuick2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -80,7 +85,8 @@ TEST_F(DebounceTest, OneKeyTooQuick2) {
}
TEST_F(DebounceTest, OneKeyBouncing1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 1, DOWN}}, {}},
@ -94,7 +100,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
}
TEST_F(DebounceTest, OneKeyBouncing2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
{6, {{0, 1, UP}}, {}},
@ -108,7 +115,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
}
TEST_F(DebounceTest, OneKeyLong) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}}},
@ -125,7 +133,8 @@ TEST_F(DebounceTest, OneKeyLong) {
}
TEST_F(DebounceTest, TwoKeysShort) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 2, DOWN}}, {}},
@ -142,7 +151,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},
{5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},
@ -154,7 +164,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
{1, {{0, 2, DOWN}}, {}},
@ -169,7 +180,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is very late */
@ -184,7 +196,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
}
TEST_F(DebounceTest, OneKeyDelayedScan2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is very late */
@ -199,7 +212,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Release key before debounce expires */
@ -210,7 +224,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
}
TEST_F(DebounceTest, OneKeyDelayedScan4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {}},
/* Processing is a bit late */

View file

@ -19,7 +19,8 @@
#include "debounce_test_common.h"
TEST_F(DebounceTest, OneKeyShort1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
}
TEST_F(DebounceTest, OneKeyShort2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
}
TEST_F(DebounceTest, OneKeyShort3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
}
TEST_F(DebounceTest, OneKeyShort4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -71,7 +75,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
}
TEST_F(DebounceTest, OneKeyShort5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -83,7 +88,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
}
TEST_F(DebounceTest, OneKeyShort6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -95,7 +101,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
}
TEST_F(DebounceTest, OneKeyBouncing1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 1, DOWN}}, {}},
@ -110,7 +117,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
}
TEST_F(DebounceTest, OneKeyBouncing2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Change twice in the same time period */
{1, {{0, 1, UP}}, {}},
@ -135,7 +143,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
}
TEST_F(DebounceTest, OneKeyLong) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{25, {{0, 1, UP}}, {{0, 1, UP}}},
@ -146,7 +155,8 @@ TEST_F(DebounceTest, OneKeyLong) {
}
TEST_F(DebounceTest, TwoKeysShort) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 2, DOWN}}, {{0, 2, DOWN}}},
@ -167,7 +177,8 @@ TEST_F(DebounceTest, TwoKeysShort) {
}
TEST_F(DebounceTest, OneKeyDelayedScan1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted */
@ -178,7 +189,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
}
TEST_F(DebounceTest, OneKeyDelayedScan2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1 scan delay */
@ -190,7 +202,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1ms delay */
@ -202,7 +215,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
}
TEST_F(DebounceTest, OneKeyDelayedScan4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is a bit late but the change will now be accepted */
@ -213,7 +227,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
}
TEST_F(DebounceTest, OneKeyDelayedScan5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1 scan delay */
@ -225,7 +240,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
}
TEST_F(DebounceTest, OneKeyDelayedScan6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1ms delay */

View file

@ -19,7 +19,8 @@
#include "debounce_test_common.h"
TEST_F(DebounceTest, OneKeyShort1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -32,7 +33,8 @@ TEST_F(DebounceTest, OneKeyShort1) {
}
TEST_F(DebounceTest, OneKeyShort2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -45,7 +47,8 @@ TEST_F(DebounceTest, OneKeyShort2) {
}
TEST_F(DebounceTest, OneKeyShort3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -58,7 +61,8 @@ TEST_F(DebounceTest, OneKeyShort3) {
}
TEST_F(DebounceTest, OneKeyShort4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -71,7 +75,8 @@ TEST_F(DebounceTest, OneKeyShort4) {
}
TEST_F(DebounceTest, OneKeyShort5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -83,7 +88,8 @@ TEST_F(DebounceTest, OneKeyShort5) {
}
TEST_F(DebounceTest, OneKeyShort6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
@ -95,7 +101,8 @@ TEST_F(DebounceTest, OneKeyShort6) {
}
TEST_F(DebounceTest, OneKeyBouncing1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
{2, {{0, 1, DOWN}}, {}},
@ -110,7 +117,8 @@ TEST_F(DebounceTest, OneKeyBouncing1) {
}
TEST_F(DebounceTest, OneKeyBouncing2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Change twice in the same time period */
{1, {{0, 1, UP}}, {}},
@ -135,7 +143,8 @@ TEST_F(DebounceTest, OneKeyBouncing2) {
}
TEST_F(DebounceTest, OneKeyLong) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{25, {{0, 1, UP}}, {{0, 1, UP}}},
@ -146,7 +155,8 @@ TEST_F(DebounceTest, OneKeyLong) {
}
TEST_F(DebounceTest, TwoRowsShort) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
{2, {{2, 0, DOWN}}, {{2, 0, DOWN}}},
@ -167,7 +177,8 @@ TEST_F(DebounceTest, TwoRowsShort) {
}
TEST_F(DebounceTest, TwoKeysOverlap) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
{1, {{0, 1, UP}}, {}},
/* Press a second key during the first debounce */
@ -190,7 +201,8 @@ TEST_F(DebounceTest, TwoKeysOverlap) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
{20, {{0, 1, UP}}, {{0, 1, UP}}},
{21, {{0, 2, UP}}, {}},
@ -202,7 +214,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous1) {
}
TEST_F(DebounceTest, TwoKeysSimultaneous2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},
{20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},
});
@ -210,7 +223,8 @@ TEST_F(DebounceTest, TwoKeysSimultaneous2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan1) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted */
@ -221,7 +235,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan1) {
}
TEST_F(DebounceTest, OneKeyDelayedScan2) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1 scan delay */
@ -233,7 +248,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan2) {
}
TEST_F(DebounceTest, OneKeyDelayedScan3) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1ms delay */
@ -245,7 +261,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan3) {
}
TEST_F(DebounceTest, OneKeyDelayedScan4) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is a bit late but the change will now be accepted */
@ -256,7 +273,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan4) {
}
TEST_F(DebounceTest, OneKeyDelayedScan5) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1 scan delay */
@ -268,7 +286,8 @@ TEST_F(DebounceTest, OneKeyDelayedScan5) {
}
TEST_F(DebounceTest, OneKeyDelayedScan6) {
addEvents({ /* Time, Inputs, Outputs */
addEvents({
/* Time, Inputs, Outputs */
{0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},
/* Processing is very late but the change will now be accepted even with a 1ms delay */

152
quantum/deferred_exec.c Normal file
View file

@ -0,0 +1,152 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#include <stddef.h>
#include <timer.h>
#include <deferred_exec.h>
#ifndef MAX_DEFERRED_EXECUTORS
# define MAX_DEFERRED_EXECUTORS 8
#endif
typedef struct deferred_executor_t {
deferred_token token;
uint32_t trigger_time;
deferred_exec_callback callback;
void * cb_arg;
} deferred_executor_t;
static deferred_token current_token = 0;
static uint32_t last_deferred_exec_check = 0;
static deferred_executor_t executors[MAX_DEFERRED_EXECUTORS] = {0};
static inline bool token_can_be_used(deferred_token token) {
if (token == INVALID_DEFERRED_TOKEN) {
return false;
}
for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
if (executors[i].token == token) {
return false;
}
}
return true;
}
static inline deferred_token allocate_token(void) {
deferred_token first = ++current_token;
while (!token_can_be_used(current_token)) {
++current_token;
if (current_token == first) {
// If we've looped back around to the first, everything is already allocated (yikes!). Need to exit with a failure.
return INVALID_DEFERRED_TOKEN;
}
}
return current_token;
}
deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {
// Ignore queueing if it's a zero-time delay, or invalid callback
if (delay_ms == 0 || !callback) {
return INVALID_DEFERRED_TOKEN;
}
// Find an unused slot and claim it
for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
deferred_executor_t *entry = &executors[i];
if (entry->token == INVALID_DEFERRED_TOKEN) {
// Work out the new token value, dropping out if none were available
deferred_token token = allocate_token();
if (token == INVALID_DEFERRED_TOKEN) {
return false;
}
// Set up the executor table entry
entry->token = current_token;
entry->trigger_time = timer_read32() + delay_ms;
entry->callback = callback;
entry->cb_arg = cb_arg;
return current_token;
}
}
// None available
return INVALID_DEFERRED_TOKEN;
}
bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) {
// Ignore queueing if it's a zero-time delay, or the token is not valid
if (delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) {
return false;
}
// Find the entry corresponding to the token
for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
deferred_executor_t *entry = &executors[i];
if (entry->token == token) {
// Found it, extend the delay
entry->trigger_time = timer_read32() + delay_ms;
return true;
}
}
// Not found
return false;
}
bool cancel_deferred_exec(deferred_token token) {
// Ignore request if the token is not valid
if (token == INVALID_DEFERRED_TOKEN) {
return false;
}
// Find the entry corresponding to the token
for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
deferred_executor_t *entry = &executors[i];
if (entry->token == token) {
// Found it, cancel and clear the table entry
entry->token = INVALID_DEFERRED_TOKEN;
entry->trigger_time = 0;
entry->callback = NULL;
entry->cb_arg = NULL;
return true;
}
}
// Not found
return false;
}
void deferred_exec_task(void) {
uint32_t now = timer_read32();
// Throttle only once per millisecond
if (((int32_t)TIMER_DIFF_32(now, last_deferred_exec_check)) > 0) {
last_deferred_exec_check = now;
// Run through each of the executors
for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) {
deferred_executor_t *entry = &executors[i];
// Check if we're supposed to execute this entry
if (entry->token != INVALID_DEFERRED_TOKEN && ((int32_t)TIMER_DIFF_32(entry->trigger_time, now)) <= 0) {
// Invoke the callback and work work out if we should be requeued
uint32_t delay_ms = entry->callback(entry->trigger_time, entry->cb_arg);
// Update the trigger time if we have to repeat, otherwise clear it out
if (delay_ms > 0) {
// Intentionally add just the delay to the existing trigger time -- this ensures the next
// invocation is with respect to the previous trigger, rather than when it got to execution. Under
// normal circumstances this won't cause issue, but if another executor is invoked that takes a
// considerable length of time, then this ensures best-effort timing between invocations.
entry->trigger_time += delay_ms;
} else {
// If it was zero, then the callback is cancelling repeated execution. Free up the slot.
entry->token = INVALID_DEFERRED_TOKEN;
entry->trigger_time = 0;
entry->callback = NULL;
entry->cb_arg = NULL;
}
}
}
}
}

38
quantum/deferred_exec.h Normal file
View file

@ -0,0 +1,38 @@
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <stdbool.h>
#include <stdint.h>
// A token that can be used to cancel an existing deferred execution.
typedef uint8_t deferred_token;
#define INVALID_DEFERRED_TOKEN 0
// Callback to execute.
// -- Parameter trigger_time: the intended trigger time to execute the callback -- equivalent time-space as timer_read32()
// cb_arg: the callback argument specified when enqueueing the deferred executor
// -- Return value: Non-zero re-queues the callback to execute after the returned number of milliseconds. Zero cancels repeated execution.
typedef uint32_t (*deferred_exec_callback)(uint32_t trigger_time, void *cb_arg);
// Configures the supplied deferred executor to be executed after the required number of milliseconds.
// -- Parameter delay_ms: the number of milliseconds before executing the callback
// -- callback: the executor to invoke
// -- cb_arg: the argument to pass to the executor, may be NULL if unused by the executor
// -- Return value: a token usable for cancellation, or INVALID_DEFERRED_TOKEN if an error occurred
deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);
// Allows for extending the timeframe before an existing deferred execution is invoked.
// -- Parameter token: the returned value from defer_exec for the deferred execution you wish to extend.
// -- delay_ms: the new delay (with respect to the current time)
// -- Return value: if the token was found, and the delay was extended
bool extend_deferred_exec(deferred_token token, uint32_t delay_ms);
// Allows for cancellation of an existing deferred execution.
// -- Parameter token: the returned value from defer_exec for the deferred execution you wish to cancel.
// -- Return value: if the token was found, and the executor was cancelled
bool cancel_deferred_exec(deferred_token token);
// Forward declaration for the main loop in order to execute any deferred executors. Should not be invoked by keyboard/user code.
void deferred_exec_task(void);

View file

@ -16,7 +16,7 @@
#include "config.h"
#include "keymap.h" // to get keymaps[][][]
#include "tmk_core/common/eeprom.h"
#include "eeprom.h"
#include "progmem.h" // to read default from flash
#include "quantum.h" // for send_string()
#include "dynamic_keymap.h"

View file

@ -111,3 +111,29 @@ void eeconfig_update_haptic(uint32_t val);
bool eeconfig_read_handedness(void);
void eeconfig_update_handedness(bool val);
#define EECONFIG_DEBOUNCE_HELPER(name, offset, config) \
static uint8_t dirty_##name = false; \
\
static inline void eeconfig_init_##name(void) { \
eeprom_read_block(&config, offset, sizeof(config)); \
dirty_##name = false; \
} \
static inline void eeconfig_flush_##name(bool force) { \
if (force || dirty_##name) { \
eeprom_update_block(&config, offset, sizeof(config)); \
dirty_##name = false; \
} \
} \
static inline void eeconfig_flush_##name##_task(uint16_t timeout) { \
static uint16_t flush_timer = 0; \
if (timer_elapsed(flush_timer) > timeout) { \
eeconfig_flush_##name(false); \
flush_timer = timer_read(); \
} \
} \
static inline void eeconfig_flag_##name(bool v) { dirty_##name |= v; } \
static inline void eeconfig_write_##name(typeof(config) conf) { \
memcpy(&config, &conf, sizeof(config)); \
eeconfig_flag_##name(true); \
}

View file

@ -0,0 +1,144 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t uidx = 0;
update updates[32];
bool encoder_update_kb(uint8_t index, bool clockwise) {
updates[uidx % 32] = {index, clockwise};
uidx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderTest : public ::testing::Test {};
TEST_F(EncoderTest, TestInit) {
uidx = 0;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestOneClockwise) {
uidx = 0;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestOneCounterClockwise) {
uidx = 0;
encoder_init();
setAndRead(1, false);
setAndRead(0, false);
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, false);
}
TEST_F(EncoderTest, TestTwoClockwiseOneCC) {
uidx = 0;
encoder_init();
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
setAndRead(1, false);
setAndRead(0, false);
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 3);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
EXPECT_EQ(updates[1].index, 0);
EXPECT_EQ(updates[1].clockwise, true);
EXPECT_EQ(updates[2].index, 0);
EXPECT_EQ(updates[2].clockwise, false);
}
TEST_F(EncoderTest, TestNoEarly) {
uidx = 0;
encoder_init();
// send 3 pulses. with resolution 4, that's not enough for a step.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
// now send last pulse
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestHalfway) {
uidx = 0;
encoder_init();
// go halfway
setAndRead(0, false);
setAndRead(1, false);
EXPECT_EQ(uidx, 0);
// back off
setAndRead(1, true);
setAndRead(0, true);
EXPECT_EQ(uidx, 0);
// go all the way
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
// should result in 1 update
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}

View file

@ -0,0 +1,143 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <vector>
#include <algorithm>
#include <stdio.h>
extern "C" {
#include "encoder.h"
#include "encoder/tests/mock_split.h"
}
struct update {
int8_t index;
bool clockwise;
};
uint8_t uidx = 0;
update updates[32];
bool isLeftHand;
bool encoder_update_kb(uint8_t index, bool clockwise) {
if (!isLeftHand) {
// this method has no effect on slave half
printf("ignoring update on right hand (%d,%s)\n", index, clockwise ? "CW" : "CC");
return true;
}
updates[uidx % 32] = {index, clockwise};
uidx++;
return true;
}
bool setAndRead(pin_t pin, bool val) {
setPin(pin, val);
return encoder_read();
}
class EncoderTest : public ::testing::Test {
protected:
void SetUp() override {
uidx = 0;
for (int i = 0; i < 32; i++) {
pinIsInputHigh[i] = 0;
pins[i] = 0;
}
}
};
TEST_F(EncoderTest, TestInitLeft) {
isLeftHand = true;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], true);
EXPECT_EQ(pinIsInputHigh[1], true);
EXPECT_EQ(pinIsInputHigh[2], false);
EXPECT_EQ(pinIsInputHigh[3], false);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestInitRight) {
isLeftHand = false;
encoder_init();
EXPECT_EQ(pinIsInputHigh[0], false);
EXPECT_EQ(pinIsInputHigh[1], false);
EXPECT_EQ(pinIsInputHigh[2], true);
EXPECT_EQ(pinIsInputHigh[3], true);
EXPECT_EQ(uidx, 0);
}
TEST_F(EncoderTest, TestOneClockwiseLeft) {
isLeftHand = true;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(0, false);
setAndRead(1, false);
setAndRead(0, true);
setAndRead(1, true);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 0);
EXPECT_EQ(updates[0].clockwise, true);
}
TEST_F(EncoderTest, TestOneClockwiseRightSent) {
isLeftHand = false;
encoder_init();
// send 4 pulses. with resolution 4, that's one step and we should get 1 update.
setAndRead(2, false);
setAndRead(3, false);
setAndRead(2, true);
setAndRead(3, true);
uint8_t slave_state[2] = {0};
encoder_state_raw(slave_state);
EXPECT_EQ((int8_t)slave_state[0], -1);
}
/* this test will not work after the previous test.
* this is due to encoder_value[1] already being set to -1 when simulating the right half.
* When we now receive this update acting as the left half, there is no change.
* This is hard to mock, as the static values inside encoder.c normally exist twice, once on each half,
* but here, they only exist once.
*/
// TEST_F(EncoderTest, TestOneClockwiseRightReceived) {
// isLeftHand = true;
// encoder_init();
// uint8_t slave_state[2] = {255, 0};
// encoder_update_raw(slave_state);
// EXPECT_EQ(uidx, 1);
// EXPECT_EQ(updates[0].index, 1);
// EXPECT_EQ(updates[0].clockwise, true);
// }
TEST_F(EncoderTest, TestOneCounterClockwiseRightReceived) {
isLeftHand = true;
encoder_init();
uint8_t slave_state[2] = {0, 0};
encoder_update_raw(slave_state);
EXPECT_EQ(uidx, 1);
EXPECT_EQ(updates[0].index, 1);
EXPECT_EQ(updates[0].clockwise, false);
}

View file

@ -1,4 +1,4 @@
/* Copyright 2016 Jack Humbert
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -14,12 +14,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "mock.h"
#include "api.h"
bool pins[32] = {0};
bool pinIsInputHigh[32] = {0};
#define API_SYSEX_MAX_SIZE 32
uint8_t mockSetPinInputHigh(pin_t pin) {
// dprintf("Setting pin %d input high.", pin);
pins[pin] = true;
pinIsInputHigh[pin] = true;
return 0;
}
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t* bytes, uint16_t length);
bool mockReadPin(pin_t pin) { return pins[pin]; }
#define SEND_BYTES(mt, dt, b, l) send_bytes_sysex(mt, dt, b, l)
bool setPin(pin_t pin, bool val) {
pins[pin] = val;
return val;
}

View file

@ -1,4 +1,4 @@
/* Copyright 2017 Fred Sundvik
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,12 +16,25 @@
#pragma once
#include "visualizer.h"
#include <stdint.h>
#include <stdbool.h>
// You can use these default animations, but of course you can also write your own custom ones instead
extern keyframe_animation_t default_startup_animation;
extern keyframe_animation_t default_suspend_animation;
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
// An animation for testing and demonstrating the led support, should probably not be used for real world
// cases
extern keyframe_animation_t led_test_animation;
typedef uint8_t pin_t;
extern bool pins[];
extern bool pinIsInputHigh[];
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
#define readPin(pin) (mockReadPin(pin))
uint8_t mockSetPinInputHigh(pin_t pin);
bool mockReadPin(pin_t pin);
bool setPin(pin_t pin, bool val);

View file

@ -1,4 +1,4 @@
/* Copyright 2017 Fred Sundvik
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -14,10 +14,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "visualizer_keyframes.h"
#include "mock_split.h"
bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
(void)animation;
(void)state;
return false;
bool pins[32] = {0};
bool pinIsInputHigh[32] = {0};
uint8_t mockSetPinInputHigh(pin_t pin) {
// dprintf("Setting pin %d input high.", pin);
pins[pin] = true;
pinIsInputHigh[pin] = true;
return 0;
}
bool mockReadPin(pin_t pin) { return pins[pin]; }
bool setPin(pin_t pin, bool val) {
pins[pin] = val;
return val;
}
void last_encoder_activity_trigger(void) {}

View file

@ -0,0 +1,48 @@
/* Copyright 2021 Balz Guenat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#define SPLIT_KEYBOARD
/* Here, "pins" from 0 to 31 are allowed. */
#define ENCODERS_PAD_A \
{ 0 }
#define ENCODERS_PAD_B \
{ 1 }
#define ENCODERS_PAD_A_RIGHT \
{ 2 }
#define ENCODERS_PAD_B_RIGHT \
{ 3 }
typedef uint8_t pin_t;
extern bool isLeftHand;
void encoder_state_raw(uint8_t* slave_state);
void encoder_update_raw(uint8_t* slave_state);
extern bool pins[];
extern bool pinIsInputHigh[];
#define setPinInputHigh(pin) (mockSetPinInputHigh(pin))
#define readPin(pin) (mockReadPin(pin))
uint8_t mockSetPinInputHigh(pin_t pin);
bool mockReadPin(pin_t pin);
bool setPin(pin_t pin, bool val);

View file

@ -0,0 +1,13 @@
encoder_DEFS := -DENCODER_MOCK_SINGLE
encoder_SRC := \
$(QUANTUM_PATH)/encoder/tests/mock.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \
$(QUANTUM_PATH)/encoder.c
encoder_split_DEFS := -DENCODER_MOCK_SPLIT
encoder_split_SRC := \
$(QUANTUM_PATH)/encoder/tests/mock_split.c \
$(QUANTUM_PATH)/encoder/tests/encoder_tests_split.cpp \
$(QUANTUM_PATH)/encoder.c

View file

@ -0,0 +1,3 @@
TEST_LIST += \
encoder \
encoder_split

View file

@ -17,6 +17,8 @@
#include "haptic.h"
#include "eeconfig.h"
#include "debug.h"
#include "usb_device_state.h"
#include "gpio.h"
#ifdef DRV2605L
# include "DRV2605L.h"
#endif
@ -26,6 +28,29 @@
haptic_config_t haptic_config;
static void update_haptic_enable_gpios(void) {
if (haptic_config.enable && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED))) {
#if defined(HAPTIC_ENABLE_PIN)
HAPTIC_ENABLE_PIN_WRITE_ACTIVE();
#endif
#if defined(HAPTIC_ENABLE_STATUS_LED)
HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE();
#endif
} else {
#if defined(HAPTIC_ENABLE_PIN)
HAPTIC_ENABLE_PIN_WRITE_INACTIVE();
#endif
#if defined(HAPTIC_ENABLE_STATUS_LED)
HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE();
#endif
}
}
static void set_haptic_config_enable(bool enabled) {
haptic_config.enable = enabled;
update_haptic_enable_gpios();
}
void haptic_init(void) {
if (!eeconfig_is_enabled()) {
eeconfig_init();
@ -44,6 +69,10 @@ void haptic_init(void) {
// or the previous firmware didn't have solenoid enabled,
// and the current one has solenoid enabled.
haptic_reset();
} else {
// Haptic configuration has been loaded through the "raw" union item.
// This is to execute any side effects of the configuration.
set_haptic_config_enable(haptic_config.enable);
}
#ifdef SOLENOID_ENABLE
solenoid_setup();
@ -54,6 +83,12 @@ void haptic_init(void) {
dprintf("DRV2605 driver initialized\n");
#endif
eeconfig_debug_haptic();
#ifdef HAPTIC_ENABLE_PIN
setPinOutput(HAPTIC_ENABLE_PIN);
#endif
#ifdef HAPTIC_ENABLE_STATUS_LED
setPinOutput(HAPTIC_ENABLE_STATUS_LED);
#endif
}
void haptic_task(void) {
@ -69,13 +104,13 @@ void eeconfig_debug_haptic(void) {
}
void haptic_enable(void) {
haptic_config.enable = 1;
set_haptic_config_enable(true);
xprintf("haptic_config.enable = %u\n", haptic_config.enable);
eeconfig_update_haptic(haptic_config.raw);
}
void haptic_disable(void) {
haptic_config.enable = 0;
set_haptic_config_enable(false);
xprintf("haptic_config.enable = %u\n", haptic_config.enable);
eeconfig_update_haptic(haptic_config.raw);
}
@ -157,7 +192,7 @@ void haptic_dwell_decrease(void) {
}
void haptic_reset(void) {
haptic_config.enable = true;
set_haptic_config_enable(true);
uint8_t feedback = HAPTIC_FEEDBACK_DEFAULT;
haptic_config.feedback = feedback;
#ifdef DRV2605L
@ -293,3 +328,13 @@ void haptic_shutdown(void) {
solenoid_shutdown();
#endif
}
void haptic_notify_usb_device_state_change(void) {
update_haptic_enable_gpios();
#if defined(HAPTIC_ENABLE_PIN)
setPinOutput(HAPTIC_ENABLE_PIN);
#endif
#if defined(HAPTIC_ENABLE_STATUS_LED)
setPinOutput(HAPTIC_ENABLE_STATUS_LED);
#endif
}

View file

@ -75,3 +75,30 @@ void haptic_cont_decrease(void);
void haptic_play(void);
void haptic_shutdown(void);
void haptic_notify_usb_device_state_change(void);
#ifdef HAPTIC_ENABLE_PIN_ACTIVE_LOW
# ifndef HAPTIC_ENABLE_PIN
# error HAPTIC_ENABLE_PIN not defined
# endif
# define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
# define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
#else
# define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_PIN)
# define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_PIN)
#endif
#ifdef HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW
# ifndef HAPTIC_ENABLE_STATUS_LED
# error HAPTIC_ENABLE_STATUS_LED not defined
# endif
# define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
# define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
#else
# define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() writePinHigh(HAPTIC_ENABLE_STATUS_LED)
# define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() writePinLow(HAPTIC_ENABLE_STATUS_LED)
#endif
#ifndef HAPTIC_OFF_IN_LOW_POWER
# define HAPTIC_OFF_IN_LOW_POWER 0
#endif

View file

@ -40,12 +40,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef PS2_MOUSE_ENABLE
# include "ps2_mouse.h"
#endif
#ifdef SERIAL_MOUSE_ENABLE
# include "serial_mouse.h"
#endif
#ifdef ADB_MOUSE_ENABLE
# include "adb.h"
#endif
#ifdef RGBLIGHT_ENABLE
# include "rgblight.h"
#endif
@ -61,12 +55,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef STENO_ENABLE
# include "process_steno.h"
#endif
#ifdef SERIAL_LINK_ENABLE
# include "serial_link/system/serial_link.h"
#endif
#ifdef VISUALIZER_ENABLE
# include "visualizer/visualizer.h"
#endif
#ifdef POINTING_DEVICE_ENABLE
# include "pointing_device.h"
#endif
@ -82,9 +70,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef HD44780_ENABLE
# include "hd44780.h"
#endif
#ifdef QWIIC_ENABLE
# include "qwiic.h"
#endif
#ifdef OLED_ENABLE
# include "oled_driver.h"
#endif
@ -109,6 +94,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef DIGITIZER_ENABLE
# include "digitizer.h"
#endif
#ifdef VIRTSER_ENABLE
# include "virtser.h"
#endif
#ifdef SLEEP_LED_ENABLE
# include "sleep_led.h"
#endif
static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) { return last_input_modification_time; }
@ -313,9 +304,6 @@ void keyboard_init(void) {
#if defined(CRC_ENABLE)
crc_init();
#endif
#ifdef QWIIC_ENABLE
qwiic_init();
#endif
#ifdef OLED_ENABLE
oled_init(OLED_ROTATION_0);
#endif
@ -325,12 +313,6 @@ void keyboard_init(void) {
#ifdef PS2_MOUSE_ENABLE
ps2_mouse_init();
#endif
#ifdef SERIAL_MOUSE_ENABLE
serial_mouse_init();
#endif
#ifdef ADB_MOUSE_ENABLE
adb_mouse_init();
#endif
#ifdef BACKLIGHT_ENABLE
backlight_init();
#endif
@ -353,6 +335,12 @@ void keyboard_init(void) {
#ifdef DIP_SWITCH_ENABLE
dip_switch_init();
#endif
#ifdef SLEEP_LED_ENABLE
sleep_led_init();
#endif
#ifdef VIRTSER_ENABLE
virtser_init();
#endif
#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)
debug_enable = true;
@ -381,7 +369,6 @@ void switch_events(uint8_t row, uint8_t col, bool pressed) {
*
* * scan matrix
* * handle mouse movements
* * run visualizer code
* * handle midi commands
* * light LEDs
*
@ -470,10 +457,6 @@ MATRIX_LOOP_END:
if (encoders_changed) last_encoder_activity_trigger();
#endif
#ifdef QWIIC_ENABLE
qwiic_task();
#endif
#ifdef OLED_ENABLE
oled_task();
# if OLED_TIMEOUT > 0
@ -507,22 +490,6 @@ MATRIX_LOOP_END:
ps2_mouse_task();
#endif
#ifdef SERIAL_MOUSE_ENABLE
serial_mouse_task();
#endif
#ifdef ADB_MOUSE_ENABLE
adb_mouse_task();
#endif
#ifdef SERIAL_LINK_ENABLE
serial_link_update();
#endif
#ifdef VISUALIZER_ENABLE
visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds());
#endif
#ifdef POINTING_DEVICE_ENABLE
pointing_device_task();
#endif

View file

@ -29,7 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED)
#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)
#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL)
#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI)
#define IS_MOD(code) (KC_LEFT_CTRL <= (code) && (code) <= KC_RIGHT_GUI)
#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF))
#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE)
@ -46,10 +46,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MOD_BIT(code) (1 << MOD_INDEX(code))
#define MOD_INDEX(code) ((code)&0x07)
#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL))
#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))
#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT))
#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI))
#define MOD_MASK_CTRL (MOD_BIT(KC_LEFT_CTRL) | MOD_BIT(KC_RIGHT_CTRL))
#define MOD_MASK_SHIFT (MOD_BIT(KC_LEFT_SHIFT) | MOD_BIT(KC_RIGHT_SHIFT))
#define MOD_MASK_ALT (MOD_BIT(KC_LEFT_ALT) | MOD_BIT(KC_RIGHT_ALT))
#define MOD_MASK_GUI (MOD_BIT(KC_LEFT_GUI) | MOD_BIT(KC_RIGHT_GUI))
#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)
#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)
#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)
@ -67,6 +67,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define FN_MIN KC_FN0
#define FN_MAX KC_FN31
// clang-format off
/*
* Short names for ease of definition of keymap
*/
@ -75,47 +77,55 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KC_TRNS KC_TRANSPARENT
/* Punctuation */
#define KC_ENT KC_ENTER
#define KC_ESC KC_ESCAPE
#define KC_BSPC KC_BSPACE
#define KC_SPC KC_SPACE
#define KC_ENT KC_ENTER
#define KC_ESC KC_ESCAPE
#define KC_BSPC KC_BACKSPACE
#define KC_SPC KC_SPACE
#define KC_MINS KC_MINUS
#define KC_EQL KC_EQUAL
#define KC_LBRC KC_LBRACKET
#define KC_RBRC KC_RBRACKET
#define KC_BSLS KC_BSLASH
#define KC_EQL KC_EQUAL
#define KC_LBRC KC_LEFT_BRACKET
#define KC_RBRC KC_RIGHT_BRACKET
#define KC_BSLS KC_BACKSLASH
#define KC_NUHS KC_NONUS_HASH
#define KC_SCLN KC_SCOLON
#define KC_SCLN KC_SEMICOLON
#define KC_QUOT KC_QUOTE
#define KC_GRV KC_GRAVE
#define KC_GRV KC_GRAVE
#define KC_COMM KC_COMMA
#define KC_SLSH KC_SLASH
#define KC_NUBS KC_NONUS_BSLASH
#define KC_NUBS KC_NONUS_BACKSLASH
/* Lock Keys */
#define KC_CLCK KC_CAPSLOCK
#define KC_CAPS KC_CAPSLOCK
#define KC_SLCK KC_SCROLLLOCK
#define KC_NLCK KC_NUMLOCK
#define KC_LCAP KC_LOCKING_CAPS
#define KC_LNUM KC_LOCKING_NUM
#define KC_LSCR KC_LOCKING_SCROLL
#define KC_CAPS KC_CAPS_LOCK
#define KC_SCRL KC_SCROLL_LOCK
#define KC_NUM KC_NUM_LOCK
#define KC_LCAP KC_LOCKING_CAPS_LOCK
#define KC_LNUM KC_LOCKING_NUM_LOCK
#define KC_LSCR KC_LOCKING_SCROLL_LOCK
/* Commands */
#define KC_PSCR KC_PSCREEN
#define KC_PSCR KC_PRINT_SCREEN
#define KC_PAUS KC_PAUSE
#define KC_BRK KC_PAUSE
#define KC_INS KC_INSERT
#define KC_DEL KC_DELETE
#define KC_PGDN KC_PGDOWN
#define KC_BRK KC_PAUSE
#define KC_INS KC_INSERT
#define KC_PGUP KC_PAGE_UP
#define KC_DEL KC_DELETE
#define KC_PGDN KC_PAGE_DOWN
#define KC_RGHT KC_RIGHT
#define KC_APP KC_APPLICATION
#define KC_APP KC_APPLICATION
#define KC_EXEC KC_EXECUTE
#define KC_SLCT KC_SELECT
#define KC_AGIN KC_AGAIN
#define KC_PSTE KC_PASTE
#define KC_ERAS KC_ALT_ERASE
#define KC_CLR KC_CLEAR
#define KC_ERAS KC_ALTERNATE_ERASE
#define KC_SYRQ KC_SYSTEM_REQUEST
#define KC_CNCL KC_CANCEL
#define KC_CLR KC_CLEAR
#define KC_PRIR KC_PRIOR
#define KC_RETN KC_RETURN
#define KC_SEPR KC_SEPARATOR
#define KC_CLAG KC_CLEAR_AGAIN
#define KC_CRSL KC_CRSEL
#define KC_EXSL KC_EXSEL
/* Keypad */
#define KC_PSLS KC_KP_SLASH
@ -123,47 +133,59 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KC_PMNS KC_KP_MINUS
#define KC_PPLS KC_KP_PLUS
#define KC_PENT KC_KP_ENTER
#define KC_P1 KC_KP_1
#define KC_P2 KC_KP_2
#define KC_P3 KC_KP_3
#define KC_P4 KC_KP_4
#define KC_P5 KC_KP_5
#define KC_P6 KC_KP_6
#define KC_P7 KC_KP_7
#define KC_P8 KC_KP_8
#define KC_P9 KC_KP_9
#define KC_P0 KC_KP_0
#define KC_P1 KC_KP_1
#define KC_P2 KC_KP_2
#define KC_P3 KC_KP_3
#define KC_P4 KC_KP_4
#define KC_P5 KC_KP_5
#define KC_P6 KC_KP_6
#define KC_P7 KC_KP_7
#define KC_P8 KC_KP_8
#define KC_P9 KC_KP_9
#define KC_P0 KC_KP_0
#define KC_PDOT KC_KP_DOT
#define KC_PEQL KC_KP_EQUAL
#define KC_PCMM KC_KP_COMMA
/* Japanese specific */
#define KC_ZKHK KC_GRAVE
#define KC_RO KC_INT1
#define KC_KANA KC_INT2
#define KC_JYEN KC_INT3
#define KC_HENK KC_INT4
#define KC_MHEN KC_INT5
/* Korean specific */
#define KC_HAEN KC_LANG1
#define KC_HANJ KC_LANG2
/* Language Specific */
#define KC_INT1 KC_INTERNATIONAL_1
#define KC_INT2 KC_INTERNATIONAL_2
#define KC_INT3 KC_INTERNATIONAL_3
#define KC_INT4 KC_INTERNATIONAL_4
#define KC_INT5 KC_INTERNATIONAL_5
#define KC_INT6 KC_INTERNATIONAL_6
#define KC_INT7 KC_INTERNATIONAL_7
#define KC_INT8 KC_INTERNATIONAL_8
#define KC_INT9 KC_INTERNATIONAL_9
#define KC_LNG1 KC_LANGUAGE_1
#define KC_LNG2 KC_LANGUAGE_2
#define KC_LNG3 KC_LANGUAGE_3
#define KC_LNG4 KC_LANGUAGE_4
#define KC_LNG5 KC_LANGUAGE_5
#define KC_LNG6 KC_LANGUAGE_6
#define KC_LNG7 KC_LANGUAGE_7
#define KC_LNG8 KC_LANGUAGE_8
#define KC_LNG9 KC_LANGUAGE_9
/* Modifiers */
#define KC_LCTL KC_LCTRL
#define KC_LSFT KC_LSHIFT
#define KC_LOPT KC_LALT
#define KC_LCMD KC_LGUI
#define KC_LWIN KC_LGUI
#define KC_RCTL KC_RCTRL
#define KC_RSFT KC_RSHIFT
#define KC_ALGR KC_RALT
#define KC_ROPT KC_RALT
#define KC_RCMD KC_RGUI
#define KC_RWIN KC_RGUI
#define KC_LCTL KC_LEFT_CTRL
#define KC_LSFT KC_LEFT_SHIFT
#define KC_LALT KC_LEFT_ALT
#define KC_LOPT KC_LEFT_ALT
#define KC_LGUI KC_LEFT_GUI
#define KC_LCMD KC_LEFT_GUI
#define KC_LWIN KC_LEFT_GUI
#define KC_RCTL KC_RIGHT_CTRL
#define KC_RSFT KC_RIGHT_SHIFT
#define KC_RALT KC_RIGHT_ALT
#define KC_ALGR KC_RIGHT_ALT
#define KC_ROPT KC_RIGHT_ALT
#define KC_RGUI KC_RIGHT_GUI
#define KC_RCMD KC_RIGHT_GUI
#define KC_RWIN KC_RIGHT_GUI
/* Generic Desktop Page (0x01) */
#define KC_PWR KC_SYSTEM_POWER
#define KC_PWR KC_SYSTEM_POWER
#define KC_SLEP KC_SYSTEM_SLEEP
#define KC_WAKE KC_SYSTEM_WAKE
@ -193,7 +215,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
/* System Specific */
#define KC_BRMU KC_PAUSE
#define KC_BRMD KC_SCROLLLOCK
#define KC_BRMD KC_SCROLL_LOCK
/* Mouse Keys */
#define KC_MS_U KC_MS_UP
@ -216,6 +238,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define KC_ACL1 KC_MS_ACCEL1
#define KC_ACL2 KC_MS_ACCEL2
// clang-format on
/* Keyboard/Keypad Page (0x07) */
enum hid_keyboard_keypad_usage {
KC_NO = 0x00,
@ -260,22 +284,22 @@ enum hid_keyboard_keypad_usage {
KC_0,
KC_ENTER,
KC_ESCAPE,
KC_BSPACE,
KC_BACKSPACE,
KC_TAB,
KC_SPACE,
KC_MINUS,
KC_EQUAL,
KC_LBRACKET,
KC_RBRACKET, // 0x30
KC_BSLASH,
KC_LEFT_BRACKET,
KC_RIGHT_BRACKET, // 0x30
KC_BACKSLASH,
KC_NONUS_HASH,
KC_SCOLON,
KC_SEMICOLON,
KC_QUOTE,
KC_GRAVE,
KC_COMMA,
KC_DOT,
KC_SLASH,
KC_CAPSLOCK,
KC_CAPS_LOCK,
KC_F1,
KC_F2,
KC_F3,
@ -288,20 +312,20 @@ enum hid_keyboard_keypad_usage {
KC_F10,
KC_F11,
KC_F12,
KC_PSCREEN,
KC_SCROLLLOCK,
KC_PRINT_SCREEN,
KC_SCROLL_LOCK,
KC_PAUSE,
KC_INSERT,
KC_HOME,
KC_PGUP,
KC_PAGE_UP,
KC_DELETE,
KC_END,
KC_PGDOWN,
KC_PAGE_DOWN,
KC_RIGHT,
KC_LEFT, // 0x50
KC_DOWN,
KC_UP,
KC_NUMLOCK,
KC_NUM_LOCK,
KC_KP_SLASH,
KC_KP_ASTERISK,
KC_KP_MINUS,
@ -318,9 +342,9 @@ enum hid_keyboard_keypad_usage {
KC_KP_9,
KC_KP_0,
KC_KP_DOT,
KC_NONUS_BSLASH,
KC_NONUS_BACKSLASH,
KC_APPLICATION,
KC_POWER,
KC_KB_POWER,
KC_KP_EQUAL,
KC_F13,
KC_F14,
@ -345,34 +369,34 @@ enum hid_keyboard_keypad_usage {
KC_COPY,
KC_PASTE,
KC_FIND,
KC__MUTE,
KC__VOLUP, // 0x80
KC__VOLDOWN,
KC_LOCKING_CAPS,
KC_LOCKING_NUM,
KC_LOCKING_SCROLL,
KC_KB_MUTE,
KC_KB_VOLUME_UP, // 0x80
KC_KB_VOLUME_DOWN,
KC_LOCKING_CAPS_LOCK,
KC_LOCKING_NUM_LOCK,
KC_LOCKING_SCROLL_LOCK,
KC_KP_COMMA,
KC_KP_EQUAL_AS400,
KC_INT1,
KC_INT2,
KC_INT3,
KC_INT4,
KC_INT5,
KC_INT6,
KC_INT7,
KC_INT8,
KC_INT9,
KC_LANG1, // 0x90
KC_LANG2,
KC_LANG3,
KC_LANG4,
KC_LANG5,
KC_LANG6,
KC_LANG7,
KC_LANG8,
KC_LANG9,
KC_ALT_ERASE,
KC_SYSREQ,
KC_INTERNATIONAL_1,
KC_INTERNATIONAL_2,
KC_INTERNATIONAL_3,
KC_INTERNATIONAL_4,
KC_INTERNATIONAL_5,
KC_INTERNATIONAL_6,
KC_INTERNATIONAL_7,
KC_INTERNATIONAL_8,
KC_INTERNATIONAL_9,
KC_LANGUAGE_1, // 0x90
KC_LANGUAGE_2,
KC_LANGUAGE_3,
KC_LANGUAGE_4,
KC_LANGUAGE_5,
KC_LANGUAGE_6,
KC_LANGUAGE_7,
KC_LANGUAGE_8,
KC_LANGUAGE_9,
KC_ALTERNATE_ERASE,
KC_SYSTEM_REQUEST,
KC_CANCEL,
KC_CLEAR,
KC_PRIOR,
@ -397,12 +421,12 @@ enum hid_keyboard_keypad_usage {
KC_DECIMAL_SEPARATOR,
KC_CURRENCY_UNIT,
KC_CURRENCY_SUB_UNIT,
KC_KP_LPAREN,
KC_KP_RPAREN,
KC_KP_LCBRACKET,
KC_KP_RCBRACKET,
KC_KP_LEFT_PARENTHESIS,
KC_KP_RIGHT_PARENTHESIS,
KC_KP_LEFT_BRACE,
KC_KP_RIGHT_BRACE,
KC_KP_TAB,
KC_KP_BSPACE,
KC_KP_BACKSPACE,
KC_KP_A,
KC_KP_B,
KC_KP_C,
@ -411,17 +435,17 @@ enum hid_keyboard_keypad_usage {
KC_KP_F,
KC_KP_XOR,
KC_KP_HAT,
KC_KP_PERC,
KC_KP_LT,
KC_KP_GT,
KC_KP_PERCENT,
KC_KP_LESS_THAN,
KC_KP_GREATER_THAN,
KC_KP_AND,
KC_KP_LAZYAND,
KC_KP_LAZY_AND,
KC_KP_OR,
KC_KP_LAZYOR,
KC_KP_LAZY_OR,
KC_KP_COLON,
KC_KP_HASH,
KC_KP_SPACE,
KC_KP_ATMARK,
KC_KP_AT,
KC_KP_EXCLAMATION,
KC_KP_MEM_STORE, //0xD0
KC_KP_MEM_RECALL,
@ -440,14 +464,14 @@ enum hid_keyboard_keypad_usage {
#endif
/* Modifiers */
KC_LCTRL = 0xE0,
KC_LSHIFT,
KC_LALT,
KC_LGUI,
KC_RCTRL,
KC_RSHIFT,
KC_RALT,
KC_RGUI
KC_LEFT_CTRL = 0xE0,
KC_LEFT_SHIFT,
KC_LEFT_ALT,
KC_LEFT_GUI,
KC_RIGHT_CTRL,
KC_RIGHT_SHIFT,
KC_RIGHT_ALT,
KC_RIGHT_GUI
// **********************************************
// * 0xF0-0xFF are unallocated in the HID spec. *
@ -558,3 +582,5 @@ enum mouse_keys {
KC_MS_ACCEL1,
KC_MS_ACCEL2 // 0xFF
};
#include "keycode_legacy.h"

View file

@ -25,89 +25,89 @@ extern keymap_config_t keymap_config;
*/
uint16_t keycode_config(uint16_t keycode) {
switch (keycode) {
case KC_CAPSLOCK:
case KC_LOCKING_CAPS:
case KC_CAPS_LOCK:
case KC_LOCKING_CAPS_LOCK:
if (keymap_config.swap_control_capslock || keymap_config.capslock_to_control) {
return KC_LCTL;
return KC_LEFT_CTRL;
}
return keycode;
case KC_LCTL:
case KC_LEFT_CTRL:
if (keymap_config.swap_control_capslock) {
return KC_CAPSLOCK;
return KC_CAPS_LOCK;
}
if (keymap_config.swap_lctl_lgui) {
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_LGUI;
return KC_LEFT_GUI;
}
return KC_LCTL;
case KC_LALT:
return KC_LEFT_CTRL;
case KC_LEFT_ALT:
if (keymap_config.swap_lalt_lgui) {
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_LGUI;
return KC_LEFT_GUI;
}
return KC_LALT;
case KC_LGUI:
return KC_LEFT_ALT;
case KC_LEFT_GUI:
if (keymap_config.swap_lalt_lgui) {
return KC_LALT;
return KC_LEFT_ALT;
}
if (keymap_config.swap_lctl_lgui) {
return KC_LCTRL;
return KC_LEFT_CTRL;
}
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_LGUI;
case KC_RCTL:
return KC_LEFT_GUI;
case KC_RIGHT_CTRL:
if (keymap_config.swap_rctl_rgui) {
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_RGUI;
return KC_RIGHT_GUI;
}
return KC_RCTL;
case KC_RALT:
return KC_RIGHT_CTRL;
case KC_RIGHT_ALT:
if (keymap_config.swap_ralt_rgui) {
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_RGUI;
return KC_RIGHT_GUI;
}
return KC_RALT;
case KC_RGUI:
return KC_RIGHT_ALT;
case KC_RIGHT_GUI:
if (keymap_config.swap_ralt_rgui) {
return KC_RALT;
return KC_RIGHT_ALT;
}
if (keymap_config.swap_rctl_rgui) {
return KC_RCTL;
return KC_RIGHT_CTRL;
}
if (keymap_config.no_gui) {
return KC_NO;
}
return KC_RGUI;
return KC_RIGHT_GUI;
case KC_GRAVE:
if (keymap_config.swap_grave_esc) {
return KC_ESC;
return KC_ESCAPE;
}
return KC_GRAVE;
case KC_ESC:
case KC_ESCAPE:
if (keymap_config.swap_grave_esc) {
return KC_GRAVE;
}
return KC_ESC;
case KC_BSLASH:
return KC_ESCAPE;
case KC_BACKSLASH:
if (keymap_config.swap_backslash_backspace) {
return KC_BSPACE;
return KC_BACKSPACE;
}
return KC_BSLASH;
case KC_BSPACE:
return KC_BACKSLASH;
case KC_BACKSPACE:
if (keymap_config.swap_backslash_backspace) {
return KC_BSLASH;
return KC_BACKSLASH;
}
return KC_BSPACE;
return KC_BACKSPACE;
default:
return keycode;
}

53
quantum/keycode_legacy.h Normal file
View file

@ -0,0 +1,53 @@
#pragma once
// clang-format off
// These keycode names have been deprecated
#define KC_BSPACE KC_BACKSPACE
#define KC_LBRACKET KC_LEFT_BRACKET
#define KC_RBRACKET KC_RIGHT_BRACKET
#define KC_BSLASH KC_BACKSLASH
#define KC_SCOLON KC_SEMICOLON
#define KC_CAPSLOCK KC_CAPS_LOCK
#define KC_PSCREEN KC_PRINT_SCREEN
#define KC_SCROLLLOCK KC_SCROLL_LOCK
#define KC_PGDOWN KC_PAGE_DOWN
#define KC_NUMLOCK KC_NUM_LOCK
#define KC_NONUS_BSLASH KC_NONUS_BACKSLASH
#define KC_POWER KC_KB_POWER
#define KC__MUTE KC_KB_MUTE
#define KC__VOLUP KC_KB_VOLUME_UP
#define KC__VOLDOWN KC_KB_VOLUME_DOWN
#define KC_LOCKING_CAPS KC_LOCKING_CAPS_LOCK
#define KC_LOCKING_NUM KC_LOCKING_NUM_LOCK
#define KC_LOCKING_SCROLL KC_LOCKING_SCROLL_LOCK
#define KC_LANG1 KC_LANGUAGE_1
#define KC_LANG2 KC_LANGUAGE_2
#define KC_LANG3 KC_LANGUAGE_3
#define KC_LANG4 KC_LANGUAGE_4
#define KC_LANG5 KC_LANGUAGE_5
#define KC_LANG6 KC_LANGUAGE_6
#define KC_LANG7 KC_LANGUAGE_7
#define KC_LANG8 KC_LANGUAGE_8
#define KC_LANG9 KC_LANGUAGE_9
#define KC_ALT_ERASE KC_ALTERNATE_ERASE
#define KC_SYSREQ KC_SYSTEM_REQUEST
#define KC_LCTRL KC_LEFT_CTRL
#define KC_LSHIFT KC_LEFT_SHIFT
#define KC_RCTRL KC_RIGHT_CTRL
#define KC_RSHIFT KC_RIGHT_SHIFT
#define KC_ZKHK KC_GRAVE
#define KC_RO KC_INTERNATIONAL_1
#define KC_KANA KC_INTERNATIONAL_2
#define KC_JYEN KC_INTERNATIONAL_3
#define KC_HENK KC_INTERNATIONAL_4
#define KC_MHEN KC_INTERNATIONAL_5
#define KC_HAEN KC_LANGUAGE_1
#define KC_HANJ KC_LANGUAGE_2
#define KC_CLCK KC_CAPS_LOCK
#define KC_SLCK KC_SCROLL_LOCK
#define KC_NLCK KC_NUM_LOCK

View file

@ -56,7 +56,7 @@ action_t action_for_keycode(uint16_t keycode) {
switch (keycode) {
case KC_A ... KC_EXSEL:
case KC_LCTRL ... KC_RGUI:
case KC_LEFT_CTRL ... KC_RIGHT_GUI:
action.code = ACTION_KEY(keycode);
break;
#ifdef EXTRAKEY_ENABLE
@ -72,7 +72,7 @@ action_t action_for_keycode(uint16_t keycode) {
action.code = ACTION_MOUSEKEY(keycode);
break;
#endif
case KC_TRNS:
case KC_TRANSPARENT:
action.code = ACTION_TRANSPARENT;
break;
case QK_MODS ... QK_MODS_MAX:;

View file

@ -85,8 +85,8 @@
#define KR_DOT KC_DOT // .
#define KR_SLSH KC_SLSH // /
// Row 5
#define KR_HANJ KC_LANG2 // Hanja (한자)
#define KR_HAEN KC_LANG1 // Han ↔ Yeong (한 ↔ 영)
#define KR_HANJ KC_LNG2 // Hanja (한자)
#define KR_HAEN KC_LNG1 // Han ↔ Yeong (한 ↔ 영)
/* Shifted symbols
*

View file

@ -74,8 +74,7 @@ enum steno_keycodes {
};
#ifdef STENO_COMBINEDMAP
enum steno_combined_keycodes
{
enum steno_combined_keycodes {
STN_S3 = QK_STENO_COMB,
STN_TKL,
STN_PWL,

View file

@ -111,7 +111,7 @@
#define TR_LPRN S(TR_8) // (
#define TR_RPRN S(TR_9) // )
#define TR_EQL S(TR_0) // =
#define TR_QUES S(TR_ASTR) // ?
#define TR_QUES S(TR_SLSH) // ?
#define TR_UNDS S(TR_MINS) // _
// Row 4
#define TR_RABK S(TR_LABK) // >

View file

@ -17,8 +17,8 @@ bool ALPHAS_MODS(effect_params_t* params) {
led_matrix_set_value(i, val1);
}
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_ALPHAS_MODS
#endif // ENABLE_LED_MATRIX_ALPHAS_MODS

View file

@ -10,4 +10,4 @@ static uint8_t BAND_math(uint8_t val, uint8_t i, uint8_t time) {
bool BAND(effect_params_t* params) { return effect_runner_i(params, &BAND_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_BAND
#endif // ENABLE_LED_MATRIX_BAND

View file

@ -7,4 +7,4 @@ static uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t t
bool BAND_PINWHEEL(effect_params_t* params) { return effect_runner_dx_dy(params, &BAND_PINWHEEL_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_BAND_PINWHEEL
#endif // ENABLE_LED_MATRIX_BAND_PINWHEEL

View file

@ -7,4 +7,4 @@ static uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dis
bool BAND_SPIRAL(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_BAND_SPIRAL
#endif // ENABLE_LED_MATRIX_BAND_SPIRAL

View file

@ -12,8 +12,8 @@ bool BREATHING(effect_params_t* params) {
LED_MATRIX_TEST_LED_FLAGS();
led_matrix_set_value(i, val);
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_BREATHING
#endif // ENABLE_LED_MATRIX_BREATHING

View file

@ -7,4 +7,4 @@ static uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { ret
bool CYCLE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
#endif // ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT

View file

@ -7,4 +7,4 @@ static uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t di
bool CYCLE_OUT_IN(effect_params_t* params) { return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_CYCLE_OUT_IN
#endif // ENABLE_LED_MATRIX_CYCLE_OUT_IN

View file

@ -7,4 +7,4 @@ static uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
bool CYCLE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &CYCLE_UP_DOWN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_CYCLE_UP_DOWN
#endif // ENABLE_LED_MATRIX_CYCLE_UP_DOWN

View file

@ -7,4 +7,4 @@ static uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i,
bool DUAL_BEACON(effect_params_t* params) { return effect_runner_sin_cos_i(params, &DUAL_BEACON_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_DUAL_BEACON
#endif // ENABLE_LED_MATRIX_DUAL_BEACON

View file

@ -12,5 +12,5 @@ bool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) {
int16_t dy = g_led_config.point[i].y - k_led_matrix_center.y;
led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, time));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}

View file

@ -13,5 +13,5 @@ bool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func)
uint8_t dist = sqrt16(dx * dx + dy * dy);
led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, dist, time));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}

View file

@ -10,5 +10,5 @@ bool effect_runner_i(effect_params_t* params, i_f effect_func) {
LED_MATRIX_TEST_LED_FLAGS();
led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, i, time));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}

View file

@ -22,7 +22,7 @@ bool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) {
uint16_t offset = scale16by8(tick, led_matrix_eeconfig.speed);
led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, offset));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -20,7 +20,7 @@ bool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, react
}
led_matrix_set_value(i, scale8(val, led_matrix_eeconfig.val));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -12,5 +12,5 @@ bool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) {
LED_MATRIX_TEST_LED_FLAGS();
led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, cos_value, sin_value, i, time));
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}

View file

@ -9,7 +9,7 @@ bool SOLID(effect_params_t* params) {
LED_MATRIX_TEST_LED_FLAGS();
led_matrix_set_value(i, val);
}
return led_max < DRIVER_LED_TOTAL;
return led_matrix_check_finished_leds(led_max);
}
#endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS

View file

@ -1,5 +1,5 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
# if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
LED_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)
@ -31,5 +31,5 @@ bool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) { return effect_runner_r
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
# endif // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -28,5 +28,5 @@ bool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) { return effect_runner_r
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
# endif // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -8,5 +8,5 @@ static uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) { return
bool SOLID_REACTIVE_SIMPLE(effect_params_t* params) { return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
# endif // DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
# endif // ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -1,5 +1,5 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
# if !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
# ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
LED_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)
@ -26,5 +26,5 @@ bool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) { return effect_runner_re
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
# endif // !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
# endif // !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || !defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -1,5 +1,5 @@
#ifdef LED_MATRIX_KEYREACTIVE_ENABLED
# if !defined(DISABLE_LED_MATRIX_SOLID_SPLASH) || !defined(DISABLE_LED_MATRIX_SOLID_MULTISPLASH)
# if defined(ENABLE_LED_MATRIX_SOLID_SPLASH) || defined(ENABLE_LED_MATRIX_SOLID_MULTISPLASH)
# ifdef ENABLE_LED_MATRIX_SOLID_SPLASH
LED_MATRIX_EFFECT(SOLID_SPLASH)
@ -26,5 +26,5 @@ bool SOLID_MULTISPLASH(effect_params_t* params) { return effect_runner_reactive_
# endif
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
# endif // !defined(DISABLE_LED_MATRIX_SPLASH) && !defined(DISABLE_LED_MATRIX_MULTISPLASH)
# endif // defined(ENABLE_LED_MATRIX_SPLASH) || defined(ENABLE_LED_MATRIX_MULTISPLASH)
#endif // LED_MATRIX_KEYREACTIVE_ENABLED

View file

@ -7,4 +7,4 @@ static uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) { retu
bool WAVE_LEFT_RIGHT(effect_params_t* params) { return effect_runner_i(params, &WAVE_LEFT_RIGHT_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
#endif // ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT

View file

@ -7,4 +7,4 @@ static uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) { return
bool WAVE_UP_DOWN(effect_params_t* params) { return effect_runner_i(params, &WAVE_UP_DOWN_math); }
# endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS
#endif // DISABLE_LED_MATRIX_WAVE_UP_DOWN
#endif // ENABLE_LED_MATRIX_WAVE_UP_DOWN

View file

@ -33,14 +33,6 @@ const led_point_t k_led_matrix_center = {112, 32};
const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
#endif
// clang-format off
#ifndef LED_MATRIX_IMMEDIATE_EEPROM
# define led_eeconfig_update(v) led_update_eeprom |= v
#else
# define led_eeconfig_update(v) if (v) eeconfig_update_led_matrix()
#endif
// clang-format on
// Generic effect runners
#include "led_matrix_runners.inc"
@ -107,7 +99,6 @@ last_hit_t g_last_hit_tracker;
// internals
static bool suspend_state = false;
static bool led_update_eeprom = false;
static uint8_t led_last_enable = UINT8_MAX;
static uint8_t led_last_effect = UINT8_MAX;
static effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};
@ -127,9 +118,9 @@ static last_hit_t last_hit_buffer;
const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
#endif
void eeconfig_read_led_matrix(void) { eeprom_read_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); }
EECONFIG_DEBOUNCE_HELPER(led_matrix, EECONFIG_LED_MATRIX, led_matrix_eeconfig);
void eeconfig_update_led_matrix(void) { eeprom_update_block(&led_matrix_eeconfig, EECONFIG_LED_MATRIX, sizeof(led_matrix_eeconfig)); }
void eeconfig_update_led_matrix(void) { eeconfig_flush_led_matrix(true); }
void eeconfig_update_led_matrix_default(void) {
dprintf("eeconfig_update_led_matrix_default\n");
@ -138,7 +129,7 @@ void eeconfig_update_led_matrix_default(void) {
led_matrix_eeconfig.val = LED_MATRIX_STARTUP_VAL;
led_matrix_eeconfig.speed = LED_MATRIX_STARTUP_SPD;
led_matrix_eeconfig.flags = LED_FLAG_ALL;
eeconfig_update_led_matrix();
eeconfig_flush_led_matrix(true);
}
void eeconfig_debug_led_matrix(void) {
@ -165,20 +156,10 @@ uint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *l
void led_matrix_update_pwm_buffers(void) { led_matrix_driver.flush(); }
void led_matrix_set_value(int index, uint8_t value) {
#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
if (!is_keyboard_left() && index >= k_led_matrix_split[0])
# ifdef USE_CIE1931_CURVE
led_matrix_driver.set_value(index - k_led_matrix_split[0], pgm_read_byte(&CIE1931_CURVE[value]));
# else
led_matrix_driver.set_value(index - k_led_matrix_split[0], value);
# endif
else if (is_keyboard_left() && index < k_led_matrix_split[0])
#endif
#ifdef USE_CIE1931_CURVE
led_matrix_driver.set_value(index, pgm_read_byte(&CIE1931_CURVE[value]));
#else
led_matrix_driver.set_value(index, value);
value = pgm_read_byte(&CIE1931_CURVE[value]);
#endif
led_matrix_driver.set_value(index, value);
}
void led_matrix_set_value_all(uint8_t value) {
@ -232,11 +213,11 @@ void process_led_matrix(uint8_t row, uint8_t col, bool pressed) {
}
#endif // LED_MATRIX_KEYREACTIVE_ENABLED
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_LED_MATRIX_TYPING_HEATMAP)
#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
if (led_matrix_eeconfig.mode == LED_MATRIX_TYPING_HEATMAP) {
process_led_matrix_typing_heatmap(row, col);
}
#endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(DISABLE_LED_MATRIX_TYPING_HEATMAP)
#endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)
}
static bool led_matrix_none(effect_params_t *params) {
@ -279,9 +260,8 @@ static void led_task_timers(void) {
}
static void led_task_sync(void) {
eeconfig_flush_led_matrix(false);
// next task
if (led_update_eeprom) eeconfig_update_led_matrix();
led_update_eeprom = false;
if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;
}
@ -449,7 +429,7 @@ void led_matrix_init(void) {
eeconfig_update_led_matrix_default();
}
eeconfig_read_led_matrix();
eeconfig_init_led_matrix();
if (!led_matrix_eeconfig.mode) {
dprintf("led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\n");
eeconfig_update_led_matrix_default();
@ -472,7 +452,7 @@ bool led_matrix_get_suspend_state(void) { return suspend_state; }
void led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {
led_matrix_eeconfig.enable ^= 1;
led_task_state = STARTING;
led_eeconfig_update(write_to_eeprom);
eeconfig_flag_led_matrix(write_to_eeprom);
dprintf("led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.enable);
}
void led_matrix_toggle_noeeprom(void) { led_matrix_toggle_eeprom_helper(false); }
@ -480,7 +460,7 @@ void led_matrix_toggle(void) { led_matrix_toggle_eeprom_helper(true); }
void led_matrix_enable(void) {
led_matrix_enable_noeeprom();
led_eeconfig_update(true);
eeconfig_flag_led_matrix(true);
}
void led_matrix_enable_noeeprom(void) {
@ -490,7 +470,7 @@ void led_matrix_enable_noeeprom(void) {
void led_matrix_disable(void) {
led_matrix_disable_noeeprom();
led_eeconfig_update(true);
eeconfig_flag_led_matrix(true);
}
void led_matrix_disable_noeeprom(void) {
@ -512,7 +492,7 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
led_matrix_eeconfig.mode = mode;
}
led_task_state = STARTING;
led_eeconfig_update(write_to_eeprom);
eeconfig_flag_led_matrix(write_to_eeprom);
dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode);
}
void led_matrix_mode_noeeprom(uint8_t mode) { led_matrix_mode_eeprom_helper(mode, false); }
@ -539,7 +519,7 @@ void led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {
return;
}
led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;
led_eeconfig_update(write_to_eeprom);
eeconfig_flag_led_matrix(write_to_eeprom);
dprintf("led matrix set val [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.val);
}
void led_matrix_set_val_noeeprom(uint8_t val) { led_matrix_set_val_eeprom_helper(val, false); }
@ -557,7 +537,7 @@ void led_matrix_decrease_val(void) { led_matrix_decrease_val_helper(true); }
void led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
led_matrix_eeconfig.speed = speed;
led_eeconfig_update(write_to_eeprom);
eeconfig_flag_led_matrix(write_to_eeprom);
dprintf("led matrix set speed [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.speed);
}
void led_matrix_set_speed_noeeprom(uint8_t speed) { led_matrix_set_speed_eeprom_helper(speed, false); }

View file

@ -23,7 +23,6 @@
#include <stdbool.h>
#include "led_matrix_types.h"
#include "quantum.h"
#include "led_matrix_legacy_enables.h"
#ifdef IS31FL3731
# include "is31fl3731-simple.h"
@ -38,14 +37,33 @@
#endif
#if defined(LED_MATRIX_LED_PROCESS_LIMIT) && LED_MATRIX_LED_PROCESS_LIMIT > 0 && LED_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter; \
uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT; \
if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
# if defined(LED_MATRIX_SPLIT)
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter; \
uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT; \
if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL; \
uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT; \
if (is_keyboard_left() && (max > k_led_matrix_split[0])) max = k_led_matrix_split[0]; \
if (!(is_keyboard_left()) && (min < k_led_matrix_split[0])) min = k_led_matrix_split[0];
# else
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = LED_MATRIX_LED_PROCESS_LIMIT * params->iter; \
uint8_t max = min + LED_MATRIX_LED_PROCESS_LIMIT; \
if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
# endif
#else
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = 0; \
uint8_t max = DRIVER_LED_TOTAL;
# if defined(LED_MATRIX_SPLIT)
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = 0; \
uint8_t max = DRIVER_LED_TOTAL; \
const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT; \
if (is_keyboard_left() && (max > k_led_matrix_split[0])) max = k_led_matrix_split[0]; \
if (!(is_keyboard_left()) && (min < k_led_matrix_split[0])) min = k_led_matrix_split[0];
# else
# define LED_MATRIX_USE_LIMITS(min, max) \
uint8_t min = 0; \
uint8_t max = DRIVER_LED_TOTAL;
# endif
#endif
#define LED_MATRIX_TEST_LED_FLAGS() \
@ -147,6 +165,18 @@ typedef struct {
void (*flush)(void);
} led_matrix_driver_t;
static inline bool led_matrix_check_finished_leds(uint8_t led_idx) {
#if defined(LED_MATRIX_SPLIT)
if (is_keyboard_left()) {
uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
return led_idx < k_led_matrix_split[0];
} else
return led_idx < DRIVER_LED_TOTAL;
#else
return led_idx < DRIVER_LED_TOTAL;
#endif
}
extern const led_matrix_driver_t led_matrix_driver;
extern led_eeconfig_t led_matrix_eeconfig;

View file

@ -26,128 +26,123 @@
*/
#if defined(IS31FL3731) || defined(IS31FL3733)
# include "i2c_master.h"
static void init(void) {
i2c_init();
# ifdef IS31FL3731
# ifdef LED_DRIVER_ADDR_1
# if defined(IS31FL3731)
IS31FL3731_init(LED_DRIVER_ADDR_1);
# endif
# ifdef LED_DRIVER_ADDR_2
# if defined(LED_DRIVER_ADDR_2)
IS31FL3731_init(LED_DRIVER_ADDR_2);
# endif
# ifdef LED_DRIVER_ADDR_3
# if defined(LED_DRIVER_ADDR_3)
IS31FL3731_init(LED_DRIVER_ADDR_3);
# endif
# ifdef LED_DRIVER_ADDR_4
# if defined(LED_DRIVER_ADDR_4)
IS31FL3731_init(LED_DRIVER_ADDR_4);
# endif
# else
# ifdef LED_DRIVER_ADDR_1
# ifndef LED_DRIVER_SYNC_1
# define LED_DRIVER_SYNC_1 0
# endif
# endif
IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
# endif
# ifdef LED_DRIVER_ADDR_2
# ifndef LED_DRIVER_SYNC_2
# elif defined(IS31FL3733)
# if !defined(LED_DRIVER_SYNC_1)
# define LED_DRIVER_SYNC_1 0
# endif
IS31FL3733_init(LED_DRIVER_ADDR_1, LED_DRIVER_SYNC_1);
# if defined(LED_DRIVER_ADDR_2)
# if !defined(LED_DRIVER_SYNC_2)
# define LED_DRIVER_SYNC_2 0
# endif
IS31FL3733_init(LED_DRIVER_ADDR_2, LED_DRIVER_SYNC_2);
# endif
# ifdef LED_DRIVER_ADDR_3
# ifndef LED_DRIVER_SYNC_3
# define LED_DRIVER_SYNC_3 0
# endif
# if defined(LED_DRIVER_ADDR_3)
# if !defined(LED_DRIVER_SYNC_3)
# define LED_DRIVER_SYNC_3 0
# endif
IS31FL3733_init(LED_DRIVER_ADDR_3, LED_DRIVER_SYNC_3);
# endif
# ifdef LED_DRIVER_ADDR_4
# ifndef LED_DRIVER_SYNC_4
# define LED_DRIVER_SYNC_4 0
# endif
# if defined(LED_DRIVER_ADDR_4)
# if !defined(LED_DRIVER_SYNC_4)
# define LED_DRIVER_SYNC_4 0
# endif
IS31FL3733_init(LED_DRIVER_ADDR_4, LED_DRIVER_SYNC_4);
# endif
# endif
# endif
# endif
for (int index = 0; index < DRIVER_LED_TOTAL; index++) {
# ifdef IS31FL3731
# if defined(IS31FL3731)
IS31FL3731_set_led_control_register(index, true);
# else
# elif defined(IS31FL3733)
IS31FL3733_set_led_control_register(index, true);
# endif
}
// This actually updates the LED drivers
# ifdef IS31FL3731
# ifdef LED_DRIVER_ADDR_1
# if defined(IS31FL3731)
IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
# endif
# ifdef LED_DRIVER_ADDR_2
# if defined(LED_DRIVER_ADDR_2)
IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
# endif
# ifdef LED_DRIVER_ADDR_3
# if defined(LED_DRIVER_ADDR_3)
IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
# endif
# ifdef LED_DRIVER_ADDR_4
# if defined(LED_DRIVER_ADDR_4)
IS31FL3731_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
# endif
# endif
# endif
# else
# ifdef LED_DRIVER_ADDR_1
# elif defined(IS31FL3733)
IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_1, 0);
# endif
# ifdef LED_DRIVER_ADDR_2
# if defined(LED_DRIVER_ADDR_2)
IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_2, 1);
# endif
# ifdef LED_DRIVER_ADDR_3
# if defined(LED_DRIVER_ADDR_3)
IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_3, 2);
# endif
# ifdef LED_DRIVER_ADDR_4
# if defined(LED_DRIVER_ADDR_4)
IS31FL3733_update_led_control_registers(LED_DRIVER_ADDR_4, 3);
# endif
# endif
# endif
# endif
}
# if defined(IS31FL3731)
static void flush(void) {
# ifdef IS31FL3731
# ifdef LED_DRIVER_ADDR_1
IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
# endif
# ifdef LED_DRIVER_ADDR_2
# if defined(LED_DRIVER_ADDR_2)
IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
# endif
# ifdef LED_DRIVER_ADDR_3
# if defined(LED_DRIVER_ADDR_3)
IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
# endif
# ifdef LED_DRIVER_ADDR_4
# if defined(LED_DRIVER_ADDR_4)
IS31FL3731_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
# endif
# endif
# endif
# else
# ifdef LED_DRIVER_ADDR_1
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
# endif
# ifdef LED_DRIVER_ADDR_2
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
# endif
# ifdef LED_DRIVER_ADDR_3
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
# endif
# ifdef LED_DRIVER_ADDR_4
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
# endif
# endif
}
const led_matrix_driver_t led_matrix_driver = {
.init = init,
.flush = flush,
# ifdef IS31FL3731
.init = init,
.flush = flush,
.set_value = IS31FL3731_set_value,
.set_value_all = IS31FL3731_set_value_all,
# else
.set_value = IS31FL3733_set_value,
.set_value_all = IS31FL3733_set_value_all,
# endif
};
# elif defined(IS31FL3733)
static void flush(void) {
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_1, 0);
# if defined(LED_DRIVER_ADDR_2)
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_2, 1);
# if defined(LED_DRIVER_ADDR_3)
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_3, 2);
# if defined(LED_DRIVER_ADDR_4)
IS31FL3733_update_pwm_buffers(LED_DRIVER_ADDR_4, 3);
# endif
# endif
# endif
}
const led_matrix_driver_t led_matrix_driver = {
.init = init,
.flush = flush,
.set_value = IS31FL3733_set_value,
.set_value_all = IS31FL3733_set_value_all,
};
# endif
#endif

View file

@ -1,82 +0,0 @@
/* Copyright 2021 QMK
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// to-do: remove this
#pragma once
#ifndef DISABLE_LED_MATRIX_ALPHAS_MODS
# define ENABLE_LED_MATRIX_ALPHAS_MODS
#endif
#ifndef DISABLE_LED_MATRIX_BREATHING
# define ENABLE_LED_MATRIX_BREATHING
#endif
#ifndef DISABLE_LED_MATRIX_BAND
# define ENABLE_LED_MATRIX_BAND
#endif
#ifndef DISABLE_LED_MATRIX_BAND_PINWHEEL
# define ENABLE_LED_MATRIX_BAND_PINWHEEL
#endif
#ifndef DISABLE_LED_MATRIX_BAND_SPIRAL
# define ENABLE_LED_MATRIX_BAND_SPIRAL
#endif
#ifndef DISABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
# define ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT
#endif
#ifndef DISABLE_LED_MATRIX_CYCLE_UP_DOWN
# define ENABLE_LED_MATRIX_CYCLE_UP_DOWN
#endif
#ifndef DISABLE_LED_MATRIX_CYCLE_OUT_IN
# define ENABLE_LED_MATRIX_CYCLE_OUT_IN
#endif
#ifndef DISABLE_LED_MATRIX_DUAL_BEACON
# define ENABLE_LED_MATRIX_DUAL_BEACON
#endif
#if defined(LED_MATRIX_KEYREACTIVE_ENABLED)
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS
# endif
# ifndef DISABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
# define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS
# endif
# ifndef DISABLE_LED_MATRIX_SPLASH
# define ENABLE_LED_MATRIX_SPLASH
# endif
# ifndef DISABLE_LED_MATRIX_MULTISPLASH
# define ENABLE_LED_MATRIX_MULTISPLASH
# endif
#endif
#ifndef DISABLE_LED_MATRIX_WAVE_LEFT_RIGHT
# define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT
#endif
#ifndef DISABLE_LED_MATRIX_WAVE_UP_DOWN
# define ENABLE_LED_MATRIX_WAVE_UP_DOWN
#endif

View file

@ -19,8 +19,33 @@
void platform_setup(void);
void protocol_setup(void);
void protocol_init(void);
void protocol_task(void);
void protocol_pre_init(void);
void protocol_post_init(void);
void protocol_pre_task(void);
void protocol_post_task(void);
// Bodge as refactoring this area sucks....
void protocol_init(void) __attribute__((weak));
void protocol_init(void) {
protocol_pre_init();
keyboard_init();
protocol_post_init();
}
void protocol_task(void) __attribute__((weak));
void protocol_task(void) {
protocol_pre_task();
keyboard_task();
protocol_post_task();
}
#ifdef DEFERRED_EXEC_ENABLE
void deferred_exec_task(void);
#endif // DEFERRED_EXEC_ENABLE
/** \brief Main
*
@ -30,12 +55,19 @@ int main(void) __attribute__((weak));
int main(void) {
platform_setup();
protocol_setup();
keyboard_setup();
protocol_init();
/* Main loop */
while (true) {
protocol_task();
#ifdef DEFERRED_EXEC_ENABLE
// Run deferred executions
deferred_exec_task();
#endif // DEFERRED_EXEC_ENABLE
housekeeping_task();
}
}

View file

@ -69,7 +69,7 @@ uint8_t thisHand, thatHand;
// user-defined overridable functions
__attribute__((weak)) void matrix_init_pins(void);
__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter);
#ifdef SPLIT_KEYBOARD
__attribute__((weak)) void matrix_slave_scan_kb(void) { matrix_slave_scan_user(); }
__attribute__((weak)) void matrix_slave_scan_user(void) {}
@ -113,10 +113,11 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[]
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
pin_t pin = direct_pins[current_row][col_index];
if (pin != NO_PIN) {
current_row_value |= readPin(pin) ? 0 : (MATRIX_ROW_SHIFTER << col_index);
current_row_value |= readPin(pin) ? 0 : row_shifter;
}
}
@ -169,11 +170,12 @@ __attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[]
matrix_output_select_delay();
// For each col...
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {
uint8_t pin_state = readMatrixPin(col_pins[col_index]);
// Populate the matrix row with the state of the col pin
current_row_value |= pin_state ? 0 : (MATRIX_ROW_SHIFTER << col_index);
current_row_value |= pin_state ? 0 : row_shifter;
}
// Unselect row
@ -217,7 +219,7 @@ __attribute__((weak)) void matrix_init_pins(void) {
}
}
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) {
bool key_pressed = false;
// Select col
@ -231,11 +233,11 @@ __attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[]
// Check row pin state
if (readMatrixPin(row_pins[row_index]) == 0) {
// Pin LO, set col bit
current_matrix[row_index] |= (MATRIX_ROW_SHIFTER << current_col);
current_matrix[row_index] |= row_shifter;
key_pressed = true;
} else {
// Pin HI, clear col bit
current_matrix[row_index] &= ~(MATRIX_ROW_SHIFTER << current_col);
current_matrix[row_index] &= ~row_shifter;
}
}
@ -288,10 +290,8 @@ void matrix_init(void) {
matrix_init_pins();
// initialize matrix state: all keys off
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
raw_matrix[i] = 0;
matrix[i] = 0;
}
memset(matrix, 0, sizeof(matrix));
memset(raw_matrix, 0, sizeof(raw_matrix));
debounce_init(ROWS_PER_HAND);
@ -312,24 +312,22 @@ __attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_mat
bool matrix_post_scan(void) {
bool changed = false;
if (is_keyboard_master()) {
static bool last_connected = false;
matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {
for (int i = 0; i < ROWS_PER_HAND; ++i) {
if (matrix[thatHand + i] != slave_matrix[i]) {
matrix[thatHand + i] = slave_matrix[i];
changed = true;
}
}
} else {
// reset other half if disconnected
for (int i = 0; i < ROWS_PER_HAND; ++i) {
matrix[thatHand + i] = 0;
slave_matrix[i] = 0;
}
changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;
last_connected = true;
} else if (last_connected) {
// reset other half when disconnected
memset(slave_matrix, 0, sizeof(slave_matrix));
changed = true;
last_connected = false;
}
if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));
matrix_scan_quantum();
} else {
transport_slave(matrix + thatHand, matrix + thisHand);
@ -351,8 +349,9 @@ uint8_t matrix_scan(void) {
}
#elif (DIODE_DIRECTION == ROW2COL)
// Set col, read rows
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
matrix_read_rows_on_col(curr_matrix, current_col);
matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {
matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);
}
#endif

View file

@ -1,601 +0,0 @@
MCU_ORIG := $(MCU)
ifneq ($(findstring MKL26Z64, $(MCU)),)
# Cortex version
MCU = cortex-m0plus
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 6
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = KINETIS
MCU_SERIES = KL2x
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= MKL26Z64
# Startup code to use
# - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= kl2x
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= PJRC_TEENSY_LC
endif
ifneq ($(findstring MK20DX128, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = KINETIS
MCU_SERIES = K20x
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= MK20DX128
# Startup code to use
# - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= k20x5
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= PJRC_TEENSY_3
endif
ifneq ($(findstring MK20DX256, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = KINETIS
MCU_SERIES = K20x
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= MK20DX256
# Startup code to use
# - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= k20x7
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= PJRC_TEENSY_3_1
endif
ifneq ($(findstring MK66F18, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = KINETIS
MCU_SERIES = MK66F18
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= MK66FX1M0
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= MK66F18
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= PJRC_TEENSY_3_6
endif
ifneq ($(findstring STM32F042, $(MCU)),)
# Cortex version
MCU = cortex-m0
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 6
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F0xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F042x6
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f0xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F042X6
USE_FPU ?= no
# UF2 settings
UF2_FAMILY ?= STM32F0
endif
ifneq ($(findstring STM32F072, $(MCU)),)
# Cortex version
MCU = cortex-m0
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 6
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F0xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F072xB
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f0xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F072XB
USE_FPU ?= no
# UF2 settings
UF2_FAMILY ?= STM32F0
endif
ifneq ($(findstring STM32F103, $(MCU)),)
# Cortex version
MCU = cortex-m3
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F1xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F103x8
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f1xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F103
USE_FPU ?= no
# UF2 settings
UF2_FAMILY ?= STM32F1
endif
ifneq ($(findstring STM32F303, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F3xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F303xC
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f3xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F303XC
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32F3
endif
ifneq ($(findstring STM32F401, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
ifeq ($(strip $(BOOTLOADER)), tinyuf2)
MCU_LDSCRIPT ?= STM32F401xC_tinyuf2
FIRMWARE_FORMAT ?= uf2
else
MCU_LDSCRIPT ?= STM32F401xC
endif
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= BLACKPILL_STM32_F401
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32F4
endif
ifneq ($(findstring STM32F407, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F407xE
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F407XE
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32F4
endif
ifneq ($(findstring STM32F411, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
ifeq ($(strip $(BOOTLOADER)), tinyuf2)
MCU_LDSCRIPT ?= STM32F411xE_tinyuf2
FIRMWARE_FORMAT ?= uf2
else
MCU_LDSCRIPT ?= STM32F411xE
endif
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= BLACKPILL_STM32_F411
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32F4
endif
ifneq ($(findstring STM32F446, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32F4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32F446xE
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32f4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_F446XE
USE_FPU ?= yes
endif
ifneq ($(findstring STM32G431, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32G4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32G431xB
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32g4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_G431XB
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32G4
endif
ifneq ($(findstring STM32G474, $(MCU)),)
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32G4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32G474xE
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32g4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_G474XE
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32G4
endif
ifneq (,$(filter $(MCU),STM32L433 STM32L443))
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32L4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32L432xC
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32l4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_L433XC
PLATFORM_NAME ?= platform_l432
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32L4
endif
ifneq (,$(filter $(MCU),STM32L412 STM32L422))
# Cortex version
MCU = cortex-m4
# ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
ARMV = 7
## chip/board settings
# - the next two should match the directories in
# <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
MCU_FAMILY = STM32
MCU_SERIES = STM32L4xx
# Linker script to use
# - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
# or <keyboard_dir>/ld/
MCU_LDSCRIPT ?= STM32L412xB
# Startup code to use
# - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
MCU_STARTUP ?= stm32l4xx
# Board: it should exist either in <chibios>/os/hal/boards/,
# <keyboard_dir>/boards/, or drivers/boards/
BOARD ?= GENERIC_STM32_L412XB
PLATFORM_NAME ?= platform_l432
USE_FPU ?= yes
# UF2 settings
UF2_FAMILY ?= STM32L4
endif
ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
PROTOCOL = LUFA
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
#
# This will be an integer division of F_USB below, as it is sourced by
# F_USB after it has run through any CPU prescalers. Note that this value
# does not *change* the processor frequency - it should merely be updated to
# reflect the processor speed set externally so that the code can use accurate
# software delays.
F_CPU ?= 16000000
# LUFA specific
#
# Target architecture (see library "Board Types" documentation).
ARCH = AVR8
# Input clock frequency.
# This will define a symbol, F_USB, in all source code files equal to the
# input clock frequency (before any prescaling is performed) in Hz. This value may
# differ from F_CPU if prescaling is used on the latter, and is required as the
# raw input clock is fed directly to the PLL sections of the AVR for high speed
# clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
# at the end, this will be done automatically to create a 32-bit value in your
# source code.
#
# If no clock division is performed on the input clock inside the AVR (via the
# CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
F_USB ?= $(F_CPU)
# Interrupt driven control endpoint task
ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
endif
ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2))
NO_I2C = yes
endif
endif
ifneq (,$(filter $(MCU),atmega32a))
# MCU name for avrdude
AVRDUDE_MCU = m32
PROTOCOL = VUSB
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
F_CPU ?= 12000000
endif
ifneq (,$(filter $(MCU),atmega328p))
# MCU name for avrdude
AVRDUDE_MCU = m328p
PROTOCOL = VUSB
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
F_CPU ?= 16000000
endif
ifneq (,$(filter $(MCU),atmega328))
# MCU name for avrdude
AVRDUDE_MCU = m328
PROTOCOL = VUSB
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
F_CPU ?= 16000000
endif
ifneq (,$(filter $(MCU),attiny85))
PROTOCOL = VUSB
# Processor frequency.
# This will define a symbol, F_CPU, in all source code files equal to the
# processor frequency in Hz. You can then use this symbol in your source code to
# calculate timings. Do NOT tack on a 'UL' at the end, this will be done
# automatically to create a 32-bit value in your source code.
F_CPU ?= 16500000
endif

View file

@ -1,34 +1,57 @@
/*
Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
/* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2021 Dasky (@daskygit)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "report.h"
#include "host.h"
#include "timer.h"
#include "print.h"
#include "debug.h"
#include "pointing_device.h"
#include <string.h>
#ifdef MOUSEKEY_ENABLE
# include "mousekey.h"
#endif
#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
# error More than one rotation selected. This is not supported.
#endif
static report_mouse_t mouseReport = {};
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return (new.buttons != old.buttons) || (new.x&& new.x != old.x) || (new.y&& new.y != old.y) || (new.h&& new.h != old.h) || (new.v&& new.v != old.v); }
extern const pointing_device_driver_t pointing_device_driver;
__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old) { return memcmp(&new, &old, sizeof(new)); }
__attribute__((weak)) void pointing_device_init_kb(void) {}
__attribute__((weak)) void pointing_device_init_user(void) {}
__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) { return pointing_device_task_user(mouse_report); }
__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) { return mouse_report; }
__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {
if (pressed) {
buttons |= 1 << (button);
} else {
buttons &= ~(1 << (button));
}
return buttons;
}
__attribute__((weak)) void pointing_device_init(void) {
// initialize device, if that needs to be done.
pointing_device_driver.init();
#ifdef POINTING_DEVICE_MOTION_PIN
setPinInputHigh(POINTING_DEVICE_MOTION_PIN);
#endif
pointing_device_init_kb();
pointing_device_init_user();
}
__attribute__((weak)) void pointing_device_send(void) {
@ -43,20 +66,55 @@ __attribute__((weak)) void pointing_device_send(void) {
mouseReport.y = 0;
mouseReport.v = 0;
mouseReport.h = 0;
old_report = mouseReport;
memcpy(&old_report, &mouseReport, sizeof(mouseReport));
}
__attribute__((weak)) void pointing_device_task(void) {
// gather info and put it in:
// mouseReport.x = 127 max -127 min
// mouseReport.y = 127 max -127 min
// mouseReport.v = 127 max -127 min (scroll vertical)
// mouseReport.h = 127 max -127 min (scroll horizontal)
// mouseReport.buttons = 0x1F (decimal 31, binary 00011111) max (bitmask for mouse buttons 1-5, 1 is rightmost, 5 is leftmost) 0x00 min
// send the report
// Gather report info
#ifdef POINTING_DEVICE_MOTION_PIN
if (!readPin(POINTING_DEVICE_MOTION_PIN))
#endif
mouseReport = pointing_device_driver.get_report(mouseReport);
// Support rotation of the sensor data
#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)
int8_t x = mouseReport.x, y = mouseReport.y;
# if defined(POINTING_DEVICE_ROTATION_90)
mouseReport.x = y;
mouseReport.y = -x;
# elif defined(POINTING_DEVICE_ROTATION_180)
mouseReport.x = -x;
mouseReport.y = -y;
# elif defined(POINTING_DEVICE_ROTATION_270)
mouseReport.x = -y;
mouseReport.y = x;
# else
# error "How the heck did you get here?!"
# endif
#endif
// Support Inverting the X and Y Axises
#if defined(POINTING_DEVICE_INVERT_X)
mouseReport.x = -mouseReport.x;
#endif
#if defined(POINTING_DEVICE_INVERT_Y)
mouseReport.y = -mouseReport.y;
#endif
// allow kb to intercept and modify report
mouseReport = pointing_device_task_kb(mouseReport);
// combine with mouse report to ensure that the combined is sent correctly
#ifdef MOUSEKEY_ENABLE
report_mouse_t mousekey_report = mousekey_get_report();
mouseReport.buttons = mouseReport.buttons | mousekey_report.buttons;
#endif
pointing_device_send();
}
report_mouse_t pointing_device_get_report(void) { return mouseReport; }
void pointing_device_set_report(report_mouse_t newMouseReport) { mouseReport = newMouseReport; }
uint16_t pointing_device_get_cpi(void) { return pointing_device_driver.get_cpi(); }
void pointing_device_set_cpi(uint16_t cpi) { pointing_device_driver.set_cpi(cpi); }

View file

@ -21,9 +21,68 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "host.h"
#include "report.h"
#if defined(POINTING_DEVICE_DRIVER_adns5050)
# include "drivers/sensors/adns5050.h"
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
# include "spi_master.h"
# include "drivers/sensors/adns9800.h"
#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)
# include "analog.h"
# include "drivers/sensors/analog_joystick.h"
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
# include "drivers/sensors/cirque_pinnacle.h"
#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
# include "i2c_master.h"
# include "drivers/sensors/pimoroni_trackball.h"
// support for legacy pimoroni defines
# ifdef PIMORONI_TRACKBALL_INVERT_X
# define POINTING_DEVICE_INVERT_X
# endif
# ifdef PIMORONI_TRACKBALL_INVERT_Y
# define POINTING_DEVICE_INVERT_Y
# endif
# ifdef PIMORONI_TRACKBALL_ROTATE
# define POINTING_DEVICE_ROTATION_90
# endif
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
# include "spi_master.h"
# include "drivers/sensors/pmw3360.h"
#else
void pointing_device_driver_init(void);
report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);
uint16_t pointing_device_driver_get_cpi(void);
void pointing_device_driver_set_cpi(uint16_t cpi);
#endif
typedef struct {
void (*init)(void);
report_mouse_t (*get_report)(report_mouse_t mouse_report);
void (*set_cpi)(uint16_t);
uint16_t (*get_cpi)(void);
} pointing_device_driver_t;
typedef enum {
POINTING_DEVICE_BUTTON1,
POINTING_DEVICE_BUTTON2,
POINTING_DEVICE_BUTTON3,
POINTING_DEVICE_BUTTON4,
POINTING_DEVICE_BUTTON5,
POINTING_DEVICE_BUTTON6,
POINTING_DEVICE_BUTTON7,
POINTING_DEVICE_BUTTON8,
} pointing_device_buttons_t;
void pointing_device_init(void);
void pointing_device_task(void);
void pointing_device_send(void);
report_mouse_t pointing_device_get_report(void);
void pointing_device_set_report(report_mouse_t newMouseReport);
bool has_mouse_report_changed(report_mouse_t new, report_mouse_t old);
uint16_t pointing_device_get_cpi(void);
void pointing_device_set_cpi(uint16_t cpi);
void pointing_device_init_kb(void);
void pointing_device_init_user(void);
report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);
report_mouse_t pointing_device_task_user(report_mouse_t mouse_report);
uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);

View file

@ -0,0 +1,262 @@
/* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2021 Dasky (@daskygit)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pointing_device.h"
#include "debug.h"
#include "wait.h"
#include "timer.h"
#include <stddef.h>
// hid mouse reports cannot exceed -127 to 127, so constrain to that value
#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt)))
// get_report functions should probably be moved to their respective drivers.
#if defined(POINTING_DEVICE_DRIVER_adns5050)
report_mouse_t adns5050_get_report(report_mouse_t mouse_report) {
report_adns5050_t data = adns5050_read_burst();
if (data.dx != 0 || data.dy != 0) {
# ifdef CONSOLE_ENABLE
if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.dx, data.dy);
# endif
mouse_report.x = data.dx;
mouse_report.y = data.dy;
}
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = adns5050_init,
.get_report = adns5050_get_report,
.set_cpi = adns5050_set_cpi,
.get_cpi = adns5050_get_cpi,
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_adns9800)
report_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {
report_adns9800_t sensor_report = adns9800_get_report();
int8_t clamped_x = constrain_hid(sensor_report.x);
int8_t clamped_y = constrain_hid(sensor_report.y);
mouse_report.x = clamped_x;
mouse_report.y = clamped_y;
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = adns9800_init,
.get_report = adns9800_get_report_driver,
.set_cpi = adns9800_set_cpi,
.get_cpi = adns9800_get_cpi
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)
report_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) {
report_analog_joystick_t data = analog_joystick_read();
# ifdef CONSOLE_ENABLE
if (debug_mouse) dprintf("Raw ] X: %d, Y: %d\n", data.x, data.y);
# endif
mouse_report.x = data.x;
mouse_report.y = data.y;
mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1);
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = analog_joystick_init,
.get_report = analog_joystick_get_report,
.set_cpi = NULL,
.get_cpi = NULL
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
# ifndef CIRQUE_PINNACLE_TAPPING_TERM
# ifdef TAPPING_TERM_PER_KEY
# include "action.h"
# include "action_tapping.h"
# define CIRQUE_PINNACLE_TAPPING_TERM get_tapping_term(KC_BTN1, NULL)
# else
# ifdef TAPPING_TERM
# define CIRQUE_PINNACLE_TAPPING_TERM TAPPING_TERM
# else
# define CIRQUE_PINNACLE_TAPPING_TERM 200
# endif
# endif
# endif
# ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE
# define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8)
# endif
report_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {
pinnacle_data_t touchData = cirque_pinnacle_read_data();
static uint16_t x = 0, y = 0, mouse_timer = 0;
int8_t report_x = 0, report_y = 0;
static bool is_z_down = false;
cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale()); // Scale coordinates to arbitrary X, Y resolution
if (x && y && touchData.xValue && touchData.yValue) {
report_x = (int8_t)(touchData.xValue - x);
report_y = (int8_t)(touchData.yValue - y);
}
x = touchData.xValue;
y = touchData.yValue;
if ((bool)touchData.zValue != is_z_down) {
is_z_down = (bool)touchData.zValue;
if (!touchData.zValue) {
if (timer_elapsed(mouse_timer) < CIRQUE_PINNACLE_TAPPING_TERM && mouse_timer != 0) {
mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);
pointing_device_set_report(mouse_report);
pointing_device_send();
# if TAP_CODE_DELAY > 0
wait_ms(TAP_CODE_DELAY);
# endif
mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);
pointing_device_set_report(mouse_report);
pointing_device_send();
}
}
mouse_timer = timer_read();
}
if (timer_elapsed(mouse_timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) {
mouse_timer = 0;
}
mouse_report.x = report_x;
mouse_report.y = report_y;
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = cirque_pinnacle_init,
.get_report = cirque_pinnacle_get_report,
.set_cpi = cirque_pinnacle_set_scale,
.get_cpi = cirque_pinnacle_get_scale
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)
report_mouse_t pimorono_trackball_get_report(report_mouse_t mouse_report) {
static fast_timer_t throttle = 0;
static uint16_t debounce = 0;
static uint8_t error_count = 0;
pimoroni_data_t pimoroni_data = {0};
static int16_t x_offset = 0, y_offset = 0;
if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT && timer_elapsed_fast(throttle) >= PIMORONI_TRACKBALL_INTERVAL_MS) {
i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);
if (status == I2C_STATUS_SUCCESS) {
error_count = 0;
if (!(pimoroni_data.click & 128)) {
mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);
if (!debounce) {
x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE);
y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE);
pimoroni_trackball_adapt_values(&mouse_report.x, &x_offset);
pimoroni_trackball_adapt_values(&mouse_report.y, &y_offset);
} else {
debounce--;
}
} else {
mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);
debounce = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES;
}
} else {
error_count++;
}
throttle = timer_read_fast();
}
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = pimironi_trackball_device_init,
.get_report = pimorono_trackball_get_report,
.set_cpi = NULL,
.get_cpi = NULL
};
// clang-format on
#elif defined(POINTING_DEVICE_DRIVER_pmw3360)
static void init(void) { pmw3360_init(); }
report_mouse_t pmw3360_get_report(report_mouse_t mouse_report) {
report_pmw3360_t data = pmw3360_read_burst();
static uint16_t MotionStart = 0; // Timer for accel, 0 is resting state
if (data.isOnSurface && data.isMotion) {
// Reset timer if stopped moving
if (!data.isMotion) {
if (MotionStart != 0) MotionStart = 0;
return mouse_report;
}
// Set timer if new motion
if ((MotionStart == 0) && data.isMotion) {
# ifdef CONSOLE_ENABLE
if (debug_mouse) dprintf("Starting motion.\n");
# endif
MotionStart = timer_read();
}
mouse_report.x = constrain_hid(data.dx);
mouse_report.y = constrain_hid(data.dy);
}
return mouse_report;
}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = init,
.get_report = pmw3360_get_report,
.set_cpi = pmw3360_set_cpi,
.get_cpi = pmw3360_get_cpi
};
// clang-format on
#else
__attribute__((weak)) void pointing_device_driver_init(void) {}
__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { return mouse_report; }
__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) { return 0; }
__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {}
// clang-format off
const pointing_device_driver_t pointing_device_driver = {
.init = pointing_device_driver_init,
.get_report = pointing_device_driver_get_report,
.get_cpi = pointing_device_driver_get_cpi,
.set_cpi = pointing_device_driver_set_cpi
};
// clang-format on
#endif

View file

@ -18,14 +18,33 @@
# include <stdbool.h>
# include <stdio.h>
# include "process_auto_shift.h"
static uint16_t autoshift_time = 0;
static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
static uint16_t autoshift_lastkey = KC_NO;
# ifndef AUTO_SHIFT_DISABLED_AT_STARTUP
# define AUTO_SHIFT_STARTUP_STATE true /* enabled */
# else
# define AUTO_SHIFT_STARTUP_STATE false /* disabled */
# endif
// Stores the last Auto Shift key's up or down time, for evaluation or keyrepeat.
static uint16_t autoshift_time = 0;
# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
// Stores the last key's up or down time, to replace autoshift_time so that Tap Hold times are accurate.
static uint16_t retroshift_time = 0;
// Stores a possibly Retro Shift key's up or down time, as retroshift_time needs
// to be set before the Retro Shift key is evaluated if it is interrupted by an
// Auto Shifted key.
static uint16_t last_retroshift_time;
# endif
static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
static uint16_t autoshift_lastkey = KC_NO;
static keyrecord_t autoshift_lastrecord;
// Keys take 8 bits if modifiers are excluded. This records the shift state
// when pressed for each key, so that can be passed to the release function
// and it knows which key needs to be released (if shifted is different base).
static uint16_t autoshift_shift_states[((1 << 8) + 15) / 16];
static struct {
// Whether autoshift is enabled.
// Whether Auto Shift is enabled.
bool enabled : 1;
// Whether the last auto-shifted key was released after the timeout. This
// is used to replicate the last key for a tap-then-hold.
@ -34,43 +53,157 @@ static struct {
bool in_progress : 1;
// Whether the auto-shifted keypress has been registered.
bool holding_shift : 1;
} autoshift_flags = {true, false, false, false};
// Whether the user is holding a shift and we removed it.
bool cancelling_lshift : 1;
bool cancelling_rshift : 1;
// clang-format wants to remove the true for some reason.
// clang-format off
} autoshift_flags = {AUTO_SHIFT_STARTUP_STATE, false, false, false, false, false};
// clang-format on
/** \brief Called on physical press, returns whether key should be added to Auto Shift */
__attribute__((weak)) bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) { return false; }
/** \brief Called on physical press, returns whether is Auto Shift key */
__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
# ifndef NO_AUTO_SHIFT_ALPHA
case AUTO_SHIFT_ALPHA:
# endif
# ifndef NO_AUTO_SHIFT_NUMERIC
case AUTO_SHIFT_NUMERIC:
# endif
# ifndef NO_AUTO_SHIFT_SPECIAL
case AUTO_SHIFT_SPECIAL:
# endif
return true;
}
return get_custom_auto_shifted_key(keycode, record);
}
/** \brief Called to check whether defines should apply if PER_KEY is set for it */
__attribute__((weak)) bool get_auto_shift_repeat(uint16_t keycode, keyrecord_t *record) { return true; }
__attribute__((weak)) bool get_auto_shift_no_auto_repeat(uint16_t keycode, keyrecord_t *record) { return true; }
/** \brief Called when an Auto Shift key needs to be pressed */
__attribute__((weak)) void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) {
if (shifted) {
add_weak_mods(MOD_BIT(KC_LSFT));
}
register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);
}
/** \brief Called when an Auto Shift key needs to be released */
__attribute__((weak)) void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) { unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode); }
/** \brief Sets the shift state to use when keyrepeating, required by custom shifts */
void set_autoshift_shift_state(uint16_t keycode, bool shifted) {
keycode = keycode & 0xFF;
if (shifted) {
autoshift_shift_states[keycode / 16] |= (uint16_t)1 << keycode % 16;
} else {
autoshift_shift_states[keycode / 16] &= ~((uint16_t)1 << keycode % 16);
}
}
/** \brief Gets the shift state to use when keyrepeating, required by custom shifts */
bool get_autoshift_shift_state(uint16_t keycode) {
keycode = keycode & 0xFF;
return (autoshift_shift_states[keycode / 16] & (uint16_t)1 << keycode % 16) != (uint16_t)0;
}
/** \brief Restores the shift key if it was cancelled by Auto Shift */
static void autoshift_flush_shift(void) {
autoshift_flags.holding_shift = false;
del_weak_mods(MOD_BIT(KC_LSFT));
if (autoshift_flags.cancelling_lshift) {
autoshift_flags.cancelling_lshift = false;
add_mods(MOD_BIT(KC_LSFT));
}
if (autoshift_flags.cancelling_rshift) {
autoshift_flags.cancelling_rshift = false;
add_mods(MOD_BIT(KC_RSFT));
}
send_keyboard_report();
}
/** \brief Record the press of an autoshiftable key
*
* \return Whether the record should be further processed.
*/
static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) {
if (!autoshift_flags.enabled) {
return true;
}
// clang-format off
if ((get_mods()
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
| get_oneshot_mods()
# endif
) & (~MOD_BIT(KC_LSFT))
) {
// clang-format on
// Prevents keyrepeating unshifted value of key after using it in a key combo.
autoshift_lastkey = KC_NO;
# ifndef AUTO_SHIFT_MODIFIERS
if (get_mods()) {
return true;
}
// We can't return true here anymore because custom unshifted values are
// possible and there's no good way to tell whether the press returned
// true upon release.
set_autoshift_shift_state(keycode, false);
autoshift_press_user(keycode, false, record);
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));
clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
# endif
return false;
# endif
# ifdef AUTO_SHIFT_REPEAT
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
if (!autoshift_flags.lastshifted) {
# endif
if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) {
// Allow a tap-then-hold for keyrepeat.
if (!autoshift_flags.lastshifted) {
register_code(autoshift_lastkey);
} else {
// Simulate pressing the shift key.
add_weak_mods(MOD_BIT(KC_LSFT));
register_code(autoshift_lastkey);
}
return false;
}
# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
}
// Store record to be sent to user functions if there's no release record then.
autoshift_lastrecord = *record;
autoshift_lastrecord.event.pressed = false;
autoshift_lastrecord.event.time = 0;
// clang-format off
# if defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)
if (keycode == autoshift_lastkey &&
# ifdef AUTO_SHIFT_REPEAT_PER_KEY
get_auto_shift_repeat(autoshift_lastkey, record) &&
# endif
# if !defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY)
(
!autoshift_flags.lastshifted
# ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY
|| get_auto_shift_no_auto_repeat(autoshift_lastkey, record)
# endif
) &&
# endif
TIMER_DIFF_16(now, autoshift_time) <
# ifdef TAPPING_TERM_PER_KEY
get_tapping_term(autoshift_lastkey, record)
# else
TAPPING_TERM
# endif
) {
// clang-format on
// Allow a tap-then-hold for keyrepeat.
if (get_mods() & MOD_BIT(KC_LSFT)) {
autoshift_flags.cancelling_lshift = true;
del_mods(MOD_BIT(KC_LSFT));
}
if (get_mods() & MOD_BIT(KC_RSFT)) {
autoshift_flags.cancelling_rshift = true;
del_mods(MOD_BIT(KC_RSFT));
}
// autoshift_shift_state doesn't need to be changed.
autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
return false;
}
# endif
// Use physical shift state of press event to be more like normal typing.
# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
autoshift_flags.lastshifted = (get_mods() | get_oneshot_mods()) & MOD_BIT(KC_LSFT);
set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));
# else
autoshift_flags.lastshifted = get_mods() & MOD_BIT(KC_LSFT);
# endif
// Record the keycode so we can simulate it later.
autoshift_lastkey = keycode;
autoshift_time = now;
@ -84,51 +217,70 @@ static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record)
/** \brief Registers an autoshiftable key under the right conditions
*
* If the autoshift delay has elapsed, register a shift and the key.
* If autoshift_timeout has elapsed, register a shift and the key.
*
* If the autoshift key is released before the delay has elapsed, register the
* If the Auto Shift key is released before the delay has elapsed, register the
* key without a shift.
*
* Called on key down with keycode=KC_NO, auto-shifted key up, and timeout.
*/
static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
// Called on key down with KC_NO, auto-shifted key up, and timeout.
if (autoshift_flags.in_progress) {
static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger, keyrecord_t *record) {
if (autoshift_flags.in_progress && (keycode == autoshift_lastkey || keycode == KC_NO)) {
// Process the auto-shiftable key.
autoshift_flags.in_progress = false;
// Time since the initial press was recorded.
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
if (elapsed < autoshift_timeout) {
register_code(autoshift_lastkey);
autoshift_flags.lastshifted = false;
} else {
// Simulate pressing the shift key.
add_weak_mods(MOD_BIT(KC_LSFT));
register_code(autoshift_lastkey);
autoshift_flags.lastshifted = true;
# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT)
if (matrix_trigger) {
// Prevents release.
return;
}
// clang-format off
autoshift_flags.lastshifted =
autoshift_flags.lastshifted
|| TIMER_DIFF_16(now, autoshift_time) >=
# ifdef AUTO_SHIFT_TIMEOUT_PER_KEY
get_autoshift_timeout(autoshift_lastkey, record)
# else
autoshift_timeout
# endif
;
// clang-format on
set_autoshift_shift_state(autoshift_lastkey, autoshift_flags.lastshifted);
if (get_mods() & MOD_BIT(KC_LSFT)) {
autoshift_flags.cancelling_lshift = true;
del_mods(MOD_BIT(KC_LSFT));
}
if (get_mods() & MOD_BIT(KC_RSFT)) {
autoshift_flags.cancelling_rshift = true;
del_mods(MOD_BIT(KC_RSFT));
}
autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
// clang-format off
# if (defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)) && (!defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY))
if (matrix_trigger
# ifdef AUTO_SHIFT_REPEAT_PER_KEY
&& get_auto_shift_repeat(autoshift_lastkey, record)
# endif
# ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY
&& !get_auto_shift_no_auto_repeat(autoshift_lastkey, record)
# endif
) {
// Prevents release.
return;
}
# endif
// clang-format on
# if TAP_CODE_DELAY > 0
wait_ms(TAP_CODE_DELAY);
# endif
unregister_code(autoshift_lastkey);
del_weak_mods(MOD_BIT(KC_LSFT));
autoshift_release_user(autoshift_lastkey, autoshift_flags.lastshifted, record);
autoshift_flush_shift();
} else {
// Release after keyrepeat.
unregister_code(keycode);
autoshift_release_user(keycode, get_autoshift_shift_state(keycode), record);
if (keycode == autoshift_lastkey) {
// This will only fire when the key was the last auto-shiftable
// pressed. That prevents aaaaBBBB then releasing a from unshifting
// later Bs (if B wasn't auto-shiftable).
del_weak_mods(MOD_BIT(KC_LSFT));
// pressed. That prevents 'aaaaBBBB' then releasing a from unshifting
// later 'B's (if 'B' wasn't auto-shiftable).
autoshift_flush_shift();
}
}
send_keyboard_report(); // del_weak_mods doesn't send one.
// Roll the autoshift_time forward for detecting tap-and-hold.
autoshift_time = now;
}
@ -141,24 +293,29 @@ static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
*/
void autoshift_matrix_scan(void) {
if (autoshift_flags.in_progress) {
const uint16_t now = timer_read();
const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
if (elapsed >= autoshift_timeout) {
autoshift_end(autoshift_lastkey, now, true);
const uint16_t now = timer_read();
if (TIMER_DIFF_16(now, autoshift_time) >=
# ifdef AUTO_SHIFT_TIMEOUT_PER_KEY
get_autoshift_timeout(autoshift_lastkey, &autoshift_lastrecord)
# else
autoshift_timeout
# endif
) {
autoshift_end(autoshift_lastkey, now, true, &autoshift_lastrecord);
}
}
}
void autoshift_toggle(void) {
autoshift_flags.enabled = !autoshift_flags.enabled;
del_weak_mods(MOD_BIT(KC_LSFT));
autoshift_flush_shift();
}
void autoshift_enable(void) { autoshift_flags.enabled = true; }
void autoshift_disable(void) {
autoshift_flags.enabled = false;
del_weak_mods(MOD_BIT(KC_LSFT));
autoshift_flush_shift();
}
# ifndef AUTO_SHIFT_NO_SETUP
@ -173,76 +330,158 @@ void autoshift_timer_report(void) {
bool get_autoshift_state(void) { return autoshift_flags.enabled; }
uint16_t get_autoshift_timeout(void) { return autoshift_timeout; }
uint16_t get_generic_autoshift_timeout() { return autoshift_timeout; }
__attribute__((weak)) uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) { return autoshift_timeout; }
void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; }
bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
// Note that record->event.time isn't reliable, see:
// https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550
const uint16_t now = timer_read();
// clang-format off
const uint16_t now =
# if !defined(RETRO_SHIFT) || defined(NO_ACTION_TAPPING)
timer_read()
# else
(record->event.pressed) ? retroshift_time : timer_read()
# endif
;
// clang-format on
if (record->event.pressed) {
if (autoshift_flags.in_progress) {
// Evaluate previous key if there is one. Doing this elsewhere is
// more complicated and easier to break.
autoshift_end(KC_NO, now, false);
// Evaluate previous key if there is one.
autoshift_end(KC_NO, now, false, &autoshift_lastrecord);
}
// For pressing another key while keyrepeating shifted autoshift.
del_weak_mods(MOD_BIT(KC_LSFT));
switch (keycode) {
case KC_ASTG:
autoshift_toggle();
return true;
break;
case KC_ASON:
autoshift_enable();
return true;
break;
case KC_ASOFF:
autoshift_disable();
return true;
break;
# ifndef AUTO_SHIFT_NO_SETUP
case KC_ASUP:
autoshift_timeout += 5;
return true;
break;
case KC_ASDN:
autoshift_timeout -= 5;
return true;
break;
case KC_ASRP:
autoshift_timer_report();
return true;
break;
# endif
}
// If Retro Shift is disabled, possible custom actions shouldn't happen.
// clang-format off
if (IS_RETRO(keycode)
# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
// Not tapped or #defines mean that rolls should use hold action.
&& (
record->tap.count == 0
# ifdef RETRO_TAPPING_PER_KEY
|| !get_retro_tapping(keycode, record)
# endif
|| (record->tap.interrupted && (IS_LT(keycode)
# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
? get_hold_on_other_key_press(keycode, record)
# else
? true
# endif
# else
? false
# endif
# if defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
: !get_ignore_mod_tap_interrupt(keycode, record)
# else
: false
# endif
# else
: true
# endif
))
)
# endif
) {
// clang-format on
autoshift_lastkey = KC_NO;
return true;
}
} else {
if (keycode == KC_LSFT) {
autoshift_flags.cancelling_lshift = false;
} else if (keycode == KC_RSFT) {
autoshift_flags.cancelling_rshift = false;
}
// Same as above (for pressed), additional checks are not needed because
// tap.count gets set to 0 in process_action
// clang-format off
else if (IS_RETRO(keycode)
# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
&& (
record->tap.count == 0
# ifdef RETRO_TAPPING_PER_KEY
|| !get_retro_tapping(keycode, record)
# endif
)
# endif
) {
// Fixes modifiers not being applied to rolls with AUTO_SHIFT_MODIFIERS set.
# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)
if (autoshift_flags.in_progress
# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY
&& !get_ignore_mod_tap_interrupt(keycode, record)
# endif
) {
autoshift_end(KC_NO, now, false, &autoshift_lastrecord);
}
# endif
// clang-format on
return true;
}
}
if (!autoshift_flags.enabled) {
return true;
}
if (get_auto_shifted_key(keycode, record)) {
if (record->event.pressed) {
return autoshift_press(keycode, now, record);
} else {
autoshift_end(keycode, now, false);
autoshift_end(keycode, now, false, record);
return false;
}
}
// Prevent keyrepeating of older keys upon non-AS key event.
// Not commented at above returns but they serve the same function.
if (record->event.pressed) {
autoshift_lastkey = KC_NO;
}
return true;
}
__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
# ifndef NO_AUTO_SHIFT_ALPHA
case KC_A ... KC_Z:
# endif
# ifndef NO_AUTO_SHIFT_NUMERIC
case KC_1 ... KC_0:
# endif
# ifndef NO_AUTO_SHIFT_SPECIAL
case KC_TAB:
case KC_MINUS ... KC_SLASH:
case KC_NONUS_BSLASH:
# endif
return true;
}
return false;
# if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)
// Called to record time before possible delays by action_tapping_process.
void retroshift_poll_time(keyevent_t *event) {
last_retroshift_time = retroshift_time;
retroshift_time = timer_read();
}
// Used to swap the times of Retro Shifted key and Auto Shift key that interrupted it.
void retroshift_swap_times() {
if (last_retroshift_time != 0 && autoshift_flags.in_progress) {
uint16_t temp = retroshift_time;
retroshift_time = last_retroshift_time;
last_retroshift_time = temp;
}
}
# endif
#endif

View file

@ -22,13 +22,31 @@
# define AUTO_SHIFT_TIMEOUT 175
#endif
#define IS_LT(kc) ((kc) >= QK_LAYER_TAP && (kc) <= QK_LAYER_TAP_MAX)
#define IS_MT(kc) ((kc) >= QK_MOD_TAP && (kc) <= QK_MOD_TAP_MAX)
#define IS_RETRO(kc) (IS_MT(kc) || IS_LT(kc))
#define DO_GET_AUTOSHIFT_TIMEOUT(keycode, record, ...) record
// clang-format off
#define AUTO_SHIFT_ALPHA KC_A ... KC_Z
#define AUTO_SHIFT_NUMERIC KC_1 ... KC_0
#define AUTO_SHIFT_SPECIAL \
KC_TAB: \
case KC_MINUS ... KC_SLASH: \
case KC_NONUS_BSLASH
// clang-format on
bool process_auto_shift(uint16_t keycode, keyrecord_t *record);
void retroshift_poll_time(keyevent_t *event);
void retroshift_swap_times(void);
void autoshift_enable(void);
void autoshift_disable(void);
void autoshift_toggle(void);
bool get_autoshift_state(void);
uint16_t get_autoshift_timeout(void);
uint16_t get_generic_autoshift_timeout(void);
// clang-format off
uint16_t (get_autoshift_timeout)(uint16_t keycode, keyrecord_t *record);
void set_autoshift_timeout(uint16_t timeout);
void autoshift_matrix_scan(void);
bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record);
// clang-format on

View file

@ -18,10 +18,9 @@
#include "process_combo.h"
#include "action_tapping.h"
#ifdef COMBO_COUNT
__attribute__((weak)) combo_t key_combos[COMBO_COUNT];
uint16_t COMBO_LEN = COMBO_COUNT;
__attribute__((weak)) combo_t key_combos[COMBO_COUNT];
uint16_t COMBO_LEN = COMBO_COUNT;
#else
extern combo_t key_combos[];
extern uint16_t COMBO_LEN;
@ -46,64 +45,86 @@ __attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo
#endif
#ifndef COMBO_NO_TIMER
static uint16_t timer = 0;
static uint16_t timer = 0;
#endif
static bool b_combo_enable = true; // defaults to enabled
static uint16_t longest_term = 0;
static bool b_combo_enable = true; // defaults to enabled
static uint16_t longest_term = 0;
typedef struct {
keyrecord_t record;
uint16_t combo_index;
uint16_t keycode;
uint16_t combo_index;
uint16_t keycode;
} queued_record_t;
static uint8_t key_buffer_size = 0;
static uint8_t key_buffer_size = 0;
static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH];
typedef struct {
uint16_t combo_index;
} queued_combo_t;
static uint8_t combo_buffer_write= 0;
static uint8_t combo_buffer_read = 0;
static uint8_t combo_buffer_write = 0;
static uint8_t combo_buffer_read = 0;
static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];
#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH
#define COMBO_KEY_POS ((keypos_t){.col=254, .row=254})
#define COMBO_KEY_POS ((keypos_t){.col = 254, .row = 254})
#ifndef EXTRA_SHORT_COMBOS
/* flags are their own elements in combo_t struct. */
# define COMBO_ACTIVE(combo) (combo->active)
# define COMBO_ACTIVE(combo) (combo->active)
# define COMBO_DISABLED(combo) (combo->disabled)
# define COMBO_STATE(combo) (combo->state)
# define COMBO_STATE(combo) (combo->state)
# define ACTIVATE_COMBO(combo) do {combo->active = true;}while(0)
# define DEACTIVATE_COMBO(combo) do {combo->active = false;}while(0)
# define DISABLE_COMBO(combo) do {combo->disabled = true;}while(0)
# define RESET_COMBO_STATE(combo) do { \
combo->disabled = false; \
combo->state = 0; \
}while(0)
# define ACTIVATE_COMBO(combo) \
do { \
combo->active = true; \
} while (0)
# define DEACTIVATE_COMBO(combo) \
do { \
combo->active = false; \
} while (0)
# define DISABLE_COMBO(combo) \
do { \
combo->disabled = true; \
} while (0)
# define RESET_COMBO_STATE(combo) \
do { \
combo->disabled = false; \
combo->state = 0; \
} while (0)
#else
/* flags are at the two high bits of state. */
# define COMBO_ACTIVE(combo) (combo->state & 0x80)
# define COMBO_ACTIVE(combo) (combo->state & 0x80)
# define COMBO_DISABLED(combo) (combo->state & 0x40)
# define COMBO_STATE(combo) (combo->state & 0x3F)
# define COMBO_STATE(combo) (combo->state & 0x3F)
# define ACTIVATE_COMBO(combo) do {combo->state |= 0x80;}while(0)
# define DEACTIVATE_COMBO(combo) do {combo->state &= ~0x80;}while(0)
# define DISABLE_COMBO(combo) do {combo->state |= 0x40;}while(0)
# define RESET_COMBO_STATE(combo) do {combo->state &= ~0x7F;}while(0)
# define ACTIVATE_COMBO(combo) \
do { \
combo->state |= 0x80; \
} while (0)
# define DEACTIVATE_COMBO(combo) \
do { \
combo->state &= ~0x80; \
} while (0)
# define DISABLE_COMBO(combo) \
do { \
combo->state |= 0x40; \
} while (0)
# define RESET_COMBO_STATE(combo) \
do { \
combo->state &= ~0x7F; \
} while (0)
#endif
static inline void release_combo(uint16_t combo_index, combo_t *combo) {
if (combo->keycode) {
keyrecord_t record = {
.event = {
.key = COMBO_KEY_POS,
.time = timer_read()|1,
.pressed = false,
},
.event =
{
.key = COMBO_KEY_POS,
.time = timer_read() | 1,
.pressed = false,
},
.keycode = combo->keycode,
};
#ifndef NO_ACTION_TAPPING
@ -123,18 +144,17 @@ static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) {
#elif defined(COMBO_MUST_HOLD_PER_COMBO)
return get_combo_must_hold(combo_index, combo);
#elif defined(COMBO_MUST_HOLD_MODS)
return (KEYCODE_IS_MOD(combo->keycode) ||
(combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
return (KEYCODE_IS_MOD(combo->keycode) || (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
#endif
return false;
}
static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo) {
if (_get_combo_must_hold(combo_index, combo)
#ifdef COMBO_MUST_TAP_PER_COMBO
|| get_combo_must_tap(combo_index, combo)
|| get_combo_must_tap(combo_index, combo)
#endif
) {
) {
if (longest_term < COMBO_HOLD_TERM) {
return COMBO_HOLD_TERM;
}
@ -144,9 +164,8 @@ static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
}
static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
#if defined(COMBO_TERM_PER_COMBO)
return get_combo_term(combo_index, combo);
return get_combo_term(combo_index, combo);
#endif
return COMBO_TERM;
@ -154,7 +173,7 @@ static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
void clear_combos(void) {
uint16_t index = 0;
longest_term = 0;
longest_term = 0;
for (index = 0; index < COMBO_LEN; ++index) {
combo_t *combo = &key_combos[index];
if (!COMBO_ACTIVE(combo)) {
@ -175,7 +194,7 @@ static inline void dump_key_buffer(void) {
key_buffer_next = key_buffer_i + 1;
queued_record_t *qrecord = &key_buffer[key_buffer_i];
keyrecord_t *record = &qrecord->record;
keyrecord_t * record = &qrecord->record;
if (IS_NOEVENT(record->event)) {
continue;
@ -185,9 +204,9 @@ static inline void dump_key_buffer(void) {
process_combo_event(qrecord->combo_index, true);
} else {
#ifndef NO_ACTION_TAPPING
action_tapping_process(*record);
action_tapping_process(*record);
#else
process_record(record);
process_record(record);
#endif
}
record->event.time = 0;
@ -242,7 +261,9 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
/* Apply combo's result keycode to the last chord key of the combo and
* disable the other keys. */
if (COMBO_DISABLED(combo)) { return; }
if (COMBO_DISABLED(combo)) {
return;
}
// state to check against so we find the last key of the combo from the buffer
#if defined(EXTRA_EXTRA_LONG_COMBOS)
@ -254,12 +275,11 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
#endif
for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
queued_record_t *qrecord = &key_buffer[key_buffer_i];
keyrecord_t *record = &qrecord->record;
uint16_t keycode = qrecord->keycode;
keyrecord_t * record = &qrecord->record;
uint16_t keycode = qrecord->keycode;
uint8_t key_count = 0;
uint8_t key_count = 0;
uint16_t key_index = -1;
_find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
@ -271,7 +291,7 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
KEY_STATE_DOWN(state, key_index);
if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {
// this in the end executes the combo when the key_buffer is dumped.
record->keycode = combo->keycode;
record->keycode = combo->keycode;
record->event.key = COMBO_KEY_POS;
qrecord->combo_index = combo_index;
@ -283,19 +303,15 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
// by making it a TICK event.
record->event.time = 0;
}
}
drop_combo_from_buffer(combo_index);
}
static inline void apply_combos(void) {
// Apply all buffered normal combos.
for (uint8_t i = combo_buffer_read;
i != combo_buffer_write;
INCREMENT_MOD(i)) {
for (uint8_t i = combo_buffer_read; i != combo_buffer_write; INCREMENT_MOD(i)) {
queued_combo_t *buffered_combo = &combo_buffer[i];
combo_t *combo = &key_combos[buffered_combo->combo_index];
combo_t * combo = &key_combos[buffered_combo->combo_index];
#ifdef COMBO_MUST_TAP_PER_COMBO
if (get_combo_must_tap(buffered_combo->combo_index, combo)) {
@ -310,15 +326,15 @@ static inline void apply_combos(void) {
clear_combos();
}
combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
combo_t *overlaps(combo_t *combo1, combo_t *combo2) {
/* Checks if the combos overlap and returns the combo that should be
* dropped from the combo buffer.
* The combo that has less keys will be dropped. If they have the same
* amount of keys, drop combo1. */
uint8_t idx1 = 0, idx2 = 0;
uint8_t idx1 = 0, idx2 = 0;
uint16_t key1, key2;
bool overlaps = false;
bool overlaps = false;
while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {
idx2 = 0;
@ -335,7 +351,7 @@ combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
}
static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
uint8_t key_count = 0;
uint8_t key_count = 0;
uint16_t key_index = -1;
_find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
@ -369,12 +385,9 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
// disable readied combos that overlap with this combo
combo_t *drop = NULL;
for (uint8_t combo_buffer_i = combo_buffer_read;
combo_buffer_i != combo_buffer_write;
INCREMENT_MOD(combo_buffer_i)) {
queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
combo_t *buffered_combo = &key_combos[qcombo->combo_index];
for (uint8_t combo_buffer_i = combo_buffer_read; combo_buffer_i != combo_buffer_write; INCREMENT_MOD(combo_buffer_i)) {
queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
combo_t * buffered_combo = &key_combos[qcombo->combo_index];
if ((drop = overlaps(buffered_combo, combo))) {
DISABLE_COMBO(drop);
@ -387,21 +400,19 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
INCREMENT_MOD(combo_buffer_read);
}
}
}
if (drop != combo) {
// save this combo to buffer
combo_buffer[combo_buffer_write] = (queued_combo_t){
.combo_index=combo_index,
.combo_index = combo_index,
};
INCREMENT_MOD(combo_buffer_write);
// get possible longer waiting time for tap-/hold-only combos.
longest_term = _get_wait_time(combo_index, combo);
}
} // if timer elapsed end
} // if timer elapsed end
}
} else {
// chord releases
@ -416,7 +427,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
else if (get_combo_must_tap(combo_index, combo)) {
// immediately apply tap-only combo
apply_combo(combo_index, combo);
apply_combos(); // also apply other prepared combos and dump key buffer
apply_combos(); // also apply other prepared combos and dump key buffer
# ifdef COMBO_PROCESS_KEY_RELEASE
if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
release_combo(combo_index, combo);
@ -424,10 +435,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
# endif
}
#endif
} else if (COMBO_ACTIVE(combo)
&& ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo))
&& KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
) {
} else if (COMBO_ACTIVE(combo) && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo)) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {
/* last key released */
release_combo(combo_index, combo);
key_is_part_of_combo = true;
@ -435,9 +443,7 @@ static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *
#ifdef COMBO_PROCESS_KEY_RELEASE
process_combo_key_release(combo_index, combo, key_index, keycode);
#endif
} else if (COMBO_ACTIVE(combo)
&& KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
) {
} else if (COMBO_ACTIVE(combo) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {
/* first or middle key released */
key_is_part_of_combo = true;
@ -489,21 +495,21 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed && is_combo_key) {
#ifndef COMBO_NO_TIMER
# ifdef COMBO_STRICT_TIMER
# ifdef COMBO_STRICT_TIMER
if (!timer) {
// timer is set only on the first key
timer = timer_read();
}
# else
# else
timer = timer_read();
# endif
# endif
#endif
if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
key_buffer[key_buffer_size++] = (queued_record_t){
.record = *record,
.keycode = keycode,
.combo_index = -1, // this will be set when applying combos
.record = *record,
.keycode = keycode,
.combo_index = -1, // this will be set when applying combos
};
}
} else {
@ -532,7 +538,7 @@ void combo_task(void) {
if (combo_buffer_read != combo_buffer_write) {
apply_combos();
longest_term = 0;
timer = 0;
timer = 0;
} else {
dump_key_buffer();
timer = 0;
@ -546,9 +552,9 @@ void combo_enable(void) { b_combo_enable = true; }
void combo_disable(void) {
#ifndef COMBO_NO_TIMER
timer = 0;
timer = 0;
#endif
b_combo_enable = false;
b_combo_enable = false;
combo_buffer_read = combo_buffer_write;
clear_combos();
dump_key_buffer();

View file

@ -43,8 +43,8 @@ typedef struct {
#ifdef EXTRA_SHORT_COMBOS
uint8_t state;
#else
bool disabled;
bool active;
bool disabled;
bool active;
# if defined(EXTRA_EXTRA_LONG_COMBOS)
uint32_t state;
# elif defined(EXTRA_LONG_COMBOS)

View file

@ -0,0 +1,50 @@
/* Copyright 2020 Vladislav Kucheriavykh
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "quantum.h"
#include "process_dynamic_tapping_term.h"
#ifndef DYNAMIC_TAPPING_TERM_INCREMENT
# define DYNAMIC_TAPPING_TERM_INCREMENT 5
#endif
static void tapping_term_report(void) {
const char *tapping_term_str = get_u16_str(g_tapping_term, ' ');
// Skip padding spaces
while (*tapping_term_str == ' ') {
tapping_term_str++;
}
send_string(tapping_term_str);
}
bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case DT_PRNT:
tapping_term_report();
return false;
case DT_UP:
g_tapping_term += DYNAMIC_TAPPING_TERM_INCREMENT;
return false;
case DT_DOWN:
g_tapping_term -= DYNAMIC_TAPPING_TERM_INCREMENT;
return false;
}
}
return true;
}

View file

@ -1,4 +1,4 @@
/* Copyright 2017 Fred Sundvik
/* Copyright 2020 Vladislav Kucheriavykh
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,8 +16,11 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "action.h"
#ifdef LCD_ENABLE
extern const uint8_t resource_lcd_logo[];
#ifndef DYNAMIC_TAPPING_TERM_INCREMENT
# define DYNAMIC_TAPPING_TERM_INCREMENT 5
#endif
bool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record);

View file

@ -17,6 +17,7 @@
#include "process_haptic.h"
#include "quantum_keycodes.h"
#include "action_tapping.h"
#include "usb_device_state.h"
__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
@ -30,8 +31,9 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
if (record->tap.count == 0) return false;
break;
case KC_LCTRL ... KC_RGUI:
case KC_LEFT_CTRL ... KC_RIGHT_GUI:
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
#endif
#ifdef NO_HAPTIC_FN
case KC_FN0 ... KC_FN31:
@ -42,34 +44,34 @@ __attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t
#ifdef NO_HAPTIC_PUNCTUATION
case KC_ENTER:
case KC_ESCAPE:
case KC_BSPACE:
case KC_BACKSPACE:
case KC_SPACE:
case KC_MINUS:
case KC_EQUAL:
case KC_LBRACKET:
case KC_RBRACKET:
case KC_BSLASH:
case KC_LEFT_BRACKET:
case KC_RIGHT_BRACKET:
case KC_BACKSLASH:
case KC_NONUS_HASH:
case KC_SCOLON:
case KC_SEMICOLON:
case KC_QUOTE:
case KC_GRAVE:
case KC_COMMA:
case KC_SLASH:
case KC_DOT:
case KC_NONUS_BSLASH:
case KC_NONUS_BACKSLASH:
#endif
#ifdef NO_HAPTIC_LOCKKEYS
case KC_CAPSLOCK:
case KC_SCROLLLOCK:
case KC_NUMLOCK:
case KC_CAPS_LOCK:
case KC_SCROLL_LOCK:
case KC_NUM_LOCK:
#endif
#ifdef NO_HAPTIC_NAV
case KC_PSCREEN:
case KC_PRINT_SCREEN:
case KC_PAUSE:
case KC_INSERT:
case KC_DELETE:
case KC_PGDOWN:
case KC_PGUP:
case KC_PAGE_DOWN:
case KC_PAGE_UP:
case KC_LEFT:
case KC_UP:
case KC_RIGHT:
@ -130,7 +132,7 @@ bool process_haptic(uint16_t keycode, keyrecord_t *record) {
}
}
if (haptic_get_enable()) {
if (haptic_get_enable() && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state == USB_DEVICE_STATE_CONFIGURED))) {
if (record->event.pressed) {
// keypress
if (haptic_get_feedback() < 2 && get_haptic_enabled_key(keycode, record)) {

View file

@ -43,6 +43,7 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case MAGIC_SWAP_CONTROL_CAPSLOCK ... MAGIC_TOGGLE_ALT_GUI:
case MAGIC_SWAP_LCTL_LGUI ... MAGIC_EE_HANDS_RIGHT:
case MAGIC_TOGGLE_GUI:
/* keymap config */
keymap_config.raw = eeconfig_read_keymap();
switch (keycode) {

View file

@ -146,7 +146,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
if (music_activated || midi_activated) {
if (record->event.pressed) {
if (keycode == KC_LCTL) { // Start recording
if (keycode == KC_LEFT_CTRL) { // Start recording
music_all_notes_off();
music_sequence_recording = true;
music_sequence_recorded = false;
@ -155,7 +155,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
return false;
}
if (keycode == KC_LALT) { // Stop recording/playing
if (keycode == KC_LEFT_ALT) { // Stop recording/playing
music_all_notes_off();
if (music_sequence_recording) { // was recording
music_sequence_recorded = true;
@ -165,7 +165,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
return false;
}
if (keycode == KC_LGUI && music_sequence_recorded) { // Start playing
if (keycode == KC_LEFT_GUI && music_sequence_recorded) { // Start playing
music_all_notes_off();
music_sequence_recording = false;
music_sequence_playing = true;

View file

@ -31,7 +31,7 @@ uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0
// uint8_t keycode_to_ascii[0xFF][2];
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
// keycode_to_ascii[KC_MINUS] = {0x2D, 0x5F};
void print_char(char c) {
USB_Disable();
@ -90,8 +90,8 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
case KC_PIPE:
case KC_TILD:
keycode &= 0xFF;
case KC_LSFT:
case KC_RSFT:
case KC_LEFT_SHIFT:
case KC_RIGHT_SHIFT:
if (record->event.pressed) {
character_shift++;
} else {
@ -107,13 +107,13 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
print_box_string("This is a line of text!");
}
return false;
case KC_ESC:
case KC_ESCAPE:
if (record->event.pressed) {
print_char(0x1B);
}
return false;
break;
case KC_SPC:
case KC_SPACE:
if (record->event.pressed) {
print_char(0x20);
}
@ -139,7 +139,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_ENT:
case KC_ENTER:
if (record->event.pressed) {
if (character_shift) {
print_char(0x0C);
@ -149,7 +149,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_BSPC:
case KC_BACKSPACE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x18);
@ -169,7 +169,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_COMM:
case KC_COMMA:
if (record->event.pressed) {
if (character_shift) {
print_char(0x3C);
@ -179,7 +179,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_SLSH:
case KC_SLASH:
if (record->event.pressed) {
if (character_shift) {
print_char(0x3F);
@ -189,7 +189,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_QUOT:
case KC_QUOTE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x22);
@ -199,7 +199,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_GRV:
case KC_GRAVE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7E);
@ -209,7 +209,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_MINS:
case KC_MINUS:
if (record->event.pressed) {
if (character_shift) {
print_char(0x5F);
@ -219,7 +219,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_EQL:
case KC_EQUAL:
if (record->event.pressed) {
if (character_shift) {
print_char(0x2B);
@ -229,7 +229,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_LBRC:
case KC_LEFT_BRACKET:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7B);
@ -239,7 +239,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_RBRC:
case KC_RIGHT_BRACKET:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7D);
@ -249,7 +249,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_BSLS:
case KC_BACKSLASH:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7C);

View file

@ -45,7 +45,7 @@ uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0
// uint8_t keycode_to_ascii[0xFF][2];
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
// keycode_to_ascii[KC_MINUS] = {0x2D, 0x5F};
void print_char(char c) {
uint8_t b = 8;
@ -84,8 +84,8 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
case KC_PIPE:
case KC_TILD:
keycode &= 0xFF;
case KC_LSFT:
case KC_RSFT:
case KC_LEFT_SHIFT:
case KC_RIGHT_SHIFT:
if (record->event.pressed) {
character_shift++;
} else {
@ -101,13 +101,13 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
print_string("This is a line of text!\n\n\n");
}
return false;
case KC_ESC:
case KC_ESCAPE:
if (record->event.pressed) {
print_char(0x1B);
}
return false;
break;
case KC_SPC:
case KC_SPACE:
if (record->event.pressed) {
print_char(0x20);
}
@ -133,7 +133,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_ENT:
case KC_ENTER:
if (record->event.pressed) {
if (character_shift) {
print_char(0x0C);
@ -143,7 +143,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_BSPC:
case KC_BACKSPACE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x18);
@ -163,7 +163,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_COMM:
case KC_COMMA:
if (record->event.pressed) {
if (character_shift) {
print_char(0x3C);
@ -173,7 +173,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_SLSH:
case KC_SLASH:
if (record->event.pressed) {
if (character_shift) {
print_char(0x3F);
@ -183,7 +183,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_QUOT:
case KC_QUOTE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x22);
@ -193,7 +193,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_GRV:
case KC_GRAVE:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7E);
@ -203,7 +203,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_MINS:
case KC_MINUS:
if (record->event.pressed) {
if (character_shift) {
print_char(0x5F);
@ -213,7 +213,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_EQL:
case KC_EQUAL:
if (record->event.pressed) {
if (character_shift) {
print_char(0x2B);
@ -223,7 +223,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_LBRC:
case KC_LEFT_BRACKET:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7B);
@ -233,7 +233,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_RBRC:
case KC_RIGHT_BRACKET:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7D);
@ -243,7 +243,7 @@ bool process_printer(uint16_t keycode, keyrecord_t *record) {
}
return false;
break;
case KC_BSLS:
case KC_BACKSLASH:
if (record->event.pressed) {
if (character_shift) {
print_char(0x7C);

Some files were not shown because too many files have changed in this diff Show more