1 // Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string.h>
16 #include "freertos/FreeRTOS.h"
17 #include "freertos/semphr.h"
18 #include "freertos/queue.h"
19 #include "esp_timer.h"
20 #include "esp_log.h"
21 #include "hal/touch_sensor_hal.h"  //TODO: remove hal
22 #include "touch_element/touch_element_private.h"
23 
24 #define TE_CLASS_ITEM(cls, cls_type, cls_item)  ((&((cls)[cls_type]))->cls_item)
25 
26 #define TE_CLASS_FOREACH(cls_var, cls_start, cls_end)                         \
27     for ((cls_var) = (cls_start);                                             \
28         (cls_var) < (cls_end);                                                \
29         (cls_var)++)
30 
31 #define TE_CLS_METHODS_INITIALIZER(cls, cls_start, cls_end)  do {             \
32     typeof(cls_start) cls_method;                                             \
33     TE_CLASS_FOREACH(cls_method, cls_start, cls_end) {                        \
34         TE_CLASS_ITEM(cls, cls_method, handle) = NULL;                        \
35     }                                                                         \
36 } while (0)
37 
38 #define TE_CLASS_FOREACH_CHECK_CHANNEL(cls, cls_start, cls_end, channel) ({   \
39     bool ret = false;                                                         \
40     typeof(cls_start) cls_method;                                             \
41     TE_CLASS_FOREACH(cls_method, cls_start, cls_end) {                        \
42         if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) {                 \
43             ret |= TE_CLASS_ITEM(cls, cls_method, check_channel(channel));    \
44         }                                                                     \
45     }                                                                         \
46     ret;                                                                      \
47 })
48 
49 #define TE_CLASS_FOREACH_SET_THRESHOLD(cls, cls_start, cls_end) do {          \
50         typeof(cls_start) cls_method;                                         \
51         TE_CLASS_FOREACH(cls_method, cls_start, cls_end) {                    \
52             if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) {             \
53                 TE_CLASS_ITEM(cls, cls_method, set_threshold());              \
54             }                                                                 \
55         }                                                                     \
56 } while (0)
57 
58 #define TE_CLASS_FOREACH_PROCESS_STATE(cls, cls_start, cls_end) do {          \
59         typeof(cls_start) cls_method;                                         \
60         TE_CLASS_FOREACH(cls_method, cls_start, cls_end) {                    \
61             if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) {             \
62                 TE_CLASS_ITEM(cls, cls_method, process_state());              \
63             }                                                                 \
64         }                                                                     \
65 } while (0)
66 
67 #define TE_CLASS_FOREACH_UPDATE_STATE(cls, cls_start, cls_end, channel, state) do {\
68         typeof(cls_start) cls_method;                                         \
69         TE_CLASS_FOREACH(cls_method, cls_start, cls_end) {                    \
70             if (TE_CLASS_ITEM(cls, cls_method, handle) != NULL) {             \
71                 TE_CLASS_ITEM(cls, cls_method, update_state(channel, state)); \
72             }                                                                 \
73         }                                                                     \
74 } while (0)
75 
76 #define TE_PROCESSING_PERIOD(obj)                 ((obj)->global_config->software.processing_period)
77 #define TE_WATERPROOF_DIVIDER(obj)                ((obj)->global_config->software.waterproof_threshold_divider)
78 
79 typedef enum {
80     TE_INTR_PRESS = 0,          //Touch sensor press interrupt(TOUCH_PAD_INTR_MASK_ACTIVE)
81     TE_INTR_RELEASE,            //Touch sensor release interrupt(TOUCH_PAD_INTR_MASK_INACTIVE)
82     TE_INTR_TIMEOUT,            //Touch sensor scan timeout interrupt(TOUCH_PAD_INTR_MASK_TIMEOUT)
83     TE_INTR_SCAN_DONE,          //Touch sensor scan done interrupt(TOUCH_PAD_INTR_MASK_SCAN_DONE), now just use for setting threshold
84     TE_INTR_MAX
85 } te_intr_t;
86 
87 typedef struct {
88     te_intr_t intr_type;                //channel interrupt type
89     te_state_t channel_state;           //channel state
90     touch_pad_t channel_num;            //channel index
91 } te_intr_msg_t;
92 
93 typedef struct {
94     te_object_methods_t object_methods[TE_CLS_TYPE_MAX];    //Class(object) methods
95     touch_elem_global_config_t *global_config;              //Global initialization
96     te_waterproof_handle_t waterproof_handle;               //Waterproof configuration
97     esp_timer_handle_t proc_timer;                          //Processing timer handle
98     QueueHandle_t event_msg_queue;                          //Application event message queue (for user)
99     QueueHandle_t intr_msg_queue;                           //Interrupt message (for internal)
100     SemaphoreHandle_t mutex;                                //Global resource mutex
101     bool is_set_threshold;                                  //Threshold configuration state bit
102     uint32_t denoise_channel_raw;                           //De-noise channel(TO) raw signal
103 } te_obj_t;
104 
105 static te_obj_t *s_te_obj = NULL;
106 
107 /**
108  * Internal de-noise channel(Touch channel 0) equivalent capacitance table, depends on hardware design
109  *
110  * Units: pF
111  */
112 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};
113 
114 /**
115  * Waterproof shield channel(Touch channel 14) equivalent capacitance table, depends on hardware design
116  *
117  * Units: pF
118  */
119 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};
120 
121 /* -------------------------------------------- Internal shared methods --------------------------------------------- */
122 /*                                 -------------------------------------------------                                  */
123 /* ------------------------------------------------- System methods ------------------------------------------------- */
124 static esp_err_t te_hw_init(const touch_elem_hw_config_t *hardware_init);
125 static esp_err_t te_sw_init(const touch_elem_sw_config_t *software_init);
126 static inline float te_get_internal_equ_cap(touch_pad_denoise_cap_t denoise_level);
127 static float te_channel_get_equ_cap(touch_pad_t channel_num);
128 static uint32_t te_read_raw_signal(touch_pad_t channel_num);
129 static void te_intr_cb(void *arg);
130 static void te_proc_timer_cb(void *arg);
131 static inline esp_err_t te_object_set_threshold(void);
132 static inline void te_object_process_state(void);
133 static inline void te_object_update_state(te_intr_msg_t te_intr_msg);
134 /* ----------------------------------------------- Waterproof methods ----------------------------------------------- */
135 static inline bool waterproof_check_state(void);
136 static inline bool waterproof_shield_check_state(void);
137 static inline bool waterproof_guard_check_state(void);
138 static bool waterproof_channel_check(touch_pad_t channel_num);
139 static void waterproof_guard_set_threshold(void);
140 static void waterproof_guard_update_state(touch_pad_t current_channel, te_state_t current_state);
141 static touch_pad_shield_driver_t waterproof_get_shield_level(touch_pad_t guard_channel_num);
142 /* ------------------------------------------------------------------------------------------------------------------ */
143 
touch_element_install(const touch_elem_global_config_t * global_config)144 esp_err_t touch_element_install(const touch_elem_global_config_t *global_config)
145 {
146     TE_CHECK(s_te_obj == NULL, ESP_ERR_INVALID_STATE);
147     TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
148 
149     s_te_obj = (te_obj_t *)calloc(1, sizeof(te_obj_t));
150     TE_CHECK(s_te_obj != NULL, ESP_ERR_NO_MEM);
151 
152     esp_err_t ret = ESP_ERR_NO_MEM;
153     s_te_obj->global_config = (touch_elem_global_config_t *)calloc(1, sizeof(touch_elem_global_config_t));
154     s_te_obj->mutex = xSemaphoreCreateMutex();
155     TE_CHECK_GOTO(s_te_obj->global_config != NULL && s_te_obj->mutex != NULL, cleanup);
156     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
157     TE_CLS_METHODS_INITIALIZER(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
158     ret = te_hw_init(&global_config->hardware);
159     if (ret != ESP_OK) {
160         abort();
161     }
162     ret = te_sw_init(&global_config->software);
163     if (ret != ESP_OK) {
164         xSemaphoreGive(s_te_obj->mutex);
165         goto cleanup;
166     }
167     xSemaphoreGive(s_te_obj->mutex);
168     return ESP_OK;
169 
170 cleanup:
171     TE_FREE_AND_NULL(s_te_obj->global_config);
172     if (s_te_obj->mutex != NULL) {
173         vSemaphoreDelete(s_te_obj->mutex);
174     }
175     TE_FREE_AND_NULL(s_te_obj);
176     return ret;
177 }
178 
touch_element_start(void)179 esp_err_t touch_element_start(void)
180 {
181     TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
182     esp_err_t ret;
183     uint16_t inited_channel_mask;
184     do {
185         xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
186         ret = touch_pad_get_channel_mask(&inited_channel_mask);
187         if (inited_channel_mask == 0x0) {
188             ESP_LOGE(TE_TAG, "Can not find Touch Sensor channel that has been initialized");
189             ret = ESP_ERR_INVALID_STATE;
190             break;
191         }
192         if (ret != ESP_OK) {
193             break;
194         }
195         s_te_obj->is_set_threshold = false;  //Threshold configuration will be set on touch sense start
196         ret = esp_timer_start_periodic(s_te_obj->proc_timer, TE_PROCESSING_PERIOD(s_te_obj) * 1000);
197         if (ret != ESP_OK) {
198             break;
199         }
200         ret = touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_SCAN_DONE); //Use scan done interrupt to set threshold
201         if (ret != ESP_OK) {
202             break;
203         }
204         ret = touch_pad_fsm_start();
205         if (ret != ESP_OK) {
206             break;
207         }
208         xQueueReset(s_te_obj->event_msg_queue);
209         xQueueReset(s_te_obj->intr_msg_queue);
210         xSemaphoreGive(s_te_obj->mutex);
211         return ESP_OK;
212     } while (0);
213 
214     ESP_LOGE(TE_TAG, "Touch interface start failed:(%s)", __FUNCTION__ );
215     xSemaphoreGive(s_te_obj->mutex);
216     return ret;
217 }
218 
touch_element_stop(void)219 esp_err_t touch_element_stop(void)
220 {
221     TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
222     esp_err_t ret;
223     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
224     ret = touch_pad_fsm_stop();
225     if (ret != ESP_OK) {
226         return ret;
227     }
228     ret = touch_pad_intr_disable(TOUCH_PAD_INTR_MASK_SCAN_DONE);
229     if (ret != ESP_OK) {
230         return ret;
231     }
232     ret = esp_timer_stop(s_te_obj->proc_timer);
233     if (ret != ESP_OK) {
234         return ret;
235     }
236     xSemaphoreGive(s_te_obj->mutex);
237     return ESP_OK;
238 }
239 
240 //TODO: add a new api that output system's run-time state
241 
touch_element_uninstall(void)242 void touch_element_uninstall(void)
243 {
244     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
245     if (s_te_obj == NULL) {
246         xSemaphoreGive(s_te_obj->mutex);
247         return;
248     }
249     esp_err_t ret;
250     ret = touch_pad_deinit();
251     if (ret != ESP_OK) {
252         abort();
253     }
254     ret = esp_timer_delete(s_te_obj->proc_timer);
255     if (ret != ESP_OK) {
256         abort();
257     }
258     ret = touch_pad_intr_disable(TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE | TOUCH_PAD_INTR_MASK_TIMEOUT);
259     if (ret != ESP_OK) {
260         abort();
261     }
262     ret = touch_pad_isr_deregister(te_intr_cb, NULL);
263     if (ret != ESP_OK) {
264         abort();
265     }
266     vQueueDelete(s_te_obj->event_msg_queue);
267     vQueueDelete(s_te_obj->intr_msg_queue);
268     xSemaphoreGive(s_te_obj->mutex);
269     vSemaphoreDelete(s_te_obj->mutex);
270     free(s_te_obj->global_config);
271     s_te_obj->global_config = NULL;
272     free(s_te_obj);
273     s_te_obj = NULL;
274 }
275 
touch_element_message_receive(touch_elem_message_t * element_message,uint32_t ticks_to_wait)276 esp_err_t touch_element_message_receive(touch_elem_message_t *element_message, uint32_t ticks_to_wait)
277 {
278     //TODO: Use the generic data struct to refactor this api
279     TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
280     TE_CHECK(element_message != NULL, ESP_ERR_INVALID_ARG);
281     TE_CHECK(s_te_obj->event_msg_queue != NULL, ESP_ERR_INVALID_STATE);
282     int ret = xQueueReceive(s_te_obj->event_msg_queue, element_message, ticks_to_wait);
283     return (ret == pdTRUE) ? ESP_OK : ESP_ERR_TIMEOUT;
284 }
285 
te_read_raw_signal(touch_pad_t channel_num)286 static uint32_t te_read_raw_signal(touch_pad_t channel_num)
287 {
288     uint32_t raw_signal = 0;
289     touch_pad_sleep_channel_t sleep_channel_info;
290     touch_pad_sleep_channel_get_info(&sleep_channel_info);
291     if (channel_num != sleep_channel_info.touch_num) {
292         touch_pad_read_raw_data(channel_num, &raw_signal);
293     } else {
294         touch_pad_sleep_channel_read_data(channel_num, &raw_signal);
295     }
296     return raw_signal;
297 }
298 
te_read_smooth_signal(touch_pad_t channel_num)299 uint32_t te_read_smooth_signal(touch_pad_t channel_num)
300 {
301     uint32_t smooth_signal = 0;
302     touch_pad_sleep_channel_t sleep_channel_info;
303     touch_pad_sleep_channel_get_info(&sleep_channel_info);
304     if (channel_num != sleep_channel_info.touch_num) {
305         touch_pad_filter_read_smooth(channel_num, &smooth_signal);
306     } else {
307         touch_pad_sleep_channel_read_smooth(channel_num, &smooth_signal);
308     }
309     return smooth_signal;
310 }
311 
te_event_give(touch_elem_message_t te_message)312 esp_err_t te_event_give(touch_elem_message_t te_message)
313 {
314     //TODO: add queue overwrite here when the queue is full
315     int ret = xQueueSend(s_te_obj->event_msg_queue, &te_message, 0);
316     if (ret != pdTRUE) {
317         ESP_LOGE(TE_TAG, "event queue send failed, event message queue is full");
318         return ESP_ERR_TIMEOUT;
319     }
320     return ESP_OK;
321 }
322 
323 /**
324  * @brief Touch sensor interrupt service routine
325  *
326  * This function is touch sensor ISR, all the touch
327  * sensor channel state will be updated here.
328  */
te_intr_cb(void * arg)329 static void te_intr_cb(void *arg)
330 {
331     TE_UNUSED(arg);
332     static int scan_done_cnt = 0;
333     int task_awoken = pdFALSE;
334     te_intr_msg_t te_intr_msg;
335     /*< Figure out which touch sensor channel is triggered and the trigger type */
336     uint32_t intr_mask = touch_pad_read_intr_status_mask();
337     te_intr_msg.channel_num = touch_pad_get_current_meas_channel();
338     if (intr_mask == 0x0) {  //For dummy interrupt
339         return;
340     }
341     bool need_send_queue = true;
342     if (intr_mask & TOUCH_PAD_INTR_MASK_ACTIVE) {
343         te_intr_msg.channel_state = TE_STATE_PRESS;
344         te_intr_msg.intr_type = TE_INTR_PRESS;
345     } else if (intr_mask & TOUCH_PAD_INTR_MASK_INACTIVE) {
346         te_intr_msg.channel_state = TE_STATE_RELEASE;
347         te_intr_msg.intr_type = TE_INTR_RELEASE;
348     } else if (intr_mask & TOUCH_PAD_INTR_MASK_TIMEOUT) {
349         te_intr_msg.channel_state = TE_STATE_IDLE;
350         te_intr_msg.intr_type = TE_INTR_TIMEOUT;
351     } else if (intr_mask & TOUCH_PAD_INTR_MASK_SCAN_DONE) {
352         te_intr_msg.channel_state = TE_STATE_IDLE;
353         te_intr_msg.intr_type = TE_INTR_SCAN_DONE;
354         need_send_queue = false;
355         /*< Due to a hardware issue, all of the data read operation(read raw, read smooth, read benchmark) */
356         /*< must be after the second times of measure_done interrupt. */
357         if (++scan_done_cnt >= 5) {
358             touch_hal_intr_disable(TOUCH_PAD_INTR_MASK_SCAN_DONE);  //TODO: remove hal
359             scan_done_cnt = 0;
360             need_send_queue = true;
361         }
362         /*< De-noise channel signal must be read at the time between SCAN_DONE and next measurement beginning(sleep)!!! */
363         touch_pad_denoise_read_data(&s_te_obj->denoise_channel_raw); //Update de-noise signal
364     } else {
365         te_intr_msg.intr_type = TE_INTR_MAX;  // Unknown Exception
366     }
367     if (need_send_queue) {
368         xQueueSendFromISR(s_te_obj->intr_msg_queue, &te_intr_msg, &task_awoken);
369     }
370     if (task_awoken == pdTRUE) {
371         portYIELD_FROM_ISR();
372     }
373 }
374 
375 /**
376  * @brief esp-timer callback routine
377  *
378  * This function is an esp-timer daemon routine, all the touch sensor
379  * application(button, slider, etc...) will be processed in here.
380  *
381  */
te_proc_timer_cb(void * arg)382 static void te_proc_timer_cb(void *arg)
383 {
384     TE_UNUSED(arg);
385     te_intr_msg_t te_intr_msg;
386     te_intr_msg.intr_type = TE_INTR_MAX;
387     BaseType_t ret = xSemaphoreTake(s_te_obj->mutex, 0);
388     if (ret != pdPASS) {
389         return;
390     }
391     ret = xQueueReceive(s_te_obj->intr_msg_queue, &te_intr_msg, 0);
392     if (ret == pdPASS) {
393         if (te_intr_msg.intr_type == TE_INTR_PRESS || te_intr_msg.intr_type == TE_INTR_RELEASE) {
394             te_object_update_state(te_intr_msg);
395         } else if (te_intr_msg.intr_type == TE_INTR_SCAN_DONE) {
396             if (s_te_obj->is_set_threshold != true) {
397                 s_te_obj->is_set_threshold = true;
398                 te_object_set_threshold();  //TODO: add set threshold error processing
399                 ESP_LOGD(TE_DEBUG_TAG, "Set threshold");
400             }
401             if (waterproof_check_state()) {
402                 te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
403                 if (waterproof_handle->is_shield_level_set != true) {
404                     waterproof_handle->is_shield_level_set = true;
405                     touch_pad_waterproof_t wp_conf;
406                     wp_conf.shield_driver = waterproof_get_shield_level(waterproof_handle->shield_channel);
407                     wp_conf.guard_ring_pad = (waterproof_guard_check_state() ? waterproof_handle->guard_device->channel : TOUCH_WATERPROOF_GUARD_NOUSE);
408                     touch_pad_waterproof_set_config(&wp_conf);
409                     touch_pad_waterproof_enable();
410                     ESP_LOGD(TE_DEBUG_TAG, "Set waterproof shield level");
411                 }
412             }
413             ESP_LOGD(TE_DEBUG_TAG, "read denoise channel %d", s_te_obj->denoise_channel_raw);
414         } else if (te_intr_msg.intr_type == TE_INTR_TIMEOUT) { //Timeout processing
415             touch_pad_timeout_resume();
416         }
417     }
418     te_object_process_state();
419     xSemaphoreGive(s_te_obj->mutex);
420 }
421 
te_object_method_register(te_object_methods_t * object_methods,te_class_type_t object_type)422 void te_object_method_register(te_object_methods_t *object_methods, te_class_type_t object_type)
423 {
424     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
425     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, handle) = object_methods->handle;
426     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, check_channel) = object_methods->check_channel;
427     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, set_threshold) = object_methods->set_threshold;
428     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, process_state) = object_methods->process_state;
429     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, update_state) = object_methods->update_state;
430     xSemaphoreGive(s_te_obj->mutex);
431 }
432 
te_object_method_unregister(te_class_type_t object_type)433 void te_object_method_unregister(te_class_type_t object_type)
434 {
435     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
436     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, handle) = NULL;
437     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, check_channel) = NULL;
438     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, set_threshold) = NULL;
439     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, process_state) = NULL;
440     TE_CLASS_ITEM(s_te_obj->object_methods, object_type, update_state) = NULL;
441     xSemaphoreGive(s_te_obj->mutex);
442 }
443 
444 /**
445  * @brief Touch Sense channel check
446  *
447  * This function will check the input channel whether is
448  * associated with the Touch Sense Object
449  *
450  * @return
451  *      - true:  Channel has been initialized, pls adjust the input channel
452  *      - false: Channel has not been initialized, pass
453  */
te_object_check_channel(const touch_pad_t * channel_array,uint8_t channel_sum)454 bool te_object_check_channel(const touch_pad_t *channel_array, uint8_t channel_sum)
455 {
456     touch_pad_t current_channel;
457     for (int idx = 0; idx < channel_sum; idx++) {
458         current_channel = channel_array[idx];
459         if (waterproof_channel_check(current_channel)) {
460             goto INITIALIZED;
461         }
462         if (TE_CLASS_FOREACH_CHECK_CHANNEL(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX, current_channel)) {
463             goto INITIALIZED;
464         }
465     }
466     return false;
467 
468 INITIALIZED:
469     ESP_LOGE(TE_TAG, "Current channel [%d] has been initialized:(%s)", current_channel, __FUNCTION__ );
470     return true;
471 }
472 
473 
te_object_set_threshold(void)474 static inline esp_err_t te_object_set_threshold(void)
475 {
476     if (waterproof_guard_check_state() == true) {   //TODO: add to object methods
477         waterproof_guard_set_threshold();
478     }
479 
480     TE_CLASS_FOREACH_SET_THRESHOLD(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
481     return ESP_OK;
482 }
483 
te_object_process_state(void)484 static inline void te_object_process_state(void)
485 {
486     TE_CLASS_FOREACH_PROCESS_STATE(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX);
487 }
488 
te_object_update_state(te_intr_msg_t te_intr_msg)489 static inline void te_object_update_state(te_intr_msg_t te_intr_msg)
490 {
491     if (waterproof_guard_check_state()) {
492         waterproof_guard_update_state(te_intr_msg.channel_num, te_intr_msg.channel_state);
493     }
494     TE_CLASS_FOREACH_UPDATE_STATE(s_te_obj->object_methods, TE_CLS_TYPE_BUTTON, TE_CLS_TYPE_MAX,
495                                   te_intr_msg.channel_num, te_intr_msg.channel_state);
496 }
497 
te_get_timer_period(void)498 uint8_t te_get_timer_period(void)
499 {
500     return (TE_PROCESSING_PERIOD(s_te_obj));
501 }
502 
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)503 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)
504 {
505     for (int idx = 0; idx < device_num; idx++) {
506         device[idx]->channel = channel[idx];
507         device[idx]->sens = sens[idx] * divider;
508         device[idx]->type = type;
509         device[idx]->state = TE_STATE_IDLE;
510         esp_err_t ret = touch_pad_config(device[idx]->channel);
511         TE_CHECK(ret == ESP_OK, ret);
512     }
513     return ESP_OK;
514 }
515 
te_dev_deinit(te_dev_t ** device,uint8_t device_num)516 void te_dev_deinit(te_dev_t **device, uint8_t device_num)
517 {
518     for (int idx = 0; idx < device_num; idx++) {
519         touch_pad_clear_channel_mask((1UL << device[idx]->channel));
520     }
521 }
522 
te_dev_set_threshold(te_dev_t * device)523 esp_err_t te_dev_set_threshold(te_dev_t *device)
524 {
525     uint32_t smo_val = te_read_smooth_signal(device->channel);
526     esp_err_t ret = touch_pad_set_thresh(device->channel, device->sens * smo_val);
527     ESP_LOGD(TE_DEBUG_TAG, "channel: %d, smo_val: %d", device->channel, smo_val);
528     return ret;
529 }
530 
531 /**
532  * This function returns the s_te_obj whether is initialized
533  *
534  * @return
535  *      - true: initialized
536  *      - false: not initialized
537  */
te_system_check_state(void)538 bool te_system_check_state(void)
539 {
540     return (s_te_obj != NULL);
541 }
542 
te_get_internal_equ_cap(touch_pad_denoise_cap_t denoise_level)543 static inline float te_get_internal_equ_cap(touch_pad_denoise_cap_t denoise_level)
544 {
545     return denoise_channel_equ_cap[denoise_level];
546 }
547 
548 /**
549  * @brief   Get channel equivalent capacitance
550  *
551  * This function calculates the equivalent capacitance of input channel by
552  * using the Touch channel 0 equivalent capacitance. The formula is:
553  *
554  *                      Raw_N / Raw_0 = Cap_N / Cap_0
555  *
556  * Note that Raw_N and Raw_0 are the raw data of touch channel N and touch channel 0 respectively,
557  * Cap_N and Cap_0 are the equivalent capacitance of touch channel N and touch channel 0.
558  *
559  * @param[in] channel_num   Input touch sensor channel
560  *
561  * @note The unit is pF
562  *
563  * @return  Specified channel equivalent capacitance.
564  */
te_channel_get_equ_cap(touch_pad_t channel_num)565 static float te_channel_get_equ_cap(touch_pad_t channel_num)
566 {
567     //Fixme: add a mutex in here and prevent the system call this function
568     TE_CHECK(channel_num > TOUCH_PAD_NUM0 && channel_num < TOUCH_PAD_MAX, 0);
569     uint32_t tn_raw, t0_raw;
570     float tn_ref_cap, t0_ref_cap;
571     touch_pad_denoise_t denoise_channel_conf;
572     touch_pad_denoise_get_config(&denoise_channel_conf);
573     tn_raw = te_read_raw_signal(channel_num);
574     t0_raw = s_te_obj->denoise_channel_raw;
575     t0_ref_cap = te_get_internal_equ_cap(denoise_channel_conf.cap_level);
576     if (t0_raw == 0) {
577         return 0;
578     }
579     tn_ref_cap = (float)tn_raw / t0_raw * t0_ref_cap;
580     return tn_ref_cap;
581 }
582 
583 /**
584  * @brief  Touch sensor driver default init [ESP32S2 only]
585  *
586  * 1. Channel measure time: Raw_value / RTC_FAST_CLK  ==> Raw_value / 8000 000
587  * 2. Channel sleep time: TOUCH_PAD_SLEEP_CYCLE_DEFAULT / RTC_SLOW_CLK ==> 0xf / 90 000(default) = 0.16ms
588  * 3. Channel charge voltage threshold(upper/lower):  2.7V upper voltage, 0.5V lower voltage, 0.5V attenuation voltage
589  * 4. IDLE channel processing:  Connecting to GND
590  * 5. Interrupt type:  ACTIVE, INACTIVE, TIMEOUT
591  *
592  * @note A touch sensor channel will spend the time = measure time + sleep time, RTC_FAST_CLK is 8M
593  *
594  */
te_hw_init(const touch_elem_hw_config_t * hardware_init)595 static esp_err_t te_hw_init(const touch_elem_hw_config_t *hardware_init)
596 {
597     esp_err_t ret;
598     ret = touch_pad_init();
599     TE_CHECK(ret == ESP_OK, ret);
600     ret = touch_pad_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
601     TE_CHECK(ret == ESP_OK, ret);
602     ret = touch_pad_set_meas_time(hardware_init->sleep_cycle, hardware_init->sample_count);
603     TE_CHECK(ret == ESP_OK, ret);
604     ret = touch_pad_set_voltage(hardware_init->upper_voltage, hardware_init->lower_voltage,
605                                  hardware_init->voltage_attenuation);
606     TE_CHECK(ret == ESP_OK, ret);
607     ret = touch_pad_set_idle_channel_connect(hardware_init->suspend_channel_polarity);
608     TE_CHECK(ret == ESP_OK, ret);
609     ret = touch_pad_isr_register(te_intr_cb, NULL,
610                                  TOUCH_PAD_INTR_MASK_ACTIVE | TOUCH_PAD_INTR_MASK_INACTIVE |
611                                  TOUCH_PAD_INTR_MASK_TIMEOUT | TOUCH_PAD_INTR_MASK_SCAN_DONE);
612     TE_CHECK(ret == ESP_OK, ret);
613     ret = touch_pad_intr_enable(TOUCH_PAD_INTR_MASK_ACTIVE |
614                                  TOUCH_PAD_INTR_MASK_INACTIVE | TOUCH_PAD_INTR_MASK_TIMEOUT);
615     TE_CHECK(ret == ESP_OK, ret);
616 
617     /*< Internal de-noise configuration */
618     touch_pad_denoise_t denoise_config;
619     denoise_config.grade = hardware_init->denoise_level;
620     denoise_config.cap_level = hardware_init->denoise_equivalent_cap;
621     ret = touch_pad_denoise_set_config(&denoise_config);
622     TE_CHECK(ret == ESP_OK, ret);
623     ret = touch_pad_denoise_enable();
624     TE_CHECK(ret == ESP_OK, ret);
625 
626     /*< benchmark filter configuration */
627     touch_filter_config_t filter_config;
628     filter_config.smh_lvl = hardware_init->smooth_filter_mode;
629     filter_config.mode = hardware_init->benchmark_filter_mode;
630     filter_config.debounce_cnt = hardware_init->benchmark_debounce_count;
631     filter_config.noise_thr = hardware_init->benchmark_calibration_threshold;
632     filter_config.jitter_step = hardware_init->benchmark_jitter_step;
633     ret = touch_pad_filter_set_config(&filter_config);
634     TE_CHECK(ret == ESP_OK, ret);
635     ret = touch_pad_filter_enable();
636     TE_CHECK(ret == ESP_OK, ret);
637     memcpy(&s_te_obj->global_config->hardware, hardware_init, sizeof(touch_elem_hw_config_t));
638     return ESP_OK;
639 }
640 
te_sw_init(const touch_elem_sw_config_t * software_init)641 static esp_err_t te_sw_init(const touch_elem_sw_config_t *software_init)
642 {
643     TE_CHECK(software_init->processing_period > 1, ESP_ERR_INVALID_ARG);
644     TE_CHECK(software_init->waterproof_threshold_divider > 0, ESP_ERR_INVALID_ARG);
645     TE_CHECK(software_init->intr_message_size >= (TOUCH_PAD_MAX - 1), ESP_ERR_INVALID_ARG);
646     TE_CHECK(software_init->event_message_size > 0, ESP_ERR_INVALID_ARG);
647 
648     esp_err_t ret = ESP_ERR_NO_MEM;
649     s_te_obj->intr_msg_queue = xQueueCreate(software_init->intr_message_size, sizeof(te_intr_msg_t));
650     s_te_obj->event_msg_queue = xQueueCreate(software_init->event_message_size, sizeof(touch_elem_message_t));
651     TE_CHECK_GOTO(s_te_obj->event_msg_queue != NULL && s_te_obj->intr_msg_queue != NULL, cleanup);
652 
653     const esp_timer_create_args_t te_proc_timer_args = {
654         .name = "te_proc_timer_cb",
655         .arg  = NULL,
656         .callback = &te_proc_timer_cb
657     };
658     ret = esp_timer_create(&te_proc_timer_args, &s_te_obj->proc_timer);
659     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
660     memcpy(&s_te_obj->global_config->software, software_init, sizeof(touch_elem_sw_config_t));
661     return ret;
662 
663 cleanup:
664     if (s_te_obj->event_msg_queue != NULL) {
665         vQueueDelete(s_te_obj->event_msg_queue);
666     }
667     if (s_te_obj->intr_msg_queue != NULL) {
668         vQueueDelete(s_te_obj->intr_msg_queue);
669     }
670     return ret;
671 }
672 
673 //TODO: add waterproof guard-lock hysteresis
touch_element_waterproof_install(const touch_elem_waterproof_config_t * waterproof_config)674 esp_err_t touch_element_waterproof_install(const touch_elem_waterproof_config_t *waterproof_config)
675 {
676     TE_CHECK(s_te_obj != NULL, ESP_ERR_INVALID_STATE);
677     TE_CHECK(waterproof_config != NULL, ESP_ERR_INVALID_ARG);
678     TE_CHECK(waterproof_config->guard_channel >= TOUCH_PAD_NUM0 &&
679              waterproof_config->guard_channel < TOUCH_PAD_MAX,
680              ESP_ERR_INVALID_ARG);
681     te_waterproof_handle_t waterproof_handle = (te_waterproof_handle_t)calloc(1, sizeof(struct te_waterproof_s));
682     TE_CHECK(waterproof_handle != NULL, ESP_ERR_NO_MEM);
683     waterproof_handle->shield_channel = TOUCH_PAD_NUM14;
684 
685     esp_err_t ret;
686     if (waterproof_config->guard_channel != TOUCH_WATERPROOF_GUARD_NOUSE) { //Use guard sensor
687         if (te_object_check_channel(&waterproof_config->guard_channel, 1)) {
688             ret = ESP_ERR_INVALID_ARG;
689             goto cleanup;
690         }
691         ret = ESP_ERR_NO_MEM;
692         waterproof_handle->mask_handle = (touch_elem_handle_t *) calloc(TOUCH_PAD_MAX, sizeof(touch_elem_handle_t));
693         waterproof_handle->guard_device = (te_dev_t *)calloc(1, sizeof(te_dev_t));
694         TE_CHECK_GOTO(waterproof_handle->mask_handle != NULL && waterproof_handle->guard_device, cleanup);
695 
696         ret = te_dev_init(&waterproof_handle->guard_device, 1, TOUCH_ELEM_TYPE_BUTTON,
697                           &waterproof_config->guard_channel, &waterproof_config->guard_sensitivity,
698                           TE_WATERPROOF_DIVIDER(s_te_obj));
699         TE_CHECK_GOTO(ret == ESP_OK, cleanup);
700         waterproof_handle->guard_device->state = TE_STATE_RELEASE;
701         for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
702             waterproof_handle->mask_handle[idx] = NULL;
703         }
704     } else {  //No use waterproof guard sensor
705         waterproof_handle->guard_device = NULL;
706         waterproof_handle->mask_handle = NULL;
707     }
708     waterproof_handle->is_shield_level_set = 0; //Set a state bit so as to configure the shield level at the run-time
709     touch_pad_waterproof_t wp_conf;
710     wp_conf.shield_driver = TOUCH_PAD_SHIELD_DRV_L0; //Set a default shield level
711     wp_conf.guard_ring_pad = waterproof_config->guard_channel;
712     ret = touch_pad_waterproof_set_config(&wp_conf);
713     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
714     ret = touch_pad_waterproof_enable();
715     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
716     s_te_obj->waterproof_handle = waterproof_handle;  //Fixme: add mutex
717     return ESP_OK;
718 
719 cleanup:
720     TE_FREE_AND_NULL(waterproof_handle->mask_handle);
721     TE_FREE_AND_NULL(waterproof_handle->guard_device);
722     TE_FREE_AND_NULL(waterproof_handle);
723     return ret;
724 }
725 
touch_element_waterproof_add(touch_elem_handle_t element_handle)726 esp_err_t touch_element_waterproof_add(touch_elem_handle_t element_handle)
727 {
728     TE_CHECK(s_te_obj->waterproof_handle != NULL, ESP_ERR_INVALID_STATE);
729     TE_CHECK(s_te_obj->waterproof_handle->guard_device != NULL, ESP_ERR_INVALID_STATE);
730     TE_CHECK(element_handle != NULL, ESP_ERR_INVALID_ARG);
731     te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
732     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
733     for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
734         if (waterproof_handle->mask_handle[idx] == NULL) {
735             waterproof_handle->mask_handle[idx] = element_handle;
736             break;
737         }
738     }
739     xSemaphoreGive(s_te_obj->mutex);
740     return ESP_OK;
741 }
742 
touch_element_waterproof_remove(touch_elem_handle_t element_handle)743 esp_err_t touch_element_waterproof_remove(touch_elem_handle_t element_handle)
744 {
745     TE_CHECK(s_te_obj->waterproof_handle != NULL, ESP_ERR_INVALID_STATE);
746     TE_CHECK(element_handle != NULL, ESP_ERR_INVALID_ARG);
747     esp_err_t ret = ESP_ERR_NOT_FOUND;
748     te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
749     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
750     for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
751         if (waterproof_handle->mask_handle[idx] == element_handle) {
752             waterproof_handle->mask_handle[idx] = NULL;
753             ret = ESP_OK;
754             break;
755         }
756     }
757     xSemaphoreGive(s_te_obj->mutex);
758     return ret;
759 }
760 
touch_element_waterproof_uninstall(void)761 void touch_element_waterproof_uninstall(void)
762 {
763     xSemaphoreTake(s_te_obj->mutex, portMAX_DELAY);
764     touch_pad_waterproof_disable();
765     free(s_te_obj->waterproof_handle->guard_device);
766     free(s_te_obj->waterproof_handle->mask_handle);
767     free(s_te_obj->waterproof_handle);
768     s_te_obj->waterproof_handle = NULL;
769     xSemaphoreGive(s_te_obj->mutex);
770 }
771 
waterproof_get_shield_level(touch_pad_t guard_channel_num)772 static touch_pad_shield_driver_t waterproof_get_shield_level(touch_pad_t guard_channel_num)
773 {
774     touch_pad_shield_driver_t shield_level = TOUCH_PAD_SHIELD_DRV_L7;
775     float guard_ref_cap = te_channel_get_equ_cap(guard_channel_num);
776     for (int level = 0; level < TOUCH_PAD_SHIELD_DRV_MAX; level++) {
777         if (guard_ref_cap <= shield_channel_ref_cap[level]) {
778             shield_level = (touch_pad_shield_driver_t)level;
779             break;
780         }
781     }
782     return shield_level;
783 }
784 
785 /**
786  * This function returns the waterproof_handle whether is initialized
787  *
788  * @return
789  *      - true: initialized
790  *      - false: not initialized
791  */
waterproof_check_state(void)792 static inline bool waterproof_check_state(void)
793 {
794     return (s_te_obj->waterproof_handle != NULL);
795 }
796 
waterproof_shield_check_state(void)797 static inline bool waterproof_shield_check_state(void)
798 {
799     return waterproof_check_state();  //Driver does not allow to disable shield sensor after waterproof enabling
800 }
801 
waterproof_guard_check_state(void)802 static inline bool waterproof_guard_check_state(void)
803 {
804     if (waterproof_check_state() == false) {
805         return false;
806     }
807     if (s_te_obj->waterproof_handle->guard_device == NULL || s_te_obj->waterproof_handle->mask_handle == NULL) {
808         return false;
809     }
810     return true;
811 }
812 
waterproof_channel_check(touch_pad_t channel_num)813 static bool waterproof_channel_check(touch_pad_t channel_num)
814 {
815     if (waterproof_check_state() == false) {
816         return false;
817     }
818     te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
819     if (waterproof_shield_check_state()) {
820         if (channel_num == waterproof_handle->shield_channel) {
821             ESP_LOGE(TE_TAG, "TOUCH_PAD_NUM%d has been used for waterproof shield channel,"
822                      " please change the touch sensor channel or disable waterproof", channel_num);
823             return true;
824         }
825     }
826     if (waterproof_guard_check_state()) {
827         if (channel_num == waterproof_handle->guard_device->channel) {
828             ESP_LOGE(TE_TAG, "TOUCH_PAD_NUM%d has been used for waterproof guard channel,"
829                      " please change the touch sensor channel or disable waterproof", channel_num);
830             return true;
831         }
832     }
833     return false;
834 }
835 
waterproof_guard_set_threshold(void)836 static void waterproof_guard_set_threshold(void)
837 {
838     if (waterproof_check_state() == false) {
839         return;
840     }
841     if (waterproof_guard_check_state() == false) {
842         return;
843     }
844     te_dev_set_threshold(s_te_obj->waterproof_handle->guard_device);
845 }
846 
847 /**
848  *  This function will figure out current handle whether is a masked channel
849  *  while guard channel is triggered.
850  *
851  * @param[in] te_handle     Touch sensor application handle
852  * @return
853  *      - true  current handle is a masked channel
854  *      - false current handle is not a masked channel
855  */
waterproof_check_mask_handle(touch_elem_handle_t te_handle)856 bool waterproof_check_mask_handle(touch_elem_handle_t te_handle)
857 {
858     if (waterproof_check_state() == false) {
859         return false;
860     }
861     if (waterproof_guard_check_state() == false) {
862         return false;
863     }
864     te_waterproof_handle_t waterproof_handle = s_te_obj->waterproof_handle;
865     bool ret = false;
866     if (waterproof_handle->guard_device->state == TE_STATE_PRESS) {
867         for (int idx = 0; idx < TOUCH_PAD_MAX; idx++) {
868             if (waterproof_handle->mask_handle[idx] == NULL) {
869                 break;
870             }
871             if (waterproof_handle->mask_handle[idx] == te_handle) {
872                 ret = true;
873             }
874         }
875     }
876     return ret;
877 }
878 
waterproof_guard_update_state(touch_pad_t current_channel,te_state_t current_state)879 static void waterproof_guard_update_state(touch_pad_t current_channel, te_state_t current_state)
880 {
881     te_dev_t *guard_device = s_te_obj->waterproof_handle->guard_device;
882     if (current_channel == guard_device->channel) {
883         guard_device->state = current_state;
884     }
885     ESP_LOGD(TE_DEBUG_TAG, "waterproof guard state update  %d", guard_device->state);
886 }
887