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:
Michael (XIAO Xufeng) 2021-01-18 19:58:14 +08:00
commit cf9ff5eccf
10 changed files with 3699 additions and 0 deletions

View 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()

View 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

View 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

View File

@ -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

View 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

View 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

View 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;
}

View 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);
}

View 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;
}

View 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;
}