diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6caaa3d4ad..17b767ec89 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -154,7 +154,6 @@ if(CMAKE_CROSSCOMPILING) set_property(TARGET asf4-drivers PROPERTY INTERFACE_LINK_LIBRARIES "") find_library(QTOUCHLIB_A qtm_acq_samd51_0x000f PATHS asf4-drivers/qtouch/lib/gcc NO_CMAKE_FIND_ROOT_PATH) - find_library(QTOUCHLIB_B qtm_binding_layer_cm4_0x0005 PATHS asf4-drivers/qtouch/lib/gcc NO_CMAKE_FIND_ROOT_PATH) find_library(QTOUCHLIB_T qtm_touch_key_cm4_0x0002 PATHS asf4-drivers/qtouch/lib/gcc NO_CMAKE_FIND_ROOT_PATH) add_library(cryptoauthlib diff --git a/external/asf4-drivers/Config/hpl_gclk_config.h b/external/asf4-drivers/Config/hpl_gclk_config.h index e62eab55bc..331ec261e2 100644 --- a/external/asf4-drivers/Config/hpl_gclk_config.h +++ b/external/asf4-drivers/Config/hpl_gclk_config.h @@ -387,7 +387,7 @@ // Indicates whether generic clock 5 configuration is enabled or not // enable_gclk_gen_5 #ifndef CONF_GCLK_GENERATOR_5_CONFIG -#define CONF_GCLK_GENERATOR_5_CONFIG 0 +#define CONF_GCLK_GENERATOR_5_CONFIG 1 #endif // Generic Clock Generator Control @@ -404,7 +404,7 @@ // This defines the clock source for generic clock generator 5 // gclk_gen_5_oscillator #ifndef CONF_GCLK_GEN_5_SOURCE -#define CONF_GCLK_GEN_5_SOURCE GCLK_GENCTRL_SRC_GCLKGEN1 +#define CONF_GCLK_GEN_5_SOURCE GCLK_GENCTRL_SRC_DFLL #endif // Run in Standby @@ -446,7 +446,7 @@ // Indicates whether Generic Clock Generator Enable is enabled or not // gclk_arch_gen_5_enable #ifndef CONF_GCLK_GEN_5_GENEN -#define CONF_GCLK_GEN_5_GENEN 0 +#define CONF_GCLK_GEN_5_GENEN 1 #endif // @@ -454,7 +454,7 @@ // Generic clock generator 5 division <0x0000-0xFFFF> // gclk_gen_5_div #ifndef CONF_GCLK_GEN_5_DIV -#define CONF_GCLK_GEN_5_DIV 62 +#define CONF_GCLK_GEN_5_DIV 6 #endif // // diff --git a/external/asf4-drivers/Config/peripheral_clk_config.h b/external/asf4-drivers/Config/peripheral_clk_config.h index d557fe9e81..8e6eb0cf22 100644 --- a/external/asf4-drivers/Config/peripheral_clk_config.h +++ b/external/asf4-drivers/Config/peripheral_clk_config.h @@ -33,7 +33,7 @@ // Select the clock source for ADC. #ifndef CONF_GCLK_ADC0_SRC -#define CONF_GCLK_ADC0_SRC GCLK_PCHCTRL_GEN_GCLK2_Val +#define CONF_GCLK_ADC0_SRC GCLK_PCHCTRL_GEN_GCLK5_Val #endif /** @@ -41,7 +41,7 @@ * \brief ADC0's Clock frequency */ #ifndef CONF_GCLK_ADC0_FREQUENCY -#define CONF_GCLK_ADC0_FREQUENCY 12000000 +#define CONF_GCLK_ADC0_FREQUENCY 8000000 #endif /** diff --git a/external/asf4-drivers/qtouch/include/qtm_acq_same54_0x000f_api.h b/external/asf4-drivers/qtouch/include/qtm_acq_same54_0x000f_api.h index a75fbcce22..9e03ecb380 100644 --- a/external/asf4-drivers/qtouch/include/qtm_acq_same54_0x000f_api.h +++ b/external/asf4-drivers/qtouch/include/qtm_acq_same54_0x000f_api.h @@ -80,14 +80,14 @@ typedef enum tag_gain_t { GAIN_1, GAIN_2, GAIN_4, GAIN_8, GAIN_16 } gain_t; * */ typedef enum tag_prsc_div_sel_t { - PRSC_DIV_SEL_1, PRSC_DIV_SEL_2, PRSC_DIV_SEL_4, PRSC_DIV_SEL_8, PRSC_DIV_SEL_16, PRSC_DIV_SEL_32, PRSC_DIV_SEL_64, - PRSC_DIV_SEL_128 + PRSC_DIV_SEL_128, + PRSC_DIV_SEL_256 } prsc_div_sel_t; /** diff --git a/external/asf4-drivers/qtouch/include/qtm_binding_layer_0x0005_api.h b/external/asf4-drivers/qtouch/include/qtm_binding_layer_0x0005_api.h deleted file mode 100644 index 7e4950e1c9..0000000000 --- a/external/asf4-drivers/qtouch/include/qtm_binding_layer_0x0005_api.h +++ /dev/null @@ -1,226 +0,0 @@ -/*============================================================================ -Filename : qtm_binding_layer_api.h -Project : QTouch Modular Library -Purpose : Binds the acquisition and post processing modules and provides callback ------------------------------------------------------------------------------- -Copyright (c) 2017 Microchip. All rights reserved. ------------------------------------------------------------------------------- -============================================================================*/ - -/** @file */ - -/** \defgroup Misra Misra Compliance report */ - -/** - * \addtogroup Misra - * Complaiance:
- * the module code is compiled using IARs Embedded workbench for AVR
- * MISRA 2004 compliance is selected with all required options selected
- * any rule which has been violated will be documented and the rule switched off
- *
- *
- */ - -#ifndef TOUCH_API_BINDING_LAYER_H -#define TOUCH_API_BINDING_LAYER_H - -/* Include files */ -#include -#include "qtm_common_components_api.h" - -/*---------------------------------------------------------------------------- -manifest constants -----------------------------------------------------------------------------*/ -#define null '\0' - -/*---------------------------------------------------------------------------- - * type definitions - *----------------------------------------------------------------------------*/ - -/* Modular Library state */ -typedef enum QT_Modular_lib_state_tag { uninitialised, config_initialised, ready, busy, processing, error } qtm_state_t; - -typedef enum QT_ModLib_Status_flags { - time_to_measure_touch, - node_pp_request, - reburst_request, - reserved_flag_pos_3, - - reserved_flag_pos_4, - reserved_flag_pos_5, - reserved_flag_pos_6, - reserved_flag_pos_7, -} qtm_flags; - -typedef void (*qtm_measure_complete_t)(void); -typedef void (*qtm_library_init_complete_t)(void); -typedef void (*qtm_pre_process_callback_t)(uint8_t *callback); -typedef void (*qtm_error_callback_t)(uint8_t error_code); -typedef void (*qtm_post_process_callback_t)(void); - -typedef void *(*module_init_t)(void *data_model); -typedef void *(*module_proc_t)(void *data_model); -typedef void *(*module_init_inline_t)(void *data_model); -typedef void *(*module_inline_t)(void *data_model); -typedef void *(*module_conf_t)(void *data_model); -typedef void *(*module_acq_t)(void *data_model, void (*callback)(void)); - -/* this should take an arqument */ -typedef touch_ret_t(qtm_acq_pp_t)(void); - -typedef void *module_arg_t; - -/*---------------------------------------------------------------------------- - * Structure Declarations - *----------------------------------------------------------------------------*/ - -/* ---------------------------------------------------------------------------------------- */ -/* Key sensor run-time data - api common */ -/* ---------------------------------------------------------------------------------------- */ - -/* Container */ -/** - * @brief a collection of controls for the binding layer - */ -typedef struct qtm_control_tag /*!< Control structure for the bonsding layer */ -{ - uint8_t binding_layer_flags; /*!< Some Flags */ - - module_init_t *library_modules_init; /*!< List of function pointers to acquisition sets */ - module_proc_t *library_modules_proc; /*!< List of function pointers to post processing modules */ - module_acq_t * library_modules_acq; - - module_arg_t *library_module_init_data_model; /*!< Data Model for Acquisition modules */ - module_arg_t *library_module_proc_data_model; /*!< Data Model for post processing modules */ - module_arg_t *library_modules_acq_dm; /*!< Data model for inline module processes */ - - qtm_acq_pp_t *qtm_acq_pp; /*!< Post porcessing pointer */ - - /*******************************/ - /* Callbacks for Binding layer */ - /*******************************/ - qtm_library_init_complete_t qtm_init_complete_callback; - qtm_error_callback_t qtm_error_callback; - qtm_measure_complete_t qtm_measure_complete_callback; - qtm_pre_process_callback_t qtm_pre_process_callback; - qtm_post_process_callback_t qtm_post_process_callback; - -} qtm_control_t; - -/*---------------------------------------------------------------------------- - * prototypes - *----------------------------------------------------------------------------*/ - -/** - * @brief Returns a pointer to the binding layer control structure - * - * This function is never used in the example code, but it is included here - * in case the user would like to get a (qtm_control_t) - * - * This function accepts no inputs - * - * @return &qtm_control, this is a pointer to the binding layer control structure - */ -qtm_control_t *qmt_get_binding_layer_ptr(void); - -/** - * Initialises the binding layer, there are two possible outcomes - * - * - * 1). There were no errors - * \msc - * a [label="User Application"],b [label="Modular Library"]; - * a=>>b [label="qtm_lib_init(&qtm_control);"]; - * b=>b; - * a<<=b [label="qtm_init_complete_callback();"]; - * \endmsc - * - * 2). There were errors detected - * \msc - * a [label="User Application"],b [label="Modular Library"]; - * a=>>b [label="qtm_lib_init(&qtm_control);"]; - * b=>b; - * a<<=b [label="qtm_error_callback(error_code);"]; - * \endmsc - */ - -/** - * @see qtm_state_t for a state diagram - * @param qtm_control This is the control structure to the binding layer - */ - -/** - * Typical Application flow. - * - * @msc - * a [label="User Application"],b [label="Modular Library"]; - * a=>>b [label="qtm_lib_init(&qtm_control);"]; - * b=>b; - * a<<=b [label="qtm_init_complete_callback();"]; - * a=>>b [label="qtm_calibrate_hardware();"]; - * --- [ label = "library setup, Start normal acquisition" ]; - * a=>>b [label="qtm_lib_start_acquisition();"]; - * b=>b; - * a<<=b [label="qtm_measure_complete_callback();"]; - * ...; - * a=>>b [label="qtm_lib_start_acquisition();"]; - * b=>b; - * a<<=b [label="qtm_measure_complete_callback();"]; - * ...; - * a=>>b [label="qtm_lib_start_acquisition();"]; - * b=>b; - * a<<=b [label="qtm_measure_complete_callback();"]; - * ...; - * a=>>b [label="qtm_lib_start_acquisition();"]; - * b=>b; - * a<<=b [label="qtm_measure_complete_callback();"]; - * ...; - * @endmsc - */ - -void qtm_binding_layer_init(qtm_control_t *qtm_control); - -/** - * @brief Starts the Modular library on the first acquisition set - * - * An Acquisition set is defined in the Acquisition Module documentation, but - * essentially an acquisition set is a group of node with a common property, - * the common property can be, Acquisition Type: Self or Mutual, or proximity or - * spatial displacement to or from another. - * - * @return the library state, @see qtm_state_t - */ -touch_ret_t qtm_lib_start_acquisition(uint8_t set_id); - -/*qtm_state_t qtm_post_process(void);*/ -touch_ret_t qtm_lib_post_process(void); - -/** - * @brief Gets the library state - * - * some tasks cannot happen if the library is not in the correct state - * if the library is requesting a start acquisition when there is an - * ongoing acquisition then the binding layer will move to the error - * state, checking the library state before acquisition will prevent - * errors like this. - * - * @return the library state, - * @see qtm_state_t - */ -qtm_state_t qtm_lib_get_state(void); - -touch_ret_t qtm_lib_acq_process(void); - -uint16_t qtm_get_binding_layer_module_id(void); - -/*============================================================================ -uint8_t get_qtm_m328pb_acq_module_version(void) ------------------------------------------------------------------------------- -Purpose: Returns the module Firmware version -Input : none -Output : Module ID - Upper nibble major / Lower nibble minor -Notes : none -============================================================================*/ -uint8_t qtm_get_binding_layer_module_version(void); - -#endif /* TOUCH_API_BINDING_LAYER_H */ diff --git a/external/asf4-drivers/qtouch/include/qtm_scroller_0x000b_api.h b/external/asf4-drivers/qtouch/include/qtm_scroller_0x000b_api.h deleted file mode 100644 index 3079b90362..0000000000 --- a/external/asf4-drivers/qtouch/include/qtm_scroller_0x000b_api.h +++ /dev/null @@ -1,151 +0,0 @@ -/*============================================================================ -Filename : qtm_scroller_api.h -Project : QTouch Modular Library -Purpose : Structs and definitions for use within modules ------------------------------------------------------------------------------- -Copyright (c) 2017 Microchip. All rights reserved. ------------------------------------------------------------------------------- -============================================================================*/ - -#ifndef TOUCH_API_SCROLLER_H -#define TOUCH_API_SCROLLER_H - -/* Include files */ -#include -#include "qtm_common_components_api.h" - -/* Scroller status bits */ -#define TOUCH_ACTIVE (uint8_t)((uint8_t)1 << 0u) /* Bit 0 */ -#define POSITION_CHANGE (uint8_t)((uint8_t)1 << 1u) /* Bit 1 */ -#define SCROLLER_REBURST (uint8_t)((uint8_t)1 << 7u) /* Bit 7 */ - -/* Extract Resolution / Deadband */ -#define SCR_RESOLUTION(m) (uint8_t)(((m)&0xF0u) >> 4u) -#define SCR_DEADBAND(m) (uint8_t)((m)&0x0Fu) - -/* Combine Resolution / Deadband */ -#define SCR_RESOL_DEADBAND(r, p) (uint8_t)(((r) << 4u) | (p)) - -/* scroller resolution setting */ -typedef enum tag_resolution_t { - RESOL_2_BIT = 2, - RESOL_3_BIT, - RESOL_4_BIT, - RESOL_5_BIT, - RESOL_6_BIT, - RESOL_7_BIT, - RESOL_8_BIT, - RESOL_9_BIT, - RESOL_10_BIT, - RESOL_11_BIT, - RESOL_12_BIT -} scr_resolution_t; - -/* scroller deadband percentage setting */ -typedef enum tag_deadband_t { - DB_NONE, - DB_1_PERCENT, - DB_2_PERCENT, - DB_3_PERCENT, - DB_4_PERCENT, - DB_5_PERCENT, - DB_6_PERCENT, - DB_7_PERCENT, - DB_8_PERCENT, - DB_9_PERCENT, - DB_10_PERCENT, - DB_11_PERCENT, - DB_12_PERCENT, - DB_13_PERCENT, - DB_14_PERCENT, - DB_15_PERCENT -} scr_deadband_t; - -/*---------------------------------------------------------------------------- - * Structure Declarations - *----------------------------------------------------------------------------*/ - -/* Configuration - Group of scrollers */ -typedef struct { - qtm_touch_key_data_t *qtm_touch_key_data; - uint8_t num_scrollers; -} qtm_scroller_group_config_t; - -/* Data - Group of scrollers */ -typedef struct { - uint8_t scroller_group_status; -} qtm_scroller_group_data_t; - -/* Configuration - Each slider / wheel */ -typedef struct { - uint8_t type; - uint16_t start_key; - uint8_t number_of_keys; - uint8_t resol_deadband; - uint8_t position_hysteresis; - uint16_t contact_min_threshold; -} qtm_scroller_config_t; - -/* Data Each - slider / wheel */ -typedef struct { - uint8_t scroller_status; - uint8_t right_hyst; - uint8_t left_hyst; - uint16_t raw_position; - uint16_t position; - uint16_t contact_size; -} qtm_scroller_data_t; - -/* Container */ -typedef struct { - qtm_scroller_group_data_t * qtm_scroller_group_data; - qtm_scroller_group_config_t *qtm_scroller_group_config; - qtm_scroller_data_t * qtm_scroller_data; - qtm_scroller_config_t * qtm_scroller_config; -} qtm_scroller_control_t; - -/*---------------------------------------------------------------------------- - * prototypes - *----------------------------------------------------------------------------*/ - -/*============================================================================ -touch_ret_t qtm_init_scroller_module(qtm_scroller_control_t *qtm_scroller_control) ------------------------------------------------------------------------------- -Purpose: Initialize a scroller -Input : Pointer to scroller group control data -Output : TOUCH_SUCCESS -Notes : none -============================================================================*/ -touch_ret_t qtm_init_scroller_module(qtm_scroller_control_t *qtm_scroller_control); - -/*============================================================================ -touch_ret_t qtm_scroller_process(qtm_scroller_control_t *qtm_scroller_control) ------------------------------------------------------------------------------- -Purpose: Scroller position calculation and filtering -Input : Pointer to scroller group control data -Output : TOUCH_SUCCESS -Notes : none -============================================================================*/ -touch_ret_t qtm_scroller_process(qtm_scroller_control_t *qtm_scroller_control); - -/*============================================================================ -uint16_t qtm_get_scroller_module_id(void) ------------------------------------------------------------------------------- -Purpose: Returns the module ID -Input : none -Output : Module ID -Notes : none -============================================================================*/ -uint16_t qtm_get_scroller_module_id(void); - -/*============================================================================ -uint8_t qtm_get_scroller_module_ver(void) ------------------------------------------------------------------------------- -Purpose: Returns the module Firmware version -Input : none -Output : Module ID - Upper nibble major / Lower nibble minor -Notes : none -============================================================================*/ -uint8_t qtm_get_scroller_module_ver(void); - -#endif /* TOUCH_API_SCROLLER_H */ diff --git a/external/asf4-drivers/qtouch/include/touch_api_ptc.h b/external/asf4-drivers/qtouch/include/touch_api_ptc.h index 15f693cf42..2ea3b45585 100644 --- a/external/asf4-drivers/qtouch/include/touch_api_ptc.h +++ b/external/asf4-drivers/qtouch/include/touch_api_ptc.h @@ -32,30 +32,32 @@ extern "C" { #include "hal_timer.h" -#include "qtm_common_components_api.h" -#include "qtm_binding_layer_0x0005_api.h" #include "qtm_acq_samd51_0x000f_api.h" +#include "qtm_common_components_api.h" #include "qtm_touch_key_0x0002_api.h" -#include "qtm_scroller_0x000b_api.h" /*---------------------------------------------------------------------------- * prototypes *----------------------------------------------------------------------------*/ /* Application Helper API's */ uint16_t qtouch_get_sensor_node_signal(uint16_t sensor_node); -uint16_t qtouch_get_sensor_node_reference(uint16_t sensor_node); +void qtouch_update_sensor_node_signal(uint16_t sensor_node, uint16_t new_signal); uint16_t qtouch_get_sensor_node_signal_filtered(uint16_t sensor_node); +uint16_t qtouch_get_sensor_node_reference(uint16_t sensor_node); +void qtouch_update_sensor_node_reference(uint16_t sensor_node, uint16_t new_reference); uint16_t qtouch_get_sensor_cc_val(uint16_t sensor_node); +void qtouch_update_sensor_cc_val(uint16_t sensor_node, uint16_t new_cc_value); uint8_t qtouch_get_sensor_state(uint16_t sensor_node); +void qtouch_update_sensor_state(uint16_t sensor_node, uint8_t new_state); +void qtouch_calibrate_node(uint16_t sensor_node); +uint8_t qtouch_get_scroller_state(uint16_t sensor_node); bool qtouch_is_scroller_active(uint16_t sensor_node); uint16_t qtouch_get_scroller_position(uint16_t sensor_node); void qtouch_timer_handler(void); void qtouch_init(void); void qtouch_process(void); -void qtouch_force_calibrate(void); -void qtimer_task_cb(const struct timer_task *const timer_task); void qtouch_timer_config(void); #ifdef __cplusplus diff --git a/external/asf4-drivers/qtouch/lib/gcc/libqtm_binding_layer_cm4_0x0005.a b/external/asf4-drivers/qtouch/lib/gcc/libqtm_binding_layer_cm4_0x0005.a deleted file mode 100644 index c6658cc462..0000000000 Binary files a/external/asf4-drivers/qtouch/lib/gcc/libqtm_binding_layer_cm4_0x0005.a and /dev/null differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 134c20a85f..a99d0a6fa2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -504,7 +504,7 @@ if(CMAKE_CROSSCOMPILING) foreach(bootloader ${DEVDEVICE-BOOTLOADERS} ${BOOTLOADERS-BITBOX02PLUS}) set(elf ${bootloader}.elf) - target_link_libraries(${elf} PRIVATE ${QTOUCHLIB_A} ${QTOUCHLIB_B} ${QTOUCHLIB_T}) + target_link_libraries(${elf} PRIVATE ${QTOUCHLIB_A} ${QTOUCHLIB_T}) target_sources(${elf} PRIVATE ${QTOUCH-SOURCES}) endforeach(bootloader) @@ -569,7 +569,7 @@ if(CMAKE_CROSSCOMPILING) target_link_libraries(${elf} PRIVATE -Wl,--defsym=STACK_SIZE=${STACK_SIZE} -Wl,-defsym=HEAP_SIZE=${HEAP_SIZE}) target_link_libraries(${elf} PRIVATE secp256k1) - target_link_libraries(${elf} PRIVATE ${QTOUCHLIB_A} ${QTOUCHLIB_B} ${QTOUCHLIB_T}) + target_link_libraries(${elf} PRIVATE ${QTOUCHLIB_A} ${QTOUCHLIB_T}) # Select the smaller version of libc called nano. target_compile_options(${elf} PRIVATE --specs=nano.specs) diff --git a/src/bootloader/bootloader.c b/src/bootloader/bootloader.c index 55698749d0..6d130b0e86 100644 --- a/src/bootloader/bootloader.c +++ b/src/bootloader/bootloader.c @@ -363,7 +363,6 @@ extern uint8_t bootloader_pairing_code_bytes[4]; void bootloader_render_ble_confirm_screen(bool confirmed) { - qtouch_force_calibrate(); bootloader_pairing_request = true; uint32_t pairing_code_int = (*(uint32_t*)&bootloader_pairing_code_bytes[0]) % 1000000; char code_str[10] = {0}; diff --git a/src/qtouch/qtouch.c b/src/qtouch/qtouch.c index f6ad8f57eb..85417dfb28 100644 --- a/src/qtouch/qtouch.c +++ b/src/qtouch/qtouch.c @@ -18,63 +18,48 @@ Support: Visit http://www.microchip.com/support/hottopics.aspx Copyright (c) 2017 Microchip. All rights reserved. ------------------------------------------------------------------------------ ============================================================================*/ - #ifndef TOUCH_C #define TOUCH_C /*---------------------------------------------------------------------------- * include files *----------------------------------------------------------------------------*/ +#include + #include "qtouch.h" -#include "license.h" -#include "touch_api_ptc.h" -#include "util.h" -#include -#include /*---------------------------------------------------------------------------- * prototypes *----------------------------------------------------------------------------*/ -/*! \brief configure binding layer config parameter - */ -static void build_qtm_config(qtm_control_t* qtm); - /*! \brief configure keys, wheels and sliders. */ static touch_ret_t touch_sensors_config(void); -/*! \brief Init complete callback function prototype. - */ -static void init_complete_callback(void); - /*! \brief Touch measure complete callback function example prototype. */ static void qtm_measure_complete_callback(void); -/*! \brief Touch post process complete callback function prototype. - */ -static void qtm_post_process_complete(void); - /*! \brief Touch Error callback function prototype. */ -static void qtm_error_callback(uint8_t err); +static void qtm_error_callback(uint8_t error); -/*! \brief Calculate scroller positions based on custom filter. +/* Update our scroller implementation */ -static void qtouch_process_scroller_positions(void); +void qtouch_process_scroller_positions(scroller_control_t* scrollers); /*---------------------------------------------------------------------------- * Global Variables *----------------------------------------------------------------------------*/ -/* Binding layer control */ -qtm_control_t qtm_control; -qtm_control_t* p_qtm_control; -qtm_state_t qstate; +/* Flag to indicate time for touch measurement */ +volatile uint8_t time_to_measure_touch_flag = 0; + +/* postporcess request flag */ +volatile uint8_t touch_postprocess_request = 0; /* Measurement Done Touch Flag */ -volatile bool measurement_done_touch = false; +volatile uint8_t measurement_done_touch = 0; /* Error Handling */ uint8_t module_error_code = 0; @@ -110,17 +95,6 @@ qtm_acquisition_control_t qtlib_acq_set1 = { &ptc_seq_node_cfg1[0], &ptc_qtlib_node_stat1[0]}; -/* Stores recent unfiltered scroller positions for custom filter */ -__extension__ static uint16_t scroller_previous_position[][DEF_SCROLLER_NUM_PREV_POS] = { - [0 ...(DEF_NUM_SCROLLERS - 1)][0 ...(DEF_SCROLLER_NUM_PREV_POS - 1)] = 0}; - -/* Current scroller position consisting of a moving-average of preceding positions for custom filter - */ -__extension__ static uint16_t scroller_position[] = {[0 ...(DEF_NUM_SCROLLERS - 1)] = 0}; - -/* Whether or not scroller reading exceeds threshold for custom filter */ -__extension__ static bool scroller_active[DEF_NUM_SCROLLERS] = {[0 ...(DEF_NUM_SCROLLERS - 1)] = 0}; - /**********************************************************/ /*********************** Keys Module **********************/ /**********************************************************/ @@ -152,6 +126,7 @@ qtm_touch_key_config_t qtlib_key_configs_set1[DEF_NUM_SENSORS] = { KEY_5_PARAMS, KEY_6_PARAMS, KEY_7_PARAMS}; + /* Container */ qtm_touch_key_control_t qtlib_key_set1 = { &qtlib_key_grp_data_set1, @@ -171,75 +146,35 @@ qtm_touch_key_control_t qtlib_key_set1 = { * fast responsiveness. */ -/**********************************************************/ -/**************** Binding Layer Module ******************/ -/**********************************************************/ -#define LIB_MODULES_INIT_LIST {(module_init_t) & qtm_ptc_init_acquisition_module, null} - -#define LIB_MODULES_PROC_LIST {(module_proc_t) & qtm_key_sensors_process, null} - -#define LIB_INIT_DATA_MODELS_LIST {(void*)&qtlib_acq_set1, null} - -#define LIB_DATA_MODELS_PROC_LIST {(void*)&qtlib_key_set1, null} - -#define LIB_MODULES_ACQ_ENGINES_LIST {(module_acq_t) & qtm_ptc_start_measurement_seq, null} - -#define LIB_MODULES_ACQ_ENGINES_LIST_DM {(void*)&qtlib_acq_set1, null} - -/* QTM run time options */ -module_init_t library_modules_init[] = LIB_MODULES_INIT_LIST; -module_proc_t library_modules_proc[] = LIB_MODULES_PROC_LIST; -module_arg_t library_module_init_data_models[] = LIB_INIT_DATA_MODELS_LIST; -module_acq_t library_modules_acq_engines[] = LIB_MODULES_ACQ_ENGINES_LIST; - -module_arg_t library_module_acq_engine_data_model[] = LIB_MODULES_ACQ_ENGINES_LIST_DM; -module_arg_t library_module_proc_data_model[] = LIB_DATA_MODELS_PROC_LIST; - -/*---------------------------------------------------------------------------- - * function definitions - *----------------------------------------------------------------------------*/ - -/*============================================================================ -static void build_qtm_config(qtm_control_t *qtm) ------------------------------------------------------------------------------- -Purpose: Initialization of binding layer module -Input : Pointer of binding layer container data structure -Output : none -Notes : -============================================================================*/ -static void build_qtm_config(qtm_control_t* qtm) -{ - /* Initialise the Flags by clearing them */ - qtm->binding_layer_flags = 0x00U; - - /*!< List of function pointers to acquisition sets */ - qtm->library_modules_init = library_modules_init; - - /*!< List of function pointers to post processing modules */ - qtm->library_modules_proc = library_modules_proc; - - /*!< List of Acquisition Engines (Acq Modules one per AcqSet */ - qtm->library_modules_acq = library_modules_acq_engines; - - /*!< Data Model for Acquisition modules */ - qtm->library_module_init_data_model = library_module_init_data_models; - - /*!< Data Model for post processing modules */ - qtm->library_module_proc_data_model = library_module_proc_data_model; - - /*!< Data model for inline module processes */ - qtm->library_modules_acq_dm = library_module_acq_engine_data_model; - - /*!< Post porcessing pointer */ - qtm->qtm_acq_pp = qtm_acquisition_process; - - /* Register Binding layer callbacks */ - qtm->qtm_init_complete_callback = init_complete_callback; - qtm->qtm_error_callback = qtm_error_callback; - qtm->qtm_measure_complete_callback = qtm_measure_complete_callback; - qtm->qtm_pre_process_callback = null; - qtm->qtm_post_process_callback = qtm_post_process_complete; -} +/* Holds preceding touch key deltas (signal - reference) */ +__extension__ int16_t scroller_deltas[DEF_NUM_SENSORS] = {0}; + +scroller_config_t scroller_config[2] = { + [0] = + {DEF_SCROLLER_OFFSET_0, + DEF_SCROLLER_NUM_CHANNELS, + DEF_SCROLLER_RESOLUTION, + DEF_SCROLLER_TOUCH_THRESHOLD, + DEF_SCROLLER_UNTOUCH_THRESHOLD, + DEF_SCROLLER_DEADBAND, + DEF_SCROLLER_HYSTERESIS, + DEF_SCROLLER_TOUCH_DRIFT_IN}, + [1] = + {DEF_SCROLLER_OFFSET_1, + DEF_SCROLLER_NUM_CHANNELS, + DEF_SCROLLER_RESOLUTION, + DEF_SCROLLER_TOUCH_THRESHOLD, + DEF_SCROLLER_UNTOUCH_THRESHOLD, + DEF_SCROLLER_DEADBAND, + DEF_SCROLLER_HYSTERESIS, + DEF_SCROLLER_TOUCH_DRIFT_IN}, +}; +scroller_data_t scroller_data[2] = {0}; + +scroller_control_t scroller_control[2] = { + [0] = {&scroller_config[0], &scroller_data[0]}, + [1] = {&scroller_config[1], &scroller_data[1]}, +}; /*============================================================================ static touch_ret_t touch_sensors_config(void) @@ -249,69 +184,37 @@ Input : none Output : none Notes : ============================================================================*/ -/* Touch sensors config - assign nodes to buttons / wheels / sliders / surfaces / water level / etc - */ +/* Touch sensors config - assign nodes to buttons / wheels / sliders / surfaces + * / water level / etc */ static touch_ret_t touch_sensors_config(void) { uint16_t sensor_nodes; touch_ret_t touch_ret = TOUCH_SUCCESS; + /* Init acquisition module */ + qtm_ptc_init_acquisition_module(&qtlib_acq_set1); + /* Init pointers to DMA sequence memory */ qtm_ptc_qtlib_assign_signal_memory(&touch_acq_signals_raw[0]); /* Initialize sensor nodes */ - for (sensor_nodes = 0U; sensor_nodes < DEF_NUM_CHANNELS; sensor_nodes++) { + for (sensor_nodes = 0u; sensor_nodes < DEF_NUM_CHANNELS; sensor_nodes++) { /* Enable each node for measurement and mark for calibration */ qtm_enable_sensor_node(&qtlib_acq_set1, sensor_nodes); qtm_calibrate_sensor_node(&qtlib_acq_set1, sensor_nodes); } /* Enable sensor keys and assign nodes */ - for (sensor_nodes = 0U; sensor_nodes < DEF_NUM_CHANNELS; sensor_nodes++) { + for (sensor_nodes = 0u; sensor_nodes < DEF_NUM_CHANNELS; sensor_nodes++) { qtm_init_sensor_key(&qtlib_key_set1, sensor_nodes, &ptc_qtlib_node_stat1[sensor_nodes]); } return (touch_ret); } -/* - * Force calibrate - * - * Call this function when the user "probably" isn't touching the device to reset all the - * calibration values. It will only reset inputs that are not considered to be in "touched" states - */ -void qtouch_force_calibrate(void) -{ - qtm_touch_key_data_t* key; - for (uint16_t i = 0U; i < DEF_NUM_CHANNELS; i++) { - key = &qtlib_key_data_set1[i]; - uint16_t value = key->node_data_struct_ptr->node_acq_signals; - uint16_t reference = key->channel_reference; - int32_t diff = (int32_t)reference - (int32_t)value; - if (!(key->sensor_state & KEY_TOUCHED_MASK) && diff > KEY_FORCE_CALIBRATE_THRESHOLD) { - key->channel_reference = key->node_data_struct_ptr->node_acq_signals; - } - } -} - -/*============================================================================ -static void init_complete_callback(void) ------------------------------------------------------------------------------- -Purpose: Callback function from binding layer called after the completion of - acquisition module initialization. -Input : none -Output : none -Notes : -============================================================================*/ -static void init_complete_callback(void) -{ - /* Configure touch sensors with Application specific settings */ - touch_sensors_config(); -} - /*============================================================================ static void qtm_measure_complete_callback( void ) ------------------------------------------------------------------------------ -Purpose: Callback function from binding layer called after the completion of +Purpose: this function is called after the completion of measurement cycle. This function sets the post processing request flag to trigger the post processing. Input : none @@ -320,109 +223,46 @@ Notes : ============================================================================*/ static void qtm_measure_complete_callback(void) { - qtm_control.binding_layer_flags |= (1 << node_pp_request); -} - -/*============================================================================ -static void qtm_post_process_complete(void) ------------------------------------------------------------------------------- -Purpose: Callback function from binding layer called after the completion of - post processing. This function sets the reburst flag based on the - key sensor group status, calls the datastreamer output function to - display the module data. -Input : none -Output : none -Notes : -============================================================================*/ -static void qtm_post_process_complete(void) -{ - { - // This block is a workaround for a rare touch issue. - // - // After boot, reburst is required until the sensors are calibrated (see reburst_request - // below). Afterwards, they are calibrated and the sensor signal and reference values can be - // used to figure out touch gestures. - // - // Normally, calibration finishes quickly (tests showed in about 12 loop iterations). - // Afterwards, reference and signal values are non-zero. In very rare cases, a sensor can - // have a reference value of 0 until it is physically touched, meaning that touch is - // required to finish calibration. This could be due to a hardware or production quirk. In - // this case, reburst would be requested until that sensor is touched, and gesture detection - // would not start until then. In this csae, a user could not interact with the device at - // all until they first touched the faulty sensor. - // - // As a workaround for this, if we have sensors with a zero reference value of 0 after 30 - // iterations, we assume that all other sensors are calibrated and allow gesture detection - // and user interaction. When the user then touches the weird sensor with a zero reference - // value in normal use of the device, it too would be calibrated and start working normally. - static int counter = 0; - if (counter == 30 && !measurement_done_touch) { - for (uint16_t i = 0; i < DEF_NUM_SENSORS; i++) { - if (qtouch_get_sensor_node_reference(i) == 0) { - measurement_done_touch = true; - break; - } - } - } - counter++; - } - - if ((0U != (qtlib_key_set1.qtm_touch_key_group_data->qtm_keys_status & 0x80U))) { - p_qtm_control->binding_layer_flags |= (1U << reburst_request); - } else { - measurement_done_touch = true; - } - - if (measurement_done_touch) { - qtouch_process_scroller_positions(); // Run the custom filter - } + touch_postprocess_request = 1u; } /*============================================================================ static void qtm_error_callback(uint8_t error) ------------------------------------------------------------------------------ -Purpose: Callback function from binding layer called after the completion of - post processing. This function is called only when there is error. +Purpose: this function is used to report error in the modules. Input : error code Output : decoded module error code Notes : -Error Handling supported by Binding layer module: - Acquisition Module Error codes: 0x8 - 0x81 - Qtm init - 0x82 - start acq - 0x83 - cal sensors - 0x84 - cal hardware - - Post processing Modules error codes: 0x4 - 0x40, 0x41, 0x42, ... - process_id is the sequence of process IDs listed in #define LIB_MODULES_PROC_LIST macro. - Process IDs start from zero and maximum is 15 - - Examples: - 0x40 -> error in post processing module 1 - 0x42 -> error in post processing module 3 - Derived Module_error_codes: - Acquisition module error =1 - post processing module1 error = 2 - post processing module2 error = 3 - ... and so on + Acquisition module error =1 + post processing module1 error = 2 + post processing module2 error = 3 + ... and so on ============================================================================*/ -static void qtm_error_callback(uint8_t err) +static void qtm_error_callback(uint8_t error) { - module_error_code = 0; - if (err & 0x80) { - module_error_code = 1; - } else if (err & 0x40) { - module_error_code = (err & 0x0F) + 2; + module_error_code = error + 1u; +} + +static void scroller_init(void) +{ + for (int i = 0; i < DEF_NUM_SCROLLERS; i++) { + scroller_config_t* config = scroller_control[i].config; + scroller_data_t* data = scroller_control[i].data; + data->deltas = &scroller_deltas[config->start_key]; + data->hyst_left = config->hysteresis / 2; + data->hyst_right = config->hysteresis / 2; + data->position = 0; + data->raw_position = 0; + data->touch_area = 0; } } /*============================================================================ -void qtouch_init(void) +void touch_init(void) ------------------------------------------------------------------------------ -Purpose: Initialization of touch library. PTC, timer, binding layer and +Purpose: Initialization of touch library. PTC, timer, and datastreamer modules are initialized in this function. Input : none Output : none @@ -432,16 +272,15 @@ void qtouch_init(void) { qtouch_timer_config(); - build_qtm_config(&qtm_control); - - qtm_binding_layer_init(&qtm_control); + /* Configure touch sensors with Application specific settings */ + touch_sensors_config(); - /* get a pointer to the binding layer control */ - p_qtm_control = qmt_get_binding_layer_ptr(); + /* Configure scrollers */ + scroller_init(); } /*============================================================================ -void qtouch_process(void) +void touch_process(void) ------------------------------------------------------------------------------ Purpose: Main processing function of touch library. This function initiates the acquisition, calls post processing after the acquistion complete and @@ -454,44 +293,49 @@ void qtouch_process(void) { touch_ret_t touch_ret; - /* check the time_to_measure_touch flag for Touch Acquisition */ - if (p_qtm_control->binding_layer_flags & (1U << time_to_measure_touch)) { + /* check the time_to_measure_touch_flag flag for Touch Acquisition */ + if (time_to_measure_touch_flag == 1u) { /* Do the acquisition */ - touch_ret = qtm_lib_start_acquisition(0); + touch_ret = qtm_ptc_start_measurement_seq(&qtlib_acq_set1, qtm_measure_complete_callback); /* if the Acquistion request was successful then clear the request flag */ if (TOUCH_SUCCESS == touch_ret) { /* Clear the Measure request flag */ - p_qtm_control->binding_layer_flags &= (uint8_t) ~(1U << time_to_measure_touch); + time_to_measure_touch_flag = 0u; } } /* check the flag for node level post processing */ - if (p_qtm_control->binding_layer_flags & (1U << node_pp_request)) { - /* Run Acquisition moudle level post pocessing*/ - touch_ret = qtm_lib_acq_process(); + if (touch_postprocess_request == 1u) { + /* Reset the flags for node_level_post_processing */ + touch_postprocess_request = 0u; + + /* Run Acquisition module level post processing*/ + touch_ret = qtm_acquisition_process(); /* Check the return value */ if (TOUCH_SUCCESS == touch_ret) { /* Returned with success: Start module level post processing */ - qtm_lib_post_process(); + touch_ret = qtm_key_sensors_process(&qtlib_key_set1); + if (TOUCH_SUCCESS != touch_ret) { + qtm_error_callback(1); + } + qtouch_process_scroller_positions(scroller_control); } else { /* Acq module Eror Detected: Issue an Acq module common error code 0x80 */ - qtm_error_callback(0x80); + qtm_error_callback(0); } - /* Reset the flags for node_level_post_processing */ - p_qtm_control->binding_layer_flags &= (uint8_t) ~(1U << node_pp_request); - - if (p_qtm_control->binding_layer_flags & (1U << reburst_request)) { - p_qtm_control->binding_layer_flags |= (1U << time_to_measure_touch); - p_qtm_control->binding_layer_flags &= ~(1U << reburst_request); + if ((0u != (qtlib_key_set1.qtm_touch_key_group_data->qtm_keys_status & 0x80u))) { + time_to_measure_touch_flag = 1u; + } else { + measurement_done_touch = 1u; } } } /*============================================================================ -void qtouch_timer_handler(void) +void touch_timer_handler(void) ------------------------------------------------------------------------------ Purpose: This function updates the time elapsed to the touch key module to synchronize the internal time counts used by the module. @@ -502,11 +346,11 @@ Notes : void qtouch_timer_handler(void) { /* Count complete - Measure touch sensors */ - qtm_control.binding_layer_flags |= (1U << time_to_measure_touch); + time_to_measure_touch_flag = 1u; qtm_update_qtlib_timer(DEF_TOUCH_MEASUREMENT_PERIOD_MS); } -static void qtouch_timer_task_cb(const struct timer_task* const timer_task) +static void Timer_task_cb(const struct timer_task* const timer_task) { (void)timer_task; qtouch_timer_handler(); @@ -515,11 +359,18 @@ static void qtouch_timer_task_cb(const struct timer_task* const timer_task) void qtouch_timer_config(void) { static struct timer_task Timer_task; + static uint8_t timer_task_added = 0; + + if (timer_task_added) { + timer_remove_task(&TIMER_0, &Timer_task); + } Timer_task.interval = DEF_TOUCH_MEASUREMENT_PERIOD_MS; - Timer_task.cb = qtouch_timer_task_cb; + Timer_task.cb = Timer_task_cb; Timer_task.mode = TIMER_TASK_REPEAT; timer_add_task(&TIMER_0, &Timer_task); + timer_task_added = 1; + timer_start(&TIMER_0); } uint16_t qtouch_get_sensor_node_signal(uint16_t sensor_node) @@ -527,171 +378,226 @@ uint16_t qtouch_get_sensor_node_signal(uint16_t sensor_node) return (ptc_qtlib_node_stat1[sensor_node].node_acq_signals); } +void qtouch_update_sensor_node_signal(uint16_t sensor_node, uint16_t new_signal) +{ + ptc_qtlib_node_stat1[sensor_node].node_acq_signals = new_signal; +} + uint16_t qtouch_get_sensor_node_reference(uint16_t sensor_node) { return (qtlib_key_data_set1[sensor_node].channel_reference); } +void qtouch_update_sensor_node_reference(uint16_t sensor_node, uint16_t new_reference) +{ + qtlib_key_data_set1[sensor_node].channel_reference = new_reference; +} + uint16_t qtouch_get_sensor_cc_val(uint16_t sensor_node) { return (ptc_qtlib_node_stat1[sensor_node].node_comp_caps); } +void qtouch_update_sensor_cc_val(uint16_t sensor_node, uint16_t new_cc_value) +{ + ptc_qtlib_node_stat1[sensor_node].node_comp_caps = new_cc_value; +} + uint8_t qtouch_get_sensor_state(uint16_t sensor_node) { return (qtlib_key_set1.qtm_touch_key_data[sensor_node].sensor_state); } -/* Holds preceding unfiltered scroller positions */ -static uint16_t sensor_previous_filtered_reading[DEF_NUM_SENSORS][DEF_SENSOR_NUM_PREV_POS] = {0}; - -/* Custom sensor signal filter. */ -uint16_t qtouch_get_sensor_node_signal_filtered(uint16_t sensor_node) +void qtouch_update_sensor_state(uint16_t sensor_node, uint8_t new_state) { - // Filter the sensor signal. - // - // Smooth it out and saturate it so that values never go beyond DEF_SENSOR_CEILING. - // This helps to mitigate 'jumpy' channels that exist at higher sensor readings when - // in noisy environments. - // - uint16_t X; - uint16_t sensor_raw = qtouch_get_sensor_node_signal(sensor_node); - uint16_t sensor_reference = qtouch_get_sensor_node_reference(sensor_node); - - if (sensor_reference == 0) { - // If a sensor reference is 0, it means that the sensor is not yet calibrated (or dead). - // The signal can be high anyway, which makes it look like the sensor is being touched when - // it isn't. - return 0; - } - X = sensor_raw < sensor_reference ? 0 : sensor_raw - sensor_reference; - // Add more weight to edge buttons because they are physically smaller (smaller readings). - if ((sensor_node == DEF_SCROLLER_OFFSET_0) || (sensor_node == DEF_SCROLLER_OFFSET_1) || - (sensor_node == DEF_SCROLLER_OFFSET_0 + DEF_SCROLLER_NUM_CHANNELS - 1) || - (sensor_node == DEF_SCROLLER_OFFSET_1 + DEF_SCROLLER_NUM_CHANNELS - 1)) { - X = (uint16_t)((double)X * (1 + DEF_SENSOR_EDGE_WEIGHT)); - } - // Saturate out-of-range readings. - X = (X > DEF_SENSOR_CEILING) ? DEF_SENSOR_CEILING : X; - - // Calculate sensor readout using a moving average - // The moving average wieghts previous N readings twice current reading - uint16_t moving_average_cummulative_weight = 1; // Add one for current reading calculated above - uint16_t X_ave = X; - for (size_t j = 0; j < DEF_SENSOR_NUM_PREV_POS; j++) { - moving_average_cummulative_weight += 2; - X_ave += sensor_previous_filtered_reading[sensor_node][j] * 2; - } - X_ave = X_ave / moving_average_cummulative_weight; - - // Update recorded previous positions - for (size_t j = 0; j < DEF_SENSOR_NUM_PREV_POS - 1; j++) { - sensor_previous_filtered_reading[sensor_node][j] = - sensor_previous_filtered_reading[sensor_node][j + 1]; - } - sensor_previous_filtered_reading[sensor_node][DEF_SENSOR_NUM_PREV_POS - 1] = X; - - return X_ave; + qtlib_key_set1.qtm_touch_key_data[sensor_node].sensor_state = new_state; } bool qtouch_is_scroller_active(uint16_t scroller) { - return scroller_active[scroller]; + return scroller_control[scroller].data->active; } - -uint16_t qtouch_get_scroller_position(uint16_t sensor_node) +uint16_t qtouch_get_scroller_position(uint16_t scroller) { - return scroller_position[sensor_node]; + return scroller_control[scroller].data->position; } -void qtouch_process_scroller_positions(void) +// EMA is applied to the node readings to filter out noise. Fixed point is used to make it +// less resource intense than floating points. +// +// Exponential Moving Average for nodes +#define NODE_Q_FACTOR 1024 +#define NODE_ALPHA 768 +#define NODE_ONE_MINUS_ALPHA (NODE_Q_FACTOR - NODE_ALPHA) + +// Exponential Moving Average for scrollers +#define POS_Q_FACTOR 1024 +#define POS_ALPHA 512 +#define POS_ONE_MINUS_ALPHA (POS_Q_FACTOR - POS_ALPHA) + +void qtouch_process_scroller_positions(scroller_control_t* scrollers) { - for (uint8_t scroller = 0; scroller < DEF_NUM_SCROLLERS; scroller++) { - uint8_t i, j; - uint16_t sum = 0; - uint16_t max_sensor_reading = 0; - uint16_t min_sensor_reading = DEF_SENSOR_CEILING; - uint16_t weighted_sum = 0; - uint16_t filtered_readings[DEF_SCROLLER_NUM_CHANNELS] = {0}; - uint16_t sensor_location[DEF_SCROLLER_NUM_CHANNELS] = { - 1, // Offset by `1` because a `0` location cannot be weight-averaged - DEF_SCROLLER_RESOLUTION / 3, - DEF_SCROLLER_RESOLUTION / 3 * 2, - DEF_SCROLLER_RESOLUTION}; - - for (i = 0; i < DEF_SCROLLER_NUM_CHANNELS; i++) { - filtered_readings[i] = qtouch_get_sensor_node_signal_filtered( - i + (scroller ? DEF_SCROLLER_OFFSET_1 : DEF_SCROLLER_OFFSET_0)); - min_sensor_reading = (filtered_readings[i] < min_sensor_reading) ? filtered_readings[i] - : min_sensor_reading; - max_sensor_reading = (filtered_readings[i] > max_sensor_reading) ? filtered_readings[i] - : max_sensor_reading; + scroller_control_t* scroller; + scroller_control_t* scrollers_end = scrollers + DEF_NUM_SCROLLERS; + for (scroller = scrollers; scroller < scrollers_end; scroller++) { + scroller_config_t* config = scroller->config; + scroller_data_t* data = scroller->data; + int node_max = -1; + int16_t node_max_value = INT16_MIN; + data->touch_area = 0; + bool key_active = 0; + bool scroller_active = data->active; + + for (int i = 0; i < config->number_of_keys; i++) { + uint8_t node = config->start_key + i; + int32_t delta = max( + 0, qtouch_get_sensor_node_signal(node) - qtouch_get_sensor_node_reference(node)); + + // Ignore small deltas + delta = delta < 4 ? 0 : delta; + + // Some node readings have a max amplitude that is approximately 50% lower than the + // highest reading nodes. For the slider position to be accurately estimated the + // amplitudes need to have similar max amplitudes. + + // Increase some nodes with 50% + if (node == 0 || node == 1 || node == 3 || node == 4 || node == 7) { + delta += delta >> 1; + } + // Increase some nodes with 25% + if (node == 2 || node == 5 || node == 6) { + delta += delta >> 2; + } + + // Apply EMA + delta = (delta * NODE_ALPHA + data->deltas[i] * NODE_ONE_MINUS_ALPHA) / NODE_Q_FACTOR; + + data->deltas[i] = delta; + + // If any key is in touch, the scroller is considered in touch + if (qtouch_get_sensor_state(node) == QTM_KEY_STATE_DETECT) { + key_active = 1; + } + + // Find the node with the highest signal to determine how to calculate touch area. + if (data->deltas[i] > node_max_value) { + node_max = i; + node_max_value = data->deltas[i]; + } } - // Read filterd data and weight by sensor physical location - // Reduce the value by the min_sensor_reading to improve positional accuracy. - // Touch position is calculated with a weighted average of the sensor readings. - // If properly calibrated, sensors on the opposite end of a finger touch would - // be zero and thus make no contribution to the weighted average. If the baseline - // sensor readings are elevated, the sensors on the opposite edge DO contribute - // to the weighted average making a positional artifact (i.e. the position is more - // central than it should be in reality). This artifact is higher when the finger - // is a bit distant while approaching and lower/negligible when the finger is - // fully touching the device. This can cause the position to move enough to enter - // "slide" mode and disable "tap" events being emitted. - for (i = 0; i < DEF_SCROLLER_NUM_CHANNELS; i++) { - sum += filtered_readings[i] - min_sensor_reading; - weighted_sum += (filtered_readings[i] - min_sensor_reading) * sensor_location[i]; + switch (node_max) { + case 0: + data->touch_area = data->deltas[0] + (data->deltas[1] >> 2); + break; + case 1: + data->touch_area = (data->deltas[0] >> 3) + data->deltas[1] + (data->deltas[2] >> 3); + break; + case 2: + data->touch_area = (data->deltas[1] >> 3) + data->deltas[2] + (data->deltas[3] >> 3); + break; + case 3: + data->touch_area = (data->deltas[2] >> 2) + data->deltas[3]; + break; + default: + break; } - // Compensate for deadband (i.e. when only a single edge button gives a reading and - // neighbors do not) - uint16_t scaled_value = weighted_sum / sum; - scaled_value = - (scaled_value < DEF_SCROLLER_DEADBAND) ? DEF_SCROLLER_DEADBAND : (weighted_sum / sum); - scaled_value = (scaled_value > (DEF_SCROLLER_RESOLUTION - DEF_SCROLLER_DEADBAND)) - ? (DEF_SCROLLER_RESOLUTION - DEF_SCROLLER_DEADBAND) - : scaled_value; - scaled_value = ((scaled_value - DEF_SCROLLER_DEADBAND) * (DEF_SCROLLER_RESOLUTION - 1)) / - (DEF_SCROLLER_RESOLUTION - 2 * DEF_SCROLLER_DEADBAND); - - // Calculate scroller position using a moving average - // The moving average wieghts previous N readings twice current reading - if (sum >= DEF_SCROLLER_DET_THRESHOLD) { - uint16_t moving_average_cummulative_weight = 0; - scroller_position[scroller] = 0; - for (j = 0; j < DEF_SCROLLER_NUM_PREV_POS; j++) { - if (scroller_previous_position[scroller][j] != DEF_SCROLLER_OFF) { - moving_average_cummulative_weight += 2; - scroller_position[scroller] += scroller_previous_position[scroller][j] * 2; - } + // In case no single node is in touch, but multiple nodes together are, treat that as in + // touch + if (data->touch_area > config->touch_threshold) { + data->touch_count_in = min(255, data->touch_count_in + 1); + if (data->touch_count_in > config->touch_count_in) { + scroller_active = 1; } - scroller_position[scroller] += - scaled_value; // Most recent signal is half weight of others to help avoid bounce - // when finger is released - scroller_position[scroller] = - scroller_position[scroller] / (moving_average_cummulative_weight + 1); + } + if (data->touch_area < config->untouch_threshold) { + scroller_active = 0; + data->touch_count_in = 0; } - // Update recorded previous positions and scroller active state - for (j = 0; j < DEF_SCROLLER_NUM_PREV_POS - 1; j++) { - scroller_previous_position[scroller][j] = scroller_previous_position[scroller][j + 1]; + bool active = key_active | scroller_active; + + // shift up all calculations to increase precision + uint32_t fp_offset = 2; + uint32_t raw_pos = 0; + uint32_t sum = 0; + // offset weights with "half distance" to avoid "centroid at the edge" problem. i.e. that + // the weight of the zeroth node is 0 and therefore it only weakly influences the result. + uint32_t half_distance = + (1 << (config->resolution + fp_offset)) / (config->number_of_keys - 1) / 2; + for (int i = 0; i < config->number_of_keys; ++i) { + int32_t delta = data->deltas[i]; + sum += delta; + int32_t weight = + (i << (config->resolution + fp_offset)) / (config->number_of_keys - 1) + + half_distance; + raw_pos += delta * weight; } - if (sum >= DEF_SCROLLER_DET_THRESHOLD) { - scroller_previous_position[scroller][DEF_SCROLLER_NUM_PREV_POS - 1] = scaled_value; - } else { - scroller_previous_position[scroller][DEF_SCROLLER_NUM_PREV_POS - 1] = DEF_SCROLLER_OFF; + if (sum > 0) { + raw_pos /= sum; } - // Use the maximum value of all sensor readings as an estimate of pressure. - // Put a threshold on this to detect whether we're touching or not. - if (max_sensor_reading >= DEF_SCROLLER_TOUCH_THRESHOLD) { - scroller_active[scroller] = true; - } else if (max_sensor_reading <= DEF_SCROLLER_UNTOUCH_THRESHOLD) { - scroller_active[scroller] = false; + raw_pos -= min(raw_pos, half_distance); + raw_pos >>= fp_offset; + + // Initial value when scroller becomes active is the raw position + if (active && !data->active) { + data->position = raw_pos; } + + // Subsequent positions are EMA'd + if (active || data->active) { + int32_t pos = + (POS_ALPHA * raw_pos + POS_ONE_MINUS_ALPHA * data->position) / POS_Q_FACTOR; + + if (data->active) { + int32_t delta = pos - data->position; + if (delta < -data->hyst_left || delta > data->hyst_right) { + /* handle hysterisis, when finger changes direction */ + if (pos < data->position) { + data->hyst_left = 0; + data->hyst_right = config->hysteresis; + } + if (pos > data->position) { + data->hyst_left = config->hysteresis; + data->hyst_right = 0; + } + } else { + pos = data->position; + } + } + + /* Handle deadband (close to 0 and close to max resolution) */ + if (raw_pos < config->deadband) { + pos = 0; + data->hyst_left = 0; + data->hyst_right = config->hysteresis / 2; + } + if (raw_pos >= ((1U << config->resolution) - 1) - config->deadband) { + pos = (1 << config->resolution) - 1; + data->hyst_left = config->hysteresis / 2; + data->hyst_right = 0; + } + + data->position = pos; + data->raw_position = raw_pos; + } else { + data->raw_position = raw_pos; + data->hyst_left = config->hysteresis / 2; + data->hyst_right = config->hysteresis / 2; + } + data->active = active; } } +void qtouch_calibrate_node(uint16_t sensor_node) +{ + /* Calibrate Node */ + qtm_calibrate_sensor_node(&qtlib_acq_set1, sensor_node); + /* Initialize key */ + qtm_init_sensor_key(&qtlib_key_set1, sensor_node, &ptc_qtlib_node_stat1[sensor_node]); +} + /*============================================================================ ISR(ADC0_RESRDY_vect) ------------------------------------------------------------------------------ @@ -702,10 +608,8 @@ Notes : none ============================================================================*/ void ADC0_1_Handler(void) { - CRITICAL_SECTION_ENTER() - ADC0->INTFLAG.reg |= 1U; + ADC0->INTFLAG.reg |= 1u; qtm_samd51_ptc_handler(); - CRITICAL_SECTION_LEAVE() } #endif /* TOUCH_C */ diff --git a/src/qtouch/qtouch.h b/src/qtouch/qtouch.h index f119d06b00..ce40e1f1a9 100644 --- a/src/qtouch/qtouch.h +++ b/src/qtouch/qtouch.h @@ -30,6 +30,33 @@ extern "C" { #include "touch_api_ptc.h" +typedef struct { + uint16_t start_key; + uint8_t number_of_keys; + uint8_t resolution; + uint16_t touch_threshold; + uint16_t untouch_threshold; + uint8_t deadband; + uint8_t hysteresis; + uint8_t touch_count_in; +} scroller_config_t; + +typedef struct { + int16_t* deltas; + uint16_t touch_area; + uint16_t raw_position; + uint16_t position; + uint8_t active; + uint8_t hyst_left; + uint8_t hyst_right; + uint8_t touch_count_in; +} scroller_data_t; + +typedef struct { + scroller_config_t* config; + scroller_data_t* data; +} scroller_control_t; + /**********************************************************/ /******************* Acquisition controls *****************/ /**********************************************************/ @@ -44,14 +71,15 @@ extern "C" { */ #define DEF_SENSOR_TYPE NODE_SELFCAP -/* Set sensor calibration mode for charge share delay ,Prescaler or series resistor. - * Range: CAL_AUTO_TUNE_NONE / CAL_AUTO_TUNE_RSEL / CAL_AUTO_TUNE_PRSC / CAL_AUTO_TUNE_CSD - * Default value: CAL_AUTO_TUNE_NONE. +/* Set sensor calibration mode for charge share delay ,Prescaler or series + * resistor. Range: CAL_AUTO_TUNE_NONE / CAL_AUTO_TUNE_RSEL / CAL_AUTO_TUNE_PRSC + * / CAL_AUTO_TUNE_CSD Default value: CAL_AUTO_TUNE_NONE. */ #define DEF_PTC_CAL_OPTION CAL_AUTO_TUNE_NONE -/* Defines the interrupt priority for the PTC. Set low priority to PTC interrupt for applications - * having interrupt time constraints. Range: 0 to 2 Default: 2 (Lowest Priority) +/* Defines the interrupt priority for the PTC. Set low priority to PTC interrupt + * for applications having interrupt time constraints. Range: 0 to 2 Default: 2 + * (Lowest Priority) */ #define DEF_PTC_INTERRUPT_PRIORITY 2 @@ -65,7 +93,7 @@ extern "C" { * Range: FREQ_SEL_0 - FREQ_SEL_15 , FREQ_SEL_SPREAD * Default value: FREQ_SEL_0. */ -#define DEF_SEL_FREQ_INIT FREQ_SEL_8 +#define DEF_SEL_FREQ_INIT FREQ_SEL_0 /*---------------------------------------------------------------------------- * defines @@ -81,68 +109,26 @@ extern "C" { */ #define DEF_NUM_CHANNELS (8) -/* Defines node parameter setting - * {X-line, Y-line, Charge Share Delay, NODE_RSEL_PRSC(series resistor, prescaler), NODE_G(Analog - * Gain , Digital Gain), filter level} - */ -// Slider 1 buttons -#define NODE_0_PARAMS \ - {X_NONE, \ - Y_LINE(26), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_1_PARAMS \ - {X_NONE, \ - Y_LINE(27), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_2_PARAMS \ - {X_NONE, \ - Y_LINE(28), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_3_PARAMS \ - {X_NONE, \ - Y_LINE(29), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -// Slider 0 buttons -#define NODE_4_PARAMS \ - {X_NONE, \ - Y_LINE(30), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_5_PARAMS \ - {X_NONE, \ - Y_LINE(31), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_6_PARAMS \ - {X_NONE, \ - Y_LINE(20), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} -#define NODE_7_PARAMS \ - {X_NONE, \ - Y_LINE(21), \ - 0, \ - NODE_RSEL_PRSC(RSEL_VAL_20, PRSC_DIV_SEL_1), \ - NODE_GAIN(GAIN_4, GAIN_4), \ - FILTER_LEVEL_512} +/* Defines self-cap node parameter setting + * {X-line, Y-line, Charge Share Delay, Prescaler, NODE_G(Analog Gain , Digital + * Gain), filter level} + */ +#define NODE_0_PARAMS \ + {X_NONE, Y_LINE(26), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_2, GAIN_8), FILTER_LEVEL_128} +#define NODE_1_PARAMS \ + {X_NONE, Y_LINE(27), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_128} +#define NODE_2_PARAMS \ + {X_NONE, Y_LINE(28), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_128} +#define NODE_3_PARAMS \ + {X_NONE, Y_LINE(29), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_128} +#define NODE_4_PARAMS \ + {X_NONE, Y_LINE(30), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_64} +#define NODE_5_PARAMS \ + {X_NONE, Y_LINE(31), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_64} +#define NODE_6_PARAMS \ + {X_NONE, Y_LINE(20), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_64} +#define NODE_7_PARAMS \ + {X_NONE, Y_LINE(21), 0, PRSC_DIV_SEL_4, NODE_GAIN(GAIN_1, GAIN_8), FILTER_LEVEL_64} /**********************************************************/ /***************** Key Params ******************/ @@ -151,39 +137,37 @@ extern "C" { * Range: 1 to 65535. * Default value: 1 */ -#define DEF_NUM_SENSORS (DEF_NUM_CHANNELS) +#define DEF_NUM_SENSORS (8) /* Defines Key Sensor setting * {Sensor Threshold, Sensor Hysterisis, Sensor AKS} */ -// 0..3 higher Slider left to right 4..7 lower Slider right to left -#define KEY_0_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_1_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_2_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_3_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_4_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_5_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_6_PARAMS {16, HYST_50, NO_AKS_GROUP} -#define KEY_7_PARAMS {16, HYST_50, NO_AKS_GROUP} +#define KEY_0_PARAMS {22, HYST_12_5, AKS_GROUP_1} +#define KEY_1_PARAMS {15, HYST_12_5, AKS_GROUP_1} +#define KEY_2_PARAMS {15, HYST_12_5, AKS_GROUP_1} +#define KEY_3_PARAMS {11, HYST_12_5, AKS_GROUP_1} +#define KEY_4_PARAMS {11, HYST_12_5, AKS_GROUP_2} +#define KEY_5_PARAMS {15, HYST_12_5, AKS_GROUP_2} +#define KEY_6_PARAMS {17, HYST_12_5, AKS_GROUP_2} +#define KEY_7_PARAMS {10, HYST_12_5, AKS_GROUP_2} /* De-bounce counter for additional measurements to confirm touch detection * Range: 0 to 255. * Default value: 4. */ -#define DEF_TOUCH_DET_INT 0 +#define DEF_TOUCH_DET_INT 1 -/* De-bounce counter for additional measurements to confirm away from touch signal - * to initiate Away from touch re-calibration. - * Range: 0 to 255. - * Default value: 5. +/* De-bounce counter for additional measurements to confirm away from touch + * signal to initiate Away from touch re-calibration. Range: 0 to 255. Default + * value: 5. */ -#define DEF_ANTI_TCH_DET_INT 0 +#define DEF_ANTI_TCH_DET_INT 1 /* Threshold beyond with automatic sensor recalibration is initiated. * Range: RECAL_100/ RECAL_50 / RECAL_25 / RECAL_12_5 / RECAL_6_25 / MAX_RECAL * Default value: RECAL_100. */ -#define DEF_ANTI_TCH_RECAL_THRSHLD RECAL_50 +#define DEF_ANTI_TCH_RECAL_THRSHLD RECAL_100 /* Rate at which sensor reference value is adjusted towards sensor signal value * when signal value is greater than reference. @@ -191,7 +175,7 @@ extern "C" { * Range: 0-255 * Default value: 20u = 4 seconds. */ -#define DEF_TCH_DRIFT_RATE 20 +#define DEF_TCH_DRIFT_RATE 1 /* Rate at which sensor reference value is adjusted towards sensor signal value * when signal value is less than reference. @@ -199,20 +183,20 @@ extern "C" { * Range: 0-255 * Default value: 5u = 1 second. */ -#define DEF_ANTI_TCH_DRIFT_RATE 5 +#define DEF_ANTI_TCH_DRIFT_RATE 1 /* Time to restrict drift on all sensor when one or more sensors are activated. * Units: 200ms * Range: 0-255 * Default value: 20u = 4 seconds. */ -#define DEF_DRIFT_HOLD_TIME 20 +#define DEF_DRIFT_HOLD_TIME 1 /* Set mode for additional sensor measurements based on touch activity. * Range: REBURST_NONE / REBURST_UNRESOLVED / REBURST_ALL * Default value: REBURST_UNRESOLVED */ -#define DEF_REBURST_MODE REBURST_ALL +#define DEF_REBURST_MODE REBURST_NONE /* Sensor maximum ON duration upon touch. * Range: 0-255 @@ -220,13 +204,6 @@ extern "C" { */ #define DEF_MAX_ON_DURATION 0 -/* - * The count that the reference value must be above the measured value to - * allow the force calibrate procedure to overwrite the reference to the - * current measured value. - */ -#define KEY_FORCE_CALIBRATE_THRESHOLD 10 - /**********************************************************/ /***************** Slider/Wheel Parameters ****************/ /**********************************************************/ @@ -237,26 +214,18 @@ extern "C" { * This allows low noise button readings while keeping * fast responsiveness. */ + #define DEF_NUM_SCROLLERS 2 // Number of scrollers (sliders or wheels) #define DEF_SCROLLER_NUM_CHANNELS 4 // Number of channels per scroller #define DEF_SCROLLER_OFFSET_0 4 // Index of first button in scroller #define DEF_SCROLLER_OFFSET_1 0 // Index of first button in scroller -#define DEF_SCROLLER_RESOLUTION 256 // Scroller resolution in bits -#define DEF_SCROLLER_DET_THRESHOLD 25 // Scroller detect threshold -#define DEF_SCROLLER_TOUCH_THRESHOLD 25 // Scroller active threshold -#define DEF_SCROLLER_UNTOUCH_THRESHOLD 20 // Scroller active threshold -#define DEF_SCROLLER_DEADBAND 13 // 13 bits = 5% of 256-bit range -#define DEF_SCROLLER_NUM_PREV_POS \ - 4 // Number of previous scroller positions to remember; used in a simple filter -#define DEF_SCROLLER_OFF \ - 0xFFFF // Marker for indicating scroller reading does not exceed detection threshold -#define DEF_SENSOR_EDGE_WEIGHT \ - 0.15 // Percent added weight to edge sensors, which are physically smaller -#define DEF_SENSOR_NUM_PREV_POS \ - 4 // Number of previous sensor positions to remember; used in a simple filter -#define DEF_SENSOR_CEILING \ - 50 // Maximum sensor reading. Mitigates 'jumpy' channels that exist at higher - // sensor readings when in noisy environments. +#define DEF_SCROLLER_RESOLUTION 8 // Scroller resolution in bits +#define DEF_SCROLLER_TOUCH_THRESHOLD 30 // Scroller active threshold +#define DEF_SCROLLER_UNTOUCH_THRESHOLD 26 // Scroller active threshold +#define DEF_SCROLLER_DEADBAND \ + 6 // everything below deadband is locked to 0 and above max-deadband is locked to max +#define DEF_SCROLLER_HYSTERESIS 12 // Position needs to move at least this much +#define DEF_SCROLLER_TOUCH_DRIFT_IN 2 // number of counts in touch before being active #ifdef __cplusplus } diff --git a/src/touch/gestures.c b/src/touch/gestures.c index d80b5ab804..7c40a0cb00 100644 --- a/src/touch/gestures.c +++ b/src/touch/gestures.c @@ -34,14 +34,17 @@ #define MAX_REGISTRATIONS 7 #define MAX_HISTORY 30 +// Measured in qtouch measurmenent periodicity +#define KEY_REPEAT_PERIOD 20 -/** The minimum amount of sliding difference required, so that the gesture is detected as slide. */ -static const uint8_t SLIDE_DETECTION_DIFF = MAX_SLIDER_POS * 0.04; // Percent of slider range +/** The minimum amount of sliding difference required, so that the gesture is + * detected as slide. */ +static const uint8_t SLIDE_DETECTION_DIFF = 6; /** * The maximum amount of sliding that the user's finger is allowed to move * for its gesture to be still considered a tap. */ -static const uint8_t TAP_SLIDE_TOLERANCE = MAX_SLIDER_POS * 0.1; // Percent of slider range +static const uint8_t TAP_SLIDE_TOLERANCE = 30; extern volatile bool measurement_done_touch; @@ -71,10 +74,9 @@ typedef struct { * since starting the gesture. */ uint16_t max_slide_travel; - int32_t velocity_sum; - uint16_t velocity_index; - // Stores the velocities. - int16_t velocity_values[MAX_HISTORY]; + + // Velocity is generally 10x movement per period + int32_t velocity; // The gesture type (tap, slide). FUTURE: remove this? enum gesture_type_t gesture_type; // The status of the slider. @@ -90,6 +92,10 @@ static gestures_detection_state_t _state[TOUCH_NUM_SLIDERS] = {0}; /********************************** STATE UPDATE **********************************/ +#define Q_FACTOR 1024 +#define ALPHA 686 +#define ONE_MINUS_ALPHA (Q_FACTOR - ALPHA) + /** * Updates the state of a slider. */ @@ -101,17 +107,16 @@ static void _slider_state_update(gestures_detection_state_t* state, uint16_t pos state->max_slide_travel = 0; state->gesture_type = TAP; } - int16_t velocity_current = position - state->position_current; + int32_t velocity = (position - state->position_current) * 10; state->position_current = position; - int16_t velocity_removed = state->velocity_values[state->velocity_index]; - state->velocity_sum = state->velocity_sum - velocity_removed + velocity_current; - state->velocity_values[state->velocity_index] = velocity_current; - state->velocity_index = (state->velocity_index + 1) % MAX_HISTORY; + + // EMA of velocity + state->velocity = (ALPHA * velocity + ONE_MINUS_ALPHA * state->velocity) / Q_FACTOR; uint16_t distance_from_start = abs((int)position - (int)state->position_start); state->max_slide_travel = MAX(distance_from_start, state->max_slide_travel); state->slider_status = ACTIVE; - if (abs(state->position_current - state->position_start) > SLIDE_DETECTION_DIFF) { + if (distance_from_start > SLIDE_DETECTION_DIFF) { state->gesture_type = SLIDE; } state->duration++; @@ -151,7 +156,8 @@ typedef bool (*gesture_detect_fn)(uint8_t location); static bool _is_continuous_tap(uint8_t location) { return _state[location].max_slide_travel < TAP_SLIDE_TOLERANCE && - _state[location].slider_status == ACTIVE; + _state[location].slider_status == ACTIVE && + _state[location].duration % KEY_REPEAT_PERIOD == 0; } static bool _is_tap_release(uint8_t location) @@ -188,7 +194,7 @@ static void _gesture_emit_event(uint8_t id, slider_location_t location) .source = location, .position = _state[location].position_current, .diff = _state[location].position_current - _state[location].position_start, - .velocity = _state[location].velocity_sum, + .velocity = _state[location].velocity, }; event_t event = {.id = id, .data = slider_data}; emit_event(&event); diff --git a/src/touch/gestures.h b/src/touch/gestures.h index 90069e06c1..3bbd148f59 100644 --- a/src/touch/gestures.h +++ b/src/touch/gestures.h @@ -24,7 +24,7 @@ #include "qtouch.h" #define TOUCH_NUM_BUTTONS DEF_NUM_CHANNELS #define TOUCH_NUM_SLIDERS DEF_NUM_SCROLLERS - #define MAX_SLIDER_POS (DEF_SCROLLER_RESOLUTION - 1) + #define MAX_SLIDER_POS ((1 << DEF_SCROLLER_RESOLUTION) - 1) #else #define TOUCH_NUM_BUTTONS (8) #define TOUCH_NUM_SLIDERS (2) diff --git a/src/ui/components/label.c b/src/ui/components/label.c index ae0bb23d85..1039ac855b 100644 --- a/src/ui/components/label.c +++ b/src/ui/components/label.c @@ -34,9 +34,14 @@ typedef struct { bool slider_is_touched; bool slider_was_touched; uint16_t slider_position; - float slider_position_diff; + // float slider_position_diff; int16_t text_position; - int16_t text_position_last; + int16_t text_position_acc; + // int16_t text_render_counter; + // int16_t text_position_last; + // int16_t text_inertia; + int16_t text_velocity; + uint16_t text_velocity_counter; uint8_t xoffset; uint8_t yoffset; } data_t; @@ -96,15 +101,36 @@ static void _render(component_t* component) data_t* data = (data_t*)component->data; // Slider indicators if (data->scrollable) { - int x = data->slider_position * (SCREEN_WIDTH - 1) / MAX_SLIDER_POS; + int x = data->slider_position / 2; int y = SCREEN_HEIGHT - 1; if (!data->slider_was_touched) { - data->text_position = component->dimension.width / 2 + SCREEN_WIDTH * 1 / 6; - data->text_position_last = data->text_position; + data->text_position = component->dimension.width / 2 + SCREEN_WIDTH / 6; + // data->text_position_last = data->text_position; ui_util_component_render_subcomponents(component); } else if (data->slider_is_touched) { UG_DrawLine(MIN(SCREEN_WIDTH, x + 3), y, MAX(0, x - 3), y, screen_front_color); } + // The input sensor has twice the resolution of the screen + // text_position_acc is used to accumulate movement until there is enough to trigger a + // repositioning of the text + if (abs(data->text_position_acc) > 1) { + data->text_position += data->text_position_acc / 2; + data->text_position_acc = 0; + } + static int32_t render_counter = 0; + if (!data->slider_is_touched && render_counter++ % 2 == 0 && data->text_velocity != 0) { + if (data->text_velocity > 0) { + data->text_velocity = MAX(data->text_velocity - 3, 0); + } else { + data->text_velocity = MIN(data->text_velocity + 3, 0); + } + data->text_position_acc += data->text_velocity / 10; + } + // Stop label from moving out of screen + int16_t margin = SCREEN_WIDTH / 5; + data->text_position = + MIN(component->dimension.width / 2 + margin, + MAX(-margin - component->dimension.width / 2 + SCREEN_WIDTH, data->text_position)); } // Label UG_FontSetVSpace(2); @@ -144,21 +170,20 @@ static void _on_event(const event_t* event, component_t* component) if (data->scrollable) { switch (event->id) { case EVENT_SLIDE: { - // Variable scroll speed - int16_t margin = SCREEN_WIDTH / 5; - data->slider_position_diff += SIGMOID(event->data.velocity); - data->text_position = data->text_position_last + (int16_t)data->slider_position_diff; - data->text_position = MIN( - component->dimension.width / 2 + margin, - MAX(-margin - component->dimension.width / 2 + SCREEN_WIDTH, data->text_position)); - data->slider_position = event->data.position; + if (event->data.position != data->slider_position) { + int16_t movement = event->data.position - data->slider_position; + data->slider_position = event->data.position; + data->text_position_acc += movement; + data->text_velocity = event->data.velocity; + // data->text_inertia = movement * 5; + } data->slider_is_touched = true; data->slider_was_touched = true; break; } + case EVENT_SHORT_TAP: + case EVENT_LONG_TAP: case EVENT_SLIDE_RELEASED: - data->text_position_last = data->text_position; - data->slider_position_diff = 0; data->slider_is_touched = false; break; diff --git a/src/ui/components/orientation_arrows.c b/src/ui/components/orientation_arrows.c index 9bd962f4f3..12a9444671 100644 --- a/src/ui/components/orientation_arrows.c +++ b/src/ui/components/orientation_arrows.c @@ -132,7 +132,6 @@ static void _render(component_t* component) } data->enable_touch = true; } - qtouch_force_calibrate(); data->screen_count++; }