mirror of
https://github.com/espressif/esp-idf
synced 2025-03-12 02:29:10 -04:00
Merge branch 'feature/touch_sense_driver_core' into 'master'
feature(touch): add touch sense driver core Closes IDF-2378 See merge request espressif/esp-idf!11824
This commit is contained in:
commit
cf9ff5eccf
8
components/touch_element/CMakeLists.txt
Normal file
8
components/touch_element/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
if(IDF_TARGET STREQUAL "esp32s2")
|
||||
idf_component_register(SRCS "touch_element.c"
|
||||
"touch_button.c"
|
||||
"touch_slider.c"
|
||||
"touch_matrix.c"
|
||||
INCLUDE_DIRS include
|
||||
REQUIRES driver)
|
||||
endif()
|
203
components/touch_element/include/touch_element/touch_button.h
Normal file
203
components/touch_element/include/touch_element/touch_button.h
Normal file
@ -0,0 +1,203 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "touch_element/touch_element.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/* --------------------------------- General button instance default configuration --------------------------------- */
|
||||
#define TOUCH_BUTTON_GLOBAL_DEFAULT_CONFIG() \
|
||||
{ \
|
||||
.threshold_divider = 0.8, \
|
||||
.default_lp_time = 1000 \
|
||||
}
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Button initialization configuration passed to touch_button_install
|
||||
*/
|
||||
typedef struct {
|
||||
float threshold_divider; //!< Button channel threshold divider
|
||||
uint32_t default_lp_time; //!< Button default LongPress event time (ms)
|
||||
} touch_button_global_config_t;
|
||||
|
||||
/**
|
||||
* @brief Button configuration (for new instance) passed to touch_button_create()
|
||||
*/
|
||||
typedef struct {
|
||||
touch_pad_t channel_num; //!< Button channel number (index)
|
||||
float channel_sens; //!< Button channel sensitivity
|
||||
} touch_button_config_t;
|
||||
|
||||
/**
|
||||
* @brief Button event type
|
||||
*/
|
||||
typedef enum {
|
||||
TOUCH_BUTTON_EVT_ON_PRESS, //!< Button Press event
|
||||
TOUCH_BUTTON_EVT_ON_RELEASE, //!< Button Release event
|
||||
TOUCH_BUTTON_EVT_ON_LONGPRESS, //!< Button LongPress event
|
||||
TOUCH_BUTTON_EVT_MAX
|
||||
} touch_button_event_t;
|
||||
|
||||
/**
|
||||
* @brief Button message type
|
||||
*/
|
||||
typedef struct {
|
||||
touch_button_event_t event; //!< Button event
|
||||
} touch_button_message_t;
|
||||
|
||||
typedef touch_elem_handle_t touch_button_handle_t; //!< Button handle
|
||||
typedef void(*touch_button_callback_t)(touch_button_handle_t, touch_button_message_t, void *); //!< Button callback type
|
||||
|
||||
/**
|
||||
* @brief Touch Button initialize
|
||||
*
|
||||
* This function initializes touch button global and acts on all
|
||||
* touch button instances.
|
||||
*
|
||||
* @param[in] global_config Button object initialization configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized touch button
|
||||
* - ESP_ERR_INVALID_STATE: Touch element library was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_init is NULL
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_button_install(const touch_button_global_config_t *global_config);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_button_install()
|
||||
*/
|
||||
void touch_button_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief Create a new touch button instance
|
||||
*
|
||||
* @param[in] button_config Button configuration
|
||||
* @param[out] button_handle Button handle
|
||||
*
|
||||
* @note The sensitivity has to be explored in experiments,
|
||||
* Sensitivity = (Raw(touch) - Raw(release)) / Raw(release) * 100%
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully create touch button
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
* - ESP_ERR_INVALID_ARG: Invalid configuration struct or arguments is NULL
|
||||
*/
|
||||
esp_err_t touch_button_create(const touch_button_config_t *button_config, touch_button_handle_t *button_handle);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_button_create()
|
||||
*
|
||||
* @param[in] button_handle Button handle
|
||||
* @return
|
||||
* - ESP_OK: Successfully released resources
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_handle is null
|
||||
* - ESP_ERR_NOT_FOUND: Input handle is not a button handle
|
||||
*/
|
||||
esp_err_t touch_button_delete(touch_button_handle_t button_handle);
|
||||
|
||||
/**
|
||||
* @brief Touch button subscribes event
|
||||
*
|
||||
* This function uses event mask to subscribe to touch button events, once one of
|
||||
* the subscribed events occurs, the event message could be retrieved by calling
|
||||
* touch_element_message_receive() or input callback routine.
|
||||
*
|
||||
* @param[in] button_handle Button handle
|
||||
* @param[in] event_mask Button subscription event mask
|
||||
* @param[in] arg User input argument
|
||||
*
|
||||
* @note Touch button only support three kind of event masks, they are
|
||||
* TOUCH_ELEM_EVENT_ON_PRESS, TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_LONGPRESS.
|
||||
* You can use those event masks in any combination to achieve the desired effect.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully subscribed touch button event
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_handle is null or event is not supported
|
||||
*/
|
||||
esp_err_t touch_button_subscribe_event(touch_button_handle_t button_handle, uint32_t event_mask, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Touch button set dispatch method
|
||||
*
|
||||
* This function sets a dispatch method that the driver core will use
|
||||
* this method as the event notification method.
|
||||
*
|
||||
* @param[in] button_handle Button handle
|
||||
* @param[in] dispatch_method Dispatch method (By callback/event)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set dispatch method
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_handle is null or dispatch_method is invalid
|
||||
*/
|
||||
esp_err_t touch_button_set_dispatch_method(touch_button_handle_t button_handle, touch_elem_dispatch_t dispatch_method);
|
||||
|
||||
/**
|
||||
* @brief Touch button set callback
|
||||
*
|
||||
* This function sets a callback routine into touch element driver core,
|
||||
* when the subscribed events occur, the callback routine will be called.
|
||||
*
|
||||
* @param[in] button_handle Button handle
|
||||
* @param[in] button_callback User input callback
|
||||
*
|
||||
* @warning Since this input callback routine runs on driver core (esp-timer callback routine),
|
||||
* it should not do something that attempts to Block, such as calling vTaskDelay().
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set callback
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_handle or button_callback is null
|
||||
*/
|
||||
esp_err_t touch_button_set_callback(touch_button_handle_t button_handle, touch_button_callback_t button_callback);
|
||||
|
||||
/**
|
||||
* @brief Touch button set long press trigger time
|
||||
*
|
||||
* This function sets the threshold time (ms) for a long press event. If a button is pressed
|
||||
* and held for a period of time that exceeds the threshold time, a long press event is triggered.
|
||||
*
|
||||
* @param[in] button_handle Button handle
|
||||
* @param[in] threshold_time Threshold time (ms) of long press event occur
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set the threshold time of long press event
|
||||
* - ESP_ERR_INVALID_STATE: Touch button driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: button_handle is null or time (ms) is not lager than 0
|
||||
*/
|
||||
esp_err_t touch_button_set_longpress(touch_button_handle_t button_handle, uint32_t threshold_time);
|
||||
|
||||
/**
|
||||
* @brief Touch button get message
|
||||
*
|
||||
* This function decodes the element message from touch_element_message_receive() and return
|
||||
* a button message pointer.
|
||||
*
|
||||
* @param[in] element_message element message
|
||||
*
|
||||
* @return Touch button message pointer
|
||||
*/
|
||||
const touch_button_message_t* touch_button_get_message(const touch_elem_message_t* element_message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
273
components/touch_element/include/touch_element/touch_element.h
Normal file
273
components/touch_element/include/touch_element/touch_element.h
Normal file
@ -0,0 +1,273 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/touch_sensor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------- General hardware & system default configuration -------------------------------- */
|
||||
/* Since those are important hardware and algorithm parameters, user should not change them before knowing all details*/
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
#define TOUCH_ELEM_GLOBAL_DEFAULT_CONFIG() \
|
||||
{ \
|
||||
.hardware = { \
|
||||
.upper_voltage = TOUCH_HVOLT_2V7, \
|
||||
.voltage_attenuation = TOUCH_HVOLT_ATTEN_0V5, \
|
||||
.lower_voltage = TOUCH_LVOLT_0V5, \
|
||||
.suspend_channel_polarity = TOUCH_PAD_CONN_HIGHZ, \
|
||||
.denoise_level = TOUCH_PAD_DENOISE_BIT4, \
|
||||
.denoise_equivalent_cap = TOUCH_PAD_DENOISE_CAP_L0, \
|
||||
.smooth_filter_mode = TOUCH_PAD_SMOOTH_IIR_2, \
|
||||
.benchmark_filter_mode = TOUCH_PAD_FILTER_IIR_16, \
|
||||
.sample_count = 500, \
|
||||
.sleep_cycle = 0xf, \
|
||||
.benchmark_debounce_count = 2, \
|
||||
.benchmark_calibration_threshold = 2, \
|
||||
.benchmark_jitter_step = 5 \
|
||||
}, \
|
||||
.software = { \
|
||||
.waterproof_threshold_divider = 0.8, \
|
||||
.processing_period = 10, \
|
||||
.intr_message_size = 14, \
|
||||
.event_message_size = 20 \
|
||||
} \
|
||||
}
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
/* ---------------------------------------------- Event subscription ----------------------------------------------- */
|
||||
#define TOUCH_ELEM_EVENT_NONE BIT(0) //!< None event
|
||||
#define TOUCH_ELEM_EVENT_ON_PRESS BIT(1) //!< On Press event
|
||||
#define TOUCH_ELEM_EVENT_ON_RELEASE BIT(2) //!< On Release event
|
||||
#define TOUCH_ELEM_EVENT_ON_LONGPRESS BIT(3) //!< On LongPress event
|
||||
#define TOUCH_ELEM_EVENT_ON_CALCULATION BIT(4) //!< On Calculation event
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
#define TOUCH_WATERPROOF_GUARD_NOUSE (0) //!< Waterproof no use guard sensor
|
||||
/* -------------------------------- Global hardware & software configuration struct --------------------------------- */
|
||||
/**
|
||||
* @brief Touch element software configuration
|
||||
*/
|
||||
typedef struct {
|
||||
float waterproof_threshold_divider; //!< Waterproof guard channel threshold divider
|
||||
uint8_t processing_period; //!< Processing period(ms)
|
||||
uint8_t intr_message_size; //!< Interrupt message queue size
|
||||
uint8_t event_message_size; //!< Event message queue size
|
||||
} touch_elem_sw_config_t;
|
||||
|
||||
/**
|
||||
* @brief Touch element hardware configuration
|
||||
*/
|
||||
typedef struct {
|
||||
touch_high_volt_t upper_voltage; //!< Touch sensor channel upper charge voltage
|
||||
touch_volt_atten_t voltage_attenuation; //!< Touch sensor channel upper charge voltage attenuation (Diff voltage is upper - attenuation - lower)
|
||||
touch_low_volt_t lower_voltage; //!< Touch sensor channel lower charge voltage
|
||||
touch_pad_conn_type_t suspend_channel_polarity; //!< Suspend channel polarity (High Impedance State or GND)
|
||||
touch_pad_denoise_grade_t denoise_level; //!< Internal de-noise level
|
||||
touch_pad_denoise_cap_t denoise_equivalent_cap; //!< Internal de-noise channel (Touch channel 0) equivalent capacitance
|
||||
touch_smooth_mode_t smooth_filter_mode; //!< Smooth value filter mode (This only apply to touch_pad_filter_read_smooth())
|
||||
touch_filter_mode_t benchmark_filter_mode; //!< Benchmark filter mode
|
||||
uint16_t sample_count; //!< The count of sample in each measurement of touch sensor
|
||||
uint16_t sleep_cycle; //!< The cycle (RTC slow clock) of sleep
|
||||
uint8_t benchmark_debounce_count; //!< Benchmark debounce count
|
||||
uint8_t benchmark_calibration_threshold; //!< Benchmark calibration threshold
|
||||
uint8_t benchmark_jitter_step; //!< Benchmark jitter filter step (This only works at while benchmark filter mode is jitter filter)
|
||||
} touch_elem_hw_config_t;
|
||||
|
||||
/**
|
||||
* @brief Touch element global configuration passed to touch_element_install
|
||||
*/
|
||||
typedef struct {
|
||||
touch_elem_hw_config_t hardware; //!< Hardware configuration
|
||||
touch_elem_sw_config_t software; //!< Software configuration
|
||||
} touch_elem_global_config_t;
|
||||
|
||||
/**
|
||||
* @brief Touch element waterproof configuration passed to touch_element_waterproof_install
|
||||
*/
|
||||
typedef struct {
|
||||
touch_pad_t guard_channel; //!< Waterproof Guard-Sensor channel number (index)
|
||||
float guard_sensitivity; //!< Waterproof Guard-Sensor sensitivity
|
||||
} touch_elem_waterproof_config_t;
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
typedef void *touch_elem_handle_t; //!< Touch element handle type
|
||||
typedef uint32_t touch_elem_event_t; //!< Touch element event type
|
||||
|
||||
/**
|
||||
* @brief Touch element handle type
|
||||
*/
|
||||
typedef enum {
|
||||
TOUCH_ELEM_TYPE_BUTTON, //!< Touch element button
|
||||
TOUCH_ELEM_TYPE_SLIDER, //!< Touch element slider
|
||||
TOUCH_ELEM_TYPE_MATRIX, //!< Touch element matrix button
|
||||
} touch_elem_type_t;
|
||||
|
||||
/**
|
||||
* @brief Touch element event dispatch methods (event queue/callback)
|
||||
*/
|
||||
typedef enum {
|
||||
TOUCH_ELEM_DISP_EVENT, //!< Event queue dispatch
|
||||
TOUCH_ELEM_DISP_CALLBACK, //!< Callback dispatch
|
||||
TOUCH_ELEM_DISP_MAX
|
||||
} touch_elem_dispatch_t;
|
||||
|
||||
/**
|
||||
* @brief Touch element event message type from touch_element_message_receive()
|
||||
*/
|
||||
typedef struct {
|
||||
touch_elem_handle_t handle; //!< Touch element handle
|
||||
touch_elem_type_t element_type; //!< Touch element type
|
||||
void *arg; //!< User input argument
|
||||
uint8_t child_msg[8]; //!< Encoded message
|
||||
} touch_elem_message_t;
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Touch element processing initialization
|
||||
*
|
||||
* @param[in] global_config Global initialization configuration structure
|
||||
*
|
||||
* @note To reinitialize the touch element object, call touch_element_uninstall() first
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
* - ESP_ERR_INVALID_STATE: Touch element is already initialized
|
||||
* - Others: Unknown touch driver layer or lower layer error
|
||||
*/
|
||||
esp_err_t touch_element_install(const touch_elem_global_config_t *global_config);
|
||||
|
||||
/**
|
||||
* @brief Touch element processing start
|
||||
*
|
||||
* This function starts the touch element processing system
|
||||
*
|
||||
* @note This function must only be called after all the touch element instances finished creating
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully started to process
|
||||
* - Others: Unknown touch driver layer or lower layer error
|
||||
*/
|
||||
esp_err_t touch_element_start(void);
|
||||
|
||||
/**
|
||||
* @brief Touch element processing stop
|
||||
*
|
||||
* This function stops the touch element processing system
|
||||
*
|
||||
* @note This function must be called before changing the system (hardware, software) parameters
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully stopped to process
|
||||
* - Others: Unknown touch driver layer or lower layer error
|
||||
*/
|
||||
esp_err_t touch_element_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_element_install
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully released touch element object
|
||||
* - ESP_ERR_INVALID_STATE: Touch element object is not initialized
|
||||
* - Others: Unknown touch driver layer or lower layer error
|
||||
*/
|
||||
void touch_element_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief Get current event message of touch element instance
|
||||
*
|
||||
* This function will receive the touch element message (handle, event type, etc...)
|
||||
* from te_event_give(). It will block until a touch element event or a timeout occurs.
|
||||
*
|
||||
* @param[out] element_message Touch element event message structure
|
||||
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block for waiting event
|
||||
* @return
|
||||
* - ESP_OK: Successfully received touch element event
|
||||
* - ESP_ERR_INVALID_STATE: Touch element library is not initialized
|
||||
* - ESP_ERR_INVALID_ARG: element_message is null
|
||||
* - ESP_ERR_TIMEOUT: Timed out waiting for event
|
||||
*/
|
||||
esp_err_t touch_element_message_receive(touch_elem_message_t *element_message, uint32_t ticks_to_wait);
|
||||
|
||||
/**
|
||||
* @brief Touch element waterproof initialization
|
||||
*
|
||||
* This function enables the hardware waterproof, then touch element system uses Shield-Sensor
|
||||
* and Guard-Sensor to mitigate the influence of water-drop and water-stream.
|
||||
*
|
||||
* @param[in] waterproof_config Waterproof configuration
|
||||
*
|
||||
* @note If the waterproof function is used, Shield-Sensor can not be disabled and it will use channel 14 as
|
||||
* it's internal channel. Hence, the user can not use channel 14 for another propose. And the Guard-Sensor
|
||||
* is not necessary since it is optional.
|
||||
*
|
||||
* @note Shield-Sensor: It always uses channel 14 as the shield channel, so user must connect
|
||||
* the channel 14 and Shield-Layer in PCB since it will generate a synchronous signal automatically
|
||||
*
|
||||
* @note Guard-Sensor: This function is optional. If used, the user must connect the guard channel and Guard-Ring
|
||||
* in PCB. Any channels user wants to protect should be added into Guard-Ring in PCB.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized
|
||||
* - ESP_ERR_INVALID_STATE: Touch element library is not initialized
|
||||
* - ESP_ERR_INVALID_ARG: waterproof_config is null or invalid Guard-Sensor channel
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_element_waterproof_install(const touch_elem_waterproof_config_t *waterproof_config);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_element_waterproof_install()
|
||||
*/
|
||||
void touch_element_waterproof_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief Add a masked handle to protect while Guard-Sensor has been triggered
|
||||
*
|
||||
* This function will add an application handle (button, slider, etc...) as a masked handle. While Guard-Sensor
|
||||
* has been triggered, waterproof function will start working and lock the application internal state. While the
|
||||
* influence of water is reduced, the application will be unlock and reset into IDLE state.
|
||||
*
|
||||
* @param[in] element_handle Touch element instance handle
|
||||
*
|
||||
* @note The waterproof protection logic must follow the real circuit in PCB, it means that all of the channels
|
||||
* inside the input handle must be inside the Guard-Ring in real circuit.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully added a masked handle
|
||||
* - ESP_ERR_INVALID_STATE: Waterproof is not initialized
|
||||
* - ESP_ERR_INVALID_ARG: element_handle is null
|
||||
*/
|
||||
esp_err_t touch_element_waterproof_add(touch_elem_handle_t element_handle);
|
||||
|
||||
/**
|
||||
* @brief Remove a masked handle to protect
|
||||
*
|
||||
* This function will remove an application handle from masked handle table.
|
||||
*
|
||||
* @param[in] element_handle Touch element instance handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully removed a masked handle
|
||||
* - ESP_ERR_INVALID_STATE: Waterproof is not initialized
|
||||
* - ESP_ERR_INVALID_ARG: element_handle is null
|
||||
* - ESP_ERR_NOT_FOUND: Failed to search element_handle from waterproof mask_handle list
|
||||
*/
|
||||
esp_err_t touch_element_waterproof_remove(touch_elem_handle_t element_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,185 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "touch_element/touch_element.h"
|
||||
#include "touch_element/touch_button.h"
|
||||
#include "touch_element/touch_slider.h"
|
||||
#include "touch_element/touch_matrix.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define TE_TAG "Touch Element"
|
||||
#define TE_DEBUG_TAG "Touch Element Debug"
|
||||
#define TE_UNUSED(arg) (void)arg
|
||||
|
||||
#define TE_CHECK(cond, ret_val) ({ \
|
||||
if (!(cond)) { \
|
||||
ESP_LOGE(TE_TAG, "%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \
|
||||
return (ret_val); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define TE_CHECK_GOTO(cond, label) ({ \
|
||||
if (!(cond)) { \
|
||||
goto label; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define TE_FREE_AND_NULL(ptr) ({ \
|
||||
free(ptr); \
|
||||
(ptr) = NULL; \
|
||||
})
|
||||
|
||||
#define TE_DEFAULT_THRESHOLD_DIVIDER(obj) ((obj)->global_config->threshold_divider)
|
||||
#define TE_DEFAULT_LONGPRESS_TIME(obj) ((obj)->global_config->default_lp_time)
|
||||
|
||||
typedef enum {
|
||||
TE_STATE_IDLE = 0,
|
||||
TE_STATE_PRESS,
|
||||
TE_STATE_RELEASE,
|
||||
} te_state_t;
|
||||
|
||||
typedef te_state_t te_dev_state_t;
|
||||
typedef touch_elem_type_t te_dev_type_t;
|
||||
|
||||
typedef struct {
|
||||
float sens; //!< Touch channel sensitivity
|
||||
touch_pad_t channel; //!< Touch channel number(index)
|
||||
te_dev_type_t type; //!< Touch channel type TODO: need to refactor as te_class_type_t
|
||||
te_dev_state_t state; //!< Touch channel current state
|
||||
} te_dev_t;
|
||||
|
||||
typedef enum {
|
||||
TE_CLS_TYPE_BUTTON = 0,
|
||||
TE_CLS_TYPE_SLIDER,
|
||||
TE_CLS_TYPE_MATRIX,
|
||||
TE_CLS_TYPE_MAX //TODO: add waterproof class
|
||||
} te_class_type_t;
|
||||
|
||||
typedef struct {
|
||||
touch_elem_handle_t handle;
|
||||
bool (*check_channel) (touch_pad_t);
|
||||
esp_err_t (*set_threshold) (void);
|
||||
void (*process_state) (void);
|
||||
void (*update_state) (touch_pad_t, te_state_t);
|
||||
} te_object_methods_t;
|
||||
|
||||
/* -------------------------------------------- Waterproof basic type --------------------------------------------- */
|
||||
struct te_waterproof_s {
|
||||
te_dev_t *guard_device; //Waterproof guard channel device
|
||||
touch_elem_handle_t *mask_handle; //Waterproof masked handle array
|
||||
touch_pad_t shield_channel; //Waterproof shield channel
|
||||
bool is_shield_level_set; //Waterproof shield level setting bit
|
||||
};
|
||||
typedef struct te_waterproof_s* te_waterproof_handle_t;
|
||||
/* -------------------------------------------- Button basic type --------------------------------------------- */
|
||||
typedef struct {
|
||||
touch_elem_dispatch_t dispatch_method; //Button dispatch method
|
||||
touch_button_callback_t callback; //Button callback routine
|
||||
uint32_t event_mask; //Button subscribed event mask
|
||||
void *arg; //User input argument
|
||||
} te_button_handle_config_t;
|
||||
|
||||
typedef te_state_t te_button_state_t; //TODO: add Long Press state
|
||||
|
||||
struct te_button_s {
|
||||
te_button_handle_config_t *config; //Button configuration
|
||||
te_dev_t *device; //Base device information
|
||||
te_button_state_t current_state; //Button current state
|
||||
te_button_state_t last_state; //Button last state
|
||||
touch_button_event_t event; //Button outside state(for application layer)
|
||||
uint32_t trigger_cnt; //Button long time trigger counter
|
||||
uint32_t trigger_thr; //Button long time trigger counter threshold
|
||||
};
|
||||
|
||||
typedef struct te_button_s* te_button_handle_t;
|
||||
/* -------------------------------------------- Slider basic type --------------------------------------------- */
|
||||
typedef struct {
|
||||
touch_elem_dispatch_t dispatch_method; //Slider dispatch method
|
||||
touch_slider_callback_t callback; //Slider callback routine
|
||||
uint32_t event_mask; //Slider subscribed event mask
|
||||
void *arg; //User input argument
|
||||
} te_slider_handle_config_t;
|
||||
|
||||
typedef te_state_t te_slider_state_t;
|
||||
|
||||
struct te_slider_s {
|
||||
te_slider_handle_config_t *config; //Slider configuration
|
||||
te_dev_t **device; //Base device information set
|
||||
te_slider_state_t current_state; //Slider current state
|
||||
te_slider_state_t last_state; //Slider last state
|
||||
touch_slider_event_t event; //Slider outside state(for application layer)
|
||||
float position_scale; //Slider position scale(step size)
|
||||
float *quantify_signal_array; //Slider re-quantization array
|
||||
uint32_t *channel_bcm; //Channel benchmark array
|
||||
uint32_t channel_bcm_update_cnt; //Channel benchmark update counter
|
||||
uint32_t filter_reset_cnt; //Slider reset counter
|
||||
uint32_t last_position; //Slider last position
|
||||
touch_slider_position_t position; //Slider position
|
||||
uint8_t position_range; //Slider position range([0, position_range])
|
||||
uint8_t channel_sum; //Slider channel sum
|
||||
uint8_t *pos_filter_window; //Slider position moving average filter window
|
||||
uint8_t pos_window_idx; //Slider position moving average filter window index
|
||||
bool is_first_sample; //Slider first time sample record bit
|
||||
};
|
||||
|
||||
typedef struct te_slider_s* te_slider_handle_t;
|
||||
/* -------------------------------------------- Matrix basic type --------------------------------------------- */
|
||||
typedef struct {
|
||||
touch_elem_dispatch_t dispatch_method; //Matrix button dispatch method
|
||||
touch_matrix_callback_t callback; //Matrix button callback routine
|
||||
uint32_t event_mask; //Matrix button subscribed event mask
|
||||
void *arg; //User input argument
|
||||
} te_matrix_handle_config_t;
|
||||
|
||||
typedef te_state_t te_matrix_state_t; //TODO: add Long Press state
|
||||
|
||||
struct te_matrix_s {
|
||||
te_matrix_handle_config_t *config; //Matrix button configuration
|
||||
te_dev_t **device; //Base device information
|
||||
te_matrix_state_t current_state; //Matrix button current state
|
||||
te_matrix_state_t last_state; //Matrix button current state
|
||||
touch_matrix_event_t event; //Matrix button outside state(for application layer)
|
||||
uint32_t trigger_cnt; //Matrix button long time trigger counter
|
||||
uint32_t trigger_thr; //Matrix button long time trigger counter threshold
|
||||
touch_matrix_position_t position; //Matrix button position
|
||||
uint8_t x_channel_num; //The number of touch sensor channel in x axis
|
||||
uint8_t y_channel_num; //The number of touch sensor channel in y axis
|
||||
};
|
||||
|
||||
typedef struct te_matrix_s* te_matrix_handle_t;
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
/* --------------------------------------------- Global system methods ---------------------------------------------- */
|
||||
uint32_t te_read_smooth_signal(touch_pad_t channel_num);
|
||||
bool te_system_check_state(void);
|
||||
//TODO: Refactor this function with function overload
|
||||
esp_err_t te_dev_init(te_dev_t **device, uint8_t device_num, te_dev_type_t type, const touch_pad_t *channel, const float *sens, float divider);
|
||||
void te_dev_deinit(te_dev_t **device, uint8_t device_num);
|
||||
esp_err_t te_dev_set_threshold(te_dev_t *device);
|
||||
esp_err_t te_event_give(touch_elem_message_t te_message);
|
||||
uint8_t te_get_timer_period(void);
|
||||
void te_object_method_register(te_object_methods_t *object_methods, te_class_type_t object_type);
|
||||
void te_object_method_unregister(te_class_type_t object_type);
|
||||
bool te_object_check_channel(const touch_pad_t *channel_array, uint8_t channel_sum);
|
||||
bool waterproof_check_mask_handle(touch_elem_handle_t te_handle);
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
221
components/touch_element/include/touch_element/touch_matrix.h
Normal file
221
components/touch_element/include/touch_element/touch_matrix.h
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "touch_element/touch_element.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ----------------------------- General matrix button instance default configuration ------------------------------ */
|
||||
#define TOUCH_MATRIX_GLOBAL_DEFAULT_CONFIG() \
|
||||
{ \
|
||||
.threshold_divider = 0.8, \
|
||||
.default_lp_time = 1000 \
|
||||
}
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
/**
|
||||
* @brief Matrix button initialization configuration passed to touch_matrix_install
|
||||
*/
|
||||
typedef struct {
|
||||
float threshold_divider; //!< Matrix button channel threshold divider
|
||||
uint32_t default_lp_time; //!< Matrix button default LongPress event time (ms)
|
||||
} touch_matrix_global_config_t;
|
||||
|
||||
/**
|
||||
* @brief Matrix button configuration (for new instance) passed to touch_matrix_create()
|
||||
*/
|
||||
typedef struct {
|
||||
const touch_pad_t *x_channel_array; //!< Matrix button x-axis channels array
|
||||
const touch_pad_t *y_channel_array; //!< Matrix button y-axis channels array
|
||||
const float *x_sensitivity_array; //!< Matrix button x-axis channels sensitivity array
|
||||
const float *y_sensitivity_array; //!< Matrix button y-axis channels sensitivity array
|
||||
uint8_t x_channel_num; //!< The number of channels in x-axis
|
||||
uint8_t y_channel_num; //!< The number of channels in y-axis
|
||||
} touch_matrix_config_t;
|
||||
|
||||
/**
|
||||
* @brief Matrix button event type
|
||||
*/
|
||||
typedef enum {
|
||||
TOUCH_MATRIX_EVT_ON_PRESS, //!< Matrix button Press event
|
||||
TOUCH_MATRIX_EVT_ON_RELEASE, //!< Matrix button Press event
|
||||
TOUCH_MATRIX_EVT_ON_LONGPRESS, //!< Matrix button LongPress event
|
||||
TOUCH_MATRIX_EVT_MAX
|
||||
} touch_matrix_event_t;
|
||||
|
||||
/**
|
||||
* @brief Matrix button position data type
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t x_axis; //!< Matrix button x axis position
|
||||
uint8_t y_axis; //!< Matrix button y axis position
|
||||
uint8_t index; //!< Matrix button position index
|
||||
} touch_matrix_position_t;
|
||||
|
||||
/**
|
||||
* @brief Matrix message type
|
||||
*/
|
||||
typedef struct {
|
||||
touch_matrix_event_t event; //!< Matrix event
|
||||
touch_matrix_position_t position; //!< Matrix position
|
||||
} touch_matrix_message_t;
|
||||
|
||||
typedef touch_elem_handle_t touch_matrix_handle_t; //!< Matrix button instance handle
|
||||
typedef void(*touch_matrix_callback_t)(touch_matrix_handle_t, touch_matrix_message_t, void *); //!< Matrix button callback type
|
||||
|
||||
/**
|
||||
* @brief Touch matrix button initialize
|
||||
*
|
||||
* This function initializes touch matrix button object and acts on all
|
||||
* touch matrix button instances.
|
||||
*
|
||||
* @param[in] global_config Touch matrix global initialization configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized touch matrix button
|
||||
* - ESP_ERR_INVALID_STATE: Touch element library was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_init is NULL
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_matrix_install(const touch_matrix_global_config_t *global_config);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_matrix_install()
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully released resources
|
||||
*/
|
||||
void touch_matrix_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief Create a new touch matrix button instance
|
||||
*
|
||||
* @param[in] matrix_config Matrix button configuration
|
||||
* @param[out] matrix_handle Matrix button handle
|
||||
*
|
||||
* @note Channel array and sensitivity array must be one-one correspondence in those array
|
||||
*
|
||||
* @note Touch matrix button does not support Multi-Touch now
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully create touch matrix button
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: Invalid configuration struct or arguments is NULL
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_matrix_create(const touch_matrix_config_t *matrix_config, touch_matrix_handle_t *matrix_handle);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_matrix_create()
|
||||
*
|
||||
* @param[in] matrix_handle Matrix handle
|
||||
* @return
|
||||
* - ESP_OK: Successfully released resources
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_handle is null
|
||||
* - ESP_ERR_NOT_FOUND: Input handle is not a matrix handle
|
||||
*/
|
||||
esp_err_t touch_matrix_delete(touch_matrix_handle_t matrix_handle);
|
||||
|
||||
/**
|
||||
* @brief Touch matrix button subscribes event
|
||||
*
|
||||
* This function uses event mask to subscribe to touch matrix events, once one of
|
||||
* the subscribed events occurs, the event message could be retrieved by calling
|
||||
* touch_element_message_receive() or input callback routine.
|
||||
*
|
||||
* @param[in] matrix_handle Matrix handle
|
||||
* @param[in] event_mask Matrix subscription event mask
|
||||
* @param[in] arg User input argument
|
||||
*
|
||||
* @note Touch matrix button only support three kind of event masks, they are
|
||||
* TOUCH_ELEM_EVENT_ON_PRESS, TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_LONGPRESS. You can use those event
|
||||
* masks in any combination to achieve the desired effect.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully subscribed touch matrix event
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_handle is null or event is not supported
|
||||
*/
|
||||
esp_err_t touch_matrix_subscribe_event(touch_matrix_handle_t matrix_handle, uint32_t event_mask, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Touch matrix button set dispatch method
|
||||
*
|
||||
* This function sets a dispatch method that the driver core will use
|
||||
* this method as the event notification method.
|
||||
*
|
||||
* @param[in] matrix_handle Matrix button handle
|
||||
* @param[in] dispatch_method Dispatch method (By callback/event)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set dispatch method
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_handle is null or dispatch_method is invalid
|
||||
*/
|
||||
esp_err_t touch_matrix_set_dispatch_method(touch_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method);
|
||||
|
||||
/**
|
||||
* @brief Touch matrix button set callback
|
||||
*
|
||||
* This function sets a callback routine into touch element driver core,
|
||||
* when the subscribed events occur, the callback routine will be called.
|
||||
*
|
||||
* @param[in] matrix_handle Matrix button handle
|
||||
* @param[in] matrix_callback User input callback
|
||||
*
|
||||
* @warning Since this input callback routine runs on driver core (esp-timer callback routine),
|
||||
* it should not do something that attempts to Block, such as calling vTaskDelay().
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set callback
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_handle or matrix_callback is null
|
||||
*/
|
||||
esp_err_t touch_matrix_set_callback(touch_matrix_handle_t matrix_handle, touch_matrix_callback_t matrix_callback);
|
||||
|
||||
/**
|
||||
* @brief Touch matrix button set long press trigger time
|
||||
*
|
||||
* This function sets the threshold time (ms) for a long press event. If a matrix button is pressed
|
||||
* and held for a period of time that exceeds the threshold time, a long press event is triggered.
|
||||
*
|
||||
* @param[in] matrix_handle Matrix button handle
|
||||
* @param[in] threshold_time Threshold time (ms) of long press event occur
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set the time of long press event
|
||||
* - ESP_ERR_INVALID_STATE: Touch matrix driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: matrix_handle is null or time (ms) is 0
|
||||
*/
|
||||
esp_err_t touch_matrix_set_longpress(touch_matrix_handle_t matrix_handle, uint32_t threshold_time);
|
||||
|
||||
/**
|
||||
* @brief Touch matrix get message
|
||||
*
|
||||
* This function decodes the element message from touch_element_message_receive() and return
|
||||
* a matrix message pointer.
|
||||
*
|
||||
* @param[in] element_message element message
|
||||
*
|
||||
* @return Touch matrix message pointer
|
||||
*/
|
||||
const touch_matrix_message_t* touch_matrix_get_message(const touch_elem_message_t* element_message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
205
components/touch_element/include/touch_element/touch_slider.h
Normal file
205
components/touch_element/include/touch_element/touch_slider.h
Normal file
@ -0,0 +1,205 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "touch_element/touch_element.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* --------------------------------- General slider instance default configuration --------------------------------- */
|
||||
#define TOUCH_SLIDER_GLOBAL_DEFAULT_CONFIG() \
|
||||
{ \
|
||||
.quantify_lower_threshold = 0.3, \
|
||||
.threshold_divider = 0.8, \
|
||||
.filter_reset_time = 50, \
|
||||
.benchmark_update_time = 500, \
|
||||
.position_filter_size = 10, \
|
||||
.position_filter_factor = 2, \
|
||||
.calculate_channel_count = 3 \
|
||||
}
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Slider initialization configuration passed to touch_slider_install
|
||||
*/
|
||||
typedef struct {
|
||||
float quantify_lower_threshold; //!< Slider signal quantification threshold
|
||||
float threshold_divider; //!< Slider channel threshold divider
|
||||
uint16_t filter_reset_time; //!< Slider position filter reset time (Unit is esp_timer callback tick)
|
||||
uint16_t benchmark_update_time; //!< Slider benchmark update time (Unit is esp_timer callback tick)
|
||||
uint8_t position_filter_size; //!< Moving window filter buffer size
|
||||
uint8_t position_filter_factor; //!< One-order IIR filter factor
|
||||
uint8_t calculate_channel_count; //!< The number of channels which will take part in calculation
|
||||
} touch_slider_global_config_t;
|
||||
|
||||
/**
|
||||
* @brief Slider configuration (for new instance) passed to touch_slider_create()
|
||||
*/
|
||||
typedef struct {
|
||||
const touch_pad_t *channel_array; //!< Slider channel array
|
||||
const float *sensitivity_array; //!< Slider channel sensitivity array
|
||||
uint8_t channel_num; //!< The number of slider channels
|
||||
uint8_t position_range; //!< The right region of touch slider position range, [0, position_range (less than or equal to 255)]
|
||||
} touch_slider_config_t;
|
||||
|
||||
/**
|
||||
* @brief Slider event type
|
||||
*/
|
||||
typedef enum {
|
||||
TOUCH_SLIDER_EVT_ON_PRESS, //!< Slider on Press event
|
||||
TOUCH_SLIDER_EVT_ON_RELEASE, //!< Slider on Release event
|
||||
TOUCH_SLIDER_EVT_ON_CALCULATION, //!< Slider on Calculation event
|
||||
TOUCH_SLIDER_EVT_MAX
|
||||
} touch_slider_event_t;
|
||||
|
||||
typedef uint32_t touch_slider_position_t; //!< Slider position data type
|
||||
|
||||
/**
|
||||
* @brief Slider message type
|
||||
*/
|
||||
typedef struct {
|
||||
touch_slider_event_t event; //!< Slider event
|
||||
touch_slider_position_t position; //!< Slider position
|
||||
} touch_slider_message_t;
|
||||
|
||||
typedef touch_elem_handle_t touch_slider_handle_t; //!< Slider instance handle
|
||||
typedef void(*touch_slider_callback_t)(touch_slider_handle_t, touch_slider_message_t, void *); //!< Slider callback type
|
||||
|
||||
/**
|
||||
* @brief Touch slider initialize
|
||||
*
|
||||
* This function initializes touch slider object and acts on all
|
||||
* touch slider instances.
|
||||
*
|
||||
* @param[in] global_config Touch slider global initialization configuration
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initialized touch slider
|
||||
* - ESP_ERR_INVALID_STATE: Touch element library was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: slider_init is NULL
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_slider_install(const touch_slider_global_config_t *global_config);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_slider_install()
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully released resources
|
||||
*/
|
||||
void touch_slider_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief Create a new touch slider instance
|
||||
*
|
||||
* @param[in] slider_config Slider configuration
|
||||
* @param[out] slider_handle Slider handle
|
||||
*
|
||||
* @note The index of Channel array and sensitivity array must be one-one correspondence
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully create touch slider
|
||||
* - ESP_ERR_INVALID_STATE: Touch slider driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: Invalid configuration struct or arguments is NULL
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
*/
|
||||
esp_err_t touch_slider_create(const touch_slider_config_t *slider_config, touch_slider_handle_t *slider_handle);
|
||||
|
||||
/**
|
||||
* @brief Release resources allocated using touch_slider_create
|
||||
*
|
||||
* @param[in] slider_handle Slider handle
|
||||
* @return
|
||||
* - ESP_OK: Successfully released resources
|
||||
* - ESP_ERR_INVALID_STATE: Touch slider driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: slider_handle is null
|
||||
* - ESP_ERR_NOT_FOUND: Input handle is not a slider handle
|
||||
*/
|
||||
esp_err_t touch_slider_delete(touch_slider_handle_t slider_handle);
|
||||
|
||||
/**
|
||||
* @brief Touch slider subscribes event
|
||||
*
|
||||
* This function uses event mask to subscribe to touch slider events, once one of
|
||||
* the subscribed events occurs, the event message could be retrieved by calling
|
||||
* touch_element_message_receive() or input callback routine.
|
||||
*
|
||||
* @param[in] slider_handle Slider handle
|
||||
* @param[in] event_mask Slider subscription event mask
|
||||
* @param[in] arg User input argument
|
||||
*
|
||||
* @note Touch slider only support three kind of event masks, they are
|
||||
* TOUCH_ELEM_EVENT_ON_PRESS, TOUCH_ELEM_EVENT_ON_RELEASE. You can use those event masks in any
|
||||
* combination to achieve the desired effect.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully subscribed touch slider event
|
||||
* - ESP_ERR_INVALID_STATE: Touch slider driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: slider_handle is null or event is not supported
|
||||
*/
|
||||
esp_err_t touch_slider_subscribe_event(touch_slider_handle_t slider_handle, uint32_t event_mask, void *arg);
|
||||
|
||||
/**
|
||||
* @brief Touch slider set dispatch method
|
||||
*
|
||||
* This function sets a dispatch method that the driver core will use
|
||||
* this method as the event notification method.
|
||||
*
|
||||
* @param[in] slider_handle Slider handle
|
||||
* @param[in] dispatch_method Dispatch method (By callback/event)
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set dispatch method
|
||||
* - ESP_ERR_INVALID_STATE: Touch slider driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: slider_handle is null or dispatch_method is invalid
|
||||
*/
|
||||
esp_err_t touch_slider_set_dispatch_method(touch_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method);
|
||||
|
||||
/**
|
||||
* @brief Touch slider set callback
|
||||
*
|
||||
* This function sets a callback routine into touch element driver core,
|
||||
* when the subscribed events occur, the callback routine will be called.
|
||||
*
|
||||
* @param[in] slider_handle Slider handle
|
||||
* @param[in] slider_callback User input callback
|
||||
*
|
||||
* @warning Since this input callback routine runs on driver core (esp-timer callback routine),
|
||||
* it should not do something that attempts to Block, such as calling vTaskDelay().
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully set callback
|
||||
* - ESP_ERR_INVALID_STATE: Touch slider driver was not initialized
|
||||
* - ESP_ERR_INVALID_ARG: slider_handle or slider_callback is null
|
||||
*/
|
||||
esp_err_t touch_slider_set_callback(touch_slider_handle_t slider_handle, touch_slider_callback_t slider_callback);
|
||||
|
||||
/**
|
||||
* @brief Touch slider get message
|
||||
*
|
||||
* This function decodes the element message from touch_element_message_receive() and return
|
||||
* a slider message pointer.
|
||||
*
|
||||
* @param[in] element_message element message
|
||||
*
|
||||
* @return Touch slider message pointer
|
||||
*/
|
||||
const touch_slider_message_t* touch_slider_get_message(const touch_elem_message_t* element_message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
426
components/touch_element/touch_button.c
Normal file
426
components/touch_element/touch_button.c
Normal file
@ -0,0 +1,426 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "touch_element/touch_element_private.h"
|
||||
|
||||
typedef struct te_button_handle_list {
|
||||
te_button_handle_t button_handle; //Button handle
|
||||
SLIST_ENTRY(te_button_handle_list) next; //Button handle list entry
|
||||
} te_button_handle_list_t;
|
||||
|
||||
typedef struct {
|
||||
SLIST_HEAD(te_button_handle_list_head, te_button_handle_list) handle_list; //Button handle (instance) list
|
||||
touch_button_global_config_t *global_config; //Button global configuration
|
||||
SemaphoreHandle_t mutex; //Button object mutex
|
||||
} te_button_obj_t;
|
||||
|
||||
static te_button_obj_t *s_te_btn_obj = NULL; //Button object
|
||||
/* ---------------------------------------- Button handle(instance) methods ----------------------------------------- */
|
||||
static bool button_channel_check(te_button_handle_t button_handle, touch_pad_t channel_num);
|
||||
static esp_err_t button_set_threshold(te_button_handle_t button_handle);
|
||||
static inline te_state_t button_get_state(te_dev_t *device);
|
||||
static void button_reset_state(te_button_handle_t button_handle);
|
||||
static void button_update_state(te_button_handle_t button_handle, touch_pad_t channel_num, te_state_t channel_state);
|
||||
static void button_proc_state(te_button_handle_t button_handle);
|
||||
static void button_event_give(te_button_handle_t button_handle);
|
||||
static inline void button_dispatch(te_button_handle_t button_handle, touch_elem_dispatch_t dispatch_method);
|
||||
/* ------------------------------------------ Button object(class) methods ------------------------------------------ */
|
||||
static esp_err_t button_object_add_instance(te_button_handle_t button_handle);
|
||||
static esp_err_t button_object_remove_instance(te_button_handle_t button_handle);
|
||||
static bool button_object_check_channel(touch_pad_t channel_num);
|
||||
static esp_err_t button_object_set_threshold(void);
|
||||
static void button_object_process_state(void);
|
||||
static void button_object_update_state(touch_pad_t channel_num, te_state_t channel_state);
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
esp_err_t touch_button_install(const touch_button_global_config_t *global_config)
|
||||
{
|
||||
TE_CHECK(te_system_check_state() == true, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
//Fixme: Make it thread-safe
|
||||
s_te_btn_obj = (te_button_obj_t *)calloc(1, sizeof(te_button_obj_t));
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_NO_MEM);
|
||||
s_te_btn_obj->global_config = (touch_button_global_config_t *)calloc(1, sizeof(touch_button_global_config_t));
|
||||
s_te_btn_obj->mutex = xSemaphoreCreateMutex();
|
||||
TE_CHECK_GOTO(s_te_btn_obj->global_config != NULL && s_te_btn_obj->global_config != NULL, cleanup);
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INIT(&s_te_btn_obj->handle_list);
|
||||
memcpy(s_te_btn_obj->global_config, global_config, sizeof(touch_button_global_config_t));
|
||||
te_object_methods_t button_methods = {
|
||||
.handle = s_te_btn_obj,
|
||||
.check_channel = button_object_check_channel,
|
||||
.set_threshold = button_object_set_threshold,
|
||||
.process_state = button_object_process_state,
|
||||
.update_state = button_object_update_state
|
||||
};
|
||||
te_object_method_register(&button_methods, TE_CLS_TYPE_BUTTON);
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(s_te_btn_obj->global_config);
|
||||
if (s_te_btn_obj->mutex != NULL) {
|
||||
vSemaphoreDelete(s_te_btn_obj->mutex);
|
||||
}
|
||||
TE_FREE_AND_NULL(s_te_btn_obj);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
void touch_button_uninstall(void)
|
||||
{
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
if (s_te_btn_obj == NULL) {
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return;
|
||||
}
|
||||
te_object_method_unregister(TE_CLS_TYPE_BUTTON);
|
||||
free(s_te_btn_obj->global_config);
|
||||
s_te_btn_obj->global_config = NULL;
|
||||
while (!SLIST_EMPTY(&s_te_btn_obj->handle_list)) {
|
||||
SLIST_FIRST(&s_te_btn_obj->handle_list);
|
||||
SLIST_REMOVE_HEAD(&s_te_btn_obj->handle_list, next);
|
||||
}
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
vSemaphoreDelete(s_te_btn_obj->mutex);
|
||||
free(s_te_btn_obj);
|
||||
s_te_btn_obj = NULL;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_create(const touch_button_config_t *button_config, touch_button_handle_t *button_handle)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL && button_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(button_config->channel_num > TOUCH_PAD_NUM0 &&
|
||||
button_config->channel_num < TOUCH_PAD_MAX &&
|
||||
button_config->channel_sens > 0,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(te_object_check_channel(&button_config->channel_num, 1) == false, ESP_ERR_INVALID_ARG);
|
||||
te_button_handle_t te_button = (te_button_handle_t)calloc(1, sizeof(struct te_button_s));
|
||||
TE_CHECK(te_button != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NO_MEM;
|
||||
te_button->config = (te_button_handle_config_t *)calloc(1, sizeof(te_button_handle_config_t));
|
||||
te_button->device = (te_dev_t *)calloc(1, sizeof(te_dev_t));
|
||||
TE_CHECK_GOTO(te_button->config != NULL && te_button->device != NULL, cleanup);
|
||||
|
||||
ret = te_dev_init(&te_button->device, 1, TOUCH_ELEM_TYPE_BUTTON,
|
||||
&button_config->channel_num, &button_config->channel_sens, TE_DEFAULT_THRESHOLD_DIVIDER(s_te_btn_obj));
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
|
||||
te_button->config->event_mask = TOUCH_ELEM_EVENT_NONE;
|
||||
te_button->config->dispatch_method = TOUCH_ELEM_DISP_MAX;
|
||||
te_button->config->callback = NULL;
|
||||
te_button->config->arg = NULL;
|
||||
te_button->current_state = TE_STATE_IDLE;
|
||||
te_button->last_state = TE_STATE_IDLE;
|
||||
te_button->event = TOUCH_BUTTON_EVT_MAX;
|
||||
te_button->trigger_cnt = 0;
|
||||
te_button->trigger_thr = 0xffffffff;
|
||||
ret = button_object_add_instance(te_button);
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
*button_handle = (touch_elem_handle_t)te_button;
|
||||
ESP_LOGD(TE_DEBUG_TAG, "channel: %d, channel_sens: %f", button_config->channel_num, button_config->channel_sens);
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(te_button->config);
|
||||
TE_FREE_AND_NULL(te_button->device);
|
||||
TE_FREE_AND_NULL(te_button);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_delete(touch_button_handle_t button_handle)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = button_object_remove_instance(button_handle);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
te_button_handle_t te_button = (te_button_handle_t)button_handle;
|
||||
te_dev_deinit(&te_button->device, 1);
|
||||
free(te_button->config);
|
||||
free(te_button->device);
|
||||
free(te_button);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_set_dispatch_method(touch_button_handle_t button_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(dispatch_method >= TOUCH_ELEM_DISP_EVENT && dispatch_method <= TOUCH_ELEM_DISP_MAX, ESP_ERR_INVALID_ARG);
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
te_button_handle_t te_button = (te_button_handle_t)button_handle;
|
||||
te_button->config->dispatch_method = dispatch_method;
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_subscribe_event(touch_button_handle_t button_handle, uint32_t event_mask, void *arg)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
if (!(event_mask & TOUCH_ELEM_EVENT_ON_PRESS) && !(event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) &&
|
||||
!(event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) && !(event_mask & TOUCH_ELEM_EVENT_NONE)) {
|
||||
ESP_LOGE(TE_TAG, "Touch button only support TOUCH_ELEM_EVENT_ON_PRESS, "
|
||||
"TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_LONGPRESS event mask");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
|
||||
touch_button_set_longpress(button_handle, TE_DEFAULT_LONGPRESS_TIME(s_te_btn_obj)); //set the default time(1000ms) for long press
|
||||
}
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
te_button_handle_t te_button = (te_button_handle_t)button_handle;
|
||||
te_button->config->event_mask = event_mask;
|
||||
te_button->config->arg = arg;
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_set_callback(touch_button_handle_t button_handle, touch_button_callback_t button_callback)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(button_callback != NULL, ESP_ERR_INVALID_ARG);
|
||||
te_button_handle_t te_button = (te_button_handle_t)button_handle;
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
te_button->config->callback = button_callback;
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_button_set_longpress(touch_button_handle_t button_handle, uint32_t threshold_time)
|
||||
{
|
||||
TE_CHECK(s_te_btn_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(button_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(threshold_time > 0, ESP_ERR_INVALID_ARG);
|
||||
te_button_handle_t te_button = (te_button_handle_t)button_handle;
|
||||
touch_elem_dispatch_t dispatch_method = te_button->config->dispatch_method;
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
}
|
||||
uint8_t timer_period = te_get_timer_period();
|
||||
te_button->trigger_thr = threshold_time / timer_period;
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const touch_button_message_t* touch_button_get_message(const touch_elem_message_t* element_message)
|
||||
{
|
||||
return (touch_button_message_t*)&element_message->child_msg;
|
||||
_Static_assert(sizeof(element_message->child_msg) >= sizeof(touch_button_message_t), "Message size overflow");
|
||||
}
|
||||
|
||||
static bool button_object_check_channel(touch_pad_t channel_num)
|
||||
{
|
||||
te_button_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_btn_obj->handle_list, next) {
|
||||
if (button_channel_check(item->button_handle, channel_num)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t button_object_set_threshold(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
te_button_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_btn_obj->handle_list, next) {
|
||||
ret = button_set_threshold(item->button_handle);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void button_object_process_state(void)
|
||||
{
|
||||
te_button_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_btn_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->button_handle)) {
|
||||
button_reset_state(item->button_handle);
|
||||
continue;
|
||||
}
|
||||
button_proc_state(item->button_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void button_object_update_state(touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_button_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_btn_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->button_handle)) {
|
||||
continue;
|
||||
}
|
||||
button_update_state(item->button_handle, channel_num, channel_state);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t button_object_add_instance(te_button_handle_t button_handle)
|
||||
{
|
||||
te_button_handle_list_t *item = (te_button_handle_list_t *)calloc(1, sizeof(te_button_handle_list_t));
|
||||
TE_CHECK(item != NULL, ESP_ERR_NO_MEM);
|
||||
item->button_handle = button_handle;
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INSERT_HEAD(&s_te_btn_obj->handle_list, item, next);
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t button_object_remove_instance(te_button_handle_t button_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_NOT_FOUND;
|
||||
te_button_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_btn_obj->handle_list, next) {
|
||||
if (button_handle == item->button_handle) {
|
||||
xSemaphoreTake(s_te_btn_obj->mutex, portMAX_DELAY);
|
||||
SLIST_REMOVE(&s_te_btn_obj->handle_list, item, te_button_handle_list, next);
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
free(item);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool button_channel_check(te_button_handle_t button_handle, touch_pad_t channel_num)
|
||||
{
|
||||
return (channel_num == button_handle->device->channel);
|
||||
}
|
||||
|
||||
static esp_err_t button_set_threshold(te_button_handle_t button_handle)
|
||||
{
|
||||
return te_dev_set_threshold(button_handle->device);
|
||||
}
|
||||
|
||||
static void button_update_state(te_button_handle_t button_handle, touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_dev_t *device = button_handle->device;
|
||||
if (channel_num != device->channel) {
|
||||
return;
|
||||
}
|
||||
device->state = channel_state;
|
||||
}
|
||||
|
||||
static void button_reset_state(te_button_handle_t button_handle)
|
||||
{
|
||||
button_handle->trigger_cnt = 0;
|
||||
button_handle->current_state = TE_STATE_IDLE;
|
||||
button_handle->device->state = TE_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void button_event_give(te_button_handle_t button_handle)
|
||||
{
|
||||
touch_elem_message_t element_message;
|
||||
touch_button_message_t button_message = {
|
||||
.event = button_handle->event
|
||||
};
|
||||
element_message.handle = (touch_elem_handle_t)button_handle;
|
||||
element_message.element_type = TOUCH_ELEM_TYPE_BUTTON;
|
||||
element_message.arg = button_handle->config->arg;
|
||||
memcpy(element_message.child_msg, &button_message, sizeof(button_message));
|
||||
te_event_give(element_message);
|
||||
}
|
||||
|
||||
static inline void button_dispatch(te_button_handle_t button_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
button_event_give(button_handle); //Event queue
|
||||
} else if (dispatch_method == TOUCH_ELEM_DISP_CALLBACK) {
|
||||
touch_button_message_t button_info;
|
||||
button_info.event = button_handle->event;
|
||||
button_handle->config->callback(button_handle, button_info, button_handle->config->arg); //Event callback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Button process
|
||||
*
|
||||
* This function will process the button state and maintain a button FSM:
|
||||
* IDLE ----> Press ----> Release ----> IDLE
|
||||
*
|
||||
* The state transition procedure is as follow:
|
||||
* (channel state ----> button state)
|
||||
*
|
||||
* TODO: add state transition diagram
|
||||
*/
|
||||
static void button_proc_state(te_button_handle_t button_handle)
|
||||
{
|
||||
uint32_t event_mask = button_handle->config->event_mask;
|
||||
touch_elem_dispatch_t dispatch_method = button_handle->config->dispatch_method;
|
||||
|
||||
BaseType_t mux_ret = xSemaphoreTake(s_te_btn_obj->mutex, 0);
|
||||
if (mux_ret != pdPASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
button_handle->current_state = button_get_state(button_handle->device);
|
||||
|
||||
if (button_handle->current_state == TE_STATE_PRESS) {
|
||||
if (button_handle->last_state == TE_STATE_IDLE) { //IDLE ---> Press = On_Press
|
||||
ESP_LOGD(TE_DEBUG_TAG, "button press");
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_PRESS) {
|
||||
button_handle->event = TOUCH_BUTTON_EVT_ON_PRESS;
|
||||
button_dispatch(button_handle, dispatch_method);
|
||||
}
|
||||
} else if (button_handle->last_state == TE_STATE_PRESS) { //Press ---> Press = On_LongPress
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
|
||||
if (++button_handle->trigger_cnt >= button_handle->trigger_thr) {
|
||||
ESP_LOGD(TE_DEBUG_TAG, "button longpress");
|
||||
button_handle->event = TOUCH_BUTTON_EVT_ON_LONGPRESS;
|
||||
button_dispatch(button_handle, dispatch_method);
|
||||
button_handle->trigger_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (button_handle->current_state == TE_STATE_RELEASE) {
|
||||
if (button_handle->last_state == TE_STATE_PRESS) { //Press ---> Release = On_Release
|
||||
ESP_LOGD(TE_DEBUG_TAG, "button release");
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) {
|
||||
button_handle->event = TOUCH_BUTTON_EVT_ON_RELEASE;
|
||||
button_dispatch(button_handle, dispatch_method);
|
||||
}
|
||||
} else if (button_handle->last_state == TE_STATE_RELEASE) { // Release ---> Release = On_IDLE (Not dispatch)
|
||||
button_reset_state(button_handle); //Reset the button state for the next time touch action detection
|
||||
}
|
||||
} else if (button_handle->current_state == TE_STATE_IDLE) {
|
||||
if (button_handle->last_state == TE_STATE_RELEASE) { //Release ---> IDLE = On_IDLE (Not dispatch)
|
||||
//Nothing
|
||||
} else if (button_handle->last_state == TE_STATE_IDLE) { //IDLE ---> IDLE = Running IDLE (Not dispatch)
|
||||
//Nothing
|
||||
}
|
||||
}
|
||||
button_handle->last_state = button_handle->current_state;
|
||||
xSemaphoreGive(s_te_btn_obj->mutex);
|
||||
}
|
||||
|
||||
static inline te_state_t button_get_state(te_dev_t *device)
|
||||
{
|
||||
te_state_t button_state;
|
||||
if (device->state == TE_STATE_PRESS) {
|
||||
button_state = TE_STATE_PRESS;
|
||||
} else if (device->state == TE_STATE_RELEASE) {
|
||||
button_state = TE_STATE_RELEASE;
|
||||
} else {
|
||||
button_state = TE_STATE_IDLE;
|
||||
}
|
||||
return button_state;
|
||||
}
|
886
components/touch_element/touch_element.c
Normal file
886
components/touch_element/touch_element.c
Normal file
@ -0,0 +1,886 @@
|
||||
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#include "hal/touch_sensor_hal.h" //TODO: remove hal
|
||||
#include "touch_element/touch_element_private.h"
|
||||
|
||||
#define TE_CLASS_ITEM(cls, cls_type, cls_item) ((&((cls)[cls_type]))->cls_item)
|
||||
|
||||
#define TE_CLASS_FOREACH(cls_var, cls_start, cls_end) \
|
||||
for ((cls_var) = (cls_start); \
|
||||
(cls_var) < (cls_end); \
|
||||
(cls_var)++)
|
||||
|
||||
#define TE_CLS_METHODS_INITIALIZER(cls, cls_start, cls_end) do { \
|
||||
typeof(cls_start) cls_method; \
|
||||
TE_CLASS_FOREACH(cls_method, cls_start, cls_end) { \
|
||||
TE_CLASS_ITEM(cls, cls_method, handle) = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TE_CLASS_FOREACH_CHECK_CHANNEL(cls, cls_start, cls_end, channel) ({ \
|
||||
bool ret = false; \
|
||||
typeof(cls_start) cls_method; \
|
||||
TE_CLASS_FOREACH(cls_method, cls_start, cls_end) { \
|
||||
if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) { \
|
||||
ret |= TE_CLASS_ITEM(cls, cls_method, check_channel(channel)); \
|
||||
} \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define TE_CLASS_FOREACH_SET_THRESHOLD(cls, cls_start, cls_end) do { \
|
||||
typeof(cls_start) cls_method; \
|
||||
TE_CLASS_FOREACH(cls_method, cls_start, cls_end) { \
|
||||
if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) { \
|
||||
TE_CLASS_ITEM(cls, cls_method, set_threshold()); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TE_CLASS_FOREACH_PROCESS_STATE(cls, cls_start, cls_end) do { \
|
||||
typeof(cls_start) cls_method; \
|
||||
TE_CLASS_FOREACH(cls_method, cls_start, cls_end) { \
|
||||
if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) { \
|
||||
TE_CLASS_ITEM(cls, cls_method, process_state()); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TE_CLASS_FOREACH_UPDATE_STATE(cls, cls_start, cls_end, channel, state) do {\
|
||||
typeof(cls_start) cls_method; \
|
||||
TE_CLASS_FOREACH(cls_method, cls_start, cls_end) { \
|
||||
if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) { \
|
||||
TE_CLASS_ITEM(cls, cls_method, update_state(channel, state)); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TE_PROCESSING_PERIOD(obj) ((obj)->global_config->software.processing_period)
|
||||
#define TE_WATERPROOF_DIVIDER(obj) ((obj)->global_config->software.waterproof_threshold_divider)
|
||||
|
||||
typedef enum {
|
||||
TE_INTR_PRESS = 0, //Touch sensor press interrupt(TOUCH_PAD_INTR_MASK_ACTIVE)
|
||||
TE_INTR_RELEASE, //Touch sensor release interrupt(TOUCH_PAD_INTR_MASK_INACTIVE)
|
||||
TE_INTR_TIMEOUT, //Touch sensor scan timeout interrupt(TOUCH_PAD_INTR_MASK_TIMEOUT)
|
||||
TE_INTR_SCAN_DONE, //Touch sensor scan done interrupt(TOUCH_PAD_INTR_MASK_SCAN_DONE), now just use for setting threshold
|
||||
TE_INTR_MAX
|
||||
} te_intr_t;
|
||||
|
||||
typedef struct {
|
||||
te_intr_t intr_type; //channel interrupt type
|
||||
te_state_t channel_state; //channel state
|
||||
touch_pad_t channel_num; //channel index
|
||||
} te_intr_msg_t;
|
||||
|
||||
typedef struct {
|
||||
te_object_methods_t object_methods[TE_CLS_TYPE_MAX]; //Class(object) methods
|
||||
touch_elem_global_config_t *global_config; //Global initialization
|
||||
te_waterproof_handle_t waterproof_handle; //Waterproof configuration
|
||||
esp_timer_handle_t proc_timer; //Processing timer handle
|
||||
QueueHandle_t event_msg_queue; //Application event message queue (for user)
|
||||
QueueHandle_t intr_msg_queue; //Interrupt message (for internal)
|
||||
SemaphoreHandle_t mutex; //Global resource mutex
|
||||
bool is_set_threshold; //Threshold configuration state bit
|
||||
uint32_t denoise_channel_raw; //De-noise channel(TO) raw signal
|
||||
} te_obj_t;
|
||||
|
||||
static te_obj_t *s_te_obj = NULL;
|
||||
|
||||
/**
|
||||
* Internal de-noise channel(Touch channel 0) equivalent capacitance table, depends on hardware design
|
||||
*
|
||||
* Units: pF
|
||||
*/
|
||||
static const float denoise_channel_equ_cap[TOUCH_PAD_DENOISE_CAP_MAX] = {5.0f, 6.4f, 7.8f, 9.2f, 10.6f, 12.0f, 13.4f, 14.8f};
|
||||
|
||||
/**
|
||||
* Waterproof shield channel(Touch channel 14) equivalent capacitance table, depends on hardware design
|
||||
*
|
||||
* Units: pF
|
||||
*/
|
||||
static const float shield_channel_ref_cap[TOUCH_PAD_SHIELD_DRV_MAX] = {40.0f, 80.0f, 120.0f, 160.0f, 200.0f, 240.0f, 280.0f, 320.0f};
|
||||
|
||||
/* -------------------------------------------- Internal shared methods --------------------------------------------- */
|
||||
/* ------------------------------------------------- */
|
||||
/* ------------------------------------------------- System methods ------------------------------------------------- */
|
||||
static esp_err_t te_hw_init(const touch_elem_hw_config_t *hardware_init);
|
||||
static esp_err_t te_sw_init(const touch_elem_sw_config_t *software_init);
|
||||
static inline float te_get_internal_equ_cap(touch_pad_denoise_cap_t denoise_level);
|
||||
static float te_channel_get_equ_cap(touch_pad_t channel_num);
|
||||
static uint32_t te_read_raw_signal(touch_pad_t channel_num);
|
||||
static void te_intr_cb(void *arg);
|
||||
static void te_proc_timer_cb(void *arg);
|
||||
static inline esp_err_t te_object_set_threshold(void);
|
||||
static inline void te_object_process_state(void);
|
||||
static inline void te_object_update_state(te_intr_msg_t te_intr_msg);
|
||||
/* ----------------------------------------------- Waterproof methods ----------------------------------------------- */
|
||||
static inline bool waterproof_check_state(void);
|
||||
static inline bool waterproof_shield_check_state(void);
|
||||
static inline bool waterproof_guard_check_state(void);
|
||||
static bool waterproof_channel_check(touch_pad_t channel_num);
|
||||
static void waterproof_guard_set_threshold(void);
|
||||
static void waterproof_guard_update_state(touch_pad_t current_channel, te_state_t current_state);
|
||||
static touch_pad_shield_driver_t waterproof_get_shield_level(touch_pad_t guard_channel_num);
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
esp_err_t touch_element_install(const touch_elem_global_config_t *global_config)
|
||||
{
|
||||
TE_CHECK(s_te_obj == NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
|
||||
s_te_obj = (te_obj_t *)calloc(1, sizeof(te_obj_t));
|
||||
TE_CHECK(s_te_obj != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NO_MEM;
|
||||
s_te_obj->global_config = (touch_elem_global_config_t *)calloc(1, sizeof(touch_elem_global_config_t));
|
||||
s_te_obj->mutex = xSemaphoreCreateMutex();
|
||||
TE_CHECK_GOTO(s_te_obj->global_config != NULL && s_te_obj->mutex != NULL, cleanup);
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
TE_CLS_METHODS_INITIALIZER(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
|
||||
ret = te_hw_init(&global_config->hardware);
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
ret = te_sw_init(&global_config->software);
|
||||
if (ret != ESP_OK) {
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
goto cleanup;
|
||||
}
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(s_te_obj->global_config);
|
||||
if (s_te_obj->mutex != NULL) {
|
||||
vSemaphoreDelete(s_te_obj->mutex);
|
||||
}
|
||||
TE_FREE_AND_NULL(s_te_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_element_start(void)
|
||||
{
|
||||
TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
esp_err_t ret;
|
||||
uint16_t inited_channel_mask;
|
||||
do {
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
ret = touch_pad_get_channel_mask(&inited_channel_mask);
|
||||
if (inited_channel_mask == 0x0) {
|
||||
ESP_LOGE(TE_TAG, "Can not find Touch Sensor channel that has been initialized");
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
s_te_obj->is_set_threshold = false; //Threshold configuration will be set on touch sense start
|
||||
ret = esp_timer_start_periodic(s_te_obj->proc_timer, TE_PROCESSING_PERIOD(s_te_obj) * 1000);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
ret = touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_SCAN_DONE); //Use scan done interrupt to set threshold
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
ret = touch_pad_fsm_start();
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
xQueueReset(s_te_obj->event_msg_queue);
|
||||
xQueueReset(s_te_obj->intr_msg_queue);
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ESP_OK;
|
||||
} while (0);
|
||||
|
||||
ESP_LOGE(TE_TAG, "Touch interface start failed:(%s)", __FUNCTION__ );
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_element_stop(void)
|
||||
{
|
||||
TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
esp_err_t ret;
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
ret = touch_pad_fsm_stop();
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = touch_pad_intr_disable(TOUCH_PAD_INTR_MASK_SCAN_DONE);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = esp_timer_stop(s_te_obj->proc_timer);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
//TODO: add a new api that output system's run-time state
|
||||
|
||||
void touch_element_uninstall(void)
|
||||
{
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
if (s_te_obj == NULL) {
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return;
|
||||
}
|
||||
esp_err_t ret;
|
||||
ret = touch_pad_deinit();
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
ret = esp_timer_delete(s_te_obj->proc_timer);
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
ret = touch_pad_intr_disable(TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE | TOUCH_PAD_INTR_MASK_TIMEOUT);
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
ret = touch_pad_isr_deregister(te_intr_cb, NULL);
|
||||
if (ret != ESP_OK) {
|
||||
abort();
|
||||
}
|
||||
vQueueDelete(s_te_obj->event_msg_queue);
|
||||
vQueueDelete(s_te_obj->intr_msg_queue);
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
vSemaphoreDelete(s_te_obj->mutex);
|
||||
free(s_te_obj->global_config);
|
||||
s_te_obj->global_config = NULL;
|
||||
free(s_te_obj);
|
||||
s_te_obj = NULL;
|
||||
}
|
||||
|
||||
esp_err_t touch_element_message_receive(touch_elem_message_t *element_message, uint32_t ticks_to_wait)
|
||||
{
|
||||
//TODO: Use the generic data struct to refactor this api
|
||||
TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(element_message != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(s_te_obj->event_msg_queue != NULL, ESP_ERR_INVALID_STATE);
|
||||
int ret = xQueueReceive(s_te_obj->event_msg_queue, element_message, ticks_to_wait);
|
||||
return (ret == pdTRUE) ? ESP_OK : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
static uint32_t te_read_raw_signal(touch_pad_t channel_num)
|
||||
{
|
||||
uint32_t raw_signal = 0;
|
||||
touch_pad_sleep_channel_t sleep_channel_info;
|
||||
touch_pad_sleep_channel_get_info(&sleep_channel_info);
|
||||
if (channel_num != sleep_channel_info.touch_num) {
|
||||
touch_pad_read_raw_data(channel_num, &raw_signal);
|
||||
} else {
|
||||
touch_pad_sleep_channel_read_data(channel_num, &raw_signal);
|
||||
}
|
||||
return raw_signal;
|
||||
}
|
||||
|
||||
uint32_t te_read_smooth_signal(touch_pad_t channel_num)
|
||||
{
|
||||
uint32_t smooth_signal = 0;
|
||||
touch_pad_sleep_channel_t sleep_channel_info;
|
||||
touch_pad_sleep_channel_get_info(&sleep_channel_info);
|
||||
if (channel_num != sleep_channel_info.touch_num) {
|
||||
touch_pad_filter_read_smooth(channel_num, &smooth_signal);
|
||||
} else {
|
||||
touch_pad_sleep_channel_read_smooth(channel_num, &smooth_signal);
|
||||
}
|
||||
return smooth_signal;
|
||||
}
|
||||
|
||||
esp_err_t te_event_give(touch_elem_message_t te_message)
|
||||
{
|
||||
//TODO: add queue overwrite here when the queue is full
|
||||
int ret = xQueueSend(s_te_obj->event_msg_queue, &te_message, 0);
|
||||
if (ret != pdTRUE) {
|
||||
ESP_LOGE(TE_TAG, "event queue send failed, event message queue is full");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Touch sensor interrupt service routine
|
||||
*
|
||||
* This function is touch sensor ISR, all the touch
|
||||
* sensor channel state will be updated here.
|
||||
*/
|
||||
static void te_intr_cb(void *arg)
|
||||
{
|
||||
TE_UNUSED(arg);
|
||||
static int scan_done_cnt = 0;
|
||||
int task_awoken = pdFALSE;
|
||||
te_intr_msg_t te_intr_msg;
|
||||
/*< Figure out which touch sensor channel is triggered and the trigger type */
|
||||
uint32_t intr_mask = touch_pad_read_intr_status_mask();
|
||||
te_intr_msg.channel_num = touch_pad_get_current_meas_channel();
|
||||
if (intr_mask == 0x0) { //For dummy interrupt
|
||||
return;
|
||||
}
|
||||
bool need_send_queue = true;
|
||||
if (intr_mask & TOUCH_PAD_INTR_MASK_ACTIVE) {
|
||||
te_intr_msg.channel_state = TE_STATE_PRESS;
|
||||
te_intr_msg.intr_type = TE_INTR_PRESS;
|
||||
} else if (intr_mask & TOUCH_PAD_INTR_MASK_INACTIVE) {
|
||||
te_intr_msg.channel_state = TE_STATE_RELEASE;
|
||||
te_intr_msg.intr_type = TE_INTR_RELEASE;
|
||||
} else if (intr_mask & TOUCH_PAD_INTR_MASK_TIMEOUT) {
|
||||
te_intr_msg.channel_state = TE_STATE_IDLE;
|
||||
te_intr_msg.intr_type = TE_INTR_TIMEOUT;
|
||||
} else if (intr_mask & TOUCH_PAD_INTR_MASK_SCAN_DONE) {
|
||||
te_intr_msg.channel_state = TE_STATE_IDLE;
|
||||
te_intr_msg.intr_type = TE_INTR_SCAN_DONE;
|
||||
need_send_queue = false;
|
||||
/*< Due to a hardware issue, all of the data read operation(read raw, read smooth, read benchmark) */
|
||||
/*< must be after the second times of measure_done interrupt. */
|
||||
if (++scan_done_cnt >= 5) {
|
||||
touch_hal_intr_disable(TOUCH_PAD_INTR_MASK_SCAN_DONE); //TODO: remove hal
|
||||
scan_done_cnt = 0;
|
||||
need_send_queue = true;
|
||||
}
|
||||
/*< De-noise channel signal must be read at the time between SCAN_DONE and next measurement beginning(sleep)!!! */
|
||||
touch_pad_denoise_read_data(&s_te_obj->denoise_channel_raw); //Update de-noise signal
|
||||
} else {
|
||||
te_intr_msg.intr_type = TE_INTR_MAX; // Unknown Exception
|
||||
}
|
||||
if (need_send_queue) {
|
||||
xQueueSendFromISR(s_te_obj->intr_msg_queue, &te_intr_msg, &task_awoken);
|
||||
}
|
||||
if (task_awoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief esp-timer callback routine
|
||||
*
|
||||
* This function is an esp-timer daemon routine, all the touch sensor
|
||||
* application(button, slider, etc...) will be processed in here.
|
||||
*
|
||||
*/
|
||||
static void te_proc_timer_cb(void *arg)
|
||||
{
|
||||
TE_UNUSED(arg);
|
||||
te_intr_msg_t te_intr_msg;
|
||||
te_intr_msg.intr_type = TE_INTR_MAX;
|
||||
BaseType_t ret = xSemaphoreTake(s_te_obj->mutex, 0);
|
||||
if (ret != pdPASS) {
|
||||
return;
|
||||
}
|
||||
ret = xQueueReceive(s_te_obj->intr_msg_queue, &te_intr_msg, 0);
|
||||
if (ret == pdPASS) {
|
||||
if (te_intr_msg.intr_type == TE_INTR_PRESS || te_intr_msg.intr_type == TE_INTR_RELEASE) {
|
||||
te_object_update_state(te_intr_msg);
|
||||
} else if (te_intr_msg.intr_type == TE_INTR_SCAN_DONE) {
|
||||
if (s_te_obj->is_set_threshold != true) {
|
||||
s_te_obj->is_set_threshold = true;
|
||||
te_object_set_threshold(); //TODO: add set threshold error processing
|
||||
ESP_LOGD(TE_DEBUG_TAG, "Set threshold");
|
||||
}
|
||||
if (waterproof_check_state()) {
|
||||
te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
|
||||
if (waterproof_handle->is_shield_level_set != true) {
|
||||
waterproof_handle->is_shield_level_set = true;
|
||||
touch_pad_waterproof_t wp_conf;
|
||||
wp_conf.shield_driver = waterproof_get_shield_level(waterproof_handle->shield_channel);
|
||||
wp_conf.guard_ring_pad = (waterproof_guard_check_state() ? waterproof_handle->guard_device->channel : TOUCH_WATERPROOF_GUARD_NOUSE);
|
||||
touch_pad_waterproof_set_config(&wp_conf);
|
||||
touch_pad_waterproof_enable();
|
||||
ESP_LOGD(TE_DEBUG_TAG, "Set waterproof shield level");
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TE_DEBUG_TAG, "read denoise channel %d", s_te_obj->denoise_channel_raw);
|
||||
} else if (te_intr_msg.intr_type == TE_INTR_TIMEOUT) { //Timeout processing
|
||||
touch_pad_timeout_resume();
|
||||
}
|
||||
}
|
||||
te_object_process_state();
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
}
|
||||
|
||||
void te_object_method_register(te_object_methods_t *object_methods, te_class_type_t object_type)
|
||||
{
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, handle) = object_methods->handle;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, check_channel) = object_methods->check_channel;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, set_threshold) = object_methods->set_threshold;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, process_state) = object_methods->process_state;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, update_state) = object_methods->update_state;
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
}
|
||||
|
||||
void te_object_method_unregister(te_class_type_t object_type)
|
||||
{
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, handle) = NULL;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, check_channel) = NULL;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, set_threshold) = NULL;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, process_state) = NULL;
|
||||
TE_CLASS_ITEM(s_te_obj->object_methods, object_type, update_state) = NULL;
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Touch Sense channel check
|
||||
*
|
||||
* This function will check the input channel whether is
|
||||
* associated with the Touch Sense Object
|
||||
*
|
||||
* @return
|
||||
* - true: Channel has been initialized, pls adjust the input channel
|
||||
* - false: Channel has not been initialized, pass
|
||||
*/
|
||||
bool te_object_check_channel(const touch_pad_t *channel_array, uint8_t channel_sum)
|
||||
{
|
||||
touch_pad_t current_channel;
|
||||
for (int idx = 0; idx < channel_sum; idx++) {
|
||||
current_channel = channel_array[idx];
|
||||
if (waterproof_channel_check(current_channel)) {
|
||||
goto INITIALIZED;
|
||||
}
|
||||
if (TE_CLASS_FOREACH_CHECK_CHANNEL(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX, current_channel)) {
|
||||
goto INITIALIZED;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
INITIALIZED:
|
||||
ESP_LOGE(TE_TAG, "Current channel [%d] has been initialized:(%s)", current_channel, __FUNCTION__ );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline esp_err_t te_object_set_threshold(void)
|
||||
{
|
||||
if (waterproof_guard_check_state() == true) { //TODO: add to object methods
|
||||
waterproof_guard_set_threshold();
|
||||
}
|
||||
|
||||
TE_CLASS_FOREACH_SET_THRESHOLD(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static inline void te_object_process_state(void)
|
||||
{
|
||||
TE_CLASS_FOREACH_PROCESS_STATE(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
|
||||
}
|
||||
|
||||
static inline void te_object_update_state(te_intr_msg_t te_intr_msg)
|
||||
{
|
||||
if (waterproof_guard_check_state()) {
|
||||
waterproof_guard_update_state(te_intr_msg.channel_num, te_intr_msg.channel_state);
|
||||
}
|
||||
TE_CLASS_FOREACH_UPDATE_STATE(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX,
|
||||
te_intr_msg.channel_num, te_intr_msg.channel_state);
|
||||
}
|
||||
|
||||
uint8_t te_get_timer_period(void)
|
||||
{
|
||||
return (TE_PROCESSING_PERIOD(s_te_obj));
|
||||
}
|
||||
|
||||
esp_err_t te_dev_init(te_dev_t **device, uint8_t device_num, te_dev_type_t type, const touch_pad_t *channel, const float *sens, float divider)
|
||||
{
|
||||
for (int idx = 0; idx < device_num; idx++) {
|
||||
device[idx]->channel = channel[idx];
|
||||
device[idx]->sens = sens[idx] * divider;
|
||||
device[idx]->type = type;
|
||||
device[idx]->state = TE_STATE_IDLE;
|
||||
esp_err_t ret = touch_pad_config(device[idx]->channel);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void te_dev_deinit(te_dev_t **device, uint8_t device_num)
|
||||
{
|
||||
for (int idx = 0; idx < device_num; idx++) {
|
||||
touch_pad_clear_channel_mask((1UL << device[idx]->channel));
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t te_dev_set_threshold(te_dev_t *device)
|
||||
{
|
||||
uint32_t smo_val = te_read_smooth_signal(device->channel);
|
||||
esp_err_t ret = touch_pad_set_thresh(device->channel, device->sens * smo_val);
|
||||
ESP_LOGD(TE_DEBUG_TAG, "channel: %d, smo_val: %d", device->channel, smo_val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the s_te_obj whether is initialized
|
||||
*
|
||||
* @return
|
||||
* - true: initialized
|
||||
* - false: not initialized
|
||||
*/
|
||||
bool te_system_check_state(void)
|
||||
{
|
||||
return (s_te_obj != NULL);
|
||||
}
|
||||
|
||||
static inline float te_get_internal_equ_cap(touch_pad_denoise_cap_t denoise_level)
|
||||
{
|
||||
return denoise_channel_equ_cap[denoise_level];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get channel equivalent capacitance
|
||||
*
|
||||
* This function calculates the equivalent capacitance of input channel by
|
||||
* using the Touch channel 0 equivalent capacitance. The formula is:
|
||||
*
|
||||
* Raw_N / Raw_0 = Cap_N / Cap_0
|
||||
*
|
||||
* Note that Raw_N and Raw_0 are the raw data of touch channel N and touch channel 0 respectively,
|
||||
* Cap_N and Cap_0 are the equivalent capacitance of touch channel N and touch channel 0.
|
||||
*
|
||||
* @param[in] channel_num Input touch sensor channel
|
||||
*
|
||||
* @note The unit is pF
|
||||
*
|
||||
* @return Specified channel equivalent capacitance.
|
||||
*/
|
||||
static float te_channel_get_equ_cap(touch_pad_t channel_num)
|
||||
{
|
||||
//Fixme: add a mutex in here and prevent the system call this function
|
||||
TE_CHECK(channel_num > TOUCH_PAD_NUM0 && channel_num < TOUCH_PAD_MAX, 0);
|
||||
uint32_t tn_raw, t0_raw;
|
||||
float tn_ref_cap, t0_ref_cap;
|
||||
touch_pad_denoise_t denoise_channel_conf;
|
||||
touch_pad_denoise_get_config(&denoise_channel_conf);
|
||||
tn_raw = te_read_raw_signal(channel_num);
|
||||
t0_raw = s_te_obj->denoise_channel_raw;
|
||||
t0_ref_cap = te_get_internal_equ_cap(denoise_channel_conf.cap_level);
|
||||
if (t0_raw == 0) {
|
||||
return 0;
|
||||
}
|
||||
tn_ref_cap = (float)tn_raw / t0_raw * t0_ref_cap;
|
||||
return tn_ref_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Touch sensor driver default init [ESP32S2 only]
|
||||
*
|
||||
* 1. Channel measure time: Raw_value / RTC_FAST_CLK ==> Raw_value / 8000 000
|
||||
* 2. Channel sleep time: TOUCH_PAD_SLEEP_CYCLE_DEFAULT / RTC_SLOW_CLK ==> 0xf / 90 000(default) = 0.16ms
|
||||
* 3. Channel charge voltage threshold(upper/lower): 2.7V upper voltage, 0.5V lower voltage, 0.5V attenuation voltage
|
||||
* 4. IDLE channel processing: Connecting to GND
|
||||
* 5. Interrupt type: ACTIVE, INACTIVE, TIMEOUT
|
||||
*
|
||||
* @note A touch sensor channel will spend the time = measure time + sleep time, RTC_FAST_CLK is 8M
|
||||
*
|
||||
*/
|
||||
static esp_err_t te_hw_init(const touch_elem_hw_config_t *hardware_init)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = touch_pad_init();
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_set_meas_time(hardware_init->sleep_cycle, hardware_init->sample_count);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_set_voltage(hardware_init->upper_voltage, hardware_init->lower_voltage,
|
||||
hardware_init->voltage_attenuation);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_set_idle_channel_connect(hardware_init->suspend_channel_polarity);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_isr_register(te_intr_cb, NULL,
|
||||
TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE |
|
||||
TOUCH_PAD_INTR_MASK_TIMEOUT | TOUCH_PAD_INTR_MASK_SCAN_DONE);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE |
|
||||
TOUCH_PAD_INTR_MASK_INACTIVE | TOUCH_PAD_INTR_MASK_TIMEOUT);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
|
||||
/*< Internal de-noise configuration */
|
||||
touch_pad_denoise_t denoise_config;
|
||||
denoise_config.grade = hardware_init->denoise_level;
|
||||
denoise_config.cap_level = hardware_init->denoise_equivalent_cap;
|
||||
ret = touch_pad_denoise_set_config(&denoise_config);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_denoise_enable();
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
|
||||
/*< benchmark filter configuration */
|
||||
touch_filter_config_t filter_config;
|
||||
filter_config.smh_lvl = hardware_init->smooth_filter_mode;
|
||||
filter_config.mode = hardware_init->benchmark_filter_mode;
|
||||
filter_config.debounce_cnt = hardware_init->benchmark_debounce_count;
|
||||
filter_config.noise_thr = hardware_init->benchmark_calibration_threshold;
|
||||
filter_config.jitter_step = hardware_init->benchmark_jitter_step;
|
||||
ret = touch_pad_filter_set_config(&filter_config);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
ret = touch_pad_filter_enable();
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
memcpy(&s_te_obj->global_config->hardware, hardware_init, sizeof(touch_elem_hw_config_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t te_sw_init(const touch_elem_sw_config_t *software_init)
|
||||
{
|
||||
TE_CHECK(software_init->processing_period > 1, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(software_init->waterproof_threshold_divider > 0, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(software_init->intr_message_size >= (TOUCH_PAD_MAX - 1), ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(software_init->event_message_size > 0, ESP_ERR_INVALID_ARG);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NO_MEM;
|
||||
s_te_obj->intr_msg_queue = xQueueCreate(software_init->intr_message_size, sizeof(te_intr_msg_t));
|
||||
s_te_obj->event_msg_queue = xQueueCreate(software_init->event_message_size, sizeof(touch_elem_message_t));
|
||||
TE_CHECK_GOTO(s_te_obj->event_msg_queue != NULL && s_te_obj->intr_msg_queue != NULL, cleanup);
|
||||
|
||||
const esp_timer_create_args_t te_proc_timer_args = {
|
||||
.name = "te_proc_timer_cb",
|
||||
.arg = NULL,
|
||||
.callback = &te_proc_timer_cb
|
||||
};
|
||||
ret = esp_timer_create(&te_proc_timer_args, &s_te_obj->proc_timer);
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
memcpy(&s_te_obj->global_config->software, software_init, sizeof(touch_elem_sw_config_t));
|
||||
return ret;
|
||||
|
||||
cleanup:
|
||||
if (s_te_obj->event_msg_queue != NULL) {
|
||||
vQueueDelete(s_te_obj->event_msg_queue);
|
||||
}
|
||||
if (s_te_obj->intr_msg_queue != NULL) {
|
||||
vQueueDelete(s_te_obj->intr_msg_queue);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//TODO: add waterproof guard-lock hysteresis
|
||||
esp_err_t touch_element_waterproof_install(const touch_elem_waterproof_config_t *waterproof_config)
|
||||
{
|
||||
TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(waterproof_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(waterproof_config->guard_channel >= TOUCH_PAD_NUM0 &&
|
||||
waterproof_config->guard_channel < TOUCH_PAD_MAX,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
te_waterproof_handle_t waterproof_handle = (te_waterproof_handle_t)calloc(1, sizeof(struct te_waterproof_s));
|
||||
TE_CHECK(waterproof_handle != NULL, ESP_ERR_NO_MEM);
|
||||
waterproof_handle->shield_channel = TOUCH_PAD_NUM14;
|
||||
|
||||
esp_err_t ret;
|
||||
if (waterproof_config->guard_channel != TOUCH_WATERPROOF_GUARD_NOUSE) { //Use guard sensor
|
||||
if (te_object_check_channel(&waterproof_config->guard_channel, 1)) {
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto cleanup;
|
||||
}
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
waterproof_handle->mask_handle = (touch_elem_handle_t *) calloc(TOUCH_PAD_MAX, sizeof(touch_elem_handle_t));
|
||||
waterproof_handle->guard_device = (te_dev_t *)calloc(1, sizeof(te_dev_t));
|
||||
TE_CHECK_GOTO(waterproof_handle->mask_handle != NULL && waterproof_handle->guard_device, cleanup);
|
||||
|
||||
ret = te_dev_init(&waterproof_handle->guard_device, 1, TOUCH_ELEM_TYPE_BUTTON,
|
||||
&waterproof_config->guard_channel, &waterproof_config->guard_sensitivity,
|
||||
TE_WATERPROOF_DIVIDER(s_te_obj));
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
waterproof_handle->guard_device->state = TE_STATE_RELEASE;
|
||||
for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
|
||||
waterproof_handle->mask_handle[idx] = NULL;
|
||||
}
|
||||
} else { //No use waterproof guard sensor
|
||||
waterproof_handle->guard_device = NULL;
|
||||
waterproof_handle->mask_handle = NULL;
|
||||
}
|
||||
waterproof_handle->is_shield_level_set = 0; //Set a state bit so as to configure the shield level at the run-time
|
||||
touch_pad_waterproof_t wp_conf;
|
||||
wp_conf.shield_driver = TOUCH_PAD_SHIELD_DRV_L0; //Set a default shield level
|
||||
wp_conf.guard_ring_pad = waterproof_config->guard_channel;
|
||||
ret = touch_pad_waterproof_set_config(&wp_conf);
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
ret = touch_pad_waterproof_enable();
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
s_te_obj->waterproof_handle = waterproof_handle; //Fixme: add mutex
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(waterproof_handle->mask_handle);
|
||||
TE_FREE_AND_NULL(waterproof_handle->guard_device);
|
||||
TE_FREE_AND_NULL(waterproof_handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_element_waterproof_add(touch_elem_handle_t element_handle)
|
||||
{
|
||||
TE_CHECK(s_te_obj->waterproof_handle != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(s_te_obj->waterproof_handle->guard_device != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(element_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
|
||||
if (waterproof_handle->mask_handle[idx] == NULL) {
|
||||
waterproof_handle->mask_handle[idx] = element_handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_element_waterproof_remove(touch_elem_handle_t element_handle)
|
||||
{
|
||||
TE_CHECK(s_te_obj->waterproof_handle != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(element_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
esp_err_t ret = ESP_ERR_NOT_FOUND;
|
||||
te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
|
||||
if (waterproof_handle->mask_handle[idx] == element_handle) {
|
||||
waterproof_handle->mask_handle[idx] = NULL;
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void touch_element_waterproof_uninstall(void)
|
||||
{
|
||||
xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
|
||||
touch_pad_waterproof_disable();
|
||||
free(s_te_obj->waterproof_handle->guard_device);
|
||||
free(s_te_obj->waterproof_handle->mask_handle);
|
||||
free(s_te_obj->waterproof_handle);
|
||||
s_te_obj->waterproof_handle = NULL;
|
||||
xSemaphoreGive(s_te_obj->mutex);
|
||||
}
|
||||
|
||||
static touch_pad_shield_driver_t waterproof_get_shield_level(touch_pad_t guard_channel_num)
|
||||
{
|
||||
touch_pad_shield_driver_t shield_level = TOUCH_PAD_SHIELD_DRV_L7;
|
||||
float guard_ref_cap = te_channel_get_equ_cap(guard_channel_num);
|
||||
for (int level = 0; level < TOUCH_PAD_SHIELD_DRV_MAX; level++) {
|
||||
if (guard_ref_cap <= shield_channel_ref_cap[level]) {
|
||||
shield_level = (touch_pad_shield_driver_t)level;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return shield_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the waterproof_handle whether is initialized
|
||||
*
|
||||
* @return
|
||||
* - true: initialized
|
||||
* - false: not initialized
|
||||
*/
|
||||
static inline bool waterproof_check_state(void)
|
||||
{
|
||||
return (s_te_obj->waterproof_handle != NULL);
|
||||
}
|
||||
|
||||
static inline bool waterproof_shield_check_state(void)
|
||||
{
|
||||
return waterproof_check_state(); //Driver does not allow to disable shield sensor after waterproof enabling
|
||||
}
|
||||
|
||||
static inline bool waterproof_guard_check_state(void)
|
||||
{
|
||||
if (waterproof_check_state() == false) {
|
||||
return false;
|
||||
}
|
||||
if (s_te_obj->waterproof_handle->guard_device == NULL || s_te_obj->waterproof_handle->mask_handle == NULL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool waterproof_channel_check(touch_pad_t channel_num)
|
||||
{
|
||||
if (waterproof_check_state() == false) {
|
||||
return false;
|
||||
}
|
||||
te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
|
||||
if (waterproof_shield_check_state()) {
|
||||
if (channel_num == waterproof_handle->shield_channel) {
|
||||
ESP_LOGE(TE_TAG, "TOUCH_PAD_NUM%d has been used for waterproof shield channel,"
|
||||
" please change the touch sensor channel or disable waterproof", channel_num);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (waterproof_guard_check_state()) {
|
||||
if (channel_num == waterproof_handle->guard_device->channel) {
|
||||
ESP_LOGE(TE_TAG, "TOUCH_PAD_NUM%d has been used for waterproof guard channel,"
|
||||
" please change the touch sensor channel or disable waterproof", channel_num);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void waterproof_guard_set_threshold(void)
|
||||
{
|
||||
if (waterproof_check_state() == false) {
|
||||
return;
|
||||
}
|
||||
if (waterproof_guard_check_state() == false) {
|
||||
return;
|
||||
}
|
||||
te_dev_set_threshold(s_te_obj->waterproof_handle->guard_device);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will figure out current handle whether is a masked channel
|
||||
* while guard channel is triggered.
|
||||
*
|
||||
* @param[in] te_handle Touch sensor application handle
|
||||
* @return
|
||||
* - true current handle is a masked channel
|
||||
* - false current handle is not a masked channel
|
||||
*/
|
||||
bool waterproof_check_mask_handle(touch_elem_handle_t te_handle)
|
||||
{
|
||||
if (waterproof_check_state() == false) {
|
||||
return false;
|
||||
}
|
||||
if (waterproof_guard_check_state() == false) {
|
||||
return false;
|
||||
}
|
||||
te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
|
||||
bool ret = false;
|
||||
if (waterproof_handle->guard_device->state == TE_STATE_PRESS) {
|
||||
for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
|
||||
if (waterproof_handle->mask_handle[idx] == NULL) {
|
||||
break;
|
||||
}
|
||||
if (waterproof_handle->mask_handle[idx] == te_handle) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void waterproof_guard_update_state(touch_pad_t current_channel, te_state_t current_state)
|
||||
{
|
||||
te_dev_t *guard_device = s_te_obj->waterproof_handle->guard_device;
|
||||
if (current_channel == guard_device->channel) {
|
||||
guard_device->state = current_state;
|
||||
}
|
||||
ESP_LOGD(TE_DEBUG_TAG, "waterproof guard state update %d", guard_device->state);
|
||||
}
|
631
components/touch_element/touch_matrix.c
Normal file
631
components/touch_element/touch_matrix.c
Normal file
@ -0,0 +1,631 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "touch_element/touch_element_private.h"
|
||||
|
||||
#define TE_MAT_POS_MAX (0xff) //!< Matrix button startup position
|
||||
|
||||
typedef struct te_matrix_handle_list {
|
||||
te_matrix_handle_t matrix_handle; //Matrix handle
|
||||
SLIST_ENTRY(te_matrix_handle_list) next; //Matrix handle list entry
|
||||
} te_matrix_handle_list_t;
|
||||
|
||||
typedef struct {
|
||||
SLIST_HEAD(te_matrix_handle_list_head, te_matrix_handle_list) handle_list; //Matrix handle (instance) list
|
||||
touch_matrix_global_config_t *global_config; //Matrix global configuration
|
||||
SemaphoreHandle_t mutex; //Matrix object mutex
|
||||
} te_matrix_obj_t;
|
||||
|
||||
te_matrix_obj_t *s_te_mat_obj = NULL;
|
||||
/* ---------------------------------------- Matrix handle(instance) methods ----------------------------------------- */
|
||||
static bool matrix_channel_check(te_matrix_handle_t matrix_handle, touch_pad_t channel_num);
|
||||
static esp_err_t matrix_set_threshold(te_matrix_handle_t matrix_handle);
|
||||
static inline te_state_t matrix_get_state(te_matrix_state_t x_axis_state, te_matrix_state_t y_axis_state);
|
||||
static void matrix_reset_state(te_matrix_handle_t matrix_handle);
|
||||
static void matrix_update_state(te_matrix_handle_t matrix_handle, touch_pad_t channel_num, te_state_t channel_state);
|
||||
static void matrix_update_position(te_matrix_handle_t matrix_handle, touch_matrix_position_t new_pos);
|
||||
static void matrix_proc_state(te_matrix_handle_t matrix_handle);
|
||||
static void matrix_event_give(te_matrix_handle_t matrix_handle);
|
||||
static inline void matrix_dispatch(te_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method);
|
||||
/* ------------------------------------------ Matrix object(class) methods ------------------------------------------ */
|
||||
static esp_err_t matrix_object_add_instance(te_matrix_handle_t matrix_handle);
|
||||
static esp_err_t matrix_object_remove_instance(te_matrix_handle_t matrix_handle);
|
||||
static bool matrix_object_check_channel(touch_pad_t channel_num);
|
||||
static esp_err_t matrix_object_set_threshold(void);
|
||||
static void matrix_object_process_state(void);
|
||||
static void matrix_object_update_state(touch_pad_t channel_num, te_state_t channel_state);
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
esp_err_t touch_matrix_install(const touch_matrix_global_config_t *global_config)
|
||||
{
|
||||
TE_CHECK(te_system_check_state() == true, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
|
||||
//Fixme: Make it thread-safe
|
||||
s_te_mat_obj = (te_matrix_obj_t *)calloc(1, sizeof(te_matrix_obj_t));
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_NO_MEM);
|
||||
s_te_mat_obj->global_config = (touch_matrix_global_config_t *)calloc(1, sizeof(touch_matrix_global_config_t));
|
||||
s_te_mat_obj->mutex = xSemaphoreCreateMutex();
|
||||
TE_CHECK_GOTO(s_te_mat_obj->global_config != NULL && s_te_mat_obj->mutex != NULL, cleanup);
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INIT(&s_te_mat_obj->handle_list);
|
||||
memcpy(s_te_mat_obj->global_config, global_config, sizeof(touch_matrix_global_config_t));
|
||||
te_object_methods_t matrix_methods = {
|
||||
.handle = s_te_mat_obj,
|
||||
.check_channel = matrix_object_check_channel,
|
||||
.set_threshold = matrix_object_set_threshold,
|
||||
.process_state = matrix_object_process_state,
|
||||
.update_state = matrix_object_update_state
|
||||
};
|
||||
te_object_method_register(&matrix_methods, TE_CLS_TYPE_MATRIX);
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(s_te_mat_obj->global_config);
|
||||
if (s_te_mat_obj->mutex != NULL) {
|
||||
vSemaphoreDelete(s_te_mat_obj->mutex);
|
||||
}
|
||||
TE_FREE_AND_NULL(s_te_mat_obj);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
void touch_matrix_uninstall(void)
|
||||
{
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
if (s_te_mat_obj == NULL) {
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return;
|
||||
}
|
||||
te_object_method_unregister(TE_CLS_TYPE_MATRIX);
|
||||
free(s_te_mat_obj->global_config);
|
||||
s_te_mat_obj->global_config = NULL;
|
||||
while (!SLIST_EMPTY(&s_te_mat_obj->handle_list)) {
|
||||
SLIST_FIRST(&s_te_mat_obj->handle_list);
|
||||
SLIST_REMOVE_HEAD(&s_te_mat_obj->handle_list, next);
|
||||
}
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
vSemaphoreDelete(s_te_mat_obj->mutex);
|
||||
free(s_te_mat_obj);
|
||||
s_te_mat_obj = NULL;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_create(const touch_matrix_config_t *matrix_config, touch_matrix_handle_t *matrix_handle)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL && matrix_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(matrix_config->x_channel_array != NULL &&
|
||||
matrix_config->y_channel_array != NULL &&
|
||||
matrix_config->x_sensitivity_array != NULL &&
|
||||
matrix_config->y_sensitivity_array != NULL &&
|
||||
matrix_config->x_channel_num > 1 &&
|
||||
matrix_config->x_channel_num < TOUCH_PAD_MAX &&
|
||||
matrix_config->y_channel_num > 1 &&
|
||||
matrix_config->y_channel_num < TOUCH_PAD_MAX,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(te_object_check_channel(matrix_config->x_channel_array, matrix_config->x_channel_num) == false &&
|
||||
te_object_check_channel(matrix_config->y_channel_array, matrix_config->y_channel_num) == false,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)calloc(1, sizeof(struct te_slider_s));
|
||||
TE_CHECK(te_matrix != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NO_MEM;
|
||||
te_matrix->config = (te_matrix_handle_config_t *)calloc(1, sizeof(te_matrix_handle_config_t));
|
||||
te_matrix->device = (te_dev_t **)calloc(matrix_config->x_channel_num + matrix_config->y_channel_num, sizeof(te_dev_t *));
|
||||
TE_CHECK_GOTO(te_matrix->config != NULL && te_matrix->device != NULL, cleanup);
|
||||
for (int idx = 0; idx < matrix_config->x_channel_num + matrix_config->y_channel_num; idx++) {
|
||||
te_matrix->device[idx] = (te_dev_t *)calloc(1, sizeof(te_dev_t));
|
||||
if (te_matrix->device[idx] == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/*< Initialize x-axis */
|
||||
ret = te_dev_init(&te_matrix->device[0], matrix_config->x_channel_num, TOUCH_ELEM_TYPE_MATRIX,
|
||||
matrix_config->x_channel_array, matrix_config->x_sensitivity_array,
|
||||
TE_DEFAULT_THRESHOLD_DIVIDER(s_te_mat_obj));
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
/*< Initialize y-axis */
|
||||
ret = te_dev_init(&te_matrix->device[matrix_config->x_channel_num], matrix_config->y_channel_num,
|
||||
TOUCH_ELEM_TYPE_MATRIX, matrix_config->y_channel_array, matrix_config->y_sensitivity_array,
|
||||
TE_DEFAULT_THRESHOLD_DIVIDER(s_te_mat_obj));
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
|
||||
te_matrix->config->event_mask = TOUCH_ELEM_EVENT_NONE;
|
||||
te_matrix->config->dispatch_method = TOUCH_ELEM_DISP_MAX;
|
||||
te_matrix->config->callback = NULL;
|
||||
te_matrix->config->arg = NULL;
|
||||
te_matrix->current_state = TE_STATE_IDLE;
|
||||
te_matrix->last_state = TE_STATE_IDLE;
|
||||
te_matrix->event = TOUCH_MATRIX_EVT_MAX;
|
||||
te_matrix->x_channel_num = matrix_config->x_channel_num;
|
||||
te_matrix->y_channel_num = matrix_config->y_channel_num;
|
||||
te_matrix->trigger_cnt = 0;
|
||||
te_matrix->trigger_thr = 0xffffffff;
|
||||
te_matrix->position.x_axis = TE_MAT_POS_MAX;
|
||||
te_matrix->position.y_axis = TE_MAT_POS_MAX;
|
||||
te_matrix->position.index = TE_MAT_POS_MAX;
|
||||
ret = matrix_object_add_instance(te_matrix);
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
*matrix_handle = (touch_elem_handle_t) te_matrix;
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(te_matrix->config);
|
||||
if (te_matrix->device != NULL) {
|
||||
for (int idx = 0; idx < matrix_config->x_channel_num + matrix_config->y_channel_num; idx++) {
|
||||
TE_FREE_AND_NULL(te_matrix->device[idx]);
|
||||
}
|
||||
free(te_matrix->device);
|
||||
te_matrix->device = NULL;
|
||||
}
|
||||
TE_FREE_AND_NULL(te_matrix);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_delete(touch_matrix_handle_t matrix_handle)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
/*< Release touch sensor application resource */
|
||||
esp_err_t ret = matrix_object_remove_instance(matrix_handle);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
|
||||
/*< Release touch sensor device resource */
|
||||
te_dev_deinit(te_matrix->device, te_matrix->x_channel_num + te_matrix->y_channel_num);
|
||||
for (int idx = 0; idx < te_matrix->x_channel_num + te_matrix->y_channel_num; idx++) {
|
||||
free(te_matrix->device[idx]);
|
||||
}
|
||||
free(te_matrix->config);
|
||||
free(te_matrix->device);
|
||||
free(te_matrix);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_set_dispatch_method(touch_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(dispatch_method >= TOUCH_ELEM_DISP_EVENT && dispatch_method <= TOUCH_ELEM_DISP_MAX, ESP_ERR_INVALID_ARG);
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
|
||||
te_matrix->config->dispatch_method = dispatch_method;
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_subscribe_event(touch_matrix_handle_t matrix_handle, uint32_t event_mask, void *arg)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
if (!(event_mask & TOUCH_ELEM_EVENT_ON_PRESS) && !(event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) &&
|
||||
!(event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) && !(event_mask & TOUCH_ELEM_EVENT_NONE)) {
|
||||
ESP_LOGE(TE_TAG, "Touch button only support TOUCH_ELEM_EVENT_ON_PRESS, "
|
||||
"TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_LONGPRESS event mask");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
|
||||
touch_matrix_set_longpress(matrix_handle, TE_DEFAULT_LONGPRESS_TIME(s_te_mat_obj)); //set the default time(1000ms) for long press
|
||||
}
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
|
||||
te_matrix->config->event_mask = event_mask;
|
||||
te_matrix->config->arg = arg;
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_set_callback(touch_matrix_handle_t matrix_handle, touch_matrix_callback_t matrix_callback)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(matrix_callback != NULL, ESP_ERR_INVALID_ARG);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
te_matrix->config->callback = matrix_callback;
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_matrix_set_longpress(touch_matrix_handle_t matrix_handle, uint32_t threshold_time)
|
||||
{
|
||||
TE_CHECK(s_te_mat_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(matrix_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(threshold_time > 0, ESP_ERR_INVALID_ARG);
|
||||
te_matrix_handle_t te_matrix = (te_matrix_handle_t)matrix_handle;
|
||||
touch_elem_dispatch_t dispatch_method = te_matrix->config->dispatch_method;
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
}
|
||||
uint8_t timer_period = te_get_timer_period();
|
||||
te_matrix->trigger_thr = threshold_time / timer_period;
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const touch_matrix_message_t* touch_matrix_get_message(const touch_elem_message_t* element_message)
|
||||
{
|
||||
return (touch_matrix_message_t*)&element_message->child_msg;
|
||||
_Static_assert(sizeof(element_message->child_msg) >= sizeof(touch_matrix_message_t), "Message size overflow");
|
||||
}
|
||||
|
||||
static bool matrix_object_check_channel(touch_pad_t channel_num)
|
||||
{
|
||||
te_matrix_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
|
||||
if (matrix_channel_check(item->matrix_handle, channel_num)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t matrix_object_set_threshold(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
te_matrix_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
|
||||
ret = matrix_set_threshold(item->matrix_handle);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void matrix_object_process_state(void)
|
||||
{
|
||||
te_matrix_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->matrix_handle)) {
|
||||
matrix_reset_state(item->matrix_handle);
|
||||
continue;
|
||||
}
|
||||
matrix_proc_state(item->matrix_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void matrix_object_update_state(touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_matrix_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->matrix_handle)) {
|
||||
continue;
|
||||
}
|
||||
matrix_update_state(item->matrix_handle, channel_num, channel_state);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t matrix_object_add_instance(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
te_matrix_handle_list_t *item = (te_matrix_handle_list_t *)calloc(1, sizeof(te_matrix_handle_list_t));
|
||||
TE_CHECK(item != NULL, ESP_ERR_NO_MEM);
|
||||
item->matrix_handle = matrix_handle;
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INSERT_HEAD(&s_te_mat_obj->handle_list, item, next);
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t matrix_object_remove_instance(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_NOT_FOUND;
|
||||
te_matrix_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_mat_obj->handle_list, next) {
|
||||
if (matrix_handle == item->matrix_handle) {
|
||||
xSemaphoreTake(s_te_mat_obj->mutex, portMAX_DELAY);
|
||||
SLIST_REMOVE(&s_te_mat_obj->handle_list, item, te_matrix_handle_list, next);
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
free(item);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool matrix_channel_check(te_matrix_handle_t matrix_handle, touch_pad_t channel_num)
|
||||
{
|
||||
te_dev_t *device;
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
device = matrix_handle->device[idx];
|
||||
if (device->channel == channel_num) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t matrix_set_threshold(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
ret |= te_dev_set_threshold(matrix_handle->device[idx]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void matrix_update_state(te_matrix_handle_t matrix_handle, touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_dev_t *device;
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
device = matrix_handle->device[idx];
|
||||
if (channel_num == device->channel) {
|
||||
device->state = channel_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void matrix_reset_state(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
matrix_handle->device[idx]->state = TE_STATE_IDLE;
|
||||
}
|
||||
matrix_handle->trigger_cnt = 0;
|
||||
matrix_handle->current_state = TE_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void matrix_event_give(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
touch_elem_message_t element_message;
|
||||
touch_matrix_message_t matrix_message = {
|
||||
.event = matrix_handle->event,
|
||||
.position = matrix_handle->position
|
||||
};
|
||||
element_message.handle = (touch_elem_handle_t)matrix_handle;
|
||||
element_message.element_type = TOUCH_ELEM_TYPE_MATRIX;
|
||||
element_message.arg = matrix_handle->config->arg;
|
||||
memcpy(element_message.child_msg, &matrix_message, sizeof(matrix_message));
|
||||
te_event_give(element_message);
|
||||
}
|
||||
|
||||
static inline void matrix_dispatch(te_matrix_handle_t matrix_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
matrix_event_give(matrix_handle); //Event queue
|
||||
} else if (dispatch_method == TOUCH_ELEM_DISP_CALLBACK) {
|
||||
touch_matrix_message_t matrix_info;
|
||||
matrix_info.event = matrix_handle->event;
|
||||
matrix_info.position = matrix_handle->position;
|
||||
void *arg = matrix_handle->config->arg;
|
||||
matrix_handle->config->callback(matrix_handle, matrix_info, arg); //Event callback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Scan the matrix channel
|
||||
*
|
||||
* This function will output the press position and release position info
|
||||
* so as to determine which operation(press/release) is happening, since there
|
||||
* will get the invalid state if user operates multi-points at the same time.
|
||||
*
|
||||
*/
|
||||
static void matrix_scan_axis(te_matrix_handle_t matrix_handle, touch_matrix_position_t *press_pos,
|
||||
uint8_t *press_cnt, touch_matrix_position_t *release_pos, uint8_t *release_cnt)
|
||||
{
|
||||
press_pos->x_axis = TE_MAT_POS_MAX;
|
||||
press_pos->y_axis = TE_MAT_POS_MAX;
|
||||
release_pos->x_axis = TE_MAT_POS_MAX;
|
||||
release_pos->y_axis = TE_MAT_POS_MAX;
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
te_dev_t *device = matrix_handle->device[idx];
|
||||
if (device->state == TE_STATE_PRESS) {
|
||||
if (idx < matrix_handle->x_channel_num) {
|
||||
press_pos->x_axis = idx; //Write down the x axis info
|
||||
} else {
|
||||
press_pos->y_axis = idx - matrix_handle->x_channel_num; //Write down the y axis info
|
||||
}
|
||||
(*press_cnt)++;
|
||||
} else if (device->state == TE_STATE_RELEASE) {
|
||||
if (idx < matrix_handle->x_channel_num) {
|
||||
release_pos->x_axis = idx;
|
||||
} else {
|
||||
release_pos->y_axis = idx - matrix_handle->x_channel_num;
|
||||
}
|
||||
(*release_cnt)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pre-check and fix
|
||||
*
|
||||
* This function will pre-check and fix the invalid state, preparing for the
|
||||
* next detection.
|
||||
*
|
||||
*/
|
||||
static void matrix_pre_fixed(te_matrix_handle_t matrix_handle, touch_matrix_position_t *press_pos,
|
||||
uint8_t press_cnt, touch_matrix_position_t *release_pos, uint8_t release_cnt)
|
||||
{
|
||||
te_dev_t *device;
|
||||
te_matrix_state_t last_state = matrix_handle->current_state;
|
||||
touch_matrix_position_t last_pos = {
|
||||
.x_axis = matrix_handle->position.x_axis,
|
||||
.y_axis = matrix_handle->position.y_axis
|
||||
};
|
||||
if (last_state == TE_STATE_IDLE) {
|
||||
if (release_cnt > 0) {
|
||||
/*< Release is not allowed while matrix is in IDLE state, */
|
||||
/*< if that happened, reset it from Release into IDLE. */
|
||||
for (int idx = 0; idx < matrix_handle->x_channel_num + matrix_handle->y_channel_num; idx++) {
|
||||
device = matrix_handle->device[idx];
|
||||
if (device->state != TE_STATE_RELEASE) {
|
||||
continue;
|
||||
}
|
||||
device->state = TE_STATE_IDLE; //Reset to IDLE
|
||||
}
|
||||
}
|
||||
} else if (last_state == TE_STATE_PRESS) {
|
||||
if (release_cnt > 0) {
|
||||
/*< Release position must be the same as the last Press position, */
|
||||
/*< if it is not, reset it into IDLE. */
|
||||
if (release_pos->x_axis != TE_MAT_POS_MAX && release_pos->x_axis != last_pos.x_axis) {
|
||||
device = matrix_handle->device[release_pos->x_axis];
|
||||
device->state = TE_STATE_IDLE;
|
||||
}
|
||||
if (release_pos->y_axis != TE_MAT_POS_MAX && release_pos->y_axis != last_pos.y_axis) {
|
||||
device = matrix_handle->device[release_pos->y_axis + matrix_handle->x_channel_num];
|
||||
device->state = TE_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
if (press_cnt > 2) { //TODO: remove or rewrite here
|
||||
/*< If the last state is Press and current press count more than 2, */
|
||||
/*< there must be multi-touch occurred, reset all of the channels */
|
||||
/*< into IDLE except the last position channels. */
|
||||
if (press_pos->x_axis != TE_MAT_POS_MAX && press_pos->x_axis != last_pos.x_axis) {
|
||||
device = matrix_handle->device[press_pos->x_axis];
|
||||
device->state = TE_STATE_IDLE;
|
||||
}
|
||||
if (press_pos->y_axis != TE_MAT_POS_MAX && press_pos->y_axis != last_pos.y_axis) {
|
||||
device = matrix_handle->device[press_pos->y_axis + matrix_handle->x_channel_num];
|
||||
device->state = TE_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: refactor this ugly implementation
|
||||
static esp_err_t matrix_get_axis_state(touch_matrix_position_t *press_pos, uint8_t press_cnt, touch_matrix_position_t *release_pos,
|
||||
uint8_t release_cnt, te_matrix_state_t *x_axis_state, te_matrix_state_t *y_axis_state)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (press_cnt >= 2) {
|
||||
if (press_pos->x_axis != TE_MAT_POS_MAX && press_pos->y_axis != TE_MAT_POS_MAX) {
|
||||
*x_axis_state = TE_STATE_PRESS;
|
||||
*y_axis_state = TE_STATE_PRESS;
|
||||
ret = ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
} else if (release_cnt >= 2) {
|
||||
if (release_pos->x_axis != TE_MAT_POS_MAX && release_pos->y_axis != TE_MAT_POS_MAX) {
|
||||
*x_axis_state = TE_STATE_RELEASE;
|
||||
*y_axis_state = TE_STATE_RELEASE;
|
||||
ret = ESP_OK;
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
} else {
|
||||
ret = ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Matrix button process
|
||||
*
|
||||
* This function will process the matrix button state and maintain a matrix FSM:
|
||||
* IDLE ----> Press ----> Release ----> IDLE
|
||||
*
|
||||
* The state transition procedure is as follow:
|
||||
* (channel state ----> x,y axis state ----> matrix button state)
|
||||
*
|
||||
* TODO: add state transition diagram
|
||||
*/
|
||||
static void matrix_proc_state(te_matrix_handle_t matrix_handle)
|
||||
{
|
||||
esp_err_t ret;
|
||||
uint8_t press_cnt = 0;
|
||||
uint8_t release_cnt = 0;
|
||||
touch_matrix_position_t press_pos;
|
||||
touch_matrix_position_t release_pos;
|
||||
te_matrix_state_t x_axis_state = TE_STATE_IDLE;
|
||||
te_matrix_state_t y_axis_state = TE_STATE_IDLE;
|
||||
uint32_t event_mask = matrix_handle->config->event_mask;
|
||||
touch_elem_dispatch_t dispatch_method = matrix_handle->config->dispatch_method;
|
||||
|
||||
BaseType_t mux_ret = xSemaphoreTake(s_te_mat_obj->mutex, 0);
|
||||
if (mux_ret != pdPASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: refactor those functions
|
||||
/*< Scan the state of all the matrix buttons channel */
|
||||
matrix_scan_axis(matrix_handle, &press_pos, &press_cnt, &release_pos, &release_cnt);
|
||||
|
||||
/*< Pre check and fixed the invalid state */
|
||||
matrix_pre_fixed(matrix_handle, &press_pos, press_cnt, &release_pos, release_cnt);
|
||||
|
||||
/*< Figure out x,y axis state and take the position */
|
||||
ret = matrix_get_axis_state(&press_pos, press_cnt, &release_pos, release_cnt, &x_axis_state, &y_axis_state);
|
||||
if (ret != ESP_OK) { //TODO: remove return
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_handle->current_state = matrix_get_state(x_axis_state, y_axis_state);
|
||||
|
||||
if (matrix_handle->current_state == TE_STATE_PRESS) {
|
||||
if (matrix_handle->last_state == TE_STATE_IDLE) { //IDLE ---> Press = On_Press
|
||||
matrix_update_position(matrix_handle, press_pos);
|
||||
ESP_LOGD(TE_DEBUG_TAG, "matrix press (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_PRESS) {
|
||||
matrix_handle->event = TOUCH_MATRIX_EVT_ON_PRESS;
|
||||
matrix_dispatch(matrix_handle, dispatch_method);
|
||||
}
|
||||
} else if (matrix_handle->last_state == TE_STATE_PRESS) { //Press ---> Press = On_LongPress
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_LONGPRESS) {
|
||||
if (++matrix_handle->trigger_cnt >= matrix_handle->trigger_thr) {
|
||||
ESP_LOGD(TE_DEBUG_TAG, "matrix longpress (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
|
||||
matrix_handle->event = TOUCH_MATRIX_EVT_ON_LONGPRESS;
|
||||
matrix_dispatch(matrix_handle, dispatch_method);
|
||||
matrix_handle->trigger_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (matrix_handle->current_state == TE_STATE_RELEASE) {
|
||||
if (matrix_handle->last_state == TE_STATE_PRESS) { //Press ---> Release = On_Release
|
||||
ESP_LOGD(TE_DEBUG_TAG, "matrix release (%d, %d)", matrix_handle->position.x_axis, matrix_handle->position.y_axis);
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) {
|
||||
matrix_handle->event = TOUCH_MATRIX_EVT_ON_RELEASE;
|
||||
matrix_dispatch(matrix_handle, dispatch_method);
|
||||
}
|
||||
} else if (matrix_handle->last_state == TE_STATE_RELEASE) { // Release ---> Release = On_IDLE (Not dispatch)
|
||||
matrix_reset_state(matrix_handle);
|
||||
}
|
||||
} else if (matrix_handle->current_state == TE_STATE_IDLE) {
|
||||
if (matrix_handle->last_state == TE_STATE_RELEASE) { //Release ---> IDLE = On_IDLE (Not dispatch)
|
||||
//Nothing
|
||||
} else if (matrix_handle->last_state == TE_STATE_IDLE) { //IDLE ---> IDLE = Running IDLE (Not dispatch)
|
||||
//Move Pre-fix into here
|
||||
}
|
||||
}
|
||||
matrix_handle->last_state = matrix_handle->current_state;
|
||||
xSemaphoreGive(s_te_mat_obj->mutex);
|
||||
}
|
||||
|
||||
static inline te_state_t matrix_get_state(te_matrix_state_t x_axis_state, te_matrix_state_t y_axis_state)
|
||||
{
|
||||
te_state_t matrix_state;
|
||||
if ((x_axis_state == TE_STATE_PRESS) && (y_axis_state == TE_STATE_PRESS)) {
|
||||
matrix_state = TE_STATE_PRESS;
|
||||
} else if ((x_axis_state == TE_STATE_RELEASE) && (y_axis_state == TE_STATE_RELEASE)) {
|
||||
matrix_state = TE_STATE_RELEASE;
|
||||
} else {
|
||||
matrix_state = TE_STATE_IDLE;
|
||||
}
|
||||
return matrix_state;
|
||||
}
|
||||
|
||||
static void matrix_update_position(te_matrix_handle_t matrix_handle, touch_matrix_position_t new_pos) {
|
||||
matrix_handle->position.x_axis = new_pos.x_axis;
|
||||
matrix_handle->position.y_axis = new_pos.y_axis;
|
||||
matrix_handle->position.index = matrix_handle->position.x_axis * matrix_handle->y_channel_num + matrix_handle->position.y_axis;
|
||||
}
|
661
components/touch_element/touch_slider.c
Normal file
661
components/touch_element/touch_slider.c
Normal file
@ -0,0 +1,661 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "touch_element/touch_element_private.h"
|
||||
|
||||
#define TE_SLD_DEFAULT_QTF_THR(obj) ((obj)->global_config->quantify_lower_threshold)
|
||||
#define TE_SLD_DEFAULT_POS_FILTER_FACTOR(obj) ((obj)->global_config->position_filter_factor)
|
||||
#define TE_SLD_DEFAULT_CALCULATE_CHANNEL(obj) ((obj)->global_config->calculate_channel_count)
|
||||
#define TE_SLD_DEFAULT_BCM_UPDATE_TIME(obj) ((obj)->global_config->benchmark_update_time)
|
||||
#define TE_SLD_DEFAULT_FILTER_RESET_TIME(obj) ((obj)->global_config->filter_reset_time)
|
||||
#define TE_SLD_DEFAULT_POS_FILTER_SIZE(obj) ((obj)->global_config->position_filter_size)
|
||||
|
||||
typedef struct te_slider_handle_list {
|
||||
te_slider_handle_t slider_handle; //Slider handle
|
||||
SLIST_ENTRY(te_slider_handle_list) next; //Slider handle list entry
|
||||
} te_slider_handle_list_t;
|
||||
|
||||
typedef struct {
|
||||
SLIST_HEAD(te_slider_handle_list_head, te_slider_handle_list) handle_list; //Slider handle (instance) list
|
||||
touch_slider_global_config_t *global_config; //Slider global configuration
|
||||
SemaphoreHandle_t mutex; //Slider object mutex
|
||||
} te_slider_obj_t;
|
||||
|
||||
te_slider_obj_t *s_te_sld_obj = NULL;
|
||||
/* ---------------------------------------- Slider handle(instance) methods ----------------------------------------- */
|
||||
static bool slider_channel_check(te_slider_handle_t slider_handle, touch_pad_t channel_num);
|
||||
static esp_err_t slider_set_threshold(te_slider_handle_t slider_handle);
|
||||
static inline te_state_t slider_get_state(te_dev_t **device, int device_num);
|
||||
static void slider_reset_state(te_slider_handle_t slider_handle);
|
||||
static void slider_update_position(te_slider_handle_t slider_handle);
|
||||
static void slider_reset_position(te_slider_handle_t slider_handle);
|
||||
static void slider_update_benchmark(te_slider_handle_t slider_handle);
|
||||
static void slider_update_state(te_slider_handle_t slider_handle, touch_pad_t channel_num, te_state_t channel_state);
|
||||
static void slider_proc_state(te_slider_handle_t slider_handle);
|
||||
static void slider_event_give(te_slider_handle_t slider_handle);
|
||||
static inline void slider_dispatch(te_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method);
|
||||
/* ------------------------------------------ Slider object(class) methods ------------------------------------------ */
|
||||
static esp_err_t slider_object_add_instance(te_slider_handle_t slider_handle);
|
||||
static esp_err_t slider_object_remove_instance(te_slider_handle_t slider_handle);
|
||||
static bool slider_object_check_channel(touch_pad_t channel_num);
|
||||
static esp_err_t slider_object_set_threshold(void);
|
||||
static void slider_object_process_state(void);
|
||||
static void slider_object_update_state(touch_pad_t channel_num, te_state_t channel_state);
|
||||
/* ------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
esp_err_t touch_slider_install(const touch_slider_global_config_t *global_config)
|
||||
{
|
||||
TE_CHECK(te_system_check_state() == true, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
//Fixme: Make it thread-safe
|
||||
s_te_sld_obj = (te_slider_obj_t *)calloc(1, sizeof(te_slider_obj_t));
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_NO_MEM);
|
||||
s_te_sld_obj->global_config = (touch_slider_global_config_t *)calloc(1, sizeof(touch_slider_global_config_t));
|
||||
s_te_sld_obj->mutex = xSemaphoreCreateMutex();
|
||||
TE_CHECK_GOTO(s_te_sld_obj->global_config != NULL && s_te_sld_obj->mutex != NULL, cleanup);
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INIT(&s_te_sld_obj->handle_list);
|
||||
memcpy(s_te_sld_obj->global_config, global_config, sizeof(touch_slider_global_config_t));
|
||||
te_object_methods_t slider_methods = {
|
||||
.handle = s_te_sld_obj,
|
||||
.check_channel = slider_object_check_channel,
|
||||
.set_threshold = slider_object_set_threshold,
|
||||
.process_state = slider_object_process_state,
|
||||
.update_state = slider_object_update_state
|
||||
};
|
||||
te_object_method_register(&slider_methods, TE_CLS_TYPE_SLIDER);
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(s_te_sld_obj->global_config);
|
||||
if (s_te_sld_obj->mutex != NULL) {
|
||||
vSemaphoreDelete(s_te_sld_obj->mutex);
|
||||
}
|
||||
TE_FREE_AND_NULL(s_te_sld_obj);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
void touch_slider_uninstall(void)
|
||||
{
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
if (s_te_sld_obj == NULL) {
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return;
|
||||
}
|
||||
te_object_method_unregister(TE_CLS_TYPE_SLIDER);
|
||||
free(s_te_sld_obj->global_config);
|
||||
s_te_sld_obj->global_config = NULL;
|
||||
while (!SLIST_EMPTY(&s_te_sld_obj->handle_list)) {
|
||||
SLIST_FIRST(&s_te_sld_obj->handle_list);
|
||||
SLIST_REMOVE_HEAD(&s_te_sld_obj->handle_list, next);
|
||||
}
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
vSemaphoreDelete(s_te_sld_obj->mutex);
|
||||
free(s_te_sld_obj);
|
||||
s_te_sld_obj = NULL;
|
||||
}
|
||||
|
||||
esp_err_t touch_slider_create(const touch_slider_config_t *slider_config, touch_slider_handle_t *slider_handle)
|
||||
{
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(slider_handle != NULL && slider_config != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(slider_config->channel_num > 2 &&
|
||||
slider_config->channel_num < TOUCH_PAD_MAX &&
|
||||
slider_config->channel_array != NULL &&
|
||||
slider_config->sensitivity_array != NULL &&
|
||||
slider_config->position_range > slider_config->channel_num,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(te_object_check_channel(slider_config->channel_array, slider_config->channel_num) == false,
|
||||
ESP_ERR_INVALID_ARG);
|
||||
te_slider_handle_t te_slider = (te_slider_handle_t)calloc(1, sizeof(struct te_slider_s));
|
||||
TE_CHECK(te_slider != NULL, ESP_ERR_NO_MEM);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NO_MEM;
|
||||
te_slider->config = (te_slider_handle_config_t *)calloc(1, sizeof(te_slider_handle_config_t));
|
||||
te_slider->pos_filter_window = calloc(TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj), sizeof(uint8_t));
|
||||
te_slider->device = (te_dev_t **)calloc(slider_config->channel_num, sizeof(te_dev_t *));
|
||||
te_slider->channel_bcm = (uint32_t *)calloc(slider_config->channel_num, sizeof(uint32_t));
|
||||
te_slider->quantify_signal_array = (float *)calloc(slider_config->channel_num, sizeof(float));
|
||||
TE_CHECK_GOTO(te_slider->config != NULL &&
|
||||
te_slider->pos_filter_window != NULL &&
|
||||
te_slider->device != NULL &&
|
||||
te_slider->channel_bcm &&
|
||||
te_slider->quantify_signal_array,
|
||||
cleanup);
|
||||
for (int idx = 0; idx < slider_config->channel_num; idx++) {
|
||||
te_slider->device[idx] = (te_dev_t *)calloc(1, sizeof(te_dev_t));
|
||||
if (te_slider->device[idx] == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
ret = te_dev_init(te_slider->device, slider_config->channel_num, TOUCH_ELEM_TYPE_SLIDER,
|
||||
slider_config->channel_array, slider_config->sensitivity_array,
|
||||
TE_DEFAULT_THRESHOLD_DIVIDER(s_te_sld_obj));
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
|
||||
te_slider->config->event_mask = TOUCH_ELEM_EVENT_NONE;
|
||||
te_slider->config->dispatch_method = TOUCH_ELEM_DISP_MAX;
|
||||
te_slider->config->callback = NULL;
|
||||
te_slider->config->arg = NULL;
|
||||
te_slider->channel_bcm_update_cnt = TE_SLD_DEFAULT_BCM_UPDATE_TIME(s_te_sld_obj); //update at first time
|
||||
te_slider->filter_reset_cnt = TE_SLD_DEFAULT_FILTER_RESET_TIME(s_te_sld_obj); //reset at first time
|
||||
te_slider->channel_sum = slider_config->channel_num;
|
||||
te_slider->position_range = slider_config->position_range;
|
||||
te_slider->position_scale = (float)(slider_config->position_range) / (slider_config->channel_num - 1);
|
||||
te_slider->current_state = TE_STATE_IDLE;
|
||||
te_slider->last_state = TE_STATE_IDLE;
|
||||
te_slider->event = TOUCH_SLIDER_EVT_MAX;
|
||||
te_slider->position = 0;
|
||||
te_slider->last_position = 0;
|
||||
te_slider->pos_window_idx = 0;
|
||||
te_slider->is_first_sample = true;
|
||||
ret = slider_object_add_instance(te_slider);
|
||||
TE_CHECK_GOTO(ret == ESP_OK, cleanup);
|
||||
*slider_handle = (touch_elem_handle_t)te_slider;
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
TE_FREE_AND_NULL(te_slider->config);
|
||||
TE_FREE_AND_NULL(te_slider->pos_filter_window);
|
||||
TE_FREE_AND_NULL(te_slider->channel_bcm);
|
||||
TE_FREE_AND_NULL(te_slider->quantify_signal_array);
|
||||
if (te_slider->device != NULL) {
|
||||
for (int idx = 0; idx < slider_config->channel_num; idx++) {
|
||||
TE_FREE_AND_NULL(te_slider->device[idx]);
|
||||
}
|
||||
free(te_slider->device);
|
||||
te_slider->device = NULL;
|
||||
}
|
||||
TE_FREE_AND_NULL(te_slider);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t touch_slider_delete(touch_slider_handle_t slider_handle)
|
||||
{
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
/*< Release touch sensor application resource */
|
||||
esp_err_t ret = slider_object_remove_instance(slider_handle);
|
||||
TE_CHECK(ret == ESP_OK, ret);
|
||||
te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
|
||||
/*< Release touch sensor device resource */
|
||||
te_dev_deinit(te_slider->device, te_slider->channel_sum);
|
||||
for (int idx = 0; idx < te_slider->channel_sum; idx++) {
|
||||
free(te_slider->device[idx]);
|
||||
}
|
||||
free(te_slider->config);
|
||||
free(te_slider->quantify_signal_array);
|
||||
free(te_slider->pos_filter_window);
|
||||
free(te_slider->channel_bcm);
|
||||
free(te_slider->device);
|
||||
free(te_slider);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_slider_set_dispatch_method(touch_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(dispatch_method >= TOUCH_ELEM_DISP_EVENT && dispatch_method <= TOUCH_ELEM_DISP_MAX, ESP_ERR_INVALID_ARG);
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
|
||||
te_slider->config->dispatch_method = dispatch_method;
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_slider_subscribe_event(touch_slider_handle_t slider_handle, uint32_t event_mask, void *arg)
|
||||
{
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
if (!(event_mask & TOUCH_ELEM_EVENT_ON_PRESS) && !(event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) &&
|
||||
!(event_mask & TOUCH_ELEM_EVENT_NONE) && !(event_mask & TOUCH_ELEM_EVENT_ON_CALCULATION)) {
|
||||
ESP_LOGE(TE_TAG, "Touch button only support TOUCH_ELEM_EVENT_ON_PRESS, "
|
||||
"TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_CALCULATION event mask");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
|
||||
te_slider->config->event_mask = event_mask;
|
||||
te_slider->config->arg = arg;
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t touch_slider_set_callback(touch_slider_handle_t slider_handle, touch_slider_callback_t slider_callback)
|
||||
{
|
||||
TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
|
||||
TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
|
||||
TE_CHECK(slider_callback != NULL, ESP_ERR_INVALID_ARG);
|
||||
te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
te_slider->config->callback = slider_callback;
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const touch_slider_message_t* touch_slider_get_message(const touch_elem_message_t* element_message)
|
||||
{
|
||||
return (touch_slider_message_t*)&element_message->child_msg;
|
||||
_Static_assert(sizeof(element_message->child_msg) >= sizeof(touch_slider_message_t), "Message size overflow");
|
||||
}
|
||||
|
||||
static bool slider_object_check_channel(touch_pad_t channel_num)
|
||||
{
|
||||
te_slider_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
|
||||
if (slider_channel_check(item->slider_handle, channel_num)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t slider_object_set_threshold(void)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
te_slider_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
|
||||
ret = slider_set_threshold(item->slider_handle);
|
||||
if (ret != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void slider_object_process_state(void)
|
||||
{
|
||||
te_slider_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->slider_handle)) {
|
||||
slider_reset_state(item->slider_handle);
|
||||
slider_reset_position(item->slider_handle);
|
||||
continue;
|
||||
}
|
||||
slider_proc_state(item->slider_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void slider_object_update_state(touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_slider_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
|
||||
if (waterproof_check_mask_handle(item->slider_handle)) {
|
||||
continue;
|
||||
}
|
||||
slider_update_state(item->slider_handle, channel_num, channel_state);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t slider_object_add_instance(te_slider_handle_t slider_handle)
|
||||
{
|
||||
te_slider_handle_list_t *item = (te_slider_handle_list_t *)calloc(1, sizeof(te_slider_handle_list_t));
|
||||
TE_CHECK(item != NULL, ESP_ERR_NO_MEM);
|
||||
item->slider_handle = slider_handle;
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
SLIST_INSERT_HEAD(&s_te_sld_obj->handle_list, item, next);
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t slider_object_remove_instance(te_slider_handle_t slider_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_NOT_FOUND;
|
||||
te_slider_handle_list_t *item;
|
||||
SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
|
||||
if (slider_handle == item->slider_handle) {
|
||||
xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
|
||||
SLIST_REMOVE(&s_te_sld_obj->handle_list, item, te_slider_handle_list, next);
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
free(item);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool slider_channel_check(te_slider_handle_t slider_handle, touch_pad_t channel_num)
|
||||
{
|
||||
te_dev_t *device;
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
device = slider_handle->device[idx];
|
||||
if (device->channel == channel_num) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static esp_err_t slider_set_threshold(te_slider_handle_t slider_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
ret |= te_dev_set_threshold(slider_handle->device[idx]);
|
||||
}
|
||||
slider_update_benchmark(slider_handle); //Update benchmark at startup
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void slider_update_benchmark(te_slider_handle_t slider_handle)
|
||||
{
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
uint32_t bcm_val;
|
||||
te_dev_t *device = slider_handle->device[idx];
|
||||
bcm_val = te_read_smooth_signal(device->channel);
|
||||
slider_handle->channel_bcm[idx] = bcm_val;
|
||||
}
|
||||
}
|
||||
|
||||
static void slider_update_state(te_slider_handle_t slider_handle, touch_pad_t channel_num, te_state_t channel_state)
|
||||
{
|
||||
te_dev_t *device;
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
device = slider_handle->device[idx];
|
||||
if (channel_num == device->channel) {
|
||||
device->state = channel_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void slider_reset_state(te_slider_handle_t slider_handle)
|
||||
{
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
slider_handle->device[idx]->state = TE_STATE_IDLE;
|
||||
}
|
||||
slider_handle->current_state = TE_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void slider_event_give(te_slider_handle_t slider_handle)
|
||||
{
|
||||
touch_elem_message_t element_message;
|
||||
touch_slider_message_t slider_message = {
|
||||
.event = slider_handle->event,
|
||||
.position = slider_handle->position
|
||||
};
|
||||
element_message.handle = (touch_elem_handle_t)slider_handle;
|
||||
element_message.element_type = TOUCH_ELEM_TYPE_SLIDER;
|
||||
element_message.arg = slider_handle->config->arg;
|
||||
memcpy(element_message.child_msg, &slider_message, sizeof(slider_message));
|
||||
te_event_give(element_message);
|
||||
}
|
||||
|
||||
static inline void slider_dispatch(te_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method)
|
||||
{
|
||||
if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
|
||||
slider_event_give(slider_handle); //Event queue
|
||||
} else if (dispatch_method == TOUCH_ELEM_DISP_CALLBACK) {
|
||||
touch_slider_message_t slider_info;
|
||||
slider_info.event = slider_handle->event;
|
||||
slider_info.position = slider_handle->position;
|
||||
void *arg = slider_handle->config->arg;
|
||||
slider_handle->config->callback(slider_handle, slider_info, arg); //Event callback
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Slider process
|
||||
*
|
||||
* This function will process the slider state and maintain a slider FSM:
|
||||
* IDLE ----> Press ----> Release ----> IDLE
|
||||
*
|
||||
* The state transition procedure is as follow:
|
||||
* (channel state ----> slider state)
|
||||
*
|
||||
* TODO: add state transition diagram
|
||||
*/
|
||||
static void slider_proc_state(te_slider_handle_t slider_handle)
|
||||
{
|
||||
uint32_t event_mask = slider_handle->config->event_mask;
|
||||
touch_elem_dispatch_t dispatch_method = slider_handle->config->dispatch_method;
|
||||
BaseType_t mux_ret = xSemaphoreTake(s_te_sld_obj->mutex, 0);
|
||||
if (mux_ret != pdPASS) {
|
||||
return;
|
||||
}
|
||||
|
||||
slider_handle->current_state = slider_get_state(slider_handle->device, slider_handle->channel_sum);
|
||||
|
||||
if (slider_handle->current_state == TE_STATE_PRESS) {
|
||||
slider_handle->channel_bcm_update_cnt = 0; // Reset benchmark update counter
|
||||
slider_update_position(slider_handle);
|
||||
if (slider_handle->last_state == TE_STATE_IDLE) { //IDLE ---> Press = On_Press
|
||||
ESP_LOGD(TE_DEBUG_TAG, "slider press");
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_PRESS) {
|
||||
slider_handle->event = TOUCH_SLIDER_EVT_ON_PRESS;
|
||||
slider_dispatch(slider_handle, dispatch_method);
|
||||
}
|
||||
} else if (slider_handle->last_state == TE_STATE_PRESS) { //Press ---> Press = On_Calculation
|
||||
ESP_LOGD(TE_DEBUG_TAG, "slider calculation");
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_CALCULATION) {
|
||||
slider_handle->event = TOUCH_SLIDER_EVT_ON_CALCULATION;
|
||||
slider_dispatch(slider_handle, dispatch_method);
|
||||
}
|
||||
}
|
||||
} else if (slider_handle->current_state == TE_STATE_RELEASE) {
|
||||
if (slider_handle->last_state == TE_STATE_PRESS) { //Press ---> Release = On_Release
|
||||
ESP_LOGD(TE_DEBUG_TAG, "slider release");
|
||||
if (event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) {
|
||||
slider_handle->event = TOUCH_SLIDER_EVT_ON_RELEASE;
|
||||
slider_dispatch(slider_handle, dispatch_method);
|
||||
}
|
||||
} else if (slider_handle->last_state == TE_STATE_RELEASE) { // Release ---> Release = On_IDLE (Not dispatch)
|
||||
slider_reset_state(slider_handle);//Reset the slider state for the next time touch action detection
|
||||
}
|
||||
} else if (slider_handle->current_state == TE_STATE_IDLE) {
|
||||
if (slider_handle->last_state == TE_STATE_RELEASE) { //Release ---> IDLE = On_IDLE (Not dispatch)
|
||||
//Nothing
|
||||
} else if (slider_handle->last_state == TE_STATE_IDLE) { //IDLE ---> IDLE = Running IDLE (Not dispatch)
|
||||
if (++slider_handle->channel_bcm_update_cnt >= TE_SLD_DEFAULT_BCM_UPDATE_TIME(s_te_sld_obj)) { //Update channel benchmark
|
||||
slider_handle->channel_bcm_update_cnt = 0;
|
||||
slider_update_benchmark(slider_handle);
|
||||
ESP_LOGD(TE_DEBUG_TAG, "slider bcm update");
|
||||
}
|
||||
if (++slider_handle->filter_reset_cnt >= TE_SLD_DEFAULT_FILTER_RESET_TIME(s_te_sld_obj)) {
|
||||
slider_reset_position(slider_handle); //Reset slider filter so as to speed up next time position calculation
|
||||
}
|
||||
}
|
||||
}
|
||||
slider_handle->last_state = slider_handle->current_state;
|
||||
xSemaphoreGive(s_te_sld_obj->mutex);
|
||||
}
|
||||
|
||||
static inline te_state_t slider_get_state(te_dev_t **device, int device_num)
|
||||
{
|
||||
/*< Scan the state of all the slider channel and calculate the number of them if the state is Press*/
|
||||
uint8_t press_cnt = 0;
|
||||
uint8_t idle_cnt = 0;
|
||||
for (int idx = 0; idx < device_num; idx++) { //Calculate how many channel is pressed
|
||||
if (device[idx]->state == TE_STATE_PRESS) {
|
||||
press_cnt++;
|
||||
} else if (device[idx]->state == TE_STATE_IDLE) {
|
||||
idle_cnt++;
|
||||
}
|
||||
}
|
||||
if (press_cnt > 0) {
|
||||
return TE_STATE_PRESS;
|
||||
} else if (idle_cnt == device_num) {
|
||||
return TE_STATE_IDLE;
|
||||
} else {
|
||||
return TE_STATE_RELEASE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Slider channel difference-rate re-quantization
|
||||
*
|
||||
* This function will re-quantifies the touch sensor slider channel difference-rate
|
||||
* so as to make the different size of touch pad in PCB has the same difference value
|
||||
*
|
||||
*/
|
||||
static inline void slider_quantify_signal(te_slider_handle_t slider_handle)
|
||||
{
|
||||
float weight_sum = 0;
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
te_dev_t *device = slider_handle->device[idx];
|
||||
weight_sum += device->sens;
|
||||
uint32_t current_signal = te_read_smooth_signal(device->channel);
|
||||
int ans = current_signal - slider_handle->channel_bcm[idx];
|
||||
float diff_rate = (float)ans / slider_handle->channel_bcm[idx];
|
||||
slider_handle->quantify_signal_array[idx] = diff_rate / device->sens;
|
||||
if (slider_handle->quantify_signal_array[idx] < TE_SLD_DEFAULT_QTF_THR(s_te_sld_obj)) {
|
||||
slider_handle->quantify_signal_array[idx] = 0;
|
||||
}
|
||||
}
|
||||
for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
|
||||
te_dev_t *device = slider_handle->device[idx];
|
||||
slider_handle->quantify_signal_array[idx] = slider_handle->quantify_signal_array[idx] * weight_sum / device->sens;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate max sum subarray
|
||||
*
|
||||
* This function will figure out the max sum subarray from the
|
||||
* input array, return the max sum and max sum start index
|
||||
*
|
||||
*/
|
||||
static inline float slider_search_max_subarray(const float *array, int array_size, int *max_array_idx)
|
||||
{
|
||||
*max_array_idx = 0;
|
||||
float max_array_sum = 0;
|
||||
float current_array_sum = 0;
|
||||
for (int idx = 0; idx <= (array_size - TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj)); idx++) {
|
||||
for (int x = idx; x < idx + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); x++) {
|
||||
current_array_sum += array[x];
|
||||
}
|
||||
if (max_array_sum < current_array_sum) {
|
||||
max_array_sum = current_array_sum;
|
||||
*max_array_idx = idx;
|
||||
}
|
||||
current_array_sum = 0;
|
||||
}
|
||||
return max_array_sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate zero number
|
||||
*
|
||||
* This function will figure out the number of non-zero items from
|
||||
* the subarray
|
||||
*/
|
||||
static inline uint8_t slider_get_non_zero_num(const float *array, uint8_t array_idx)
|
||||
{
|
||||
uint8_t zero_cnt = 0;
|
||||
for (int idx = array_idx; idx < array_idx + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); idx++) {
|
||||
zero_cnt += (array[idx] > 0) ? 1 : 0;
|
||||
}
|
||||
return zero_cnt;
|
||||
}
|
||||
|
||||
static inline uint32_t slider_calculate_position(te_slider_handle_t slider_handle, int subarray_index, float subarray_sum, int non_zero_num)
|
||||
{
|
||||
int range = slider_handle->position_range;
|
||||
int array_size = slider_handle->channel_sum;
|
||||
float scale = slider_handle->position_scale;
|
||||
const float *array = slider_handle->quantify_signal_array;
|
||||
uint32_t position = 0;
|
||||
if (non_zero_num == 0) {
|
||||
position = slider_handle->position;
|
||||
} else if (non_zero_num == 1) {
|
||||
for (int index = subarray_index; index < subarray_index + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); index++) {
|
||||
if (0 != array[index]) {
|
||||
if (index == array_size - 1) {
|
||||
position = range;
|
||||
} else {
|
||||
position = (uint32_t)((float)index * scale);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int idx = subarray_index; idx < subarray_index + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); idx++) {
|
||||
position += ((float)idx * array[idx]);
|
||||
}
|
||||
position = position * scale / subarray_sum;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
static uint32_t slider_filter_average(te_slider_handle_t slider_handle, uint32_t current_position)
|
||||
{
|
||||
uint32_t position_average = 0;
|
||||
if (slider_handle->is_first_sample) {
|
||||
for (int win_idx = 0; win_idx < TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj); win_idx++) {
|
||||
slider_handle->pos_filter_window[win_idx] = current_position; //Preload filter buffer
|
||||
}
|
||||
slider_handle->is_first_sample = false;
|
||||
} else {
|
||||
slider_handle->pos_filter_window[slider_handle->pos_window_idx++] = current_position; //Moving average filter
|
||||
if (slider_handle->pos_window_idx >= TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj)) {
|
||||
slider_handle->pos_window_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int win_idx = 0; win_idx < TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj); win_idx++) { //Moving average filter
|
||||
position_average += slider_handle->pos_filter_window[win_idx];
|
||||
}
|
||||
position_average = position_average / TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj) + 0.5;
|
||||
return position_average;
|
||||
}
|
||||
|
||||
static inline uint32_t slider_filter_iir(uint32_t in_now, uint32_t out_last, uint32_t k)
|
||||
{
|
||||
if (k == 0) {
|
||||
return in_now;
|
||||
} else {
|
||||
uint32_t out_now = (in_now + (k - 1) * out_last) / k;
|
||||
return out_now;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief touch sensor slider position update
|
||||
*
|
||||
* This function is the core algorithm about touch sensor slider
|
||||
* position update, mainly has several steps:
|
||||
* 1. Re-quantization
|
||||
* 2. Figure out changed channel
|
||||
* 3. Calculate position
|
||||
* 4. Filter
|
||||
*
|
||||
*/
|
||||
static void slider_update_position(te_slider_handle_t slider_handle)
|
||||
{
|
||||
int max_array_idx = 0;
|
||||
float max_array_sum;
|
||||
uint8_t non_zero_num;
|
||||
uint32_t current_position;
|
||||
|
||||
slider_quantify_signal(slider_handle);
|
||||
max_array_sum = slider_search_max_subarray(slider_handle->quantify_signal_array, slider_handle->channel_sum, &max_array_idx);
|
||||
non_zero_num = slider_get_non_zero_num(slider_handle->quantify_signal_array, max_array_idx);
|
||||
current_position = slider_calculate_position(slider_handle, max_array_idx, max_array_sum, non_zero_num);
|
||||
uint32_t position_average = slider_filter_average(slider_handle, current_position);
|
||||
slider_handle->last_position = slider_handle->last_position == 0 ? (position_average << 4) : slider_handle->last_position;
|
||||
slider_handle->last_position = slider_filter_iir((position_average << 4), slider_handle->last_position, TE_SLD_DEFAULT_POS_FILTER_FACTOR(s_te_sld_obj));
|
||||
slider_handle->position = ((slider_handle->last_position + 8) >> 4); //(x + 8) >> 4 ----> (x + 8) / 16 ----> x/16 + 0.5
|
||||
}
|
||||
|
||||
static void slider_reset_position(te_slider_handle_t slider_handle)
|
||||
{
|
||||
slider_handle->is_first_sample = true;
|
||||
slider_handle->last_position = 0;
|
||||
slider_handle->pos_window_idx = 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user