blob: df9d0cbd48a26d942603f2b2b2ddff4048e8fe70 [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_touch.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <stdint.h>
#include <sys/types.h>
#include <nuttx/arch.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spinlock.h>
#include <arch/irq.h>
#include "xtensa.h"
#include "esp32s3_gpio.h"
#include "esp32s3_irq.h"
#include "esp32s3_touch.h"
#include "esp32s3_touch_lowerhalf.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define TOUCH_GET_IO_NUM(channel) (touch_channel_to_rtcio[channel])
/****************************************************************************
* Private Types
****************************************************************************/
struct touch_config_volt_s
{
enum touch_high_volt_e refh;
enum touch_low_volt_e refl;
enum touch_volt_atten_e atten;
};
struct touch_config_meas_mode_s
{
enum touch_cnt_slope_e slope;
enum touch_tie_opt_e tie_opt;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
static int touch_interrupt(int irq, void *context, void *arg);
static void touch_restore_irq(void *arg);
#endif
static void touch_config(enum touch_pad_e tp);
static void touch_init(struct touch_config_s *config);
static void touch_set_meas_mode(enum touch_pad_e tp,
struct touch_config_meas_mode_s *meas);
static void touch_set_voltage(struct touch_config_volt_s *volt);
static void touch_io_init(enum touch_pad_e tp);
/****************************************************************************
* Private Data
****************************************************************************/
static mutex_t *touch_mux = NULL;
static spinlock_t lock;
static uint32_t touch_pad_logic_threshold[TOUCH_SENSOR_PINS];
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
static uint16_t touch_pad_isr_enabled = 0x0000;
static enum touch_intr_mask_e touch_pad_isr_types = 0x0000;
static int touch_last_irq = -1;
static int (*touch_release_cb)(int, void *, void *) = NULL;
static struct rt_timer_args_s irq_timer_args;
static struct rt_timer_s *irq_timer_handler = NULL;
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: touch_interrupt
*
* Description:
* Touch pads interrupt handler.
*
* Input Parameters:
* irq - Interrupt request number;
* context - Context data from the ISR;
* arg - Opaque pointer to the internal driver state structure.
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
static int touch_interrupt(int irq, void *context, void *arg)
{
uint32_t intr_mask = *((uint32_t *) context);
enum touch_pad_e pad_num = touch_lh_get_current_meas_channel();
touch_last_irq = ESP32S3_TOUCHPAD2IRQ(pad_num);
touch_lh_intr_disable(touch_pad_isr_types);
rt_timer_start(irq_timer_handler,
CONFIG_ESP32S3_TOUCH_IRQ_INTERVAL_MS * USEC_PER_MSEC,
false);
/* Read and clear the touch interrupt status */
if (intr_mask & TOUCH_INTR_MASK_TIMEOUT)
{
touch_lh_timer_force_done();
}
if (intr_mask & (TOUCH_INTR_MASK_ACTIVE |
TOUCH_INTR_MASK_INACTIVE))
{
if ((touch_pad_isr_enabled >> pad_num) & 0x1)
{
irq_dispatch(touch_last_irq, context);
}
}
return OK;
}
#endif
/****************************************************************************
* Name: touch_restore_irq
*
* Description:
* IRQ timer callback.
* Re-enables touch IRQ after a certain time to avoid spam.
*
* Input Parameters:
* arg - Pointer to a memory location containing the function arguments.
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
static void touch_restore_irq(void *arg)
{
if (touch_last_irq > 0 && touch_release_cb != NULL)
{
/* Call the button interrup handler again so we can detect touch pad
* releases
*/
touch_release_cb(touch_last_irq, NULL, NULL);
}
touch_lh_intr_enable(touch_pad_isr_types);
}
#endif
/****************************************************************************
* Name: touch_init
*
* Description:
* Initialize the touch pad driver.
*
* Input Parameters:
* config - The touch configuration structure.
*
* Returned Value:
* None.
*
****************************************************************************/
static void touch_init(struct touch_config_s *config)
{
if (touch_mux != NULL)
{
iinfo("Touch pad driver already initialized.\n");
return;
}
touch_mux = kmm_zalloc(sizeof(mutex_t));
if (touch_mux == NULL)
{
ierr("Failed to initialize touch pad driver.\n");
PANIC();
}
nxmutex_init(touch_mux);
irqstate_t flags = spin_lock_irqsave(&lock);
touch_lh_stop_fsm();
touch_lh_intr_disable(TOUCH_INTR_MASK_ALL);
touch_lh_intr_clear(TOUCH_INTR_MASK_ALL);
touch_lh_clear_channel_mask(TOUCH_BIT_MASK_ALL);
touch_lh_clear_trigger_status_mask();
touch_lh_set_meas_time(TOUCH_MEASURE_CYCLE_DEFAULT);
touch_lh_set_sleep_time(TOUCH_SLEEP_CYCLE_DEFAULT);
/* Configure the touch-sensor power domain into self-bias since
* bandgap-bias level is different under sleep-mode compared to
* running-mode. Self-bias is always on after chip startup.
*/
touch_lh_sleep_low_power(true);
touch_lh_set_voltage_high(TOUCH_HIGH_VOLTAGE_THRESHOLD);
touch_lh_set_voltage_low(TOUCH_LOW_VOLTAGE_THRESHOLD);
touch_lh_set_voltage_attenuation(TOUCH_ATTEN_VOLTAGE_THRESHOLD);
touch_lh_set_idle_channel_connect(TOUCH_IDLE_CH_CONNECT_DEFAULT);
/* Clear touch channels to initialize the channel value (benchmark,
* raw_data).
* Note: Should call it after enable clock gate.
*/
touch_lh_clkgate(true);
touch_lh_reset_benchmark(TOUCH_PAD_ALL);
touch_lh_sleep_reset_benchmark();
#ifdef CONFIG_ESP32S3_TOUCH_FILTER
touch_lh_filter_set_filter_mode(config->filter_mode);
touch_lh_filter_set_debounce(config->filter_debounce_cnt);
touch_lh_filter_set_noise_thres(config->filter_noise_thr);
touch_lh_filter_set_jitter_step(config->filter_jitter_step);
touch_lh_filter_set_smooth_mode(config->filter_smh_lvl);
touch_lh_filter_enable();
#endif
#ifdef CONFIG_ESP32S3_TOUCH_DENOISE
touch_lh_set_slope(TOUCH_DENOISE_CHANNEL, TOUCH_SLOPE_DEFAULT);
touch_lh_set_tie_option(TOUCH_DENOISE_CHANNEL, TOUCH_TIE_OPT_DEFAULT);
touch_lh_denoise_set_cap_level(config->denoise_cap_level);
touch_lh_denoise_set_grade(config->denoise_grade);
touch_lh_denoise_enable();
#endif
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
irq_timer_args.arg = NULL;
irq_timer_args.callback = touch_restore_irq;
rt_timer_create(&(irq_timer_args), &(irq_timer_handler));
touch_pad_isr_types = TOUCH_INTR_MASK_ACTIVE |
TOUCH_INTR_MASK_INACTIVE |
TOUCH_INTR_MASK_TIMEOUT;
int ret = 0;
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_DONE,
touch_interrupt,
NULL);
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_ACTIVE,
touch_interrupt,
NULL);
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_INACTIVE,
touch_interrupt,
NULL);
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_SCAN_DONE,
touch_interrupt,
NULL);
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_TIMEOUT,
touch_interrupt,
NULL);
ret |= irq_attach(ESP32S3_IRQ_RTC_TOUCH_APPROACH_LOOP_DONE,
touch_interrupt,
NULL);
if (ret < 0)
{
ierr("ERROR: irq_attach() failed.\n");
}
#endif
spin_unlock_irqrestore(&lock, flags);
}
/****************************************************************************
* Name: touch_config
*
* Description:
* Configure a touch pad channel.
*
* Input Parameters:
* tp - The touch pad channel.
*
* Returned Value:
* None.
*
****************************************************************************/
static void touch_config(enum touch_pad_e tp)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
if (touch_mux == NULL)
{
ierr("ERROR: Touch pads not initialized.\n");
return;
}
touch_io_init(tp);
irqstate_t flags = spin_lock_irqsave(&lock);
touch_lh_set_slope(tp, TOUCH_SLOPE_DEFAULT);
touch_lh_set_tie_option(tp, TOUCH_TIE_OPT_DEFAULT);
touch_lh_set_channel_mask(BIT(tp));
spin_unlock_irqrestore(&lock, flags);
}
/****************************************************************************
* Name: touch_set_voltage
*
* Description:
* Set the touch pads voltage configuration.
*
* Input Parameters:
* volt - The new configuration struct.
*
* Returned Value:
* None.
*
****************************************************************************/
static void touch_set_voltage(struct touch_config_volt_s *volt)
{
irqstate_t flags = spin_lock_irqsave(&lock);
touch_lh_set_voltage_high(volt->refh);
touch_lh_set_voltage_low(volt->refl);
touch_lh_set_voltage_attenuation(volt->atten);
spin_unlock_irqrestore(&lock, flags);
}
/****************************************************************************
* Name: touch_set_meas_mode
*
* Description:
* Set the measurement mode for a given touch pad.
*
* Input Parameters:
* tp - The touch pad channel;
* meas - The new configuration struct.
*
* Returned Value:
* None.
*
****************************************************************************/
static void touch_set_meas_mode(enum touch_pad_e tp,
struct touch_config_meas_mode_s *meas)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
irqstate_t flags = spin_lock_irqsave(&lock);
touch_lh_set_slope(tp, meas->slope);
touch_lh_set_tie_option(tp, meas->tie_opt);
spin_unlock_irqrestore(&lock, flags);
}
/****************************************************************************
* Name: touch_io_init
*
* Description:
* Initialize GPIOs for a given touch pad.
*
* Input Parameters:
* tp - The touch pad channel.
*
* Returned Value:
* None.
*
****************************************************************************/
static void touch_io_init(enum touch_pad_e tp)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
esp32s3_configrtcio(tp, RTC_FUNCTION_RTCIO);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_configtouch
*
* Description:
* Configure a touch pad channel.
*
* Input Parameters:
* tp - The touch pad channel;
* config - The touch pad configuration structure.
*
* Returned Value:
* OK.
*
****************************************************************************/
int esp32s3_configtouch(enum touch_pad_e tp, struct touch_config_s config)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
struct touch_config_volt_s volt_config =
{
.refh = config.refh,
.refl = config.refl,
.atten = config.atten
};
struct touch_config_meas_mode_s meas_config =
{
.slope = config.slope,
.tie_opt = config.tie_opt
};
touch_init(&config);
touch_set_meas_mode(tp, &meas_config);
touch_lh_set_fsm_mode(config.fsm_mode);
touch_set_voltage(&volt_config);
touch_config(tp);
touch_lh_start_fsm();
return OK;
}
/****************************************************************************
* Name: esp32s3_touchread
*
* Description:
* Read a touch pad channel.
*
* Input Parameters:
* tp - The touch pad channel.
*
* Returned Value:
* 0 if touch pad pressed, 1 if released.
*
****************************************************************************/
bool esp32s3_touchread(enum touch_pad_e tp)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
uint32_t value = esp32s3_touchreadraw(tp);
return (value > touch_pad_logic_threshold[tp]);
}
/****************************************************************************
* Name: esp32s3_touchreadraw
*
* Description:
* Read the analog value of a touch pad channel.
*
* Input Parameters:
* tp - The touch pad channel.
*
* Returned Value:
* The number of charge cycles in the last measurement.
*
****************************************************************************/
uint32_t esp32s3_touchreadraw(enum touch_pad_e tp)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
irqstate_t flags = spin_lock_irqsave(&lock);
#ifdef CONFIG_ESP32S3_TOUCH_FILTER
uint32_t value = touch_lh_filter_read_smooth(tp);
#else
uint32_t value = touch_lh_read_raw_data(tp);
#endif
spin_unlock_irqrestore(&lock, flags);
iinfo("Touch pad %d value: %u\n", tp, value);
return value;
}
/****************************************************************************
* Name: esp32s3_touchbenchmark
*
* Description:
* Read the touch pad channel benchmark.
* After initialization, the benchmark value is the maximum during the
* first measurement period.
*
* Input Parameters:
* tp - The touch pad channel.
*
* Returned Value:
* The benchmark value.
*
****************************************************************************/
uint32_t esp32s3_touchbenchmark(enum touch_pad_e tp)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
irqstate_t flags = spin_lock_irqsave(&lock);
uint32_t value = touch_lh_read_benchmark(tp);
spin_unlock_irqrestore(&lock, flags);
iinfo("Touch pad %d benchmark value: %u\n", tp, value);
return value;
}
/****************************************************************************
* Name: esp32s3_touchsetthreshold
*
* Description:
* Set the touch pad channel threshold.
*
* Input Parameters:
* tp - The touch pad channel;
* threshold - The threshold value.
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32s3_touchsetthreshold(enum touch_pad_e tp, uint32_t threshold)
{
DEBUGASSERT(tp < TOUCH_SENSOR_PINS);
irqstate_t flags = spin_lock_irqsave(&lock);
touch_lh_set_threshold(tp, threshold);
touch_pad_logic_threshold[tp] = threshold;
spin_unlock_irqrestore(&lock, flags);
iinfo("Touch pad %d threshold set to: %u\n", tp, threshold);
}
/****************************************************************************
* Name: esp32s3_touchirqenable
*
* Description:
* Enable the interrupt for the specified touch pad.
*
* Input Parameters:
* irq - The touch pad irq number.
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
void esp32s3_touchirqenable(int irq)
{
DEBUGASSERT(irq >= ESP32S3_FIRST_RTCIOIRQ_TOUCHPAD &&
irq <= ESP32S3_LAST_RTCIOIRQ_TOUCHPAD);
int bit = ESP32S3_IRQ2TOUCHPAD(irq);
touch_lh_intr_disable(touch_pad_isr_types);
touch_pad_isr_enabled |= (UINT32_C(1) << bit);
touch_lh_intr_enable(touch_pad_isr_types);
}
#endif
/****************************************************************************
* Name: esp32s3_touchirqdisable
*
* Description:
* Disable the interrupt for the specified touch pad.
*
* Input Parameters:
* irq - The touch pad irq number.
*
* Returned Value:
* None.
*
****************************************************************************/
#ifdef CONFIG_ESP32S3_TOUCH_IRQ
void esp32s3_touchirqdisable(int irq)
{
DEBUGASSERT(irq >= ESP32S3_FIRST_RTCIOIRQ_TOUCHPAD &&
irq <= ESP32S3_LAST_RTCIOIRQ_TOUCHPAD);
int bit = ESP32S3_IRQ2TOUCHPAD(irq);
touch_lh_intr_disable(touch_pad_isr_types);
touch_pad_isr_enabled &= (~(UINT32_C(1) << bit));
touch_lh_intr_enable(touch_pad_isr_types);
}
#endif
/****************************************************************************
* Name: esp32s3_touchregisterreleasecb
*
* Description:
* Register the release callback.
*
* Input Parameters:
* func - The handler function to be used.
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32s3_touchregisterreleasecb(int (*func)(int, void *, void *))
{
DEBUGASSERT(func != NULL);
touch_release_cb = func;
}