1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 //     http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #include <string.h>
14 #include <sys/queue.h>
15 #include "freertos/FreeRTOS.h"
16 #include "freertos/semphr.h"
17 #include "esp_log.h"
18 #include "touch_element/touch_element_private.h"
19 
20 #define TE_SLD_DEFAULT_QTF_THR(obj)                       ((obj)->global_config->quantify_lower_threshold)
21 #define TE_SLD_DEFAULT_POS_FILTER_FACTOR(obj)             ((obj)->global_config->position_filter_factor)
22 #define TE_SLD_DEFAULT_CALCULATE_CHANNEL(obj)             ((obj)->global_config->calculate_channel_count)
23 #define TE_SLD_DEFAULT_BCM_UPDATE_TIME(obj)               ((obj)->global_config->benchmark_update_time)
24 #define TE_SLD_DEFAULT_FILTER_RESET_TIME(obj)             ((obj)->global_config->filter_reset_time)
25 #define TE_SLD_DEFAULT_POS_FILTER_SIZE(obj)               ((obj)->global_config->position_filter_size)
26 
27 typedef struct te_slider_handle_list {
28     te_slider_handle_t slider_handle;           //Slider handle
29     SLIST_ENTRY(te_slider_handle_list) next;    //Slider handle list entry
30 } te_slider_handle_list_t;
31 
32 typedef struct {
33     SLIST_HEAD(te_slider_handle_list_head, te_slider_handle_list) handle_list;    //Slider handle (instance) list
34     touch_slider_global_config_t *global_config;                                  //Slider global configuration
35     SemaphoreHandle_t mutex;                                                      //Slider object mutex
36 } te_slider_obj_t;
37 
38 te_slider_obj_t *s_te_sld_obj = NULL;
39 /* ---------------------------------------- Slider handle(instance) methods ----------------------------------------- */
40 static bool slider_channel_check(te_slider_handle_t slider_handle, touch_pad_t channel_num);
41 static esp_err_t slider_set_threshold(te_slider_handle_t slider_handle);
42 static inline te_state_t slider_get_state(te_dev_t **device, int device_num);
43 static void slider_reset_state(te_slider_handle_t slider_handle);
44 static void slider_update_position(te_slider_handle_t slider_handle);
45 static void slider_reset_position(te_slider_handle_t slider_handle);
46 static void slider_update_benchmark(te_slider_handle_t slider_handle);
47 static void slider_update_state(te_slider_handle_t slider_handle, touch_pad_t channel_num, te_state_t channel_state);
48 static void slider_proc_state(te_slider_handle_t slider_handle);
49 static void slider_event_give(te_slider_handle_t slider_handle);
50 static inline void slider_dispatch(te_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method);
51 /* ------------------------------------------ Slider object(class) methods ------------------------------------------ */
52 static esp_err_t slider_object_add_instance(te_slider_handle_t slider_handle);
53 static esp_err_t slider_object_remove_instance(te_slider_handle_t slider_handle);
54 static bool slider_object_check_channel(touch_pad_t channel_num);
55 static esp_err_t slider_object_set_threshold(void);
56 static void slider_object_process_state(void);
57 static void slider_object_update_state(touch_pad_t channel_num, te_state_t channel_state);
58 /* ------------------------------------------------------------------------------------------------------------------ */
59 
touch_slider_install(const touch_slider_global_config_t * global_config)60 esp_err_t touch_slider_install(const touch_slider_global_config_t *global_config)
61 {
62     TE_CHECK(te_system_check_state() == true, ESP_ERR_INVALID_STATE);
63     TE_CHECK(global_config != NULL, ESP_ERR_INVALID_ARG);
64     //Fixme: Make it thread-safe
65     s_te_sld_obj = (te_slider_obj_t *)calloc(1, sizeof(te_slider_obj_t));
66     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_NO_MEM);
67     s_te_sld_obj->global_config = (touch_slider_global_config_t *)calloc(1, sizeof(touch_slider_global_config_t));
68     s_te_sld_obj->mutex = xSemaphoreCreateMutex();
69     TE_CHECK_GOTO(s_te_sld_obj->global_config != NULL && s_te_sld_obj->mutex != NULL, cleanup);
70     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
71     SLIST_INIT(&s_te_sld_obj->handle_list);
72     memcpy(s_te_sld_obj->global_config, global_config, sizeof(touch_slider_global_config_t));
73     te_object_methods_t slider_methods = {
74         .handle = s_te_sld_obj,
75         .check_channel = slider_object_check_channel,
76         .set_threshold = slider_object_set_threshold,
77         .process_state = slider_object_process_state,
78         .update_state = slider_object_update_state
79     };
80     te_object_method_register(&slider_methods, TE_CLS_TYPE_SLIDER);
81     xSemaphoreGive(s_te_sld_obj->mutex);
82     return ESP_OK;
83 
84 cleanup:
85     TE_FREE_AND_NULL(s_te_sld_obj->global_config);
86     if (s_te_sld_obj->mutex != NULL) {
87         vSemaphoreDelete(s_te_sld_obj->mutex);
88     }
89     TE_FREE_AND_NULL(s_te_sld_obj);
90     return ESP_ERR_NO_MEM;
91 }
92 
touch_slider_uninstall(void)93 void touch_slider_uninstall(void)
94 {
95     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
96     if (s_te_sld_obj == NULL) {
97         xSemaphoreGive(s_te_sld_obj->mutex);
98         return;
99     }
100     te_object_method_unregister(TE_CLS_TYPE_SLIDER);
101     free(s_te_sld_obj->global_config);
102     s_te_sld_obj->global_config = NULL;
103     while (!SLIST_EMPTY(&s_te_sld_obj->handle_list)) {
104         SLIST_FIRST(&s_te_sld_obj->handle_list);
105         SLIST_REMOVE_HEAD(&s_te_sld_obj->handle_list, next);
106     }
107     xSemaphoreGive(s_te_sld_obj->mutex);
108     vSemaphoreDelete(s_te_sld_obj->mutex);
109     free(s_te_sld_obj);
110     s_te_sld_obj = NULL;
111 }
112 
touch_slider_create(const touch_slider_config_t * slider_config,touch_slider_handle_t * slider_handle)113 esp_err_t touch_slider_create(const touch_slider_config_t *slider_config, touch_slider_handle_t *slider_handle)
114 {
115     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
116     TE_CHECK(slider_handle != NULL && slider_config != NULL, ESP_ERR_INVALID_ARG);
117     TE_CHECK(slider_config->channel_num > 2 &&
118              slider_config->channel_num < TOUCH_PAD_MAX &&
119              slider_config->channel_array != NULL &&
120              slider_config->sensitivity_array != NULL &&
121              slider_config->position_range > slider_config->channel_num,
122              ESP_ERR_INVALID_ARG);
123     TE_CHECK(te_object_check_channel(slider_config->channel_array, slider_config->channel_num) == false,
124              ESP_ERR_INVALID_ARG);
125     te_slider_handle_t te_slider = (te_slider_handle_t)calloc(1, sizeof(struct te_slider_s));
126     TE_CHECK(te_slider != NULL, ESP_ERR_NO_MEM);
127 
128     esp_err_t ret = ESP_ERR_NO_MEM;
129     te_slider->config = (te_slider_handle_config_t *)calloc(1, sizeof(te_slider_handle_config_t));
130     te_slider->pos_filter_window = calloc(TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj), sizeof(uint8_t));
131     te_slider->device = (te_dev_t **)calloc(slider_config->channel_num, sizeof(te_dev_t *));
132     te_slider->channel_bcm = (uint32_t *)calloc(slider_config->channel_num, sizeof(uint32_t));
133     te_slider->quantify_signal_array = (float *)calloc(slider_config->channel_num, sizeof(float));
134     TE_CHECK_GOTO(te_slider->config != NULL &&
135                   te_slider->pos_filter_window != NULL &&
136                   te_slider->device != NULL &&
137                   te_slider->channel_bcm &&
138                   te_slider->quantify_signal_array,
139                   cleanup);
140     for (int idx = 0; idx < slider_config->channel_num; idx++) {
141         te_slider->device[idx] = (te_dev_t *)calloc(1, sizeof(te_dev_t));
142         if (te_slider->device[idx] == NULL) {
143             ret = ESP_ERR_NO_MEM;
144             goto cleanup;
145         }
146     }
147     ret = te_dev_init(te_slider->device, slider_config->channel_num, TOUCH_ELEM_TYPE_SLIDER,
148                       slider_config->channel_array, slider_config->sensitivity_array,
149                       TE_DEFAULT_THRESHOLD_DIVIDER(s_te_sld_obj));
150     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
151 
152     te_slider->config->event_mask = TOUCH_ELEM_EVENT_NONE;
153     te_slider->config->dispatch_method = TOUCH_ELEM_DISP_MAX;
154     te_slider->config->callback = NULL;
155     te_slider->config->arg = NULL;
156     te_slider->channel_bcm_update_cnt = TE_SLD_DEFAULT_BCM_UPDATE_TIME(s_te_sld_obj); //update at first time
157     te_slider->filter_reset_cnt = TE_SLD_DEFAULT_FILTER_RESET_TIME(s_te_sld_obj);     //reset at first time
158     te_slider->channel_sum = slider_config->channel_num;
159     te_slider->position_range = slider_config->position_range;
160     te_slider->position_scale = (float)(slider_config->position_range) / (slider_config->channel_num - 1);
161     te_slider->current_state = TE_STATE_IDLE;
162     te_slider->last_state = TE_STATE_IDLE;
163     te_slider->event = TOUCH_SLIDER_EVT_MAX;
164     te_slider->position = 0;
165     te_slider->last_position = 0;
166     te_slider->pos_window_idx = 0;
167     te_slider->is_first_sample = true;
168     ret = slider_object_add_instance(te_slider);
169     TE_CHECK_GOTO(ret == ESP_OK, cleanup);
170     *slider_handle = (touch_elem_handle_t)te_slider;
171     return ESP_OK;
172 
173 cleanup:
174     TE_FREE_AND_NULL(te_slider->config);
175     TE_FREE_AND_NULL(te_slider->pos_filter_window);
176     TE_FREE_AND_NULL(te_slider->channel_bcm);
177     TE_FREE_AND_NULL(te_slider->quantify_signal_array);
178     if (te_slider->device != NULL) {
179         for (int idx = 0; idx < slider_config->channel_num; idx++) {
180             TE_FREE_AND_NULL(te_slider->device[idx]);
181         }
182         free(te_slider->device);
183         te_slider->device = NULL;
184     }
185     TE_FREE_AND_NULL(te_slider);
186     return ret;
187 }
188 
touch_slider_delete(touch_slider_handle_t slider_handle)189 esp_err_t touch_slider_delete(touch_slider_handle_t slider_handle)
190 {
191     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
192     TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
193     /*< Release touch sensor application resource */
194     esp_err_t ret = slider_object_remove_instance(slider_handle);
195     TE_CHECK(ret == ESP_OK, ret);
196     te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
197     /*< Release touch sensor device resource */
198     te_dev_deinit(te_slider->device, te_slider->channel_sum);
199     for (int idx = 0; idx < te_slider->channel_sum; idx++) {
200         free(te_slider->device[idx]);
201     }
202     free(te_slider->config);
203     free(te_slider->quantify_signal_array);
204     free(te_slider->pos_filter_window);
205     free(te_slider->channel_bcm);
206     free(te_slider->device);
207     free(te_slider);
208     return ESP_OK;
209 }
210 
touch_slider_set_dispatch_method(touch_slider_handle_t slider_handle,touch_elem_dispatch_t dispatch_method)211 esp_err_t touch_slider_set_dispatch_method(touch_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method)
212 {
213     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
214     TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
215     TE_CHECK(dispatch_method >= TOUCH_ELEM_DISP_EVENT && dispatch_method <= TOUCH_ELEM_DISP_MAX, ESP_ERR_INVALID_ARG);
216     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
217     te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
218     te_slider->config->dispatch_method = dispatch_method;
219     xSemaphoreGive(s_te_sld_obj->mutex);
220     return ESP_OK;
221 }
222 
touch_slider_subscribe_event(touch_slider_handle_t slider_handle,uint32_t event_mask,void * arg)223 esp_err_t touch_slider_subscribe_event(touch_slider_handle_t slider_handle, uint32_t event_mask, void *arg)
224 {
225     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
226     TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
227     if (!(event_mask & TOUCH_ELEM_EVENT_ON_PRESS) && !(event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) &&
228         !(event_mask & TOUCH_ELEM_EVENT_NONE) && !(event_mask & TOUCH_ELEM_EVENT_ON_CALCULATION)) {
229         ESP_LOGE(TE_TAG, "Touch button only support TOUCH_ELEM_EVENT_ON_PRESS, "
230                          "TOUCH_ELEM_EVENT_ON_RELEASE, TOUCH_ELEM_EVENT_ON_CALCULATION event mask");
231         return ESP_ERR_INVALID_ARG;
232     }
233     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
234     te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
235     te_slider->config->event_mask = event_mask;
236     te_slider->config->arg = arg;
237     xSemaphoreGive(s_te_sld_obj->mutex);
238     return ESP_OK;
239 }
240 
touch_slider_set_callback(touch_slider_handle_t slider_handle,touch_slider_callback_t slider_callback)241 esp_err_t touch_slider_set_callback(touch_slider_handle_t slider_handle, touch_slider_callback_t slider_callback)
242 {
243     TE_CHECK(s_te_sld_obj != NULL, ESP_ERR_INVALID_STATE);
244     TE_CHECK(slider_handle != NULL, ESP_ERR_INVALID_ARG);
245     TE_CHECK(slider_callback != NULL, ESP_ERR_INVALID_ARG);
246     te_slider_handle_t te_slider = (te_slider_handle_t)slider_handle;
247     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
248     te_slider->config->callback = slider_callback;
249     xSemaphoreGive(s_te_sld_obj->mutex);
250     return ESP_OK;
251 }
252 
touch_slider_get_message(const touch_elem_message_t * element_message)253 const touch_slider_message_t* touch_slider_get_message(const touch_elem_message_t* element_message)
254 {
255     return (touch_slider_message_t*)&element_message->child_msg;
256     _Static_assert(sizeof(element_message->child_msg) >= sizeof(touch_slider_message_t), "Message size overflow");
257 }
258 
slider_object_check_channel(touch_pad_t channel_num)259 static bool slider_object_check_channel(touch_pad_t channel_num)
260 {
261     te_slider_handle_list_t *item;
262     SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
263         if (slider_channel_check(item->slider_handle, channel_num)) {
264             return true;
265         }
266     }
267     return false;
268 }
269 
slider_object_set_threshold(void)270 static esp_err_t slider_object_set_threshold(void)
271 {
272     esp_err_t ret = ESP_OK;
273     te_slider_handle_list_t *item;
274     SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
275         ret = slider_set_threshold(item->slider_handle);
276         if (ret != ESP_OK) {
277             break;
278         }
279     }
280     return ret;
281 }
282 
slider_object_process_state(void)283 static void slider_object_process_state(void)
284 {
285     te_slider_handle_list_t *item;
286     SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
287         if (waterproof_check_mask_handle(item->slider_handle)) {
288             slider_reset_state(item->slider_handle);
289             slider_reset_position(item->slider_handle);
290             continue;
291         }
292         slider_proc_state(item->slider_handle);
293     }
294 }
295 
slider_object_update_state(touch_pad_t channel_num,te_state_t channel_state)296 static void slider_object_update_state(touch_pad_t channel_num, te_state_t channel_state)
297 {
298     te_slider_handle_list_t *item;
299     SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
300         if (waterproof_check_mask_handle(item->slider_handle)) {
301             continue;
302         }
303         slider_update_state(item->slider_handle, channel_num, channel_state);
304     }
305 }
306 
slider_object_add_instance(te_slider_handle_t slider_handle)307 static esp_err_t slider_object_add_instance(te_slider_handle_t slider_handle)
308 {
309     te_slider_handle_list_t *item = (te_slider_handle_list_t *)calloc(1, sizeof(te_slider_handle_list_t));
310     TE_CHECK(item != NULL, ESP_ERR_NO_MEM);
311     item->slider_handle = slider_handle;
312     xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
313     SLIST_INSERT_HEAD(&s_te_sld_obj->handle_list, item, next);
314     xSemaphoreGive(s_te_sld_obj->mutex);
315     return ESP_OK;
316 }
317 
slider_object_remove_instance(te_slider_handle_t slider_handle)318 static esp_err_t slider_object_remove_instance(te_slider_handle_t slider_handle)
319 {
320     esp_err_t ret = ESP_ERR_NOT_FOUND;
321     te_slider_handle_list_t *item;
322     SLIST_FOREACH(item, &s_te_sld_obj->handle_list, next) {
323         if (slider_handle == item->slider_handle) {
324             xSemaphoreTake(s_te_sld_obj->mutex, portMAX_DELAY);
325             SLIST_REMOVE(&s_te_sld_obj->handle_list, item, te_slider_handle_list, next);
326             xSemaphoreGive(s_te_sld_obj->mutex);
327             free(item);
328             ret = ESP_OK;
329             break;
330         }
331     }
332     return ret;
333 }
334 
slider_channel_check(te_slider_handle_t slider_handle,touch_pad_t channel_num)335 static bool slider_channel_check(te_slider_handle_t slider_handle, touch_pad_t channel_num)
336 {
337     te_dev_t *device;
338     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
339         device = slider_handle->device[idx];
340         if (device->channel == channel_num) {
341             return true;
342         }
343     }
344     return false;
345 }
346 
slider_set_threshold(te_slider_handle_t slider_handle)347 static esp_err_t slider_set_threshold(te_slider_handle_t slider_handle)
348 {
349     esp_err_t ret = ESP_OK;
350     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
351         ret |= te_dev_set_threshold(slider_handle->device[idx]);
352     }
353     slider_update_benchmark(slider_handle);  //Update benchmark at startup
354     return ret;
355 }
356 
slider_update_benchmark(te_slider_handle_t slider_handle)357 static void slider_update_benchmark(te_slider_handle_t slider_handle)
358 {
359     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
360         uint32_t bcm_val;
361         te_dev_t *device = slider_handle->device[idx];
362         bcm_val = te_read_smooth_signal(device->channel);
363         slider_handle->channel_bcm[idx] = bcm_val;
364     }
365 }
366 
slider_update_state(te_slider_handle_t slider_handle,touch_pad_t channel_num,te_state_t channel_state)367 static void slider_update_state(te_slider_handle_t slider_handle, touch_pad_t channel_num, te_state_t channel_state)
368 {
369     te_dev_t *device;
370     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
371         device = slider_handle->device[idx];
372         if (channel_num == device->channel) {
373             device->state = channel_state;
374         }
375     }
376 }
377 
slider_reset_state(te_slider_handle_t slider_handle)378 static void slider_reset_state(te_slider_handle_t slider_handle)
379 {
380     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
381         slider_handle->device[idx]->state = TE_STATE_IDLE;
382     }
383     slider_handle->current_state = TE_STATE_IDLE;
384 }
385 
slider_event_give(te_slider_handle_t slider_handle)386 static void slider_event_give(te_slider_handle_t slider_handle)
387 {
388     touch_elem_message_t element_message;
389     touch_slider_message_t slider_message = {
390         .event = slider_handle->event,
391         .position = slider_handle->position
392     };
393     element_message.handle = (touch_elem_handle_t)slider_handle;
394     element_message.element_type = TOUCH_ELEM_TYPE_SLIDER;
395     element_message.arg = slider_handle->config->arg;
396     memcpy(element_message.child_msg, &slider_message, sizeof(slider_message));
397     te_event_give(element_message);
398 }
399 
slider_dispatch(te_slider_handle_t slider_handle,touch_elem_dispatch_t dispatch_method)400 static inline void slider_dispatch(te_slider_handle_t slider_handle, touch_elem_dispatch_t dispatch_method)
401 {
402     if (dispatch_method == TOUCH_ELEM_DISP_EVENT) {
403         slider_event_give(slider_handle);  //Event queue
404     } else if (dispatch_method == TOUCH_ELEM_DISP_CALLBACK) {
405         touch_slider_message_t slider_info;
406         slider_info.event = slider_handle->event;
407         slider_info.position = slider_handle->position;
408         void *arg = slider_handle->config->arg;
409         slider_handle->config->callback(slider_handle, &slider_info, arg);  //Event callback
410     }
411 }
412 
413 /**
414  * @brief Slider process
415  *
416  * This function will process the slider state and maintain a slider FSM:
417  *              IDLE ----> Press ----> Release ----> IDLE
418  *
419  * The state transition procedure is as follow:
420  *       (channel state ----> slider state)
421  *
422  * TODO: add state transition diagram
423  */
slider_proc_state(te_slider_handle_t slider_handle)424 static void slider_proc_state(te_slider_handle_t slider_handle)
425 {
426     uint32_t event_mask = slider_handle->config->event_mask;
427     touch_elem_dispatch_t dispatch_method = slider_handle->config->dispatch_method;
428     BaseType_t mux_ret = xSemaphoreTake(s_te_sld_obj->mutex, 0);
429     if (mux_ret != pdPASS) {
430         return;
431     }
432 
433     slider_handle->current_state = slider_get_state(slider_handle->device, slider_handle->channel_sum);
434 
435     if (slider_handle->current_state == TE_STATE_PRESS) {
436         slider_handle->channel_bcm_update_cnt = 0;  // Reset benchmark update counter
437         slider_update_position(slider_handle);
438         if (slider_handle->last_state == TE_STATE_IDLE) { //IDLE ---> Press = On_Press
439             ESP_LOGD(TE_DEBUG_TAG, "slider press");
440             if (event_mask & TOUCH_ELEM_EVENT_ON_PRESS) {
441                 slider_handle->event = TOUCH_SLIDER_EVT_ON_PRESS;
442                 slider_dispatch(slider_handle, dispatch_method);
443             }
444         } else if (slider_handle->last_state == TE_STATE_PRESS) { //Press ---> Press = On_Calculation
445             ESP_LOGD(TE_DEBUG_TAG, "slider calculation");
446             if (event_mask & TOUCH_ELEM_EVENT_ON_CALCULATION) {
447                 slider_handle->event = TOUCH_SLIDER_EVT_ON_CALCULATION;
448                 slider_dispatch(slider_handle, dispatch_method);
449             }
450         }
451     } else if (slider_handle->current_state == TE_STATE_RELEASE) {
452         if (slider_handle->last_state == TE_STATE_PRESS) { //Press ---> Release = On_Release
453             ESP_LOGD(TE_DEBUG_TAG, "slider release");
454             if (event_mask & TOUCH_ELEM_EVENT_ON_RELEASE) {
455                 slider_handle->event = TOUCH_SLIDER_EVT_ON_RELEASE;
456                 slider_dispatch(slider_handle, dispatch_method);
457             }
458         } else if (slider_handle->last_state == TE_STATE_RELEASE) {  // Release ---> Release = On_IDLE (Not dispatch)
459             slider_reset_state(slider_handle);//Reset the slider state for the next time touch action detection
460         }
461     } else if (slider_handle->current_state == TE_STATE_IDLE) {
462         if (slider_handle->last_state == TE_STATE_RELEASE) { //Release ---> IDLE = On_IDLE (Not dispatch)
463             //Nothing
464         } else if (slider_handle->last_state == TE_STATE_IDLE)  { //IDLE ---> IDLE = Running IDLE (Not dispatch)
465             if (++slider_handle->channel_bcm_update_cnt >= TE_SLD_DEFAULT_BCM_UPDATE_TIME(s_te_sld_obj)) {  //Update channel benchmark
466                 slider_handle->channel_bcm_update_cnt = 0;
467                 slider_update_benchmark(slider_handle);
468                 ESP_LOGD(TE_DEBUG_TAG, "slider bcm update");
469             }
470             if (++slider_handle->filter_reset_cnt >= TE_SLD_DEFAULT_FILTER_RESET_TIME(s_te_sld_obj)) {
471                 slider_reset_position(slider_handle);  //Reset slider filter so as to speed up next time position calculation
472             }
473         }
474     }
475     slider_handle->last_state = slider_handle->current_state;
476     xSemaphoreGive(s_te_sld_obj->mutex);
477 }
478 
slider_get_state(te_dev_t ** device,int device_num)479 static inline te_state_t slider_get_state(te_dev_t **device, int device_num)
480 {
481     /*< Scan the state of all the slider channel and calculate the number of them if the state is Press*/
482     uint8_t press_cnt = 0;
483     uint8_t idle_cnt = 0;
484     for (int idx = 0; idx < device_num; idx++) {  //Calculate how many channel is pressed
485         if (device[idx]->state == TE_STATE_PRESS) {
486             press_cnt++;
487         } else if (device[idx]->state == TE_STATE_IDLE) {
488             idle_cnt++;
489         }
490     }
491     if (press_cnt > 0) {
492         return TE_STATE_PRESS;
493     } else if (idle_cnt == device_num) {
494         return TE_STATE_IDLE;
495     } else {
496         return TE_STATE_RELEASE;
497     }
498 }
499 
500 /**
501  * @brief Slider channel difference-rate re-quantization
502  *
503  * This function will re-quantifies the touch sensor slider channel difference-rate
504  * so as to make the different size of touch pad in PCB has the same difference value
505  *
506  */
slider_quantify_signal(te_slider_handle_t slider_handle)507 static inline void slider_quantify_signal(te_slider_handle_t slider_handle)
508 {
509     float weight_sum = 0;
510     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
511         te_dev_t *device = slider_handle->device[idx];
512         weight_sum += device->sens;
513         uint32_t current_signal = te_read_smooth_signal(device->channel);
514         int ans = current_signal - slider_handle->channel_bcm[idx];
515         float diff_rate = (float)ans / slider_handle->channel_bcm[idx];
516         slider_handle->quantify_signal_array[idx] = diff_rate / device->sens;
517         if (slider_handle->quantify_signal_array[idx] < TE_SLD_DEFAULT_QTF_THR(s_te_sld_obj)) {
518             slider_handle->quantify_signal_array[idx] = 0;
519         }
520     }
521     for (int idx = 0; idx < slider_handle->channel_sum; idx++) {
522         te_dev_t *device = slider_handle->device[idx];
523         slider_handle->quantify_signal_array[idx] = slider_handle->quantify_signal_array[idx] * weight_sum / device->sens;
524     }
525 }
526 
527 /**
528  * @brief Calculate max sum subarray
529  *
530  * This function will figure out the max sum subarray from the
531  * input array, return the max sum and max sum start index
532  *
533  */
slider_search_max_subarray(const float * array,int array_size,int * max_array_idx)534 static inline float slider_search_max_subarray(const float *array, int array_size, int *max_array_idx)
535 {
536     *max_array_idx = 0;
537     float max_array_sum = 0;
538     float current_array_sum = 0;
539     for (int idx = 0; idx <= (array_size - TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj)); idx++) {
540         for (int x = idx; x < idx + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); x++) {
541             current_array_sum += array[x];
542         }
543         if (max_array_sum < current_array_sum) {
544             max_array_sum = current_array_sum;
545             *max_array_idx = idx;
546         }
547         current_array_sum = 0;
548     }
549     return max_array_sum;
550 }
551 
552 /**
553  * @brief Calculate zero number
554  *
555  * This function will figure out the number of non-zero items from
556  * the subarray
557  */
slider_get_non_zero_num(const float * array,uint8_t array_idx)558 static inline uint8_t slider_get_non_zero_num(const float *array, uint8_t array_idx)
559 {
560     uint8_t zero_cnt = 0;
561     for (int idx = array_idx; idx < array_idx + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); idx++) {
562         zero_cnt += (array[idx] > 0) ? 1 : 0;
563     }
564     return zero_cnt;
565 }
566 
slider_calculate_position(te_slider_handle_t slider_handle,int subarray_index,float subarray_sum,int non_zero_num)567 static inline uint32_t slider_calculate_position(te_slider_handle_t slider_handle, int subarray_index, float subarray_sum, int non_zero_num)
568 {
569     int range = slider_handle->position_range;
570     int array_size = slider_handle->channel_sum;
571     float scale = slider_handle->position_scale;
572     const float *array = slider_handle->quantify_signal_array;
573     uint32_t position = 0;
574     if (non_zero_num == 0) {
575         position = slider_handle->position;
576     } else if (non_zero_num == 1) {
577         for (int index = subarray_index; index < subarray_index + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); index++) {
578             if (0 != array[index]) {
579                 if (index == array_size - 1) {
580                     position = range;
581                 } else {
582                     position = (uint32_t)((float)index * scale);
583                 }
584                 break;
585             }
586         }
587     } else {
588         for (int idx = subarray_index; idx < subarray_index + TE_SLD_DEFAULT_CALCULATE_CHANNEL(s_te_sld_obj); idx++) {
589             position += ((float)idx * array[idx]);
590         }
591         position = position * scale / subarray_sum;
592     }
593     return position;
594 }
595 
slider_filter_average(te_slider_handle_t slider_handle,uint32_t current_position)596 static uint32_t slider_filter_average(te_slider_handle_t slider_handle, uint32_t current_position)
597 {
598     uint32_t position_average = 0;
599     if (slider_handle->is_first_sample) {
600         for (int win_idx = 0; win_idx < TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj); win_idx++) {
601             slider_handle->pos_filter_window[win_idx] = current_position;  //Preload filter buffer
602         }
603         slider_handle->is_first_sample = false;
604     } else {
605         slider_handle->pos_filter_window[slider_handle->pos_window_idx++] = current_position; //Moving average filter
606         if (slider_handle->pos_window_idx >= TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj)) {
607             slider_handle->pos_window_idx = 0;
608         }
609     }
610 
611     for (int win_idx = 0; win_idx < TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj); win_idx++) { //Moving average filter
612         position_average += slider_handle->pos_filter_window[win_idx];
613     }
614     position_average = position_average / TE_SLD_DEFAULT_POS_FILTER_SIZE(s_te_sld_obj) + 0.5;
615     return position_average;
616 }
617 
slider_filter_iir(uint32_t in_now,uint32_t out_last,uint32_t k)618 static inline uint32_t slider_filter_iir(uint32_t in_now, uint32_t out_last, uint32_t k)
619 {
620     if (k == 0) {
621         return in_now;
622     } else {
623         uint32_t out_now = (in_now + (k - 1) * out_last) / k;
624         return out_now;
625     }
626 }
627 
628 /**
629  * @brief touch sensor slider position update
630  *
631  * This function is the core algorithm about touch sensor slider
632  * position update, mainly has several steps:
633  *      1. Re-quantization
634  *      2. Figure out changed channel
635  *      3. Calculate position
636  *      4. Filter
637  *
638  */
slider_update_position(te_slider_handle_t slider_handle)639 static void slider_update_position(te_slider_handle_t slider_handle)
640 {
641     int max_array_idx = 0;
642     float max_array_sum;
643     uint8_t non_zero_num;
644     uint32_t current_position;
645 
646     slider_quantify_signal(slider_handle);
647     max_array_sum = slider_search_max_subarray(slider_handle->quantify_signal_array, slider_handle->channel_sum, &max_array_idx);
648     non_zero_num = slider_get_non_zero_num(slider_handle->quantify_signal_array, max_array_idx);
649     current_position = slider_calculate_position(slider_handle, max_array_idx, max_array_sum, non_zero_num);
650     uint32_t position_average = slider_filter_average(slider_handle, current_position);
651     slider_handle->last_position = slider_handle->last_position == 0 ? (position_average << 4) : slider_handle->last_position;
652     slider_handle->last_position = slider_filter_iir((position_average << 4), slider_handle->last_position, TE_SLD_DEFAULT_POS_FILTER_FACTOR(s_te_sld_obj));
653     slider_handle->position = ((slider_handle->last_position + 8) >> 4);  //(x + 8) >> 4 ---->  (x + 8) / 16 ----> x/16 + 0.5
654 }
655 
slider_reset_position(te_slider_handle_t slider_handle)656 static void slider_reset_position(te_slider_handle_t slider_handle)
657 {
658     slider_handle->is_first_sample = true;
659     slider_handle->last_position   = 0;
660     slider_handle->pos_window_idx  = 0;
661 }
662