Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/gfx_settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ lv_obj_t *prev_screen = NULL;
lv_obj_t *edge_dropdown;
lv_obj_t *bias_dropdown;
lv_obj_t *debounce_dropdown;
lv_obj_t *release_holdoff_dropdown;
lv_obj_t *trigger_dropdown;
lv_obj_t *mode_dropdown;
lv_obj_t *trigger_output_dropdown;
Expand Down Expand Up @@ -82,6 +83,10 @@ static void event_handler(lv_event_t* e)
default: break;
}
xlat_gpio_irq_holdoff_us_set(val * 1000);
} else if (obj == release_holdoff_dropdown) {
static const uint32_t vals[] = {0, 20, 50, 100, 200};
uint16_t sel = lv_dropdown_get_selected(obj);
if (sel < 5) xlat_release_holdoff_us_set(vals[sel] * 1000);
} else if (obj == trigger_dropdown) {
uint16_t sel = lv_dropdown_get_selected(obj);
xlat_auto_trigger_level_set(sel);
Expand Down Expand Up @@ -188,6 +193,17 @@ void gfx_settings_create_page(lv_obj_t *previous_screen)
lv_obj_align_to(bias_dropdown, bias_label, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
lv_obj_add_event_cb(bias_dropdown, event_handler, LV_EVENT_VALUE_CHANGED, NULL);

lv_obj_t *release_holdoff_label = lv_label_create(tab_detection);
lv_label_set_text(release_holdoff_label, "Release Holdoff:");
lv_obj_set_width(release_holdoff_label, LABEL_WIDTH);
lv_obj_align_to(release_holdoff_label, bias_label, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 30);

release_holdoff_dropdown = lv_dropdown_create(tab_detection);
lv_dropdown_set_options(release_holdoff_dropdown, "Off\n20ms\n50ms\n100ms\n200ms");
lv_obj_set_width(release_holdoff_dropdown, DROPDOWN_WIDTH);
lv_obj_align_to(release_holdoff_dropdown, release_holdoff_label, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
lv_obj_add_event_cb(release_holdoff_dropdown, event_handler, LV_EVENT_VALUE_CHANGED, NULL);

// Trigger Tab Content
// Add explanatory text for Trigger tab first
lv_obj_t *trigger_info = lv_label_create(tab_trigger);
Expand Down Expand Up @@ -266,6 +282,18 @@ void gfx_settings_create_page(lv_obj_t *previous_screen)
}
lv_dropdown_set_selected(debounce_dropdown, debounce_index);

// Set release holdoff
uint16_t release_index = 2;
switch (xlat_release_holdoff_us_get() / 1000) {
case 0: release_index = 0; break;
case 20: release_index = 1; break;
case 50: release_index = 2; break;
case 100: release_index = 3; break;
case 200: release_index = 4; break;
default: break;
}
lv_dropdown_set_selected(release_holdoff_dropdown, release_index);

// Set input bias
uint32_t current_bias = hw_config_input_bias_get();
uint16_t bias_index = 0;
Expand Down
6 changes: 5 additions & 1 deletion src/hardware_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ static void MX_USART1_UART_Init(void);
static void MX_USART6_UART_Init(void);

static bool rising_edge = false;
static input_bias_t input_bias = INPUT_BIAS_NOPULL;
static input_bias_t input_bias = INPUT_BIAS_PULLUP;

/**
* @brief The application entry point.
Expand Down Expand Up @@ -97,6 +97,10 @@ void hw_exti_interrupts_enable(void)
{
/* EXTI interrupt init */
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0);
/* Drop any edge latched while the IRQ was disabled (holdoff), otherwise the ISR
* fires immediately on enable with a bogus timestamp. */
__HAL_GPIO_EXTI_CLEAR_IT(ARDUINO_D12_Pin);
HAL_NVIC_ClearPendingIRQ(EXTI15_10_IRQn);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

Expand Down
39 changes: 39 additions & 0 deletions src/xlat.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ static volatile uint_fast8_t gpio_irq_consumer = 0;
// The GPIO edge may still follow (e.g. analog/TMR switches where XLAT's input
// threshold is crossed after the mouse already reported the click): that is a
// negative latency. Written by the xlat task, consumed by the EXTI ISR.
// USB-side button state, used to reject GPIO chatter while the button is held or right after release
static volatile bool usb_button_held = false;
static volatile uint32_t usb_release_timestamp = 0;
static volatile bool usb_event_pending = false;
static volatile uint32_t usb_event_pending_timestamp = 0;

Expand Down Expand Up @@ -334,6 +337,16 @@ void xlat_process_usb_hid_event(void)

// FOR BUTTONS/CLICKS:
if (xlat_mode_get() == XLAT_MODE_MOUSE_CLICK) {
// Track USB button state for GPIO chatter rejection (see HAL_GPIO_EXTI_Callback)
for (uint8_t i = (xlat_report_id_get() ? 1 : 0); i < hevt->report_size; i++) {
uint8_t changed = (hid_raw_data[i] ^ prev_report[i]) & xlat_button_mask_get()[i];
if (changed & hid_raw_data[i]) {
usb_button_held = true;
} else if (changed) {
usb_release_timestamp = hevt->timestamp;
usb_button_held = false;
}
}
// The correct location of button data is determined by parsing the HID descriptor
// This information is available in the button_mask
for (uint8_t i = (xlat_report_id_get() ? 1 : 0); i < hevt->report_size; i++) {
Expand Down Expand Up @@ -408,6 +421,31 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
if (cnt - last_btn_gpio_timestamp < xlat_gpio_irq_holdoff_us_get()) {
return;
}
// Reject contact chatter that cannot be a new press: edges while USB already reports the button
// held (and nothing is waiting to be paired), or within the release holdoff after a USB release.
// Only GPIO edges are filtered here; USB press reports are never dropped by this.
uint8_t ignore_reason = 0;
if (!usb_event_pending) {
if (usb_button_held) {
ignore_reason = 1;
} else if (cnt - usb_release_timestamp < xlat_release_holdoff_us_get()) {
ignore_reason = 2;
}
}
if (ignore_reason) {
// Chatter: mute the input briefly instead of taking an ISR storm. Re-arm when the release
// holdoff ends (reason 2) or after a short pause while the button is held (reason 1).
uint32_t mute_us = 5000;
if (ignore_reason == 2) {
mute_us = xlat_release_holdoff_us_get() - (cnt - usb_release_timestamp);
}
uint32_t mute_ms = (mute_us + 999) / 1000;
if (mute_ms == 0) mute_ms = 1;
hw_exti_interrupts_disable();
xTimerChangePeriodFromISR(xlat_timer_handle, pdMS_TO_TICKS(mute_ms), NULL);
xTimerStartFromISR(xlat_timer_handle, NULL);
return;
}
last_btn_gpio_timestamp = cnt;

if (usb_event_pending) {
Expand Down Expand Up @@ -533,6 +571,7 @@ void xlat_latency_reset(void)
{
taskENTER_CRITICAL();
usb_event_pending = false;
usb_button_held = false;
gpio_irq_consumer = gpio_irq_producer;
taskEXIT_CRITICAL();
for (int i = 0; i < LATENCY_TYPE_MAX; i++) {
Expand Down
2 changes: 2 additions & 0 deletions src/xlat.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ void xlat_print_measurement(void);

void xlat_gpio_irq_holdoff_us_set(uint32_t us);
uint32_t xlat_gpio_irq_holdoff_us_get(void);
void xlat_release_holdoff_us_set(uint32_t us);
uint32_t xlat_release_holdoff_us_get(void);

uint32_t xlat_counter_1mhz_get(void);

Expand Down
11 changes: 11 additions & 0 deletions src/xlat_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ bool keyboard_usage_page_found = false;
// Therefore, take a large enough time window to debounce the GPIO interrupt.
#define GPIO_IRQ_HOLDOFF_US (100 * 1000) // 100ms;
static uint32_t gpio_irq_holdoff_us = GPIO_IRQ_HOLDOFF_US;
static uint32_t release_holdoff_us = 50000; // ignore GPIO edges this long after a USB button release

// Mode configuration
void xlat_mode_set(enum xlat_mode mode)
Expand Down Expand Up @@ -103,6 +104,16 @@ void xlat_gpio_irq_holdoff_us_set(uint32_t us)
gpio_irq_holdoff_us = us;
}

void xlat_release_holdoff_us_set(uint32_t us)
{
release_holdoff_us = us;
}

uint32_t xlat_release_holdoff_us_get(void)
{
return release_holdoff_us;
}


uint32_t xlat_gpio_irq_holdoff_us_get(void)
{
Expand Down
Loading