/* * ESPRESSIF MIT License * * Copyright (c) 2018 * * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in * which case, it is free of charge, to any person obtaining a copy of this * software and associated documentation files (the "Software"), to deal in the * Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * */ #include "audio_mem.h" #include "driver/adc.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/queue.h" #include "freertos/task.h" #include "math.h" #include #include #if CONFIG_IDF_TARGET_ESP32 #include "esp_adc_cal.h" #endif #include "adc_button.h" #include "audio_thread.h" #include "esp_log.h" #include "string.h" #define V_REF 1100 #define ADC_SAMPLES_NUM 10 #define ADC_SAMPLE_INTERVAL_TIME_MS 20 #define DIAL_VOL_INTERVAL_TIME_MS 150 #define ADC_BTN_INVALID_ID -1 #define ADC_BTN_INVALID_ACT_ID -2 #define ADC_BTN_DETECT_TIME_MS 20 #ifndef ENABLE_ADC_VOLUME #define USER_KEY_MAX 7 #endif static char *TAG = "ADC_BTN"; static EventGroupHandle_t g_event_bit; typedef struct { adc_button_callback btn_callback; adc_btn_list *head; void *user_data; audio_thread_t audio_thread; } adc_btn_tag_t; static const int default_step_level[USER_KEY_MAX] = { 0, 683, 1193, 1631, 2090, 2578, 3103 }; static const int DESTROY_BIT = BIT0; static bool _task_flag; adc_btn_list * adc_btn_create_list (adc_arr_t *adc_conf, int channels) { adc_btn_list *head = NULL; adc_btn_list *node = NULL; adc_btn_list *find = NULL; for (int i = 0; i < channels; i++) { node = (adc_btn_list *)audio_calloc (1, sizeof (adc_btn_list)); if (NULL == node) { ESP_LOGE (TAG, "Memory allocation failed! Line: %d", __LINE__); return NULL; } memset (node, 0, sizeof (adc_btn_list)); adc_arr_t *info = &(node->adc_info); memcpy (info, adc_conf + i, sizeof (adc_arr_t)); info->adc_level_step = (int *)audio_calloc (1, (info->total_steps + 1) * sizeof (int)); memset (info->adc_level_step, 0, (info->total_steps + 1) * sizeof (int)); if (NULL == info->adc_level_step) { ESP_LOGE (TAG, "Memory allocation failed! Line: %d", __LINE__); audio_free (node); return NULL; } if (adc_conf[i].adc_level_step == NULL) { memcpy (info->adc_level_step, default_step_level, USER_KEY_MAX * sizeof (int)); } else { memcpy (info->adc_level_step, adc_conf[i].adc_level_step, (adc_conf[i].total_steps + 1) * sizeof (int)); } if (info->total_steps > USER_KEY_MAX) { ESP_LOGE (TAG, "The total_steps should be less than USER_KEY_MAX"); audio_free (info->adc_level_step); audio_free (node); } node->btn_dscp = (btn_decription *)audio_calloc ( 1, sizeof (btn_decription) * (adc_conf[i].total_steps)); if (NULL == node->btn_dscp) { ESP_LOGE (TAG, "Memory allocation failed! Line: %d", __LINE__); audio_free (info->adc_level_step); audio_free (node); } memset (node->btn_dscp, 0, sizeof (btn_decription) * (adc_conf[i].total_steps)); node->next = NULL; if (NULL == head) { head = node; find = head; } else { find->next = node; find = node; } } return head; } esp_err_t adc_btn_destroy_list (adc_btn_list *head) { if (head == NULL) { ESP_LOGD (TAG, "The head of list is null"); return ESP_OK; } adc_btn_list *find = head; adc_btn_list *tmp = find; while (find) { adc_arr_t *info = &(find->adc_info); tmp = find->next; audio_free (find->btn_dscp); audio_free (info->adc_level_step); audio_free (find); find = tmp; } return ESP_OK; } static int get_adc_voltage (int channel) { uint32_t data[ADC_SAMPLES_NUM] = { 0 }; uint32_t sum = 0; int tmp = 0; #if CONFIG_IDF_TARGET_ESP32 esp_adc_cal_characteristics_t characteristics; esp_adc_cal_characterize (ADC_UNIT_1, ADC_ATTEN_11db, ADC_WIDTH_12Bit, V_REF, &characteristics); for (int i = 0; i < ADC_SAMPLES_NUM; ++i) { esp_adc_cal_get_voltage (channel, &characteristics, &data[i]); } #elif CONFIG_IDF_TARGET_ESP32S2 for (int i = 0; i < ADC_SAMPLES_NUM; i++) { data[i] = adc1_get_raw ((adc1_channel_t)channel); } #endif for (int j = 0; j < ADC_SAMPLES_NUM - 1; j++) { for (int i = 0; i < ADC_SAMPLES_NUM - j - 1; i++) { if (data[i] > data[i + 1]) { tmp = data[i]; data[i] = data[i + 1]; data[i + 1] = tmp; } } } for (int num = 1; num < ADC_SAMPLES_NUM - 1; num++) sum += data[num]; return (sum / (ADC_SAMPLES_NUM - 2)); } static int get_button_id (adc_btn_list *node, int adc) { int m = ADC_BTN_INVALID_ID; adc_arr_t *info = &(node->adc_info); for (int i = 0; i < info->total_steps; i++) { ESP_LOGV (TAG, "max:%d, adc:%d, i:%d, %d, %d", info->total_steps, adc, i, info->adc_level_step[i], info->adc_level_step[i + 1]); if ((adc > info->adc_level_step[i]) && (adc <= info->adc_level_step[i + 1])) { m = i; break; } } return m; } static void reset_btn (btn_decription *btn_dscp, int btn_num) { memset (btn_dscp, 0, sizeof (btn_decription) * btn_num); for (int i = 0; i < btn_num; ++i) { btn_dscp[i].active_id = ADC_BTN_INVALID_ID; } } static adc_btn_state_t get_adc_btn_state (int adc_value, int act_id, adc_btn_list *node) { adc_btn_state_t st = ADC_BTN_STATE_IDLE; adc_arr_t *info = &(node->adc_info); btn_decription *btn_dscp = node->btn_dscp; int id = get_button_id (node, adc_value); if (id == ADC_BTN_INVALID_ID) { if (act_id == ADC_BTN_INVALID_ACT_ID) { // No old act id and new act id. return ADC_BTN_STATE_IDLE; } if (btn_dscp[act_id].click_cnt <= 1) { return ADC_BTN_STATE_IDLE; } // Have old act ID, new id is invalid // Need to send release event if (btn_dscp[act_id].click_cnt < (info->press_judge_time / ADC_BTN_DETECT_TIME_MS)) { ESP_LOGD (TAG, "pressed: Act ID:%d, ID:%d, Cnt:%d", act_id, id, btn_dscp[act_id].click_cnt); st = ADC_BTN_STATE_RELEASE; } else { ESP_LOGD (TAG, "long press release: Act ID:%d, ID:%d, Cnt:%d", act_id, id, btn_dscp[act_id].click_cnt); st = ADC_BTN_STATE_LONG_RELEASE; } btn_dscp[act_id].active_id = -1; btn_dscp[act_id].long_click = 0; btn_dscp[act_id].click_cnt = 0; return st; } // 1.ID is valid and act ID is invalid. if (act_id == ADC_BTN_INVALID_ACT_ID) { // First new act id btn_dscp[id].active_id = id; return ADC_BTN_STATE_IDLE; } // 2.ID and act ID are valid, but not equal. if (id != act_id) { ESP_LOGW (TAG, "Old ID:%d, New ID:%d", act_id, id); // Invalid the act ID btn_dscp[act_id].active_id = -1; btn_dscp[act_id].long_click = 0; btn_dscp[act_id].click_cnt = 0; // Set the new id act ID btn_dscp[id].active_id = id; // Maybe need to check release long pressed. return ADC_BTN_STATE_IDLE; } // 3.ID and act ID are valid, and equal. btn_dscp[act_id].click_cnt++; if (btn_dscp[act_id].click_cnt == 3) { return ADC_BTN_STATE_PRESSED; } if (btn_dscp[act_id].long_click) { return ADC_BTN_STATE_IDLE; } if (btn_dscp[act_id].click_cnt >= (info->press_judge_time / ADC_BTN_DETECT_TIME_MS)) { // Send long click event. ESP_LOGD (TAG, "long press: Act ID:%d, ID:%d, Cnt:%d", act_id, id, btn_dscp[act_id].click_cnt); st = ADC_BTN_STATE_LONG_PRESSED; btn_dscp[act_id].long_click = 1; } return st; } static void button_task (void *parameters) { _task_flag = true; adc_btn_tag_t *tag = (adc_btn_tag_t *)parameters; adc_btn_list *head = tag->head; adc_btn_list *find = head; xEventGroupClearBits (g_event_bit, DESTROY_BIT); #if CONFIG_IDF_TARGET_ESP32 adc1_config_width (ADC_WIDTH_BIT_12); #elif CONFIG_IDF_TARGET_ESP32S2 adc1_config_width (ADC_WIDTH_BIT_13); #endif while (find) { adc_arr_t *info = &(find->adc_info); reset_btn (find->btn_dscp, info->total_steps); adc1_config_channel_atten (info->adc_ch, ADC_ATTEN_11db); find = find->next; } find = head; #if defined ENABLE_ADC_VOLUME adc1_config_channel_atten (DIAL_adc_ch, ADC_ATTEN_11db); short adc_vol_prev = ADC_BTN_INVALID_ID; short adc_vol_cur = ADC_BTN_INVALID_ID; short internal_time_ms = DIAL_VOL_INTERVAL_TIME_MS / ADC_SAMPLE_INTERVAL_TIME_MS; /// 10 * 10 = 100ms static bool empty_flag; static bool full_flag; bool is_first_time = true; #endif // ENABLE_ADC_VOLUME static adc_btn_state_t cur_state = ADC_BTN_STATE_ADC; adc_btn_state_t btn_st = ADC_BTN_STATE_IDLE; int cur_act_id = ADC_BTN_INVALID_ACT_ID; while (_task_flag) { #if defined ENABLE_ADC_VOLUME if (internal_time_ms == 0) { adc_vol_cur = get_adc_voltage (DIAL_adc_ch); internal_time_ms = DIAL_VOL_INTERVAL_TIME_MS / ADC_SAMPLE_INTERVAL_TIME_MS; if (adc_vol_prev > 0) { short n = abs (adc_vol_cur - adc_vol_prev); if (is_first_time) { is_first_time = false; } if (adc_vol_cur < 200) { if (empty_flag == false) { ESP_LOGI (TAG, "ABS_LOW:%d, %d->0", n, adc_vol_cur / 25); empty_flag = true; } } else if (adc_vol_cur > 2500) { if (full_flag == false) { ESP_LOGI (TAG, "ABS_HIGH:%d, %d->100", n, adc_vol_cur / 25); full_flag = true; } } else if (n > 80) { empty_flag = false; full_flag = false; } } adc_vol_prev = adc_vol_cur; } internal_time_ms--; #else find = head; while (find) { adc_arr_t *info = &(find->adc_info); int act_id = ADC_BTN_INVALID_ACT_ID; btn_decription *btn_dscp = find->btn_dscp; switch (cur_state) { case ADC_BTN_STATE_ADC: { int adc = get_adc_voltage (info->adc_ch); ESP_LOGD (TAG, "ADC:%d", adc); for (int i = 0; i < info->total_steps; ++i) { if (btn_dscp[i].active_id > ADC_BTN_INVALID_ID) { act_id = i; break; } } btn_st = get_adc_btn_state (adc, act_id, find); if (btn_st != ADC_BTN_STATE_IDLE) { cur_act_id = act_id; cur_state = btn_st; ESP_LOGD (TAG, "ADC ID:%d", act_id); } break; } case ADC_BTN_STATE_PRESSED: { tag->btn_callback ((void *)tag->user_data, info->adc_ch, cur_act_id, ADC_BTN_STATE_PRESSED); cur_state = ADC_BTN_STATE_ADC; break; } case ADC_BTN_STATE_LONG_PRESSED: { tag->btn_callback ((void *)tag->user_data, info->adc_ch, cur_act_id, ADC_BTN_STATE_LONG_PRESSED); cur_state = ADC_BTN_STATE_ADC; break; } case ADC_BTN_STATE_LONG_RELEASE: { tag->btn_callback ((void *)tag->user_data, info->adc_ch, cur_act_id, ADC_BTN_STATE_LONG_RELEASE); cur_state = ADC_BTN_STATE_ADC; break; } case ADC_BTN_STATE_RELEASE: { tag->btn_callback ((void *)tag->user_data, info->adc_ch, cur_act_id, ADC_BTN_STATE_RELEASE); cur_state = ADC_BTN_STATE_ADC; break; } default: ESP_LOGE (TAG, "Not support state %d", cur_state); break; } find = find->next; } #endif // ENABLE_ADC_VOLUME vTaskDelay (ADC_SAMPLE_INTERVAL_TIME_MS / portTICK_PERIOD_MS); } if (g_event_bit) { xEventGroupSetBits (g_event_bit, DESTROY_BIT); } audio_free (tag); vTaskDelete (NULL); } void adc_btn_delete_task (void) { if (_task_flag) { _task_flag = false; } if (g_event_bit) { xEventGroupWaitBits (g_event_bit, DESTROY_BIT, pdTRUE, pdFALSE, portMAX_DELAY); vEventGroupDelete (g_event_bit); g_event_bit = NULL; } } void adc_btn_init (void *user_data, adc_button_callback cb, adc_btn_list *head, adc_btn_task_cfg_t *task_cfg) { adc_btn_tag_t *tag = audio_calloc (1, sizeof (adc_btn_tag_t)); if (NULL == tag) { ESP_LOGE (TAG, "Memory allocation failed! Line: %d", __LINE__); return; } tag->user_data = user_data; tag->head = head; tag->btn_callback = cb; g_event_bit = xEventGroupCreate (); audio_thread_create (&tag->audio_thread, "button_task", button_task, (void *)tag, task_cfg->task_stack, task_cfg->task_prio, task_cfg->ext_stack, task_cfg->task_core); }