Merge remote-tracking branch 'upstream/develop' into xap
This commit is contained in:
commit
bf66b91433
5591 changed files with 131128 additions and 54530 deletions
322
quantum/action.c
322
quantum/action.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
182
quantum/api.c
182
quantum/api.c
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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(" ");
|
||||
// }
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,4 @@
|
|||
#define COL2ROW 0
|
||||
#define ROW2COL 1
|
||||
|
||||
// Deprecated alias - avoid using
|
||||
#define KEYMAP LAYOUT
|
||||
|
||||
#include "song_list.h"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
152
quantum/deferred_exec.c
Normal 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
38
quantum/deferred_exec.h
Normal 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);
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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); \
|
||||
}
|
||||
|
|
|
|||
144
quantum/encoder/tests/encoder_tests.cpp
Normal file
144
quantum/encoder/tests/encoder_tests.cpp
Normal 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);
|
||||
}
|
||||
143
quantum/encoder/tests/encoder_tests_split.cpp
Normal file
143
quantum/encoder/tests/encoder_tests_split.cpp
Normal 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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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) {}
|
||||
48
quantum/encoder/tests/mock_split.h
Normal file
48
quantum/encoder/tests/mock_split.h
Normal 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);
|
||||
13
quantum/encoder/tests/rules.mk
Normal file
13
quantum/encoder/tests/rules.mk
Normal 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
|
||||
3
quantum/encoder/tests/testlist.mk
Normal file
3
quantum/encoder/tests/testlist.mk
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
TEST_LIST += \
|
||||
encoder \
|
||||
encoder_split
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
53
quantum/keycode_legacy.h
Normal 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
|
||||
|
|
@ -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:;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) // >
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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); }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
262
quantum/pointing_device_drivers.c
Normal file
262
quantum/pointing_device_drivers.c
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
50
quantum/process_keycode/process_dynamic_tapping_term.c
Normal file
50
quantum/process_keycode/process_dynamic_tapping_term.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue