Skip to content

Commit

Permalink
GPIO output high impedance mode
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jfedor2 committed Nov 15, 2023
1 parent 4f66bde commit 6ac0fd4
Show file tree
Hide file tree
Showing 9 changed files with 63 additions and 4 deletions.
13 changes: 12 additions & 1 deletion config-tool-web/code.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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'] = [];

Expand Down Expand Up @@ -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']],
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
11 changes: 11 additions & 0 deletions config-tool-web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ <h1 class="mt-sm-5 mt-3">HID Remapper Configuration</h1>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-4 text-end">
<label for="gpio_output_mode_dropdown" class="col-form-label">GPIO output mode</label>
</div>
<div class="col-auto">
<select class="form-select" id="gpio_output_mode_dropdown">
<option value="0">0=low, 1=high</option>
<option value="1">0=high-Z, 1=low</option>
</select>
</div>
</div>
<div class="row mt-3">
<div class="col-4 text-end">
<label for="ignore_auth_dev_inputs_checkbox" class="col-form-label">Ignore auth device inputs</label>
Expand Down
1 change: 1 addition & 0 deletions config-tool/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions config-tool/get_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": [],
Expand Down
7 changes: 6 additions & 1 deletion config-tool/set_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"<BBBBLBLBBB13B",
Expand Down
5 changes: 5 additions & 0 deletions firmware/src/config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const uint8_t CONFIG_FLAG_UNMAPPED_PASSTHROUGH = 0x01;
const uint8_t CONFIG_FLAG_UNMAPPED_PASSTHROUGH_MASK = 0b00001111;
const uint8_t CONFIG_FLAG_UNMAPPED_PASSTHROUGH_BIT = 0;
const uint8_t CONFIG_FLAG_IGNORE_AUTH_DEV_INPUTS_BIT = 4;
const uint8_t CONFIG_FLAG_GPIO_OUTPUT_MODE_BIT = 5;

ConfigCommand last_config_command = ConfigCommand::NO_COMMAND;
uint32_t requested_index = 0;
Expand Down Expand Up @@ -313,6 +314,7 @@ void load_config(const uint8_t* persisted_config) {
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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions firmware/src/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<mapping_config_t> config_mappings;

Expand Down
1 change: 1 addition & 0 deletions firmware/src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<mapping_config_t> config_mappings;

Expand Down
27 changes: 25 additions & 2 deletions firmware/src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 6ac0fd4

Please sign in to comment.