mirror of
https://github.com/Keychron/qmk_firmware.git
synced 2026-02-23 09:52:37 +00:00
Add analog matrix common files
This commit is contained in:
204
keyboards/keychron/common/analog_matrix/action_joystick.c
Normal file
204
keyboards/keychron/common/analog_matrix/action_joystick.c
Normal file
@@ -0,0 +1,204 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "profile.h"
|
||||
#include "sqrt.h"
|
||||
#include "game_controller_common.h"
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
extern point_t curve[CURVE_POINTS_COUNT];
|
||||
extern float slope[CURVE_POINTS_COUNT - 1];
|
||||
extern bool regular_trigger_action(analog_key_t *key);
|
||||
extern matrix_row_t game_controller_matrix[MATRIX_ROWS];
|
||||
|
||||
static uint8_t axis_travel[GC_MAX] = {0};
|
||||
static bool axis_changed[GC_MAX] = {0};
|
||||
static int8_t axis_dir[GC_MAX / 2] = {0};
|
||||
static int32_t axis_value[GC_MAX / 2] = {0};
|
||||
|
||||
joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {
|
||||
JOYSTICK_AXIS_VIRTUAL, // x
|
||||
JOYSTICK_AXIS_VIRTUAL, // y
|
||||
JOYSTICK_AXIS_VIRTUAL, // z
|
||||
JOYSTICK_AXIS_VIRTUAL, // rx
|
||||
JOYSTICK_AXIS_VIRTUAL, // ry
|
||||
JOYSTICK_AXIS_VIRTUAL, // rz
|
||||
};
|
||||
|
||||
static uint8_t travel_to_joystick_axis(uint8_t axis, uint8_t travel) {
|
||||
(void)axis;
|
||||
|
||||
uint8_t axis_value = 0;
|
||||
float t = travel / 6;
|
||||
if (t > curve[3].x) {
|
||||
axis_value = curve[3].y;
|
||||
} else if (t > curve[2].x) {
|
||||
axis_value = curve[2].y + slope[2] * (t - curve[2].x);
|
||||
} else if (t > curve[1].x) {
|
||||
axis_value = curve[1].y + slope[1] * (t - curve[1].x);
|
||||
} else if (t > curve[0].x) {
|
||||
axis_value = curve[0].y + slope[0] * (t - curve[0].x);
|
||||
}
|
||||
|
||||
if (axis_value > JOYSTICK_MAX_VALUE) axis_value = JOYSTICK_MAX_VALUE;
|
||||
|
||||
return axis_value;
|
||||
}
|
||||
|
||||
bool joystick_update(analog_key_t *key) {
|
||||
if (key->travel < 1 * TRAVEL_SCALE) key->travel = 0;
|
||||
|
||||
if (key->js_axis < GC_MAX) {
|
||||
axis_travel[key->js_axis] = key->travel;
|
||||
axis_changed[key->js_axis] = true;
|
||||
}
|
||||
|
||||
if (regular_trigger_action(key)) {
|
||||
if (key->state == AKS_REGULAR_PRESSED) {
|
||||
if (key->js_axis >= GC_BUTTON_0 && key->js_axis <= GC_BUTTON_31) register_joystick_button(QK_JOYSTICK_BUTTON_0 + (key->js_axis - GC_BUTTON_0));
|
||||
game_controller_matrix[key->r] |= 0x01 << key->c;
|
||||
} else {
|
||||
if (key->js_axis >= GC_BUTTON_0 && key->js_axis <= GC_BUTTON_31) unregister_joystick_button(QK_JOYSTICK_BUTTON_0 + (key->js_axis - GC_BUTTON_0));
|
||||
game_controller_matrix[key->r] &= ~(0x01 << key->c);
|
||||
}
|
||||
|
||||
if (game_controller_type_enabled()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void joystick_clear(void) {
|
||||
memset(axis_travel, 0, sizeof(axis_travel));
|
||||
memset(axis_changed, 0, sizeof(axis_changed));
|
||||
memset(axis_dir, 0, sizeof(axis_dir));
|
||||
memset(axis_value, 0, sizeof(axis_value));
|
||||
|
||||
for (uint8_t i = 0; i < JOYSTICK_BUTTON_COUNT; i++) {
|
||||
unregister_joystick_button(i);
|
||||
}
|
||||
for (uint8_t i = 0; i < GC_AXIS_MAX; i++) {
|
||||
joystick_set_axis(i, 0);
|
||||
}
|
||||
joystick_flush();
|
||||
}
|
||||
|
||||
static void joystick_action(void) {
|
||||
# define AXIS_DIRECT_VALUE (2 * TRAVEL_SCALE)
|
||||
uint8_t axis_neg, axis_pos;
|
||||
|
||||
bool changed = false;
|
||||
for (uint8_t i = 0; i < GC_AXIS_MAX; i++) {
|
||||
axis_neg = i * 2;
|
||||
axis_pos = i * 2 + 1;
|
||||
|
||||
if (axis_changed[i] || axis_changed[axis_pos]) {
|
||||
changed = true;
|
||||
if (axis_travel[axis_neg] > TRAVEL_SCALE && axis_travel[axis_pos] > TRAVEL_SCALE) {
|
||||
// Both direction key are pressed
|
||||
if (axis_travel[axis_neg] > axis_travel[axis_pos] + AXIS_DIRECT_VALUE)
|
||||
axis_dir[i] = -1;
|
||||
else if (axis_travel[axis_neg] + AXIS_DIRECT_VALUE < axis_travel[axis_pos])
|
||||
axis_dir[i] = 1;
|
||||
} else if (axis_travel[axis_neg]) {
|
||||
// Negative direction
|
||||
axis_dir[i] = -1;
|
||||
} else if (axis_travel[axis_pos]) {
|
||||
// Positive direction
|
||||
axis_dir[i] = 1;
|
||||
} else {
|
||||
axis_dir[i] = 0;
|
||||
}
|
||||
|
||||
if (axis_dir[i] < 0) {
|
||||
axis_value[i] = -travel_to_joystick_axis(axis_neg, axis_travel[axis_neg]);
|
||||
} else {
|
||||
axis_value[i] = travel_to_joystick_axis(axis_pos, axis_travel[axis_pos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
uint32_t x = axis_dir[GC_AXIS_X] < 0 ? axis_travel[GC_AXIS_X * 2] : axis_travel[GC_AXIS_X * 2 + 1];
|
||||
uint32_t y = axis_dir[GC_AXIS_Y] < 0 ? axis_travel[GC_AXIS_Y * 2] : axis_travel[GC_AXIS_Y * 2 + 1];
|
||||
|
||||
uint32_t axis_square = x * x + y * y;
|
||||
uint32_t r_square = ((FULL_TRAVEL_UNIT + 1) * TRAVEL_SCALE) * ((FULL_TRAVEL_UNIT + 1) * TRAVEL_SCALE);
|
||||
if (axis_square > r_square) {
|
||||
uint32_t sqrt = sqrt_uint32(axis_square);
|
||||
float ratio = 276.0f / sqrt;
|
||||
|
||||
axis_value[GC_AXIS_X] = axis_value[GC_AXIS_X] * ratio;
|
||||
axis_value[GC_AXIS_Y] = axis_value[GC_AXIS_Y] * ratio;
|
||||
|
||||
if (axis_dir[GC_AXIS_X] < 0) {
|
||||
if (axis_value[GC_AXIS_X] < -JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_X] = -JOYSTICK_MAX_VALUE;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_X] > JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_X] = JOYSTICK_MAX_VALUE;
|
||||
}
|
||||
|
||||
if (axis_dir[GC_AXIS_Y] < 0) {
|
||||
if (axis_value[GC_AXIS_Y] < -JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_Y] = -JOYSTICK_MAX_VALUE;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_Y] > JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_Y] = JOYSTICK_MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t rx = axis_dir[GC_AXIS_RX] < 0 ? axis_travel[GC_AXIS_RX * 2] : axis_travel[GC_AXIS_RX * 2 + 1];
|
||||
uint32_t ry = axis_dir[GC_AXIS_RY] < 0 ? axis_travel[GC_AXIS_RY * 2] : axis_travel[GC_AXIS_RY * 2 + 1];
|
||||
|
||||
axis_square = rx * rx + ry * ry;
|
||||
if (axis_square > r_square) {
|
||||
uint32_t sqrt = sqrt_uint32(axis_square);
|
||||
float ratio = 276.0f / sqrt;
|
||||
|
||||
axis_value[GC_AXIS_RX] = axis_value[GC_AXIS_RX] * ratio;
|
||||
axis_value[GC_AXIS_RY] = axis_value[GC_AXIS_RY] * ratio;
|
||||
|
||||
if (axis_dir[GC_AXIS_RX] < 0) {
|
||||
if (axis_value[GC_AXIS_RX] < -JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_RX] = -JOYSTICK_MAX_VALUE;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_RX] > JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_RX] = JOYSTICK_MAX_VALUE;
|
||||
}
|
||||
|
||||
if (axis_dir[GC_AXIS_RY] < 0) {
|
||||
if (axis_value[GC_AXIS_RY] < -JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_RY] = -JOYSTICK_MAX_VALUE;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_RY] > JOYSTICK_MAX_VALUE) axis_value[GC_AXIS_RY] = JOYSTICK_MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < GC_AXIS_MAX; i++) {
|
||||
/* Reverse Y and RY axis value */
|
||||
if (i == GC_AXIS_Y || i == GC_AXIS_RY)
|
||||
joystick_set_axis(i, -axis_value[i]);
|
||||
else
|
||||
joystick_set_axis(i, axis_value[i]);
|
||||
}
|
||||
|
||||
joystick_flush();
|
||||
}
|
||||
}
|
||||
|
||||
void joystick_action_task(void) {
|
||||
# ifdef XINPUT_ENABLE
|
||||
if (!game_controller_xinput_enabled())
|
||||
# endif
|
||||
joystick_action();
|
||||
}
|
||||
#endif
|
||||
222
keyboards/keychron/common/analog_matrix/action_okmc.c
Normal file
222
keyboards/keychron/common/analog_matrix/action_okmc.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "profile.h"
|
||||
|
||||
// OKMC action type
|
||||
enum {
|
||||
OKMC_ACTION_RELEASE = 0b001,
|
||||
OKMC_ACTION_PRESS = 0b010,
|
||||
OKMC_ACTION_TAP = 0b110,
|
||||
OKMC_ACTION_RE_PRESS = 0b111,
|
||||
};
|
||||
|
||||
enum {
|
||||
OKMC_RELEASED = AKS_REGULAR_RELEASED,
|
||||
OKMC_SHALLOW_ACTUATED,
|
||||
OKMC_DEEP_ACTUATED,
|
||||
OKMC_DEEP_DEACT_READY,
|
||||
OKMC_DEEP_DEACTUATED,
|
||||
OKMC_MAX,
|
||||
};
|
||||
|
||||
matrix_row_t okmc_matrix[MATRIX_ROWS] = {0};
|
||||
|
||||
static void report_action(bool add, uint16_t keycode) {
|
||||
if (add) {
|
||||
if (IS_BASIC_KEYCODE(keycode)) {
|
||||
if (!is_key_pressed(keycode)) {
|
||||
add_key(keycode);
|
||||
}
|
||||
} else if (IS_MODIFIER_KEYCODE(keycode)) {
|
||||
add_mods(MOD_BIT(keycode));
|
||||
}
|
||||
} else {
|
||||
if (IS_BASIC_KEYCODE(keycode)) {
|
||||
if (is_key_pressed(keycode)) {
|
||||
del_key(keycode);
|
||||
}
|
||||
} else if (IS_MODIFIER_KEYCODE(keycode)) {
|
||||
del_mods(MOD_BIT(keycode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void release_okmc_keys(okmc_config_t *okmc) {
|
||||
// Relase all keys in the OKMC settings
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
if (okmc->keycode[i]) {
|
||||
report_action(0, okmc->keycode[i]);
|
||||
}
|
||||
}
|
||||
send_keyboard_report();
|
||||
}
|
||||
|
||||
static void inline shallow_actuate(okmc_config_t *okmc) {
|
||||
bool any_action;
|
||||
|
||||
for (uint8_t bit = 0; bit < 3; bit++) {
|
||||
any_action = false;
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
if (okmc->keycode[i] && (okmc->action[i].shallow_act & (0x01 << bit))) {
|
||||
report_action(bit % 2, okmc->keycode[i]);
|
||||
any_action = true;
|
||||
}
|
||||
}
|
||||
if (any_action) {
|
||||
send_keyboard_report();
|
||||
wait_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void inline shallow_deactuate(okmc_config_t *okmc) {
|
||||
bool any_action;
|
||||
|
||||
for (uint8_t bit = 0; bit < 3; bit++) {
|
||||
any_action = false;
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
if (okmc->keycode[i] && (okmc->action[i].shallow_deact & (0x01 << bit))) {
|
||||
report_action(bit % 2, okmc->keycode[i]);
|
||||
any_action = true;
|
||||
}
|
||||
}
|
||||
if (any_action) {
|
||||
send_keyboard_report();
|
||||
wait_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
send_keyboard_report();
|
||||
release_okmc_keys(okmc);
|
||||
}
|
||||
|
||||
static void inline deep_actuate(okmc_config_t *okmc) {
|
||||
bool any_action;
|
||||
|
||||
for (uint8_t bit = 0; bit < 3; bit++) {
|
||||
any_action = false;
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
if (okmc->keycode[i] && (okmc->action[i].deep_act & (0x01 << bit))) {
|
||||
report_action(bit % 2, okmc->keycode[i]);
|
||||
any_action = true;
|
||||
}
|
||||
}
|
||||
if (any_action) {
|
||||
send_keyboard_report();
|
||||
wait_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void inline deep_deactuate(okmc_config_t *okmc) {
|
||||
bool any_action;
|
||||
|
||||
for (uint8_t bit = 0; bit < 3; bit++) {
|
||||
any_action = false;
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
if (okmc->keycode[i] && (okmc->action[i].deep_deact & (0x01 << bit))) {
|
||||
report_action(bit % 2, okmc->keycode[i]);
|
||||
any_action = true;
|
||||
}
|
||||
}
|
||||
if (any_action) {
|
||||
send_keyboard_report();
|
||||
wait_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool okmc_action(analog_key_t *key) {
|
||||
bool changed = false;
|
||||
analog_matrix_profile_t *cur_prof = profile_get_current();
|
||||
okmc_traval_config_t *travel_cfg = &cur_prof->okmc[key->okmc_idx].travel;
|
||||
|
||||
switch (key->state) {
|
||||
case OKMC_RELEASED:
|
||||
// Check shallow actuation
|
||||
if (key->travel >= travel_cfg->shallow_act * TRAVEL_SCALE) {
|
||||
key->state = OKMC_SHALLOW_ACTUATED;
|
||||
shallow_actuate(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case OKMC_SHALLOW_ACTUATED:
|
||||
// Key releasing
|
||||
if (key->travel < travel_cfg->shallow_deact * TRAVEL_SCALE && key->travel < (travel_cfg->shallow_act - 1) * TRAVEL_SCALE) {
|
||||
key->state = OKMC_RELEASED;
|
||||
release_okmc_keys(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
// Continue pressing
|
||||
else if (key->travel >= travel_cfg->deep_act * TRAVEL_SCALE) {
|
||||
key->state = OKMC_DEEP_ACTUATED;
|
||||
deep_actuate(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case OKMC_DEEP_ACTUATED:
|
||||
if (key->travel > travel_cfg->deep_deact * TRAVEL_SCALE) {
|
||||
key->state = OKMC_DEEP_DEACT_READY; // make su
|
||||
} else if (key->travel < travel_cfg->shallow_deact * TRAVEL_SCALE && key->travel < (travel_cfg->shallow_act - 1) * TRAVEL_SCALE) {
|
||||
key->state = OKMC_RELEASED;
|
||||
release_okmc_keys(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case OKMC_DEEP_DEACT_READY:
|
||||
if (key->travel <= travel_cfg->deep_deact * TRAVEL_SCALE) {
|
||||
key->state = OKMC_DEEP_DEACTUATED;
|
||||
deep_deactuate(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case OKMC_DEEP_DEACTUATED:
|
||||
// If we miss the deep deacuation point
|
||||
if (key->travel <= travel_cfg->shallow_deact * TRAVEL_SCALE && key->travel < (travel_cfg->shallow_act - 1) * TRAVEL_SCALE) {
|
||||
key->state = OKMC_RELEASED;
|
||||
shallow_deactuate(&cur_prof->okmc[key->okmc_idx]);
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (key->state >= OKMC_SHALLOW_ACTUATED && key->state <= OKMC_DEEP_DEACT_READY)
|
||||
okmc_matrix[key->r] |= 0x01 << key->c;
|
||||
else
|
||||
okmc_matrix[key->r] &= ~(0x01 << key->c);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void okmc_clear(void) {
|
||||
memset(okmc_matrix, 0, sizeof(okmc_matrix));
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "analog_matrix.h"
|
||||
|
||||
bool rapid_trigger_action(analog_key_t *key) {
|
||||
bool changed = false;
|
||||
int8_t update_rapid_pts = 0;
|
||||
|
||||
switch (key->state) {
|
||||
case AKS_REGULAR_RELEASED:
|
||||
// Chick first actuation
|
||||
if (key->travel >= key->regular.actn_pt) {
|
||||
key->state = AKS_REGULAR_PRESSED;
|
||||
changed = true;
|
||||
// First update rapid trigger point
|
||||
update_rapid_pts = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case AKS_REGULAR_PRESSED:
|
||||
// Key releasing
|
||||
if (key->travel <= key->regular.deactn_pt) {
|
||||
key->state = AKS_REGULAR_RELEASED;
|
||||
changed = true;
|
||||
} else if (key->travel <= key->rapid.deactn_pt && key->travel < BOTTOM_DEAD_ZONE * TRAVEL_SCALE - key->rpd_trig_sen_rls) {
|
||||
key->state = AKS_RAPID_RELEASED;
|
||||
changed = true;
|
||||
update_rapid_pts = -1;
|
||||
}
|
||||
// Continue pressing
|
||||
else if (key->travel > key->rapid.actn_pt) {
|
||||
update_rapid_pts = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case AKS_RAPID_RELEASED:
|
||||
// Continue releasing
|
||||
if (key->travel <= key->regular.deactn_pt) {
|
||||
key->state = AKS_REGULAR_RELEASED;
|
||||
}
|
||||
// Press again
|
||||
else if (key->travel >= key->rapid.actn_pt && key->travel >= key->regular.actn_pt) {
|
||||
key->state = AKS_RAPID_PRESSED;
|
||||
changed = true;
|
||||
update_rapid_pts = 1;
|
||||
} else if (key->travel < key->rapid.deactn_pt) {
|
||||
update_rapid_pts = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case AKS_RAPID_PRESSED:
|
||||
// Key releasing
|
||||
if (key->travel > FULL_TRAVEL_UNIT * TRAVEL_SCALE) {
|
||||
break;
|
||||
}
|
||||
if (key->travel <= key->regular.deactn_pt) {
|
||||
key->state = AKS_REGULAR_RELEASED;
|
||||
changed = true;
|
||||
} else if (key->travel <= key->rapid.deactn_pt && key->travel < BOTTOM_DEAD_ZONE * TRAVEL_SCALE - key->rpd_trig_sen_rls) {
|
||||
key->state = AKS_RAPID_RELEASED;
|
||||
changed = true;
|
||||
update_rapid_pts = -1;
|
||||
}
|
||||
// Continue pressing
|
||||
else if (key->travel > key->rapid.actn_pt) {
|
||||
update_rapid_pts = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (update_rapid_pts) {
|
||||
if (update_rapid_pts > 0) {
|
||||
key->rapid.deactn_pt = key->travel - key->rpd_trig_sen_rls > 0 ? key->travel - key->rpd_trig_sen_rls : 0;
|
||||
key->rapid.actn_pt = key->travel;
|
||||
} else {
|
||||
key->rapid.deactn_pt = key->travel;
|
||||
key->rapid.actn_pt = key->travel + key->rpd_trig_sen;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "analog_matrix.h"
|
||||
|
||||
bool regular_trigger_action(analog_key_t *key) {
|
||||
if (key->state == AKS_REGULAR_PRESSED && (key->travel == 0 || key->travel < key->regular.deactn_pt)) {
|
||||
key->state = AKS_REGULAR_RELEASED;
|
||||
return true;
|
||||
} else if (key->state == AKS_REGULAR_RELEASED && key->travel >= key->regular.actn_pt) {
|
||||
key->state = AKS_REGULAR_PRESSED;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
116
keyboards/keychron/common/analog_matrix/action_socd.c
Normal file
116
keyboards/keychron/common/analog_matrix/action_socd.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "profile.h"
|
||||
#include "action_socd.h"
|
||||
|
||||
enum {
|
||||
KEY_1_ACTIVE = 0x01,
|
||||
KEY_2_ACTIVE = 0x02,
|
||||
BOTH_KEYS_ACTIVE = 0x03,
|
||||
};
|
||||
|
||||
extern matrix_row_t analog_raw_matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t raw_matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t changed_matrix[MATRIX_ROWS];
|
||||
|
||||
void socd_action(void) {
|
||||
matrix_row_t socd_mask[MATRIX_ROWS] = {0};
|
||||
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
|
||||
socd_mask[row] = ~socd_mask[row];
|
||||
}
|
||||
|
||||
socd_config_t *socd = profile_get_current()->socd;
|
||||
static uint8_t state[SOCD_COUNT];
|
||||
bool keep_last_state;
|
||||
uint8_t row1, row2, col1, col2;
|
||||
for (uint8_t i = 0; i < SOCD_COUNT; i++) {
|
||||
if (socd[i].type) {
|
||||
keep_last_state = false;
|
||||
row1 = socd[i].key_1_row;
|
||||
col1 = socd[i].key_1_col;
|
||||
row2 = socd[i].key_2_row;
|
||||
col2 = socd[i].key_2_col;
|
||||
|
||||
if ((analog_raw_matrix[row1] & (0x01 << col1)) && (analog_raw_matrix[row2] & (0x01 << col2))) {
|
||||
switch (socd[i].type) {
|
||||
case SOCD_PRI_DEEPER_TRAVEL:
|
||||
case SOCD_PRI_DEEPER_TRAVEL_SINGLE:
|
||||
if (analog_matrix_get_travel(row1, col1) > 230 && analog_matrix_get_travel(row2, col2) > 230) {
|
||||
if (socd[i].type == SOCD_PRI_DEEPER_TRAVEL_SINGLE) {
|
||||
keep_last_state = true;
|
||||
}
|
||||
} else if (state[i] == KEY_2_ACTIVE && analog_matrix_get_travel(row1, col1) > analog_matrix_get_travel(row2, col2)) {
|
||||
socd_mask[row2] &= ~(0x01 << col2);
|
||||
} else if (state[i] == KEY_1_ACTIVE && analog_matrix_get_travel(row1, col1) < analog_matrix_get_travel(row2, col2)) {
|
||||
socd_mask[row1] &= ~(0x01 << col1);
|
||||
} else
|
||||
keep_last_state = true;
|
||||
break;
|
||||
|
||||
case SOCD_PRI_LAST_KEYSTROKE:
|
||||
if ((raw_matrix[row1] & (0x01 << col1)) && (analog_raw_matrix[row2] & (0x01 << col2)) && (changed_matrix[row2] & (0x01 << col2))) {
|
||||
socd_mask[row1] &= ~(0x01 << col1);
|
||||
state[i] = KEY_2_ACTIVE;
|
||||
|
||||
} else if ((raw_matrix[row2] & (0x01 << col2)) && (analog_raw_matrix[row1] & (0x01 << col1)) && (changed_matrix[row1] & (0x01 << col1))) {
|
||||
socd_mask[row2] &= ~(0x01 << col2);
|
||||
state[i] = KEY_1_ACTIVE;
|
||||
|
||||
} else
|
||||
keep_last_state = true;
|
||||
break;
|
||||
|
||||
case SOCD_PRI_KEY_1:
|
||||
socd_mask[row2] &= ~(0x01 << col2);
|
||||
state[i] = KEY_1_ACTIVE;
|
||||
break;
|
||||
|
||||
case SOCD_PRI_KEY_2:
|
||||
socd_mask[row1] &= ~(0x01 << col1);
|
||||
state[i] = KEY_2_ACTIVE;
|
||||
break;
|
||||
|
||||
case SOCD_PRI_NEUTRAL:
|
||||
socd_mask[row1] &= ~(0x01 << col1);
|
||||
socd_mask[row2] &= ~(0x01 << col2);
|
||||
state[i] = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (analog_raw_matrix[row1] & (0x01 << col1)) {
|
||||
state[i] = KEY_1_ACTIVE;
|
||||
} else if (analog_raw_matrix[row2] & (0x01 << col2)) {
|
||||
state[i] = KEY_2_ACTIVE;
|
||||
}
|
||||
|
||||
if (keep_last_state) {
|
||||
if (state[i] == KEY_1_ACTIVE)
|
||||
socd_mask[row2] &= ~(0x01 << col2);
|
||||
else if (state[i] == KEY_2_ACTIVE)
|
||||
socd_mask[row1] &= ~(0x01 << col1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
|
||||
raw_matrix[row] = analog_raw_matrix[row] & socd_mask[row];
|
||||
}
|
||||
}
|
||||
27
keyboards/keychron/common/analog_matrix/action_socd.h
Normal file
27
keyboards/keychron/common/analog_matrix/action_socd.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// SOCD Prioritization
|
||||
enum {
|
||||
SOCD_PRI_NONE = 0,
|
||||
SOCD_PRI_DEEPER_TRAVEL,
|
||||
SOCD_PRI_DEEPER_TRAVEL_SINGLE,
|
||||
SOCD_PRI_LAST_KEYSTROKE,
|
||||
SOCD_PRI_KEY_1,
|
||||
SOCD_PRI_KEY_2,
|
||||
SOCD_PRI_NEUTRAL,
|
||||
SOCD_PRI_MAX,
|
||||
};
|
||||
29
keyboards/keychron/common/analog_matrix/action_toggle.c
Normal file
29
keyboards/keychron/common/analog_matrix/action_toggle.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "analog_matrix.h"
|
||||
|
||||
bool toggle_action(analog_key_t *key) {
|
||||
if (key->state == AKS_REGULAR_PRESSED && (key->travel == 0 || key->travel < key->regular.deactn_pt)) {
|
||||
key->state = AKS_REGULAR_RELEASED;
|
||||
} else if (key->state == AKS_REGULAR_RELEASED && key->travel > key->regular.actn_pt) {
|
||||
key->state = AKS_REGULAR_PRESSED;
|
||||
key->hold = !key->hold;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
226
keyboards/keychron/common/analog_matrix/action_xinput.c
Normal file
226
keyboards/keychron/common/analog_matrix/action_xinput.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "game_controller_common.h"
|
||||
#include "sqrt.h"
|
||||
|
||||
#ifdef XINPUT_ENABLE
|
||||
extern point_t curve[CURVE_POINTS_COUNT];
|
||||
extern float slope[CURVE_POINTS_COUNT - 1];
|
||||
extern bool regular_trigger_action(analog_key_t *key);
|
||||
extern matrix_row_t game_controller_matrix[MATRIX_ROWS];
|
||||
|
||||
static report_xinput_t xinput;
|
||||
static bool xinput_changed = false;
|
||||
|
||||
static uint8_t axis_travel[GC_MAX] = {0};
|
||||
static bool axis_changed[GC_MAX] = {0};
|
||||
static int8_t axis_dir[GC_MAX / 2] = {0};
|
||||
static int32_t axis_value[GC_MAX / 2] = {0};
|
||||
|
||||
static uint16_t travel_to_xinput_value(uint8_t axis, uint8_t travel) {
|
||||
static uint16_t max_values[GC_MAX] = {0x8000, 0x7FFF, 0x8000, 0x7FFF, 0xFF, 0xFF, 0x8000, 0x7FFF, 0x8000, 0x7FFF, 0x8000, 0x7FFF};
|
||||
uint32_t axis_value = 0;
|
||||
|
||||
if (axis == GC_Z_AXIS_P || axis == GC_Z_AXIS_N) {
|
||||
// Don't apply curve to L/R Trigger
|
||||
axis_value = travel * 127 / FULL_TRAVEL_UNIT / TRAVEL_SCALE;
|
||||
} else if (travel == 0) {
|
||||
axis_value = 0;
|
||||
} else if (travel >= curve[3].x * TRAVEL_SCALE) {
|
||||
axis_value = curve[3].y;
|
||||
} else if (travel >= curve[2].x * TRAVEL_SCALE) {
|
||||
axis_value = curve[2].y + slope[2] * (travel - curve[2].x * TRAVEL_SCALE) / TRAVEL_SCALE;
|
||||
} else if (travel >= curve[1].x * TRAVEL_SCALE) {
|
||||
axis_value = curve[1].y + slope[1] * (travel - curve[1].x * TRAVEL_SCALE) / TRAVEL_SCALE;
|
||||
} else if (travel >= curve[0].x * TRAVEL_SCALE) {
|
||||
axis_value = curve[0].y + slope[0] * (travel - curve[0].x * TRAVEL_SCALE) / TRAVEL_SCALE;
|
||||
}
|
||||
|
||||
uint16_t range = max_values[axis];
|
||||
|
||||
axis_value = axis_value * range / 127;
|
||||
if (axis_value > range) axis_value = range;
|
||||
|
||||
return axis_value & 0xFFFF;
|
||||
}
|
||||
|
||||
bool xinput_update(analog_key_t *key) {
|
||||
|
||||
if (key->travel < 1 * TRAVEL_SCALE) key->travel = 0;
|
||||
|
||||
if (key->js_axis < GC_MAX) {
|
||||
axis_travel[key->js_axis] = key->travel;
|
||||
axis_changed[key->js_axis] = true;
|
||||
}
|
||||
|
||||
if (regular_trigger_action(key)) {
|
||||
static uint8_t xinput_buttons_map[16] = {12, 13, 14, 15, 8, 9, 5, 4, 6, 7, 0, 1, 2, 3, 10, 11};
|
||||
uint8_t idx = key->js_axis - GC_BUTTON_0;
|
||||
|
||||
if (key->state == AKS_REGULAR_PRESSED) {
|
||||
if (key->js_axis >= GC_BUTTON_0 && key->js_axis < GC_BUTTON_0 + 16) {
|
||||
xinput.buttons |= (0x01 << xinput_buttons_map[idx]);
|
||||
xinput_changed = true;
|
||||
}
|
||||
game_controller_matrix[key->r] |= 0x01 << key->c;
|
||||
} else {
|
||||
if (key->js_axis >= GC_BUTTON_0 && key->js_axis < GC_BUTTON_0 + 16) {
|
||||
xinput.buttons &= (~(0x01 << xinput_buttons_map[idx]));
|
||||
xinput_changed = true;
|
||||
}
|
||||
game_controller_matrix[key->r] &= ~(0x01 << key->c);
|
||||
}
|
||||
|
||||
if (game_controller_type_enabled()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void xinput_flush(void) {
|
||||
if (xinput_changed) {
|
||||
static report_xinput_t last_report;
|
||||
|
||||
if (memcmp(&xinput, &last_report, sizeof(report_xinput_t)) != 0) {
|
||||
memcpy(&last_report, &xinput, sizeof(report_xinput_t));
|
||||
host_xinput_send(&xinput);
|
||||
}
|
||||
xinput_changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void xinput_action(void) {
|
||||
# define AXIS_DIRECT_VALUE (2 * TRAVEL_SCALE)
|
||||
uint8_t axis_neg, axis_pos;
|
||||
|
||||
for (uint8_t i = 0; i < GC_AXIS_MAX; i++) {
|
||||
axis_neg = i * 2;
|
||||
axis_pos = i * 2 + 1;
|
||||
|
||||
if (i == GC_AXIS_Z) {
|
||||
/* Handle z axis as left trigger and right trigger */
|
||||
xinput.left_trigger = travel_to_xinput_value(axis_neg, axis_travel[axis_neg]);
|
||||
xinput.right_trigger = travel_to_xinput_value(axis_pos, axis_travel[axis_pos]);
|
||||
xinput_changed = true;
|
||||
} else if (axis_changed[i] || axis_changed[axis_pos]) {
|
||||
if (axis_travel[axis_neg] > TRAVEL_SCALE && axis_travel[axis_pos] > TRAVEL_SCALE) {
|
||||
// Both direction key are pressed
|
||||
if (axis_travel[axis_neg] > axis_travel[axis_pos] + AXIS_DIRECT_VALUE)
|
||||
axis_dir[i] = -1;
|
||||
else if (axis_travel[axis_neg] + AXIS_DIRECT_VALUE < axis_travel[axis_pos])
|
||||
axis_dir[i] = 1;
|
||||
} else if (axis_travel[axis_neg]) {
|
||||
// Negative direction
|
||||
axis_dir[i] = -1;
|
||||
} else if (axis_travel[axis_pos]) {
|
||||
// Positive direction
|
||||
axis_dir[i] = 1;
|
||||
} else {
|
||||
axis_dir[i] = 0;
|
||||
}
|
||||
|
||||
if (axis_dir[i] < 0) {
|
||||
axis_value[i] = -travel_to_xinput_value(axis_neg, axis_travel[axis_neg]);
|
||||
} else {
|
||||
axis_value[i] = travel_to_xinput_value(axis_pos, axis_travel[axis_pos]);
|
||||
}
|
||||
|
||||
xinput_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (xinput_changed) {
|
||||
uint32_t x = axis_value[GC_AXIS_X] >> 8;
|
||||
uint32_t y = axis_value[GC_AXIS_Y] >> 8;
|
||||
|
||||
uint32_t axis_square = x * x + y * y;
|
||||
uint32_t r_square = 0x80 * 0x80;
|
||||
if (axis_square > r_square) {
|
||||
uint32_t sqrt = sqrt_uint32(axis_square);
|
||||
float ratio = 140.0f / sqrt;
|
||||
|
||||
axis_value[GC_AXIS_X] = axis_value[GC_AXIS_X] * ratio;
|
||||
axis_value[GC_AXIS_Y] = axis_value[GC_AXIS_Y] * ratio;
|
||||
|
||||
if (axis_dir[GC_AXIS_X] < 0) {
|
||||
if (axis_value[GC_AXIS_X] < -0x8000) axis_value[GC_AXIS_X] = -0x8000;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_X] > 0x7FFF) axis_value[GC_AXIS_X] = 0x7FFF;
|
||||
}
|
||||
|
||||
if (axis_dir[GC_AXIS_Y] < 0) {
|
||||
if (axis_value[GC_AXIS_Y] < -0x8000) axis_value[GC_AXIS_Y] = -0x8000;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_Y] > 0x7FFF) axis_value[GC_AXIS_Y] = 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
x = axis_value[GC_AXIS_RX] >> 8;
|
||||
y = axis_value[GC_AXIS_RY] >> 8;
|
||||
|
||||
axis_square = x * x + y * y;
|
||||
if (axis_square > r_square) {
|
||||
uint32_t sqrt = sqrt_uint32(axis_square);
|
||||
float ratio = 140.0f / sqrt;
|
||||
|
||||
axis_value[GC_AXIS_RX] = axis_value[GC_AXIS_RX] * ratio;
|
||||
axis_value[GC_AXIS_RY] = axis_value[GC_AXIS_RY] * ratio;
|
||||
|
||||
if (axis_dir[GC_AXIS_RX] < 0) {
|
||||
if (axis_value[GC_AXIS_RX] < -0x8000) axis_value[GC_AXIS_RX] = -0x8000;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_RX] > 0x7FFF) axis_value[GC_AXIS_RX] = 0x7FFF;
|
||||
}
|
||||
|
||||
if (axis_dir[GC_AXIS_RY] < 0) {
|
||||
if (axis_value[GC_AXIS_RY] < -0x8000) axis_value[GC_AXIS_RY] = -0x8000;
|
||||
} else {
|
||||
if (axis_value[GC_AXIS_RY] > 0x7FFF) axis_value[GC_AXIS_RY] = 0x7FFF;
|
||||
}
|
||||
}
|
||||
|
||||
xinput.x = axis_value[GC_AXIS_X];
|
||||
xinput.y = axis_value[GC_AXIS_Y];
|
||||
xinput.rx = axis_value[GC_AXIS_RX];
|
||||
xinput.ry = axis_value[GC_AXIS_RY];
|
||||
}
|
||||
}
|
||||
|
||||
void xinput_clear(void) {
|
||||
memset(axis_travel, 0, sizeof(axis_travel));
|
||||
memset(axis_changed, 0, sizeof(axis_changed));
|
||||
memset(axis_dir, 0, sizeof(axis_dir));
|
||||
memset(axis_value, 0, sizeof(axis_value));
|
||||
|
||||
memset(&xinput, 0, sizeof(report_xinput_t));
|
||||
|
||||
xinput_changed = true;
|
||||
xinput_flush();
|
||||
}
|
||||
|
||||
void xinput_task(void) {
|
||||
# ifdef JOYSTICK_ENABLE
|
||||
if (game_controller_xinput_enabled())
|
||||
# endif
|
||||
{
|
||||
xinput_action();
|
||||
xinput_flush();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
1016
keyboards/keychron/common/analog_matrix/analog_matrix.c
Normal file
1016
keyboards/keychron/common/analog_matrix/analog_matrix.c
Normal file
File diff suppressed because it is too large
Load Diff
129
keyboards/keychron/common/analog_matrix/analog_matrix.h
Normal file
129
keyboards/keychron/common/analog_matrix/analog_matrix.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "keycodes.h"
|
||||
#include "matrix.h"
|
||||
#include "analog_matrix_eeconfig.h"
|
||||
#include "analog_matrix_type.h"
|
||||
|
||||
#define FULL_TRAVEL_UNIT 40
|
||||
|
||||
#ifndef DEFAULT_ACTUATION_POINT
|
||||
# define DEFAULT_ACTUATION_POINT 20
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_RAPID_TRIGGER_SENSITIVITY
|
||||
# define DEFAULT_RAPID_TRIGGER_SENSITIVITY 4
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_ZERO_TRAVEL_VALUE
|
||||
# define DEFAULT_ZERO_TRAVEL_VALUE 3000
|
||||
#endif
|
||||
|
||||
#ifndef DEFAULT_FULL_RANGE
|
||||
# define DEFAULT_FULL_RANGE 900
|
||||
#endif
|
||||
|
||||
#define DEFAULT_FULL_TRAVEL_VALUE (DEFAULT_ZERO_TRAVEL_VALUE - DEFAULT_FULL_RANGE)
|
||||
|
||||
#ifndef VALID_ANALOG_RAW_VALUE_MIN
|
||||
# define VALID_ANALOG_RAW_VALUE_MIN 1200
|
||||
#endif
|
||||
|
||||
#ifndef VALID_ANALOG_RAW_VALUE_MAX
|
||||
# define VALID_ANALOG_RAW_VALUE_MAX 3500
|
||||
#endif
|
||||
|
||||
#ifndef STATIC_HYSTERESIS
|
||||
# define STATIC_HYSTERESIS 5
|
||||
#endif
|
||||
|
||||
#ifndef RAPID_TRIGGER_TICK
|
||||
# define RAPID_TRIGGER_TICK 10
|
||||
#endif
|
||||
|
||||
#ifndef MIN_ACTUATION
|
||||
# define MIN_ACTUATION 5
|
||||
#endif
|
||||
|
||||
#ifndef ZERO_TRAVEL_DEAD_ZONE
|
||||
# define ZERO_TRAVEL_DEAD_ZONE 20
|
||||
#endif
|
||||
|
||||
#ifndef BOTTOM_DEAD_ZONE
|
||||
# define BOTTOM_DEAD_ZONE 38
|
||||
#endif
|
||||
|
||||
#ifndef BOTTOM_JITTER
|
||||
# define BOTTOM_JITTER 80
|
||||
#endif
|
||||
|
||||
#define TRAVEL_SCALE 6
|
||||
|
||||
#ifndef ANALOG_DEBOUCE_TIME
|
||||
# define ANALOG_DEBOUCE_TIME 3
|
||||
#endif
|
||||
|
||||
// Threshold value when the magnet switch is not installed
|
||||
#ifndef ABNORMAL_ANALOG_RAW_THRESHOLD_VALUE
|
||||
# define ABNORMAL_ANALOG_RAW_THRESHOLD_VALUE 3250
|
||||
#endif
|
||||
|
||||
//
|
||||
#ifndef AUTO_CALIB_FULL_TRAVEL_THRESHOLD_VALUE
|
||||
# define AUTO_CALIB_FULL_TRAVEL_THRESHOLD_VALUE (DEFAULT_ZERO_TRAVEL_VALUE - DEFAULT_FULL_RANGE + 100)
|
||||
#endif
|
||||
|
||||
#ifndef AUTO_CALIB_ZERO_TRAVEL_JITTER_VALUE
|
||||
# define AUTO_CALIB_ZERO_TRAVEL_JITTER_VALUE 50
|
||||
#endif
|
||||
|
||||
#ifndef AUTO_CALIB_FULL_TRAVEL_JITTER_VALUE
|
||||
# define AUTO_CALIB_FULL_TRAVEL_JITTER_VALUE 100
|
||||
#endif
|
||||
|
||||
#ifndef AUTO_CALIB_ZERO_TRAVEL_THRESHOLD_VALUE
|
||||
# define AUTO_CALIB_ZERO_TRAVEL_THRESHOLD_VALUE (DEFAULT_FULL_RANGE - AUTO_CALIB_FULL_TRAVEL_JITTER_VALUE)
|
||||
#endif
|
||||
|
||||
#ifndef AUTO_CALIB_VALID_RELASING_TIME
|
||||
# define AUTO_CALIB_VALID_RELASING_TIME 1000
|
||||
#endif
|
||||
|
||||
void analog_matrix_init(void);
|
||||
void analog_matrix_eeconfig_init(void);
|
||||
bool update_raw_value(uint8_t row, uint8_t col, uint16_t value);
|
||||
void update_travel_configs(void);
|
||||
void update_key_config(uint8_t row, uint8_t col);
|
||||
void analog_matrix_eeprom_update(const void *buf, void *addr, size_t len);
|
||||
|
||||
void analog_matrix_set_mins(uint16_t *min);
|
||||
void analog_matrix_set_maxs(uint16_t *max);
|
||||
|
||||
uint8_t analog_matrix_get_travel(uint8_t row, uint8_t col);
|
||||
uint8_t analog_matrix_get_key_mode(uint8_t row, uint8_t col);
|
||||
bool analog_matrix_get_key_state(uint8_t row, uint8_t col);
|
||||
bool analog_matrix_calibrating(void);
|
||||
matrix_row_t analog_matrix_get_row(uint8_t row);
|
||||
void analog_matrix_rx(uint8_t *data, uint8_t length);
|
||||
void analog_matrix_task(void);
|
||||
void analog_matrix_indicator(void);
|
||||
void analog_matrix_clear(void);
|
||||
void analog_matrix_clear_advance_keys(void);
|
||||
24
keyboards/keychron/common/analog_matrix/analog_matrix.mk
Normal file
24
keyboards/keychron/common/analog_matrix/analog_matrix.mk
Normal file
@@ -0,0 +1,24 @@
|
||||
USE_FPU = yes
|
||||
|
||||
OPT_DEFS += -DANANLOG_MATRIX
|
||||
|
||||
ANALOG_MATRX_DIR = common/analog_matrix
|
||||
SRC += \
|
||||
i2c_master.c \
|
||||
$(ANALOG_MATRX_DIR)/eeprom_he.c \
|
||||
$(ANALOG_MATRX_DIR)/analog_matrix_scan.c \
|
||||
$(ANALOG_MATRX_DIR)/profile.c \
|
||||
$(ANALOG_MATRX_DIR)/usb_descriptor_override.c \
|
||||
$(ANALOG_MATRX_DIR)/action_regular_trigger.c \
|
||||
$(ANALOG_MATRX_DIR)/action_rapid_trigger.c \
|
||||
$(ANALOG_MATRX_DIR)/action_okmc.c \
|
||||
$(ANALOG_MATRX_DIR)/action_toggle.c \
|
||||
$(ANALOG_MATRX_DIR)/action_joystick.c \
|
||||
$(ANALOG_MATRX_DIR)/action_xinput.c \
|
||||
$(ANALOG_MATRX_DIR)/action_socd.c \
|
||||
$(ANALOG_MATRX_DIR)/sqrt.c \
|
||||
$(ANALOG_MATRX_DIR)/game_controller_common.c \
|
||||
$(ANALOG_MATRX_DIR)/analog_matrix.c
|
||||
|
||||
VPATH += $(TOP_DIR)/keyboards/keychron/$(ANALOG_MATRX_DIR)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* Copyright 2024 ~ 2025 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef PROFILE_COUNT
|
||||
# define PROFILE_COUNT 3
|
||||
#endif
|
||||
|
||||
#ifndef OKMC_COUNT
|
||||
# define OKMC_COUNT 20
|
||||
#endif
|
||||
|
||||
#ifndef SOCD_COUNT
|
||||
# define SOCD_COUNT 20
|
||||
#endif
|
||||
|
||||
#define CURVE_POINTS_COUNT 4
|
||||
|
||||
#define KC_ANALOG_MATRIX_VERSION 0x34340004
|
||||
#define SIZE_OF_CALIB_VALUE_T 3 // Size of calibrated_value_t
|
||||
#define SIZE_OF_ANALOG_KEY_CONFIG_T 4 // Size of analog_key_config_t
|
||||
#define SIZE_OF_OKMC_CONFIG_T 19 // Size of okmc_config_t
|
||||
#define SIZE_OF_SOCD_CONFIG_T 3 // Size of socd_config_t
|
||||
#define SIZE_OF_POINT_T 2 // Size of point_t
|
||||
#define PROFILE_NAME_LEN 30
|
||||
|
||||
// clang-format off
|
||||
#define PROFILE_SIZE ( \
|
||||
2 + \
|
||||
(SIZE_OF_ANALOG_KEY_CONFIG_T) * (MATRIX_ROWS * MATRIX_COLS + 1) + \
|
||||
(PROFILE_NAME_LEN) + \
|
||||
(SIZE_OF_OKMC_CONFIG_T) * (OKMC_COUNT) + \
|
||||
(SIZE_OF_SOCD_CONFIG_T) * (SOCD_COUNT) \
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
#define OFFSET_CALIBRATION 0
|
||||
#define OFFSET_CURRENT_PROFILE (OFFSET_CALIBRATION + 1)
|
||||
|
||||
#define OFFSET_CALIBRATED_DATA_START (OFFSET_CURRENT_PROFILE)
|
||||
#define OFFSET_CALIBRATED_DATA_END (OFFSET_CALIBRATED_DATA_START + MATRIX_ROWS * MATRIX_COLS * SIZE_OF_CALIB_VALUE_T)
|
||||
|
||||
#define OFFSET_PROFILES_START (OFFSET_CALIBRATED_DATA_END)
|
||||
#define OFFSET_PROFILES_END (OFFSET_PROFILES_START + PROFILE_SIZE * PROFILE_COUNT)
|
||||
|
||||
#define OFFSET_CURVE_PTS_START (OFFSET_PROFILES_END )
|
||||
#define OFFSET_CURVE_PTS_END (OFFSET_CURVE_PTS_START + CURVE_POINTS_COUNT * SIZE_OF_POINT_T)
|
||||
|
||||
#define OFFSET_GAME_CONTROLLER_MODE_START OFFSET_CURVE_PTS_END
|
||||
#define OFFSET_GAME_CONTROLLER_MODE_END (OFFSET_GAME_CONTROLLER_MODE_START + 1)
|
||||
|
||||
/* Size of analog matrix eeconfig */
|
||||
#define EECONFIG_SIZE_ANALOG_MATRIX OFFSET_GAME_CONTROLLER_MODE_END
|
||||
/* EE config data version */
|
||||
#define EECONFIG_KB_DATA_VERSION KC_ANALOG_MATRIX_VERSION
|
||||
|
||||
#define EXTERNAL_EEPROM_OFFSET 4
|
||||
369
keyboards/keychron/common/analog_matrix/analog_matrix_scan.c
Normal file
369
keyboards/keychron/common/analog_matrix/analog_matrix_scan.c
Normal file
@@ -0,0 +1,369 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stdint.h"
|
||||
#include "hal.h"
|
||||
#include "gpio.h"
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "debounce.h"
|
||||
#ifdef LK_WIRELESS_ENABLE
|
||||
# include "lpm.h"
|
||||
#endif
|
||||
|
||||
#ifndef HC164_DS
|
||||
# define HC164_DS B3
|
||||
#endif
|
||||
#ifndef HC164_CP
|
||||
# define HC164_CP B5
|
||||
#endif
|
||||
#ifndef HC164_MR
|
||||
# define HC164_MR D2
|
||||
#endif
|
||||
|
||||
#define ADC_GRP_NUM_CHANNELS MATRIX_ROWS
|
||||
#define ADC_GRP_BUF_DEPTH 1
|
||||
#define UNUSED_DEPTH 0
|
||||
|
||||
extern matrix_row_t raw_matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t game_controller_matrix[MATRIX_ROWS];
|
||||
extern matrix_row_t okmc_matrix[MATRIX_ROWS];
|
||||
matrix_row_t analog_raw_matrix[MATRIX_ROWS];
|
||||
matrix_row_t changed_matrix[MATRIX_ROWS];
|
||||
|
||||
pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
|
||||
pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
|
||||
matrix_row_t matrix_mask[MATRIX_ROWS];
|
||||
matrix_row_t virtual_matrix[MATRIX_ROWS] = {0};
|
||||
static bool matrix_changed;
|
||||
|
||||
static adcsample_t samples[ADC_GRP_NUM_CHANNELS * ADC_GRP_BUF_DEPTH];
|
||||
|
||||
static void adcerrorcallback(ADCDriver *adcp, adcerror_t err) {
|
||||
dprintf("err\r\n");
|
||||
|
||||
(void)adcp;
|
||||
(void)err;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
ADCConversionGroup adcgrpcfg = {
|
||||
FALSE,
|
||||
6,
|
||||
NULL,
|
||||
adcerrorcallback,
|
||||
0, /* CR1 */
|
||||
ADC_CR2_SWSTART, /* CR2 */
|
||||
0, /* SMPR1 */
|
||||
0, /* SMPR2 */
|
||||
0, /* HTR */
|
||||
0, /* LTR */
|
||||
0, /* SQR1 */
|
||||
0, /* SQR2 */
|
||||
0 /* SQR3 */
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
uint8_t pinToAdcChn(pin_t pin) {
|
||||
switch (pin) {
|
||||
case A0:
|
||||
return ADC_CHANNEL_IN0;
|
||||
case A1:
|
||||
return ADC_CHANNEL_IN1;
|
||||
case A2:
|
||||
return ADC_CHANNEL_IN2;
|
||||
case A3:
|
||||
return ADC_CHANNEL_IN3;
|
||||
case A4:
|
||||
return ADC_CHANNEL_IN4;
|
||||
case A5:
|
||||
return ADC_CHANNEL_IN5;
|
||||
case A6:
|
||||
return ADC_CHANNEL_IN6;
|
||||
case A7:
|
||||
return ADC_CHANNEL_IN7;
|
||||
case B0:
|
||||
return ADC_CHANNEL_IN8;
|
||||
case B1:
|
||||
return ADC_CHANNEL_IN9;
|
||||
case C0:
|
||||
return ADC_CHANNEL_IN10;
|
||||
case C1:
|
||||
return ADC_CHANNEL_IN11;
|
||||
case C2:
|
||||
return ADC_CHANNEL_IN12;
|
||||
case C3:
|
||||
return ADC_CHANNEL_IN13;
|
||||
case C4:
|
||||
return ADC_CHANNEL_IN14;
|
||||
case C5:
|
||||
return ADC_CHANNEL_IN15;
|
||||
}
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static inline void shifter_delay(uint16_t n) {
|
||||
while (n-- > 0) {
|
||||
asm volatile("nop" ::: "memory");
|
||||
}
|
||||
}
|
||||
|
||||
static void HC164_output(uint16_t data, bool bit_flag) {
|
||||
uint8_t n = 50;
|
||||
|
||||
ATOMIC_BLOCK_FORCEON {
|
||||
for (uint8_t i = 0; i < 15; i++) {
|
||||
if (data & 0x1) {
|
||||
writePinHigh(HC164_DS);
|
||||
} else {
|
||||
writePinLow(HC164_DS);
|
||||
}
|
||||
shifter_delay(n);
|
||||
writePinHigh(HC164_CP);
|
||||
shifter_delay(n);
|
||||
writePinLow(HC164_CP);
|
||||
shifter_delay(n);
|
||||
if (bit_flag) {
|
||||
break;
|
||||
} else {
|
||||
data = data >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool select_col(uint8_t col) {
|
||||
if (col == 0) {
|
||||
writePinLow(HC164_MR);
|
||||
shifter_delay(20);
|
||||
writePinHigh(HC164_MR);
|
||||
shifter_delay(20);
|
||||
HC164_output(0x01, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void unselect_col(uint8_t col) {
|
||||
HC164_output(0x00, true);
|
||||
return;
|
||||
}
|
||||
|
||||
void select_all_cols(void) {}
|
||||
static void unselect_cols(void) {}
|
||||
|
||||
void matrix_read_rows_on_col(uint8_t current_col, matrix_row_t row_shifter) {
|
||||
// Select col
|
||||
if (!select_col(current_col)) {
|
||||
return; // skip NO_PIN col
|
||||
}
|
||||
|
||||
wait_us(40);
|
||||
|
||||
uint8_t debouce_times = ANALOG_DEBOUCE_TIME;
|
||||
uint8_t row_value = 0;
|
||||
bool changed = false;
|
||||
|
||||
do {
|
||||
adcConvert(&ADCD1, &adcgrpcfg, samples, ADC_GRP_BUF_DEPTH);
|
||||
|
||||
uint8_t row_value_recheck = 0;
|
||||
for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
|
||||
matrix_row_t row_mask = 0x01 << current_col;
|
||||
update_raw_value(row_index, current_col, samples[row_index]);
|
||||
|
||||
bool pressed = analog_matrix_get_key_state(row_index, current_col);
|
||||
if (pressed) {
|
||||
if ((analog_raw_matrix[row_index] & row_mask) == 0) changed = true;
|
||||
|
||||
if (debouce_times == ANALOG_DEBOUCE_TIME) {
|
||||
row_value |= (0x01 << row_index);
|
||||
} else {
|
||||
row_value_recheck |= (0x01 << row_index);
|
||||
}
|
||||
} else if (analog_raw_matrix[row_index] & row_mask) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (debouce_times != ANALOG_DEBOUCE_TIME && row_value != row_value_recheck) {
|
||||
// Clear state when bounce occurs
|
||||
changed = false;
|
||||
}
|
||||
|
||||
} while (--debouce_times && changed);
|
||||
|
||||
if (changed) {
|
||||
matrix_changed = true;
|
||||
for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) {
|
||||
if (row_value & (0x01 << row_index)) {
|
||||
if ((analog_raw_matrix[row_index] & row_shifter) == 0) changed_matrix[row_index] |= row_shifter; // Mark changed matrix position
|
||||
analog_raw_matrix[row_index] |= row_shifter; // Update matrix
|
||||
} else {
|
||||
if ((analog_raw_matrix[row_index] & row_shifter)) changed_matrix[row_index] |= row_shifter;
|
||||
analog_raw_matrix[row_index] &= ~row_shifter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unselect col
|
||||
unselect_col(current_col);
|
||||
}
|
||||
|
||||
void matrix_init_custom(void) {
|
||||
uint32_t smpr[2] = {0, 0};
|
||||
uint32_t sqr[3] = {0, 0, 0};
|
||||
uint8_t chn;
|
||||
uint8_t chn_cnt = 0;
|
||||
|
||||
#ifdef ANALOG_MATRIX_POWER_PIN
|
||||
setPinOutput(ANALOG_MATRIX_POWER_PIN);
|
||||
writePin(ANALOG_MATRIX_POWER_PIN, ANALOG_MATRIX_POWER_ENABLE_LEVEL);
|
||||
#endif
|
||||
#ifdef ANALOG_MATRIX_WAKEUP_PIN
|
||||
setPinInputHigh(ANALOG_MATRIX_WAKEUP_PIN);
|
||||
#endif
|
||||
#ifdef ENCODER_SWITCH_PIN
|
||||
setPinInputHigh(ENCODER_SWITCH_PIN);
|
||||
#endif
|
||||
|
||||
// Init shift register control pins
|
||||
setPinOutput(HC164_DS);
|
||||
setPinOutput(HC164_CP);
|
||||
setPinOutput(HC164_MR);
|
||||
writePinLow(HC164_MR);
|
||||
|
||||
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
|
||||
if (row_pins[x] != NO_PIN) {
|
||||
palSetLineMode(row_pins[x], PAL_MODE_INPUT_ANALOG);
|
||||
palWriteLine(row_pins[x], 0);
|
||||
}
|
||||
|
||||
chn = pinToAdcChn(row_pins[x]);
|
||||
if (chn < 0xFF) {
|
||||
if (chn > 9)
|
||||
smpr[0] |= ADC_SAMPLE_56 << ((chn - 10) * 3);
|
||||
else
|
||||
smpr[1] |= ADC_SAMPLE_56 << (chn * 3);
|
||||
|
||||
sqr[chn_cnt / 6] |= chn << ((chn_cnt % 6) * 5);
|
||||
chn_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
adcgrpcfg.smpr1 = smpr[0];
|
||||
adcgrpcfg.smpr2 = smpr[1];
|
||||
|
||||
adcgrpcfg.sqr3 = sqr[0];
|
||||
adcgrpcfg.sqr2 = sqr[1];
|
||||
adcgrpcfg.sqr1 = sqr[2];
|
||||
|
||||
unselect_cols();
|
||||
adcStart(&ADCD1, NULL);
|
||||
|
||||
// Refer to STM32 AN4073 Option 2
|
||||
SYSCFG->PMC |= SYSCFG_PMC_ADC1DC2;
|
||||
|
||||
// Value of initial ADC seems abnormal, scan to skip/drop i
|
||||
for (uint8_t i = 0; i < 5; i++)
|
||||
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
|
||||
matrix_read_rows_on_col(current_col, 0);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
|
||||
analog_raw_matrix[i] = 0;
|
||||
changed_matrix[i] = 0;
|
||||
}
|
||||
|
||||
analog_matrix_init();
|
||||
}
|
||||
|
||||
bool matrix_scan_custom(matrix_row_t current_matrix[]) {
|
||||
matrix_row_t last_raw_matrix[MATRIX_ROWS];
|
||||
|
||||
memcpy(last_raw_matrix, raw_matrix, sizeof(raw_matrix));
|
||||
memcpy(virtual_matrix, matrix, sizeof(matrix));
|
||||
memset(changed_matrix, 0, sizeof(changed_matrix));
|
||||
matrix_changed = false;
|
||||
|
||||
// Set col, read rows
|
||||
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(current_col, row_shifter);
|
||||
}
|
||||
|
||||
analog_matrix_task();
|
||||
extern matrix_row_t analog_matrix_mask[];
|
||||
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
|
||||
raw_matrix[row] &= analog_matrix_mask[row];
|
||||
}
|
||||
|
||||
#if defined(ENCODER_MATRIX_ROW) && defined(ENCODER_MATRIX_ROW)
|
||||
if (readPin(ENCODER_SWITCH_PIN) == 0) {
|
||||
if ((raw_matrix[ENCODER_MATRIX_ROW] & (1 << ENCODER_MATROX_COL)) == 0) {
|
||||
matrix_changed = true;
|
||||
raw_matrix[ENCODER_MATRIX_ROW] |= (1 << ENCODER_MATROX_COL);
|
||||
}
|
||||
} else {
|
||||
if ((raw_matrix[ENCODER_MATRIX_ROW] & (1 << ENCODER_MATROX_COL))) {
|
||||
matrix_changed = true;
|
||||
raw_matrix[ENCODER_MATRIX_ROW] &= ~(1 << ENCODER_MATROX_COL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
|
||||
virtual_matrix[row] |= (game_controller_matrix[row] | okmc_matrix[row]);
|
||||
}
|
||||
|
||||
bool changed = memcmp(raw_matrix, last_raw_matrix, sizeof(last_raw_matrix)) != 0;
|
||||
// changed = debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
|
||||
|
||||
matrix_scan_kb();
|
||||
|
||||
return matrix_changed | changed;
|
||||
}
|
||||
|
||||
void matrix_lpm(void) {
|
||||
adcStop(&ADCD1);
|
||||
|
||||
#ifdef HC164_DS
|
||||
setPinInputLow(HC164_DS);
|
||||
#endif
|
||||
#ifdef HC164_CP
|
||||
setPinInputLow(HC164_CP);
|
||||
#endif
|
||||
#ifdef HC164_MR
|
||||
setPinInputLow(HC164_MR);
|
||||
#endif
|
||||
|
||||
#ifdef ANALOG_MATRIX_POWER_PIN
|
||||
writePin(ANALOG_MATRIX_POWER_PIN, !ANALOG_MATRIX_POWER_ENABLE_LEVEL);
|
||||
#endif
|
||||
|
||||
#ifdef ANALOG_MATRIX_WAKEUP_PIN
|
||||
palEnableLineEvent(ANALOG_MATRIX_WAKEUP_PIN, PAL_EVENT_MODE_FALLING_EDGE);
|
||||
#endif
|
||||
|
||||
// Set all row to input low
|
||||
pin_t pins_row[MATRIX_ROWS] = MATRIX_ROW_PINS;
|
||||
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
|
||||
if (pins_row[x] != NO_PIN) {
|
||||
setPinInputLow(pins_row[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
162
keyboards/keychron/common/analog_matrix/analog_matrix_type.h
Normal file
162
keyboards/keychron/common/analog_matrix/analog_matrix_type.h
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Analog key mode
|
||||
enum {
|
||||
AKM_GLOBAL = 0,
|
||||
// Basic mode
|
||||
AKM_REGULAR = 1, // Static actuation point
|
||||
AKM_RAPID = 2, // Rapid Trigger
|
||||
// Advance mode
|
||||
AKM_DKS,
|
||||
AKM_GAMEPAD,
|
||||
AKM_TOGGLE,
|
||||
};
|
||||
|
||||
// Analog key state
|
||||
enum {
|
||||
AKS_REGULAR_RELEASED,
|
||||
AKS_REGULAR_PRESSED,
|
||||
AKS_RAPID_RELEASED,
|
||||
AKS_RAPID_PRESSED,
|
||||
AKS_FULL_PRESSED,
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint8_t actn_pt;
|
||||
uint8_t deactn_pt;
|
||||
} activity_point_t;
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint8_t mode; // 1 byte
|
||||
uint8_t state; // 1 byte
|
||||
uint8_t travel; // 1 byte
|
||||
uint8_t last_travel; // for debug
|
||||
bool debug;
|
||||
uint8_t r;
|
||||
uint8_t c;
|
||||
uint16_t value; // 2 bytes, value
|
||||
uint16_t last_val; // 2 bytes, last value
|
||||
|
||||
activity_point_t regular; // 4 bytes, actuation point
|
||||
union { // 4 bytes
|
||||
activity_point_t rapid;
|
||||
activity_point_t full;
|
||||
};
|
||||
union { // 2 bytes
|
||||
uint8_t rpd_trig_sen; // rapid trig sensitivity
|
||||
uint8_t okmc_idx;
|
||||
uint8_t js_axis; // joystick x/y axis
|
||||
uint8_t hold;
|
||||
};
|
||||
uint8_t rpd_trig_sen_rls;
|
||||
} analog_key_t;
|
||||
// size of analog_key_t is 16 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t actn_pt; // unit: 0.1mm
|
||||
uint8_t deactn_pt; // unit: 0.1mm
|
||||
} traval_config_t;
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t mode:2; // 2 bits, basic mode
|
||||
uint8_t act_pt:6; // 6 bits
|
||||
uint8_t rpd_trig_sen:6; // 6 bits rapid trig sensitivity, unit: 0.1mm
|
||||
uint8_t rpd_trig_sen_deact:6; // 6 bits
|
||||
uint8_t adv_mode:4; // 4 bits, advance mode
|
||||
union { // 1 byte, additional information of advance mode
|
||||
uint8_t adv_mode_data;
|
||||
uint8_t okmc_idx; // okmc setting index
|
||||
uint8_t js_axis; // joystick x/y axis
|
||||
};
|
||||
} analog_key_config_t;
|
||||
// size of analog_key_config_t is 4 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t shallow_act:6; // unit: 0.1mm
|
||||
uint8_t shallow_deact:6; // unit: 0.1mm
|
||||
uint8_t deep_act:6; // unit: 0.1mm
|
||||
uint8_t deep_deact:6; // unit: 0.1mm
|
||||
} okmc_traval_config_t;
|
||||
// size = 3 bytes
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t shallow_act:4;
|
||||
uint8_t shallow_deact:4;
|
||||
uint8_t deep_act:4;
|
||||
uint8_t deep_deact:4;
|
||||
} okmc_action_t;
|
||||
// size = 2 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
okmc_traval_config_t travel; // 3
|
||||
uint16_t keycode[4]; // 2*4
|
||||
okmc_action_t action[4]; // 2*4
|
||||
} okmc_config_t;
|
||||
// size = 19 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t key_1_row:3;
|
||||
uint8_t key_1_col:5;
|
||||
uint8_t key_2_row:3;
|
||||
uint8_t key_2_col:5;
|
||||
uint8_t type;
|
||||
} socd_config_t;
|
||||
// size = 3 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
uint8_t x; // 1
|
||||
uint8_t y;; // 1
|
||||
} point_t;
|
||||
// size = 2 bytes
|
||||
|
||||
typedef struct __attribute__((__packed__)) {
|
||||
analog_key_config_t global;
|
||||
analog_key_config_t key_config[MATRIX_ROWS][MATRIX_COLS];
|
||||
okmc_config_t okmc[OKMC_COUNT];
|
||||
socd_config_t socd[SOCD_COUNT];
|
||||
uint8_t name[PROFILE_NAME_LEN];
|
||||
uint16_t crc16;
|
||||
} analog_matrix_profile_t;
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
uint16_t zero_travel:12; // zero travel
|
||||
uint16_t full_travel:12; // zero travel
|
||||
} calibrated_value_t;
|
||||
|
||||
typedef struct __attribute__((__packed__)){
|
||||
bool calibrated:1;
|
||||
uint8_t pressed:1;
|
||||
uint8_t wait_For_release:1;
|
||||
uint8_t new_calib_value:1;
|
||||
uint8_t state:4;
|
||||
|
||||
uint8_t confidence;
|
||||
calibrated_value_t value;
|
||||
uint32_t full_travel_time;
|
||||
uint8_t cycle;
|
||||
} calibration_t;
|
||||
|
||||
#pragma pack(pop)
|
||||
158
keyboards/keychron/common/analog_matrix/eeprom_he.c
Normal file
158
keyboards/keychron/common/analog_matrix/eeprom_he.c
Normal file
@@ -0,0 +1,158 @@
|
||||
/* Copyright 2019 Nick Brassel (tzarc)
|
||||
*
|
||||
* 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 <string.h>
|
||||
#if defined(EXTERNAL_EEPROM_WP_PIN)
|
||||
# include "gpio.h"
|
||||
#endif
|
||||
# include "eeprom_he.h"
|
||||
|
||||
/*
|
||||
Note that the implementations of eeprom_XXXX_YYYY on AVR are normally
|
||||
provided by avr-libc. The same functions are reimplemented below and are
|
||||
rerouted to the external i2c equivalent.
|
||||
|
||||
Seemingly, as this is compiled from within QMK, the object file generated
|
||||
during the build overrides the avr-libc implementation during the linking
|
||||
stage.
|
||||
|
||||
On other platforms such as ARM, there are no provided implementations, so
|
||||
there is nothing to override during linkage.
|
||||
*/
|
||||
|
||||
#include "wait.h"
|
||||
#include "i2c_master.h"
|
||||
#include "eeprom.h"
|
||||
#include "eeprom_he.h"
|
||||
|
||||
// #define DEBUG_EEPROM_OUTPUT
|
||||
|
||||
#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
|
||||
# include "timer.h"
|
||||
# include "debug.h"
|
||||
#endif // DEBUG_EEPROM_OUTPUT
|
||||
|
||||
#ifdef USE_GPIOV1
|
||||
# ifndef I2C1_SCL_PAL_MODE
|
||||
# define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
|
||||
# endif
|
||||
# ifndef I2C1_SDA_PAL_MODE
|
||||
# define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN
|
||||
# endif
|
||||
#else
|
||||
// The default PAL alternate modes are used to signal that the pins are used for I2C
|
||||
# ifndef I2C1_SCL_PAL_MODE
|
||||
# define I2C1_SCL_PAL_MODE 4
|
||||
# endif
|
||||
# ifndef I2C1_SDA_PAL_MODE
|
||||
# define I2C1_SDA_PAL_MODE 4
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static inline void fill_target_address(uint8_t *buffer, const void *addr) {
|
||||
uintptr_t p = (uintptr_t)addr;
|
||||
for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) {
|
||||
buffer[EXTERNAL_EEPROM_ADDRESS_SIZE - 1 - i] = p & 0xFF;
|
||||
p >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override i2c_init */
|
||||
void i2c_init(void) {
|
||||
// Try releasing special pins for a short time
|
||||
palSetLineMode(I2C1_SCL_PIN, PAL_MODE_INPUT);
|
||||
palSetLineMode(I2C1_SDA_PIN, PAL_MODE_INPUT);
|
||||
|
||||
chThdSleepMilliseconds(10);
|
||||
#if defined(USE_GPIOV1)
|
||||
palSetLineMode(I2C1_SCL_PIN, I2C1_SCL_PAL_MODE);
|
||||
palSetLineMode(I2C1_SDA_PIN, I2C1_SDA_PAL_MODE);
|
||||
#else
|
||||
palSetLineMode(I2C1_SCL_PIN, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);
|
||||
palSetLineMode(I2C1_SDA_PIN, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void he_eeprom_driver_init(void) {
|
||||
|
||||
i2c_init();
|
||||
#if defined(EXTERNAL_EEPROM_WP_PIN)
|
||||
/* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
|
||||
writePin(EXTERNAL_EEPROM_WP_PIN, 1);
|
||||
setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void he_eeprom_driver_erase(void) {
|
||||
|
||||
#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)
|
||||
uint32_t start = timer_read32();
|
||||
#endif
|
||||
|
||||
uint8_t buf[EXTERNAL_EEPROM_PAGE_SIZE];
|
||||
memset(buf, 0x00, EXTERNAL_EEPROM_PAGE_SIZE);
|
||||
for (uint32_t addr = 0; addr < EXTERNAL_EEPROM_BYTE_COUNT; addr += EXTERNAL_EEPROM_PAGE_SIZE) {
|
||||
he_eeprom_write_block(buf, (void *)(uintptr_t)addr, EXTERNAL_EEPROM_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void he_eeprom_read_block(void *buf, const void *addr, size_t len) {
|
||||
|
||||
uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE];
|
||||
fill_target_address(complete_packet, addr);
|
||||
|
||||
i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE, 100);
|
||||
i2c_receive(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), buf, len, 100);
|
||||
}
|
||||
|
||||
void he_eeprom_write_block(const void *buf, void *addr, size_t len) {
|
||||
|
||||
uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + EXTERNAL_EEPROM_PAGE_SIZE];
|
||||
uint8_t * read_buf = (uint8_t *)buf;
|
||||
uintptr_t target_addr = (uintptr_t)addr;
|
||||
|
||||
#if defined(EXTERNAL_EEPROM_WP_PIN)
|
||||
setPinOutput(EXTERNAL_EEPROM_WP_PIN);
|
||||
writePin(EXTERNAL_EEPROM_WP_PIN, 0);
|
||||
#endif
|
||||
|
||||
while (len > 0) {
|
||||
uintptr_t page_offset = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;
|
||||
int write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;
|
||||
if (write_length > len) {
|
||||
write_length = len;
|
||||
}
|
||||
|
||||
fill_target_address(complete_packet, (const void *)target_addr);
|
||||
for (uint8_t i = 0; i < write_length; i++) {
|
||||
complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + i] = read_buf[i];
|
||||
}
|
||||
|
||||
i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE + write_length, 100);
|
||||
wait_ms(EXTERNAL_EEPROM_WRITE_TIME);
|
||||
|
||||
read_buf += write_length;
|
||||
target_addr += write_length;
|
||||
len -= write_length;
|
||||
}
|
||||
|
||||
#if defined(EXTERNAL_EEPROM_WP_PIN)
|
||||
/* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */
|
||||
writePin(EXTERNAL_EEPROM_WP_PIN, 1);
|
||||
setPinInputHigh(EXTERNAL_EEPROM_WP_PIN);
|
||||
#endif
|
||||
}
|
||||
33
keyboards/keychron/common/analog_matrix/eeprom_he.h
Normal file
33
keyboards/keychron/common/analog_matrix/eeprom_he.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* Copyright 2019 Nick Brassel (tzarc)
|
||||
*
|
||||
* 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
|
||||
|
||||
#define EXTERNAL_EEPROM_BYTE_COUNT 1024 //
|
||||
#define EXTERNAL_EEPROM_PAGE_SIZE 32
|
||||
#define EXTERNAL_EEPROM_ADDRESS_SIZE 2
|
||||
#define EXTERNAL_EEPROM_WRITE_TIME 5
|
||||
#define EXTERNAL_EEPROM_I2C_BASE_ADDRESS 0b10100010
|
||||
|
||||
#ifndef EXTERNAL_EEPROM_I2C_ADDRESS
|
||||
# define EXTERNAL_EEPROM_I2C_ADDRESS(loc) (EXTERNAL_EEPROM_I2C_BASE_ADDRESS)
|
||||
#endif
|
||||
|
||||
void he_eeprom_driver_init(void);
|
||||
void he_eeprom_driver_erase(void);
|
||||
void he_eeprom_read_block(void *buf, const void *addr, size_t len);
|
||||
void he_eeprom_write_block(const void *buf, void *addr, size_t len);
|
||||
|
||||
112
keyboards/keychron/common/analog_matrix/game_controller_common.c
Normal file
112
keyboards/keychron/common/analog_matrix/game_controller_common.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "game_controller_common.h"
|
||||
//#include "eeconfig.h"
|
||||
#include "eeprom.h"
|
||||
#include "nvm_eeprom_eeconfig_internal.h"
|
||||
#include "analog_matrix.h"
|
||||
|
||||
uint8_t game_controller_mode;
|
||||
point_t curve[CURVE_POINTS_COUNT];
|
||||
float slope[CURVE_POINTS_COUNT - 1];
|
||||
matrix_row_t game_controller_matrix[MATRIX_ROWS] = {0};
|
||||
|
||||
void game_controller_curve_init(point_t *pt) {
|
||||
memcpy(curve, pt, CURVE_POINTS_COUNT * SIZE_OF_POINT_T);
|
||||
|
||||
if (curve[0].y == 0 && curve[1].y == 0 && curve[2].y == 0 && curve[3].y == 0) {
|
||||
point_t default_curve[CURVE_POINTS_COUNT] = {{0, 0}, {10, 31}, {30, 95}, {40, 127}};
|
||||
memcpy(curve, default_curve, sizeof(default_curve));
|
||||
slope[0] = slope[1] = slope[2] = 3.175f;
|
||||
}
|
||||
}
|
||||
|
||||
void game_controller_mode_init(uint8_t mode) {
|
||||
game_controller_mode = mode;
|
||||
}
|
||||
|
||||
bool game_controller_mode_get(uint8_t *data) {
|
||||
data[1] = game_controller_mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game_controller_mode_set(uint8_t mode) {
|
||||
if (game_controller_mode == mode) return false;
|
||||
|
||||
game_controller_mode = mode;
|
||||
|
||||
// Save
|
||||
if (!eeconfig_is_kb_datablock_valid()) eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
|
||||
|
||||
analog_matrix_eeprom_update(&game_controller_mode, (uint8_t *)OFFSET_GAME_CONTROLLER_MODE_START, 1);
|
||||
// usbDisconnectBus(&USBD1);
|
||||
// usbStop(&USBD1);
|
||||
// wait_ms(1000);
|
||||
// usb_start(&USBD1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game_controller_set_curve(point_t *pt) {
|
||||
if (curve[0].x > curve[1].x || curve[1].x > curve[2].x || curve[2].x > curve[3].x) return false;
|
||||
|
||||
memcpy(curve, pt, sizeof(curve));
|
||||
if (curve[0].x != curve[1].x) slope[0] = (float)(curve[1].y - curve[0].y) / (curve[1].x - curve[0].x);
|
||||
if (curve[1].x != curve[2].x) slope[1] = (float)(curve[2].y - curve[1].y) / (curve[2].x - curve[1].x);
|
||||
if (curve[2].x != curve[3].x) slope[2] = (float)(curve[3].y - curve[2].y) / (curve[3].x - curve[2].x);
|
||||
|
||||
analog_matrix_eeprom_update(curve, (uint8_t *)OFFSET_CURVE_PTS_START, CURVE_POINTS_COUNT * SIZE_OF_POINT_T);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game_controller_get_curve(uint8_t *data) {
|
||||
memcpy(data, curve, sizeof(curve));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game_controller_xinput_enabled(void) {
|
||||
return game_controller_mode & GC_MASK_XINPUT;
|
||||
}
|
||||
|
||||
bool game_controller_type_enabled(void) {
|
||||
return game_controller_mode & GC_MASK_TYPING;
|
||||
}
|
||||
|
||||
void game_controller_clear(void) {
|
||||
memset(game_controller_matrix, 0, sizeof(game_controller_matrix));
|
||||
|
||||
#ifdef JOYSTICK_ENABLE
|
||||
# ifdef XINPUT_ENABLE
|
||||
if (!game_controller_xinput_enabled())
|
||||
# endif
|
||||
{
|
||||
extern void joystick_clear(void);
|
||||
joystick_clear();
|
||||
}
|
||||
#endif
|
||||
#ifdef XINPUT_ENABLE
|
||||
# ifdef JOYSTICK_ENABLE
|
||||
if (game_controller_xinput_enabled())
|
||||
# endif
|
||||
{
|
||||
extern void xinput_clear(void);
|
||||
xinput_clear();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include "analog_matrix_type.h"
|
||||
|
||||
/* Game controller axis */
|
||||
enum {
|
||||
GC_AXIS_X = 0,
|
||||
GC_AXIS_Y,
|
||||
GC_AXIS_Z,
|
||||
GC_AXIS_RX,
|
||||
GC_AXIS_RY,
|
||||
GC_AXIS_RZ,
|
||||
GC_AXIS_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
GC_X_AXIS_LEFT = 0,
|
||||
GC_X_AXIS_RIGHT,
|
||||
GC_Y_AXIS_DOWN,
|
||||
GC_Y_AXIS_UP,
|
||||
GC_Z_AXIS_N,
|
||||
GC_Z_AXIS_P,
|
||||
GC_RX_AXIS_LEFT,
|
||||
GC_RX_AXIS_RIGHT,
|
||||
GC_RY_AXIS_DOWN,
|
||||
GC_RY_AXIS_UP,
|
||||
GC_RZ_AXIS_N,
|
||||
GC_RZ_AXIS_P,
|
||||
GC_MAX,
|
||||
GC_BUTTON_0, // 13
|
||||
GC_BUTTON_31 = GC_BUTTON_0+31,
|
||||
GC_BUTTON_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
GC_MASK_XINPUT = 0x01 << 0,
|
||||
GC_MASK_TYPING = 0x01 << 1,
|
||||
};
|
||||
|
||||
void game_controller_curve_init(point_t *pt);
|
||||
void game_controller_mode_init(uint8_t mode);
|
||||
bool game_controller_mode_get(uint8_t *data);
|
||||
bool game_controller_mode_set(uint8_t mode);
|
||||
bool game_controller_set_curve(point_t *pt);
|
||||
bool game_controller_get_curve(uint8_t *data);
|
||||
bool game_controller_xinput_enabled(void);
|
||||
bool game_controller_type_enabled(void);
|
||||
void game_controller_clear(void);
|
||||
402
keyboards/keychron/common/analog_matrix/profile.c
Normal file
402
keyboards/keychron/common/analog_matrix/profile.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "keychron_common.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "game_controller_common.h"
|
||||
#include "profile.h"
|
||||
#include "action_socd.h"
|
||||
#include "eeconfig.h"
|
||||
#include "eeprom.h"
|
||||
#include "nvm_eeprom_eeconfig_internal.h"
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
# ifndef PROF_TRIG_KEY_ROW
|
||||
# define PROF_TRIG_KEY_ROW 2
|
||||
# endif
|
||||
|
||||
# ifndef PROF_TRIG_KEY_COL
|
||||
# define PROF_TRIG_KEY_COL 10
|
||||
# endif
|
||||
|
||||
# ifndef PROF_1_KEY_ROW
|
||||
# define PROF_1_KEY_ROW 4
|
||||
# endif
|
||||
|
||||
# ifndef PROF_1_KEY_COL
|
||||
# define PROF_1_KEY_COL 2
|
||||
# endif
|
||||
|
||||
# ifndef PROF_2_KEY_ROW
|
||||
# define PROF_2_KEY_ROW 4
|
||||
# endif
|
||||
|
||||
# ifndef PROF_2_KEY_COL
|
||||
# define PROF_2_KEY_COL 3
|
||||
# endif
|
||||
|
||||
# ifndef PROF_3_KEY_ROW
|
||||
# define PROF_3_KEY_ROW 4
|
||||
# endif
|
||||
|
||||
# ifndef PROF_3_KEY_COL
|
||||
# define PROF_3_KEY_COL 4
|
||||
# endif
|
||||
|
||||
# define KEY_MASK(r, c) (virtual_matrix[r] & (1 << c))
|
||||
|
||||
enum {
|
||||
ADV_MODE_CLEAR = 0,
|
||||
ADV_MODE_OKMC,
|
||||
ADV_MODE_GAME_CONTROLLER,
|
||||
ADV_MODE_TOGGLE,
|
||||
};
|
||||
|
||||
extern uint8_t profile_gobal_mode[PROFILE_COUNT];
|
||||
extern uint16_t default_profiles[PROFILE_COUNT][MATRIX_ROWS][MATRIX_COLS];
|
||||
|
||||
static analog_matrix_profile_t profile[PROFILE_COUNT];
|
||||
static uint8_t current_profile_index;
|
||||
static analog_matrix_profile_t *cur_prof = &profile[0]; // current profile
|
||||
uint8_t prof_combo = 0;
|
||||
static uint8_t prof_ind_state = 0;
|
||||
static uint32_t pro_ind_timer = 0;
|
||||
|
||||
void profile_init(bool reset) {
|
||||
if (reset) {
|
||||
// Write default profile setting
|
||||
for (uint8_t i = 0; i < PROFILE_COUNT; i++)
|
||||
profile_reset(i);
|
||||
} else {
|
||||
uint8_t *buf = (uint8_t *)malloc(EECONFIG_SIZE_ANALOG_MATRIX);
|
||||
memset(buf, 0, EECONFIG_SIZE_ANALOG_MATRIX);
|
||||
|
||||
eeprom_read_block(buf, (void *)EECONFIG_BASE_ANALOG_MATRIX, EECONFIG_SIZE_ANALOG_MATRIX);
|
||||
|
||||
current_profile_index = buf[OFFSET_CURRENT_PROFILE];
|
||||
if (current_profile_index >= PROFILE_COUNT) current_profile_index = 0;
|
||||
|
||||
cur_prof = &profile[current_profile_index];
|
||||
|
||||
// Load profile data
|
||||
memcpy(profile, buf + OFFSET_PROFILES_START, PROFILE_SIZE * PROFILE_COUNT);
|
||||
|
||||
for (uint8_t i = 0; i < PROFILE_COUNT; i++) {
|
||||
if (profile[i].global.mode == 0) profile[i].global.mode = profile_gobal_mode[i]; // global mode can't be 0
|
||||
|
||||
// Resotre to default if not in valid range
|
||||
if (profile[i].global.act_pt == 0 || profile[i].global.act_pt > 40) profile[i].global.act_pt = DEFAULT_ACTUATION_POINT;
|
||||
if (profile[i].global.rpd_trig_sen == 0 || profile[i].global.rpd_trig_sen > 39) profile[i].global.rpd_trig_sen = DEFAULT_RAPID_TRIGGER_SENSITIVITY;
|
||||
if (profile[i].global.rpd_trig_sen_deact == 0 || profile[i].global.rpd_trig_sen > 39) profile[i].global.rpd_trig_sen_deact = profile[i].global.rpd_trig_sen;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
analog_matrix_profile_t *profile_get(uint8_t index) {
|
||||
return &profile[index];
|
||||
}
|
||||
|
||||
analog_matrix_profile_t *profile_get_current(void) {
|
||||
return cur_prof;
|
||||
}
|
||||
|
||||
uint8_t profile_get_current_index(void) {
|
||||
return current_profile_index;
|
||||
}
|
||||
|
||||
bool profile_select(uint8_t prof_idx, bool indication) {
|
||||
if (prof_idx >= PROFILE_COUNT) return false;
|
||||
|
||||
if (prof_idx != current_profile_index) {
|
||||
current_profile_index = prof_idx;
|
||||
|
||||
cur_prof = &profile[prof_idx];
|
||||
analog_matrix_clear();
|
||||
update_travel_configs();
|
||||
|
||||
eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
|
||||
analog_matrix_eeprom_update(&prof_idx, (void *)OFFSET_CURRENT_PROFILE, 1);
|
||||
analog_matrix_clear_advance_keys();
|
||||
}
|
||||
if (indication) {
|
||||
# ifdef LED_MATRIX_ENABLE
|
||||
if (!led_matrix_is_enabled()) led_matrix_enable_noeeprom();
|
||||
# endif
|
||||
# ifdef RGB_MATRIX_ENABLE
|
||||
if (!rgb_matrix_is_enabled()) rgb_matrix_enable_noeeprom();
|
||||
# endif
|
||||
pro_ind_timer = timer_read32();
|
||||
prof_ind_state = 0x86;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_get_raw_data(uint8_t prof_idx, uint16_t offset, uint8_t size, uint8_t *data) {
|
||||
if (prof_idx >= PROFILE_COUNT || offset + size > sizeof(profile)) return false;
|
||||
|
||||
memset(data, 0, size);
|
||||
|
||||
if (offset + size > PROFILE_SIZE) size = PROFILE_SIZE - offset;
|
||||
memcpy(data, (uint8_t *)(&profile[prof_idx]) + offset, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_set_traval(uint8_t prof_idx, uint8_t mode, uint8_t act_pt, uint8_t sens, uint8_t rls_sens, bool global, uint32_t row[]) {
|
||||
analog_matrix_profile_t *prof = profile_get(prof_idx);
|
||||
|
||||
// Check validity
|
||||
if (prof_idx >= PROFILE_COUNT || mode > AKM_RAPID || act_pt < 0 || act_pt > 39 || (global && mode == AKM_GLOBAL)) return false;
|
||||
|
||||
if (global) {
|
||||
prof->global.mode = mode;
|
||||
prof->global.act_pt = act_pt;
|
||||
prof->global.rpd_trig_sen = sens;
|
||||
prof->global.rpd_trig_sen_deact = rls_sens;
|
||||
memset(row, 0xFF, sizeof(row[0]) * MATRIX_ROWS);
|
||||
} else {
|
||||
for (uint8_t r = 0; r < MATRIX_ROWS; r++)
|
||||
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
|
||||
if (row[r] & (0x01 << c)) {
|
||||
prof->key_config[r][c].mode = mode;
|
||||
prof->key_config[r][c].act_pt = act_pt;
|
||||
prof->key_config[r][c].rpd_trig_sen = sens;
|
||||
prof->key_config[r][c].rpd_trig_sen_deact = rls_sens;
|
||||
|
||||
if (prof_idx == profile_get_current_index()) update_key_config(r, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_set_adv_mode(uint8_t *data) {
|
||||
uint8_t prof_idx = data[0];
|
||||
if (prof_idx >= PROFILE_COUNT) return false;
|
||||
|
||||
uint8_t mode = data[1];
|
||||
uint8_t row = data[2];
|
||||
uint8_t col = data[3];
|
||||
uint8_t index = data[4];
|
||||
|
||||
if (row >= MATRIX_ROWS || col >= MATRIX_COLS) return false;
|
||||
|
||||
okmc_config_t okmc_config;
|
||||
memset(&okmc_config, 0, sizeof(okmc_config));
|
||||
analog_matrix_profile_t *prof = profile_get(prof_idx);
|
||||
analog_key_config_t * p_key_cfg = &prof->key_config[row][col];
|
||||
|
||||
switch (mode) {
|
||||
case ADV_MODE_CLEAR:
|
||||
p_key_cfg->adv_mode = 0;
|
||||
p_key_cfg->adv_mode_data = 0;
|
||||
break;
|
||||
|
||||
case ADV_MODE_OKMC:
|
||||
if (index >= OKMC_COUNT) return false;
|
||||
|
||||
okmc_config.travel.shallow_act = data[5];
|
||||
okmc_config.travel.shallow_deact = data[6];
|
||||
okmc_config.travel.deep_act = data[7];
|
||||
okmc_config.travel.deep_deact = data[8];
|
||||
memcpy(okmc_config.keycode, &data[9], sizeof(okmc_config.keycode));
|
||||
memcpy(okmc_config.action, &data[17], sizeof(okmc_config.action));
|
||||
|
||||
prof->okmc[index] = okmc_config;
|
||||
p_key_cfg->adv_mode = AKM_DKS;
|
||||
p_key_cfg->okmc_idx = index;
|
||||
break;
|
||||
|
||||
case ADV_MODE_GAME_CONTROLLER:
|
||||
if (index >= GC_BUTTON_MAX) return false;
|
||||
|
||||
p_key_cfg->adv_mode = AKM_GAMEPAD;
|
||||
p_key_cfg->js_axis = index;
|
||||
break;
|
||||
|
||||
case ADV_MODE_TOGGLE:
|
||||
p_key_cfg->adv_mode = AKM_TOGGLE;
|
||||
p_key_cfg->adv_mode_data = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prof_idx == profile_get_current_index()) update_key_config(row, col);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_set_socd(uint8_t *data) {
|
||||
uint8_t prof_idx = data[0];
|
||||
uint8_t row1 = data[1];
|
||||
uint8_t col1 = data[2];
|
||||
uint8_t row2 = data[3];
|
||||
uint8_t col2 = data[4];
|
||||
uint8_t index = data[5];
|
||||
uint8_t type = data[6];
|
||||
|
||||
if (prof_idx >= PROFILE_COUNT || row1 >= MATRIX_ROWS || col1 >= MATRIX_COLS || row2 >= MATRIX_ROWS || col2 >= MATRIX_COLS || index >= SOCD_COUNT || type >= SOCD_PRI_MAX) return false;
|
||||
// Same key is not allowed
|
||||
if (type && row1 == row2 && col1 == col2) return false;
|
||||
|
||||
analog_matrix_profile_t *prof = &profile[prof_idx];
|
||||
|
||||
if (type) {
|
||||
prof->socd[index].key_1_row = row1;
|
||||
prof->socd[index].key_1_col = col1;
|
||||
prof->socd[index].key_2_row = row2;
|
||||
prof->socd[index].key_2_col = col2;
|
||||
prof->socd[index].type = type;
|
||||
} else {
|
||||
memset(&prof->socd[index], 0, sizeof(socd_config_t));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_save(uint8_t prof_index) {
|
||||
if (prof_index >= PROFILE_COUNT) return false;
|
||||
|
||||
analog_matrix_profile_t *prof = &profile[prof_index];
|
||||
|
||||
if (!eeconfig_is_kb_datablock_valid()) eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
|
||||
analog_matrix_eeprom_update(prof, (void *)OFFSET_PROFILES_START + prof_index * sizeof(analog_matrix_profile_t), sizeof(analog_matrix_profile_t));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool profile_reset(uint8_t prof_index) {
|
||||
if (prof_index >= PROFILE_COUNT) return false;
|
||||
|
||||
analog_matrix_profile_t *prof = &profile[prof_index];
|
||||
|
||||
memset(prof, 0, sizeof(profile[0]));
|
||||
// Default
|
||||
prof->global.mode = profile_gobal_mode[prof_index];
|
||||
prof->global.act_pt = DEFAULT_ACTUATION_POINT;
|
||||
prof->global.rpd_trig_sen_deact = prof->global.rpd_trig_sen = DEFAULT_RAPID_TRIGGER_SENSITIVITY;
|
||||
|
||||
for (uint8_t r = 0; r < MATRIX_ROWS; r++)
|
||||
for (uint8_t c = 0; c < MATRIX_COLS; c++) {
|
||||
prof->key_config[r][c].mode = default_profiles[prof_index][r][c] & 0x3;
|
||||
prof->key_config[r][c].adv_mode = (default_profiles[prof_index][r][c] >> 2) & 0x07;
|
||||
if (prof->key_config[r][c].adv_mode == AKM_GAMEPAD) {
|
||||
prof->key_config[r][c].js_axis = default_profiles[prof_index][r][c] >> 5;
|
||||
}
|
||||
}
|
||||
|
||||
profile_save(prof_index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void process_profile_select_combo(void) {
|
||||
extern matrix_row_t virtual_matrix[MATRIX_ROWS];
|
||||
|
||||
if (prof_combo & KEY_PRESS_FN) {
|
||||
if ((prof_combo & KEY_PRESS_P) == 0 && KEY_MASK(PROF_TRIG_KEY_ROW, PROF_TRIG_KEY_COL)) {
|
||||
prof_combo |= KEY_PRESS_P;
|
||||
} else if ((prof_combo & KEY_PRESS_P) && KEY_MASK(PROF_TRIG_KEY_ROW, PROF_TRIG_KEY_COL) == 0) {
|
||||
prof_combo &= ~KEY_PRESS_P;
|
||||
}
|
||||
|
||||
if ((prof_combo & KEY_PRESS_PROF_COMBO) == KEY_PRESS_PROF_COMBO) {
|
||||
if (KEY_MASK(PROF_1_KEY_ROW, PROF_1_KEY_COL)) {
|
||||
profile_select(0, true);
|
||||
} else if (KEY_MASK(PROF_2_KEY_ROW, PROF_2_KEY_COL)) {
|
||||
profile_select(1, true);
|
||||
} else if (KEY_MASK(PROF_3_KEY_ROW, PROF_3_KEY_COL)) {
|
||||
profile_select(2, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool profile_set_name(uint8_t prof_idx, uint8_t len, uint8_t *name) {
|
||||
if (prof_idx >= PROFILE_COUNT || len > 28) return false;
|
||||
|
||||
memset(profile[prof_idx].name, 0, PROFILE_NAME_LEN);
|
||||
memcpy(profile[prof_idx].name, name, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool process_record_profile(uint16_t keycode, keyrecord_t *record) {
|
||||
switch (keycode) {
|
||||
case PROF1:
|
||||
case PROF2:
|
||||
case PROF3:
|
||||
if (record->event.pressed) profile_select(keycode - PROF1, true);
|
||||
return false; // Skip all further processing of this key
|
||||
|
||||
case MO(0)... MO(15):
|
||||
if (record->event.pressed)
|
||||
prof_combo |= KEY_PRESS_FN;
|
||||
else
|
||||
prof_combo &= ~KEY_PRESS_FN;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void profile_indication_enable(void) {
|
||||
pro_ind_timer = timer_read32();
|
||||
prof_ind_state = 1;
|
||||
}
|
||||
|
||||
void profile_indication_timer_check(void) {
|
||||
if (pro_ind_timer && timer_elapsed32(pro_ind_timer) > 500) {
|
||||
if ((prof_ind_state++ & 0xF) > 6) {
|
||||
pro_ind_timer = prof_ind_state = 0;
|
||||
|
||||
# ifdef LED_MATRIX_ENABLE
|
||||
if (!led_matrix_is_enabled()) led_matrix_disable_noeeprom();
|
||||
# endif
|
||||
# ifdef RGB_MATRIX_ENABLE
|
||||
eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config));
|
||||
if (!rgb_matrix_config.mode) {
|
||||
eeconfig_update_rgb_matrix_default();
|
||||
}
|
||||
|
||||
if (!rgb_matrix_is_enabled()) rgb_matrix_disable_noeeprom();
|
||||
# endif
|
||||
} else {
|
||||
pro_ind_timer = timer_read32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void profile_indication(void) {
|
||||
if (prof_ind_state) {
|
||||
static uint8_t prof_led_list[3] = PROFILE_LED_MATRIX_LIST;
|
||||
rgb_matrix_set_color_all(prof_ind_state % 2 ? 0 : 255, 0, 0);
|
||||
if (prof_ind_state & 0x80) {
|
||||
rgb_matrix_set_color(prof_led_list[current_profile_index], prof_ind_state % 2 ? 0 : 255, prof_ind_state % 2 ? 0 : 255, prof_ind_state % 2 ? 0 : 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
40
keyboards/keychron/common/analog_matrix/profile.h
Normal file
40
keyboards/keychron/common/analog_matrix/profile.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "action.h"
|
||||
|
||||
void profile_init(bool reset);
|
||||
analog_matrix_profile_t *profile_get(uint8_t index);
|
||||
analog_matrix_profile_t* profile_get_current(void);
|
||||
uint8_t profile_get_current_index(void);
|
||||
bool profile_select(uint8_t prof_idx, bool indication);
|
||||
bool profile_get_raw_data(uint8_t prof_idx, uint16_t offset, uint8_t size, uint8_t *data);
|
||||
bool profile_set_traval(uint8_t prof_idx, uint8_t mode, uint8_t act_pt, uint8_t sens, uint8_t rls_sens, bool global, uint32_t row[]);
|
||||
bool profile_set_name(uint8_t prof_idx, uint8_t len, uint8_t *name);
|
||||
bool profile_set_socd(uint8_t *data);
|
||||
bool profile_reset(uint8_t prof_index);
|
||||
bool profile_save(uint8_t prof_index);
|
||||
bool profile_set_adv_mode(uint8_t *data);
|
||||
void process_profile_select_combo(void);
|
||||
bool process_record_profile(uint16_t keycode, keyrecord_t *record);
|
||||
|
||||
void profile_indication_enable(void);
|
||||
void profile_indication_timer_check(void);
|
||||
void profile_indication(void);
|
||||
29
keyboards/keychron/common/analog_matrix/sqrt.c
Normal file
29
keyboards/keychron/common/analog_matrix/sqrt.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t sqrt_uint32(uint32_t n) {
|
||||
uint32_t x = n;
|
||||
uint32_t y = (x + 1) / 2;
|
||||
|
||||
while (y < x) {
|
||||
x = y;
|
||||
y = (x + n / x) / 2;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
21
keyboards/keychron/common/analog_matrix/sqrt.h
Normal file
21
keyboards/keychron/common/analog_matrix/sqrt.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
uint32_t sqrt_uint32(uint32_t n);
|
||||
@@ -0,0 +1,144 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "quantum.h"
|
||||
#include "analog_matrix.h"
|
||||
#include "raw_hid.h"
|
||||
#include "usb_main.h"
|
||||
#include <stdio.h>
|
||||
#include "game_controller_common.h"
|
||||
|
||||
extern USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor[];
|
||||
extern const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[];
|
||||
|
||||
bool get_joy_stick_report_descriptor_info(uint16_t *pos_begin, uint16_t *pos_end) {
|
||||
uint16_t share_report_len = ConfigurationDescriptor->Shared_HID.HIDReportLength;
|
||||
uint8_t joystick_report_key_begin[4] = {0x05, 0x01, 0x09, 0x04};
|
||||
bool find = false;
|
||||
|
||||
for (uint16_t i = 0; i < share_report_len - 4; i++) {
|
||||
if (memcmp(SharedReport + i, joystick_report_key_begin, 4) == 0) {
|
||||
// Search joystick report descriptor end position starting from pos_begin
|
||||
for (uint16_t j = i; j < share_report_len - 2; j++) {
|
||||
if (SharedReport[j] == 0xC0 && SharedReport[j + 1] == 0xC0) {
|
||||
*pos_begin = i;
|
||||
*pos_end = j + 2;
|
||||
find = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return find;
|
||||
}
|
||||
|
||||
void get_usb_descriptor_kb(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void **const DescriptorAddress, uint16_t *size) {
|
||||
#if defined(JOYSTICK_ENABLE) && defined(XINPUT_ENABLE)
|
||||
|
||||
const uint8_t DescriptorType = (wValue >> 8);
|
||||
const uint8_t DescriptorIndex = (wValue & 0xFF);
|
||||
static bool recal = false;
|
||||
static uint16_t pos_begin, pos_end;
|
||||
static uint16_t original_hid_rpt_len = 0;
|
||||
|
||||
if (original_hid_rpt_len == 0) original_hid_rpt_len = ConfigurationDescriptor->Shared_HID.HIDReportLength;
|
||||
|
||||
switch (DescriptorType) {
|
||||
case DTYPE_String:
|
||||
# if defined(XINPUT_ENABLE)
|
||||
if (!game_controller_xinput_enabled() && DescriptorIndex == 0xEE) {
|
||||
*DescriptorAddress = NULL;
|
||||
*size = 0;
|
||||
}
|
||||
# endif
|
||||
break;
|
||||
|
||||
case DTYPE_Configuration:
|
||||
# if defined(JOYSTICK_SHARED_EP)
|
||||
{
|
||||
if (game_controller_xinput_enabled()) {
|
||||
if (!recal && get_joy_stick_report_descriptor_info(&pos_begin, &pos_end)) {
|
||||
ConfigurationDescriptor->Shared_HID.HIDReportLength -= (pos_end - pos_begin);
|
||||
recal = true;
|
||||
}
|
||||
} else {
|
||||
uint8_t xinput_desc_len = sizeof(USB_Descriptor_Interface_t) + 0x11 + sizeof(USB_Descriptor_Endpoint_t) * 2;
|
||||
ConfigurationDescriptor->Config.TotalInterfaces = TOTAL_INTERFACES - 1;
|
||||
ConfigurationDescriptor->Config.TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t) - xinput_desc_len;
|
||||
*size = sizeof(USB_Descriptor_Configuration_t) - xinput_desc_len;
|
||||
}
|
||||
}
|
||||
# else
|
||||
// TODO:
|
||||
// uint8_t xinput_desc_len = sizeof(USB_Descriptor_Interface_t) + 0x11 + sizeof(USB_Descriptor_Endpoint_t) * 2;
|
||||
// uint8_t joy_desc_len = sizeof(USB_Descriptor_Interface_t) + sizeof(USB_HID_Descriptor_HID_t) + sizeof(USB_Descriptor_Endpoint_t);
|
||||
// if (xinput_mode) {
|
||||
// static USB_Descriptor_Configuration_t *xinput_mode_desc = NULL;
|
||||
|
||||
// if (xinput_mode_desc == NULL) {
|
||||
// xinput_mode_desc = (void *)malloc(sizeof(USB_Descriptor_Configuration_t));
|
||||
// memcpy(xinput_mode_desc, &ConfigurationDescriptor, sizeof(USB_Descriptor_Configuration_t));
|
||||
// // Move xinput descriptor
|
||||
// memcpy(&xinput_mode_desc->Joystick_Interface, &xinput_mode_desc->Xinput_Interface, xinput_desc_len);
|
||||
// }
|
||||
// *DescriptorAddress = xinput_mode_desc;
|
||||
// *Size -= joy_desc_len;
|
||||
// } else {
|
||||
// *Size -= xinput_desc_len;
|
||||
// }
|
||||
|
||||
# endif
|
||||
break;
|
||||
case HID_DTYPE_Report:
|
||||
switch (wIndex) {
|
||||
# ifdef SHARED_EP_ENABLE
|
||||
case SHARED_INTERFACE:
|
||||
|
||||
*DescriptorAddress = &SharedReport;
|
||||
uint16_t len = ConfigurationDescriptor->Shared_HID.HIDReportLength;
|
||||
|
||||
if (game_controller_xinput_enabled()) {
|
||||
static uint8_t *input_mode_rpt_desc = NULL;
|
||||
if (input_mode_rpt_desc == NULL) {
|
||||
input_mode_rpt_desc = (uint8_t *)malloc(original_hid_rpt_len);
|
||||
|
||||
memcpy(input_mode_rpt_desc, &SharedReport, original_hid_rpt_len);
|
||||
|
||||
if (pos_begin < original_hid_rpt_len - 4 && pos_end < original_hid_rpt_len - 2) {
|
||||
// Move xinput descriptor
|
||||
memcpy(input_mode_rpt_desc + pos_begin, input_mode_rpt_desc + pos_end, original_hid_rpt_len - pos_end);
|
||||
}
|
||||
}
|
||||
*DescriptorAddress = input_mode_rpt_desc;
|
||||
*size = len;
|
||||
}
|
||||
break;
|
||||
# endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_usb_vendor_descriptor_kb(uint8_t recipient, uint8_t reqeuest, const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void **const DescriptorAddress, uint16_t *size) {
|
||||
#if XINPUT_ENABLE
|
||||
if (!game_controller_xinput_enabled()) {
|
||||
*DescriptorAddress = NULL;
|
||||
*size = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
74
keyboards/keychron/common/analog_matrix/xinput_keycodes.h
Normal file
74
keyboards/keychron/common/analog_matrix/xinput_keycodes.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/* Copyright 2024 @ Keychron (https://www.keychron.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "game_controller_common.h"
|
||||
// bit 0~1: basic mode
|
||||
// bit 2~4: adv mode
|
||||
// bit 5~15: axis
|
||||
|
||||
// a-advance mode, b-mode
|
||||
#define JSM(a, m) ((a << 3 | AKM_GAMEPAD) << 2) | (m & 3)
|
||||
#define TGM(m) (AKM_TOGGLE << 2) | (m & 3)
|
||||
|
||||
#define AX(x) JSM(x, 0)
|
||||
#define BTN(x) JSM((x + GC_BUTTON_0), 0)
|
||||
/* Xbox mapping
|
||||
BTN(0) - A
|
||||
BTN(1) - B
|
||||
BTN(2) - X
|
||||
BTN(3) - Y
|
||||
BTN(4) - LB
|
||||
BTN(5) - RB
|
||||
BTN(6) - View
|
||||
BTN(7) - Menu
|
||||
BTN(8) - L3
|
||||
BTN(9) - R3
|
||||
BTN(10) - Up
|
||||
BTN(11) - Down
|
||||
BTN(12) - Left
|
||||
BTN(13) - Right
|
||||
BTN(14) - Xbox
|
||||
*/
|
||||
|
||||
/* Xinput buttons */
|
||||
#define XB_A BTN(0)
|
||||
#define XB_B BTN(1)
|
||||
#define XB_X BTN(2)
|
||||
#define XB_Y BTN(3)
|
||||
#define XB_LB BTN(4)
|
||||
#define XB_RB BTN(5)
|
||||
#define XB_VIEW BTN(6)
|
||||
#define XB_MEMU BTN(7)
|
||||
#define XB_L3 BTN(8)
|
||||
#define XB_R3 BTN(9)
|
||||
#define XB_UP BTN(10)
|
||||
#define XB_DOWN BTN(11)
|
||||
#define XB_LEFT BTN(12)
|
||||
#define XB_RGHT BTN(13)
|
||||
#define XB_XBOX BTN(14)
|
||||
#define XB_LT AX(4)
|
||||
#define XB_RT AX(5)
|
||||
/* Left Stick */
|
||||
#define LS_LEFT AX(0)
|
||||
#define LS_RGHT AX(1)
|
||||
#define LS_UP AX(3)
|
||||
#define LS_DOWN AX(2)
|
||||
/* Right Stick */
|
||||
#define RS_LEFT AX(6)
|
||||
#define RS_RGHT AX(7)
|
||||
#define RS_UP AX(9)
|
||||
#define RS_DOWN AX(8)
|
||||
@@ -24,7 +24,7 @@ void eeconfig_init_kb_datablock(void) {
|
||||
extern void debounce_config_reset(void);
|
||||
debounce_config_reset();
|
||||
#endif
|
||||
#if defined(SNAP_CLICK_ENABLE)
|
||||
#if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
extern void snap_click_config_reset(void);
|
||||
snap_click_config_reset();
|
||||
#endif
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
#define EECONFIG_BASE_DYNAMIC_DEBOUNCE EECONFIG_END_HSUSB_REPORT_RATE
|
||||
#define EECONFIG_END_DYNAMIC_DEBOUNCE (EECONFIG_BASE_DYNAMIC_DEBOUNCE + __EECONFIG_SIZE_DEBOUNCE)
|
||||
|
||||
#ifdef SNAP_CLICK_ENABLE
|
||||
#if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
# include "eeconfig_snap_click.h"
|
||||
# define __EECONFIG_SIZE_SNAP_CLICK EECONFIG_SIZE_SNAP_CLICK
|
||||
#else
|
||||
@@ -47,13 +47,22 @@
|
||||
#define EECONFIG_BASE_SNAP_CLICK (EECONFIG_END_DYNAMIC_DEBOUNCE)
|
||||
#define EECONFIG_END_SNAP_CLICK (EECONFIG_BASE_SNAP_CLICK + __EECONFIG_SIZE_SNAP_CLICK)
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
# include "analog_matrix_eeconfig.h"
|
||||
# define __EECONFIG_SIZE_ANALOG_MATRIX EECONFIG_SIZE_ANALOG_MATRIX
|
||||
#else
|
||||
# define __EECONFIG_SIZE_ANALOG_MATRIX 0
|
||||
#endif
|
||||
#define EECONFIG_BASE_ANALOG_MATRIX EECONFIG_END_SNAP_CLICK
|
||||
#define EECONFIG_END_ANALOG_MATRIX (EECONFIG_BASE_ANALOG_MATRIX + __EECONFIG_SIZE_ANALOG_MATRIX)
|
||||
|
||||
#if defined(KEYCHRON_RGB_ENABLE) && defined(RGB_MATRIX_ENABLE)
|
||||
# include "eeconfig_custom_rgb.h"
|
||||
# define __EECONFIG_SIZE_CUSTOM_RGB EECONFIG_SIZE_CUSTOM_RGB
|
||||
#else
|
||||
# define __EECONFIG_SIZE_CUSTOM_RGB 0
|
||||
#endif
|
||||
#define EECONFIG_BASE_CUSTOM_RGB EECONFIG_END_SNAP_CLICK
|
||||
#define EECONFIG_BASE_CUSTOM_RGB EECONFIG_END_ANALOG_MATRIX
|
||||
#define EECONFIG_END_CUSTOM_RGB (EECONFIG_BASE_CUSTOM_RGB + __EECONFIG_SIZE_CUSTOM_RGB)
|
||||
|
||||
#if defined(WIRELESS_CONFIG_ENABLE)
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#ifdef SNAP_CLICK_ENABLE
|
||||
# include "snap_click.h"
|
||||
#endif
|
||||
#ifdef ANANLOG_MATRIX
|
||||
# include "analog_matrix.h"
|
||||
#endif
|
||||
#include "config.h"
|
||||
#include "version.h"
|
||||
#ifdef STATE_NOTIFY_ENABLE
|
||||
@@ -55,6 +58,25 @@
|
||||
# define P2P4G_CELAR_MASK P2P4G_CLEAR_PAIRING_TYPE_C
|
||||
#endif
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
# ifndef J_KEY_ROW
|
||||
# define J_KEY_ROW 3
|
||||
# endif
|
||||
|
||||
# ifndef J_KEY_COL
|
||||
# define J_KEY_COL 7
|
||||
# endif
|
||||
|
||||
# ifndef Z_KEY_ROW
|
||||
# define Z_KEY_ROW 4
|
||||
# endif
|
||||
|
||||
# ifndef Z_KEY_COL
|
||||
# define Z_KEY_COL 2
|
||||
# endif
|
||||
#endif
|
||||
#define KEY_MASK(r, c) (virtual_matrix[r] & (1 << c))
|
||||
|
||||
enum {
|
||||
BACKLIGHT_TEST_OFF = 0,
|
||||
BACKLIGHT_TEST_WHITE,
|
||||
@@ -122,7 +144,13 @@ static inline void factory_timer_check(void) {
|
||||
#endif
|
||||
backlight_test_mode = BACKLIGHT_TEST_OFF;
|
||||
|
||||
eeconfig_init();
|
||||
#ifdef ANANLOG_MATRIX
|
||||
eeconfig_disable();
|
||||
analog_matrix_eeconfig_init();
|
||||
analog_matrix_clear_advance_keys();
|
||||
#endif
|
||||
if (!eeconfig_is_enabled()) eeconfig_init();
|
||||
|
||||
eeconfig_read_keymap(&keymap_config);
|
||||
#if !defined(KEYCOMBO_OS_SELECT_ENABLE) && !defined(KEYCOMBO_OS_TOGGLE_ENABLE)
|
||||
default_layer_set(default_layer_tmp);
|
||||
@@ -130,7 +158,7 @@ static inline void factory_timer_check(void) {
|
||||
#ifdef DYNAMIC_DEBOUNCE_ENABLE
|
||||
debounce_config_reset();
|
||||
#endif
|
||||
#ifdef SNAP_CLICK_ENABLE
|
||||
#if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
snap_click_config_reset();
|
||||
#endif
|
||||
#ifdef LED_MATRIX_ENABLE
|
||||
@@ -319,8 +347,35 @@ bool factory_test_indicator(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
void analog_matrix_factory_reset(void) {
|
||||
extern matrix_row_t virtual_matrix[MATRIX_ROWS];
|
||||
|
||||
if (factory_reset_state & KEY_PRESS_FN) {
|
||||
if ((factory_reset_state & KEY_PRESS_J) == 0 && KEY_MASK(J_KEY_ROW, J_KEY_COL)) {
|
||||
factory_reset_state |= KEY_PRESS_J;
|
||||
} else if ((factory_reset_state & KEY_PRESS_J) && KEY_MASK(J_KEY_ROW, J_KEY_COL) == 0) {
|
||||
factory_reset_state &= ~KEY_PRESS_J;
|
||||
factory_reset_timer = 0;
|
||||
}
|
||||
|
||||
if ((factory_reset_state & KEY_PRESS_Z) == 0 && KEY_MASK(Z_KEY_ROW, Z_KEY_COL)) {
|
||||
factory_reset_state |= KEY_PRESS_Z;
|
||||
} else if ((factory_reset_state & KEY_PRESS_Z) && KEY_MASK(Z_KEY_ROW, Z_KEY_COL) == 0) {
|
||||
factory_reset_state &= ~KEY_PRESS_Z;
|
||||
factory_reset_timer = 0;
|
||||
}
|
||||
|
||||
if (factory_reset_state == KEY_PRESS_FACTORY_RESET && factory_reset_timer == 0) factory_timer_start();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool factory_test_task(void) {
|
||||
if (factory_reset_timer) factory_timer_check();
|
||||
#ifdef ANANLOG_MATRIX
|
||||
analog_matrix_factory_reset();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#include "config.h"
|
||||
#include "usb_descriptor.h"
|
||||
#include "raw_hid.h"
|
||||
#ifdef ANANLOG_MATRIX
|
||||
# include "profile.h"
|
||||
#endif
|
||||
|
||||
#ifdef KEYCOMBO_OS_SELECT_ENABLE
|
||||
# ifndef MAC_BASE_LAYER
|
||||
@@ -90,7 +93,7 @@ void keychron_common_init(void) {
|
||||
extern void report_rate_init(void);
|
||||
report_rate_init();
|
||||
#endif
|
||||
#ifdef SNAP_CLICK_ENABLE
|
||||
#if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
extern void snap_click_init(void);
|
||||
snap_click_init();
|
||||
#endif
|
||||
@@ -274,6 +277,11 @@ void keychron_common_task(void) {
|
||||
is_siri_active = false;
|
||||
siri_timer = 0;
|
||||
}
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
process_profile_select_combo();
|
||||
#endif
|
||||
|
||||
#if defined(WIN_LOCK_HOLD_TIME)
|
||||
if (winlock_timer) {
|
||||
if (keymap_config.no_gui) {
|
||||
|
||||
@@ -126,6 +126,14 @@ enum {
|
||||
// clang-format on
|
||||
static_assert(NEW_SAFE_RANGE <= 0x7E1F /* QK_KB_31 */, "Keycode overflow");
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
enum {
|
||||
KEY_PRESS_FN = 0x01 << 0,
|
||||
KEY_PRESS_P = 0x01 << 1,
|
||||
KEY_PRESS_PROF_COMBO = KEY_PRESS_FN | KEY_PRESS_P,
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct PACKED {
|
||||
uint8_t len;
|
||||
uint8_t keycode[3];
|
||||
|
||||
@@ -63,7 +63,7 @@ void get_support_feature(uint8_t *data) {
|
||||
# ifdef DYNAMIC_DEBOUNCE_ENABLE
|
||||
| FEATURE_DYNAMIC_DEBOUNCE
|
||||
# endif
|
||||
# ifdef SNAP_CLICK_ENABLE
|
||||
# if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
| FEATURE_SNAP_CLICK
|
||||
# endif
|
||||
# ifdef KEYCHRON_RGB_ENABLE
|
||||
@@ -74,6 +74,31 @@ void get_support_feature(uint8_t *data) {
|
||||
data[2] = (FEATURE_QUICK_START | FEATURE_NKRO) >> 8;
|
||||
}
|
||||
|
||||
# ifdef ANANLOG_MATRIX
|
||||
void via_raw_hid_send(uint8_t src, uint8_t *data, uint8_t length);
|
||||
|
||||
void send_analog_matrix(uint8_t *data, uint8_t length) {
|
||||
uint8_t offset = data[2];
|
||||
uint8_t rows = 28 / ((MATRIX_COLS + 7) / 8);
|
||||
uint8_t i = 3;
|
||||
for (uint8_t row = 0; row < rows && row + offset < MATRIX_ROWS; row++) {
|
||||
matrix_row_t value = analog_matrix_get_row(row + offset);
|
||||
# if (MATRIX_COLS > 24)
|
||||
data[i++] = (value >> 24) & 0xFF;
|
||||
# endif
|
||||
# if (MATRIX_COLS > 16)
|
||||
data[i++] = (value >> 16) & 0xFF;
|
||||
# endif
|
||||
# if (MATRIX_COLS > 8)
|
||||
data[i++] = (value >> 8) & 0xFF;
|
||||
# endif
|
||||
data[i++] = value & 0xFF;
|
||||
}
|
||||
|
||||
via_raw_hid_send(RAW_HID_SRC_USB, data, length);
|
||||
}
|
||||
# endif
|
||||
|
||||
void get_firmware_version(uint8_t *data) {
|
||||
uint8_t i = 0;
|
||||
data[i++] = 'v';
|
||||
@@ -104,6 +129,13 @@ void kc_raw_hid_send(uint8_t src, uint8_t *data, uint8_t len) {
|
||||
}
|
||||
|
||||
bool kc_raw_hid_rx(uint8_t src, uint8_t *data, uint8_t length) {
|
||||
# if defined(ANANLOG_MATRIX) && defined(VIA_ENABLE)
|
||||
if (src == RAW_HID_SRC_USB && data[0] == id_get_keyboard_value && data[1] == id_switch_matrix_state) {
|
||||
send_analog_matrix(data, length);
|
||||
return true;
|
||||
}
|
||||
# endif
|
||||
|
||||
switch (data[0]) {
|
||||
# ifdef VIA_ENABLE
|
||||
case id_get_protocol_version:
|
||||
@@ -145,7 +177,7 @@ bool kc_raw_hid_rx(uint8_t src, uint8_t *data, uint8_t length) {
|
||||
# ifdef DYNAMIC_DEBOUNCE_ENABLE
|
||||
| MISC_DEBOUNCE
|
||||
# endif
|
||||
# ifdef SNAP_CLICK_ENABLE
|
||||
# if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
| MISC_SNAP_CLICK
|
||||
# endif
|
||||
# if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
|
||||
@@ -174,7 +206,7 @@ bool kc_raw_hid_rx(uint8_t src, uint8_t *data, uint8_t length) {
|
||||
debounce_rx(data, length);
|
||||
break;
|
||||
# endif
|
||||
# if defined(SNAP_CLICK_ENABLE)
|
||||
# if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
case SNAP_CLICK_GET_INFO ... SNAP_CLICK_SAVE:
|
||||
snap_click_rx(data, length);
|
||||
break;
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
#ifdef RETAIL_DEMO_ENABLE
|
||||
# include "retail_demo.h"
|
||||
#endif
|
||||
#ifdef ANANLOG_MATRIX
|
||||
#include "profile.h"
|
||||
#endif
|
||||
|
||||
__attribute__((weak)) bool process_record_keychron_kb(uint16_t keycode, keyrecord_t *record) {
|
||||
return true;
|
||||
@@ -33,6 +36,10 @@ __attribute__((weak)) bool process_record_keychron_kb(uint16_t keycode, keyrecor
|
||||
bool process_record_keychron(uint16_t keycode, keyrecord_t *record) {
|
||||
if (!process_record_keychron_common(keycode, record)) return false;
|
||||
|
||||
#ifdef ANANLOG_MATRIX
|
||||
if (!process_record_profile(keycode, record)) return false;
|
||||
#endif
|
||||
|
||||
#if defined(LK_WIRELESS_ENABLE) || defined(KC_BLUETOOTH_ENABLE)
|
||||
extern bool process_record_wireless(uint16_t keycode, keyrecord_t * record);
|
||||
if (!process_record_wireless(keycode, record)) return false;
|
||||
@@ -47,7 +54,7 @@ bool process_record_keychron(uint16_t keycode, keyrecord_t *record) {
|
||||
if (!process_record_factory_test(keycode, record)) return false;
|
||||
#endif
|
||||
|
||||
#ifdef SNAP_CLICK_ENABLE
|
||||
#if defined(SNAP_CLICK_ENABLE) && !defined(ANANLOG_MATRIX)
|
||||
extern bool process_record_snap_click(uint16_t keycode, keyrecord_t * record);
|
||||
if (!process_record_snap_click(keycode, record)) return false;
|
||||
#endif
|
||||
@@ -76,6 +83,9 @@ __attribute__((weak)) bool led_matrix_indicators_keychron(void) {
|
||||
extern bool led_matrix_indicators_bt(void);
|
||||
led_matrix_indicators_bt();
|
||||
# endif
|
||||
#ifdef ANANLOG_MATRIX
|
||||
analog_matrix_indicator();
|
||||
#endif
|
||||
# ifdef FACTORY_TEST_ENABLE
|
||||
factory_test_indicator();
|
||||
# endif
|
||||
@@ -92,6 +102,9 @@ __attribute__((weak)) bool rgb_matrix_indicators_keychron(void) {
|
||||
extern bool rgb_matrix_indicators_bt(void);
|
||||
rgb_matrix_indicators_bt();
|
||||
# endif
|
||||
#ifdef ANANLOG_MATRIX
|
||||
analog_matrix_indicator();
|
||||
#endif
|
||||
# ifdef FACTORY_TEST_ENABLE
|
||||
factory_test_indicator();
|
||||
# endif
|
||||
|
||||
Reference in New Issue
Block a user