From 6ac0fd42d79da33117c2f6e7d58e9d384042ab36 Mon Sep 17 00:00:00 2001 From: Jacek Fedorynski Date: Fri, 10 Nov 2023 19:50:07 +0100 Subject: [PATCH] GPIO output high impedance mode Previously GPIO output pins were set to low when the output value was 0 and high when the output value was 1. This commit adds a second mode in which the GPIO pin is set to high impedance when the output value is 0 and to low when the output value is 1. This should make it easier to use the GPIO outputs as inputs on other controller devices. --- config-tool-web/code.js | 13 ++++++++++++- config-tool-web/index.html | 11 +++++++++++ config-tool/common.py | 1 + config-tool/get_config.py | 1 + config-tool/set_config.py | 7 ++++++- firmware/src/config.cc | 5 +++++ firmware/src/globals.cc | 1 + firmware/src/globals.h | 1 + firmware/src/main.cc | 27 +++++++++++++++++++++++++-- 9 files changed, 63 insertions(+), 4 deletions(-) diff --git a/config-tool-web/code.js b/config-tool-web/code.js index 7d76b11..1341bc5 100644 --- a/config-tool-web/code.js +++ b/config-tool-web/code.js @@ -22,6 +22,7 @@ const NMACROS = 32; const NEXPRESSIONS = 8; const MACRO_ITEMS_IN_PACKET = 6; const IGNORE_AUTH_DEV_INPUTS_FLAG = 1 << 4; +const GPIO_OUTPUT_MODE_FLAG = 1 << 5; const LAYERS_USAGE_PAGE = 0xFFF10000; const EXPR_USAGE_PAGE = 0xFFF30000; @@ -107,6 +108,7 @@ let config = { 'our_descriptor_number': 0, 'ignore_auth_dev_inputs': false, 'macro_entry_duration': DEFAULT_MACRO_ENTRY_DURATION, + 'gpio_output_mode': 0, mappings: [{ 'source_usage': '0x00000000', 'target_usage': '0x00000000', @@ -155,6 +157,7 @@ document.addEventListener("DOMContentLoaded", function () { } document.getElementById("interval_override_dropdown").addEventListener("change", interval_override_onchange); document.getElementById("our_descriptor_number_dropdown").addEventListener("change", our_descriptor_number_onchange); + document.getElementById("gpio_output_mode_dropdown").addEventListener("change", gpio_output_mode_onchange); document.getElementById("ignore_auth_dev_inputs_checkbox").addEventListener("change", ignore_auth_dev_inputs_onchange); document.getElementById("nav-monitor-tab").addEventListener("shown.bs.tab", monitor_tab_shown); @@ -224,6 +227,7 @@ async function load_from_device() { config['interval_override'] = interval_override; config['our_descriptor_number'] = our_descriptor_number; config['ignore_auth_dev_inputs'] = !!(flags & IGNORE_AUTH_DEV_INPUTS_FLAG); + config['gpio_output_mode'] = (flags & GPIO_OUTPUT_MODE_FLAG) ? 1 : 0; config['macro_entry_duration'] = macro_entry_duration + 1; config['mappings'] = []; @@ -321,7 +325,8 @@ async function save_to_device() { try { await send_feature_command(SUSPEND); const flags = layer_list_to_mask(config['unmapped_passthrough_layers']) | - (config['ignore_auth_dev_inputs'] ? IGNORE_AUTH_DEV_INPUTS_FLAG : 0); + (config['ignore_auth_dev_inputs'] ? IGNORE_AUTH_DEV_INPUTS_FLAG : 0) | + (config['gpio_output_mode'] ? GPIO_OUTPUT_MODE_FLAG : 0); await send_feature_command(SET_CONFIG, [ [UINT8, flags], [UINT32, config['partial_scroll_timeout']], @@ -469,6 +474,7 @@ function set_config_ui_state() { document.getElementById('our_descriptor_number_dropdown').value = config['our_descriptor_number']; document.getElementById('ignore_auth_dev_inputs_checkbox').checked = config['ignore_auth_dev_inputs']; document.getElementById('macro_entry_duration_input').value = config['macro_entry_duration']; + document.getElementById('gpio_output_mode_dropdown').value = config['gpio_output_mode']; } function set_mappings_ui_state() { @@ -574,6 +580,7 @@ function set_ui_state() { } if (config['version'] < 10) { config['macro_entry_duration'] = DEFAULT_MACRO_ENTRY_DURATION; + config['gpio_output_mode'] = 0; } if (config['version'] < CONFIG_VERSION) { config['version'] = CONFIG_VERSION; @@ -1112,6 +1119,10 @@ function our_descriptor_number_onchange() { set_ui_state(); } +function gpio_output_mode_onchange() { + config['gpio_output_mode'] = parseInt(document.getElementById("gpio_output_mode_dropdown").value, 10); +} + function ignore_auth_dev_inputs_onchange() { config['ignore_auth_dev_inputs'] = document.getElementById("ignore_auth_dev_inputs_checkbox").checked; } diff --git a/config-tool-web/index.html b/config-tool-web/index.html index 03861ab..6d909af 100644 --- a/config-tool-web/index.html +++ b/config-tool-web/index.html @@ -164,6 +164,17 @@

HID Remapper Configuration

+
+
+ +
+
+ +
+
diff --git a/config-tool/common.py b/config-tool/common.py index b1986a5..6686cb0 100644 --- a/config-tool/common.py +++ b/config-tool/common.py @@ -51,6 +51,7 @@ HOLD_FLAG = 1 << 2 IGNORE_AUTH_DEV_INPUTS_FLAG = 1 << 4 +GPIO_OUTPUT_MODE_FLAG = 1 << 5 NMACROS = 32 NEXPRESSIONS = 8 diff --git a/config-tool/get_config.py b/config-tool/get_config.py index f61bc8c..692c162 100755 --- a/config-tool/get_config.py +++ b/config-tool/get_config.py @@ -40,6 +40,7 @@ "our_descriptor_number": our_descriptor_number, "ignore_auth_dev_inputs": bool(flags & IGNORE_AUTH_DEV_INPUTS_FLAG), "macro_entry_duration": macro_entry_duration + 1, + "gpio_output_mode": 1 if (flags & GPIO_OUTPUT_MODE_FLAG) else 0, "mappings": [], "macros": [], "expressions": [], diff --git a/config-tool/set_config.py b/config-tool/set_config.py index da560f8..020a1af 100755 --- a/config-tool/set_config.py +++ b/config-tool/set_config.py @@ -33,8 +33,13 @@ our_descriptor_number = config.get("our_descriptor_number", 0) ignore_auth_dev_inputs = config.get("ignore_auth_dev_inputs", False) macro_entry_duration = config.get("macro_entry_duration", 1) - 1 +gpio_output_mode = config.get("gpio_output_mode", 0) -flags = unmapped_passthrough_layer_mask | (IGNORE_AUTH_DEV_INPUTS_FLAG if ignore_auth_dev_inputs else 0) +flags = ( + unmapped_passthrough_layer_mask + | (IGNORE_AUTH_DEV_INPUTS_FLAG if ignore_auth_dev_inputs else 0) + | (GPIO_OUTPUT_MODE_FLAG if gpio_output_mode == 1 else 0) +) data = struct.pack( "flags & CONFIG_FLAG_UNMAPPED_PASSTHROUGH_MASK) >> CONFIG_FLAG_UNMAPPED_PASSTHROUGH_BIT; ignore_auth_dev_inputs = config->flags & (1 << CONFIG_FLAG_IGNORE_AUTH_DEV_INPUTS_BIT); + gpio_output_mode = !!(config->flags & (1 << CONFIG_FLAG_GPIO_OUTPUT_MODE_BIT)); partial_scroll_timeout = config->partial_scroll_timeout; tap_hold_threshold = config->tap_hold_threshold; gpio_debounce_time = config->gpio_debounce_time_ms * 1000; @@ -374,6 +376,7 @@ void fill_get_config(get_config_t* config) { config->flags |= (unmapped_passthrough_layer_mask << CONFIG_FLAG_UNMAPPED_PASSTHROUGH_BIT) & CONFIG_FLAG_UNMAPPED_PASSTHROUGH_MASK; config->flags |= ignore_auth_dev_inputs << CONFIG_FLAG_IGNORE_AUTH_DEV_INPUTS_BIT; + config->flags |= gpio_output_mode << CONFIG_FLAG_GPIO_OUTPUT_MODE_BIT; config->partial_scroll_timeout = partial_scroll_timeout; config->tap_hold_threshold = tap_hold_threshold; config->gpio_debounce_time_ms = gpio_debounce_time / 1000; @@ -391,6 +394,7 @@ void fill_persist_config(persist_config_t* config) { config->flags |= (unmapped_passthrough_layer_mask << CONFIG_FLAG_UNMAPPED_PASSTHROUGH_BIT) & CONFIG_FLAG_UNMAPPED_PASSTHROUGH_MASK; config->flags |= ignore_auth_dev_inputs << CONFIG_FLAG_IGNORE_AUTH_DEV_INPUTS_BIT; + config->flags |= gpio_output_mode << CONFIG_FLAG_GPIO_OUTPUT_MODE_BIT; config->partial_scroll_timeout = partial_scroll_timeout; config->tap_hold_threshold = tap_hold_threshold; config->gpio_debounce_time_ms = gpio_debounce_time / 1000; @@ -581,6 +585,7 @@ void handle_set_report1(uint8_t report_id, uint8_t const* buffer, uint16_t bufsi unmapped_passthrough_layer_mask = (config->flags & CONFIG_FLAG_UNMAPPED_PASSTHROUGH_MASK) >> CONFIG_FLAG_UNMAPPED_PASSTHROUGH_BIT; ignore_auth_dev_inputs = config->flags & (1 << CONFIG_FLAG_IGNORE_AUTH_DEV_INPUTS_BIT); + gpio_output_mode = !!(config->flags & (1 << CONFIG_FLAG_GPIO_OUTPUT_MODE_BIT)); partial_scroll_timeout = config->partial_scroll_timeout; tap_hold_threshold = config->tap_hold_threshold; gpio_debounce_time = config->gpio_debounce_time_ms * 1000; diff --git a/firmware/src/globals.cc b/firmware/src/globals.cc index 496758a..2d1cece 100644 --- a/firmware/src/globals.cc +++ b/firmware/src/globals.cc @@ -29,6 +29,7 @@ uint64_t gpio_debounce_time = 5000; uint8_t our_descriptor_number = 0; bool ignore_auth_dev_inputs = false; uint8_t macro_entry_duration = 0; // 0 means 1ms +uint8_t gpio_output_mode = 0; std::vector config_mappings; diff --git a/firmware/src/globals.h b/firmware/src/globals.h index 5db6614..5c37201 100644 --- a/firmware/src/globals.h +++ b/firmware/src/globals.h @@ -36,6 +36,7 @@ extern uint64_t gpio_debounce_time; extern uint8_t our_descriptor_number; extern bool ignore_auth_dev_inputs; extern uint8_t macro_entry_duration; +extern uint8_t gpio_output_mode; extern std::vector config_mappings; diff --git a/firmware/src/main.cc b/firmware/src/main.cc index ea64b73..2cde834 100644 --- a/firmware/src/main.cc +++ b/firmware/src/main.cc @@ -35,6 +35,7 @@ uint32_t gpio_in_mask = 0; uint32_t gpio_out_mask = 0; uint32_t prev_gpio_state = 0; uint64_t last_gpio_change[32] = { 0 }; +bool set_gpio_dir_pending = false; void print_stats_maybe() { uint64_t now = time_us_64(); @@ -65,7 +66,12 @@ void set_gpio_inout_masks(uint32_t in_mask, uint32_t out_mask) { gpio_out_mask = (out_mask & ~in_mask) & gpio_valid_pins_mask; // we treat all pins except the output ones as input so that the monitor works gpio_in_mask = gpio_valid_pins_mask & ~gpio_out_mask; - gpio_set_dir_masked(gpio_valid_pins_mask, gpio_out_mask); + set_gpio_dir_pending = true; +} + +void set_gpio_dir() { + gpio_set_dir_masked(gpio_in_mask, 0); + // output pin direction will be set in write_gpio() for (uint8_t i = 0; i <= 29; i++) { uint32_t bit = 1 << i; if (gpio_valid_pins_mask & bit) { @@ -102,8 +108,21 @@ bool read_gpio(uint64_t now) { } void write_gpio() { + if (suspended) { + return; + } + uint32_t value = gpio_out_state[0] | (gpio_out_state[1] << 8) | (gpio_out_state[2] << 16) | (gpio_out_state[3] << 24); - gpio_put_masked(gpio_out_mask, value); + switch (gpio_output_mode) { + case 0: + gpio_put_masked(gpio_out_mask, value); + gpio_set_dir_masked(gpio_out_mask, gpio_out_mask); + break; + case 1: + gpio_put_masked(gpio_out_mask, 0); + gpio_set_dir_masked(gpio_out_mask, value); + break; + } memset(gpio_out_state, 0, sizeof(gpio_out_state)); } @@ -197,6 +216,10 @@ int main() { set_mapping_from_config(); config_updated = false; } + if (set_gpio_dir_pending && !suspended) { + set_gpio_dir(); + set_gpio_dir_pending = false; + } if (tud_hid_n_ready(0)) { send_report(do_send_report); }