circuitpython/atmel-samd/common-hal/touchio/TouchIn.c

308 lines
12 KiB
C
Raw Normal View History

/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft for Adafruit Industries
*
* Permission is hereby granted, 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 <string.h>
#include "py/nlr.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "py/binary.h"
#include "py/mphal.h"
#include "shared-bindings/touchio/TouchIn.h"
#include "tick.h"
#include "QTouch/touch_api_ptc.h"
#define DEF_SELFCAP_CAL_SEQ1_COUNT 8
#define DEF_SELFCAP_CAL_SEQ2_COUNT 4
#define DEF_SELFCAP_CC_CAL_CLK_PRESCALE_PER_NODE DEF_SELFCAP_CLK_PRESCALE_PER_NODE
#define DEF_SELFCAP_CC_CAL_SENSE_RESISTOR_PER_NODE DEF_SELFCAP_SENSE_RESISTOR_PER_NODE
#define DEF_SELFCAP_QUICK_REBURST_ENABLE 1u
/**
* Mutual Cap sensors measured data pointer.
* Note: This pointer is initialized by the QTouch library once the
* touch_mutlcap_sensors_init API is called. */
touch_measure_data_t *p_mutlcap_measure_data = NULL;
/**
* Self Cap sensors measured data pointer.
* Note: This pointer is initialized by the QTouch library once the
* touch_selfcap_sensors_init API is called. */
touch_measure_data_t *p_selfcap_measure_data = NULL;
/**
* Self Cap sensors data block provided as input to Touch library.
*/
static uint8_t selfcap_data_blk[PRIV_SELFCAP_DATA_BLK_SIZE];
/**
* Self Cap sensors Pins Info.
*/
uint32_t selfcap_y_nodes[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_LINES};
filter_level_t selfcap_filter_level_per_node[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_FILTER_LEVEL_PER_NODE};
uint8_t selfcap_auto_os_per_node[DEF_SELFCAP_NUM_CHANNELS]= {DEF_SELFCAP_AUTO_OS_PER_NODE};
gain_t selfcap_gain_per_node[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_GAIN_PER_NODE};
rsel_val_t selfcap_resistor_per_node[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_SENSE_RESISTOR_PER_NODE};
prsc_div_sel_t selfcap_prsc_per_node[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_CLK_PRESCALE_PER_NODE};
prsc_div_sel_t selfcap_boot_prsc_per_node[DEF_SELFCAP_NUM_CHANNELS] = {DEF_SELFCAP_CC_CAL_CLK_PRESCALE_PER_NODE};
rsel_val_t selfcap_boot_resistor_per_node[DEF_SELFCAP_NUM_CHANNELS] = { DEF_SELFCAP_CC_CAL_SENSE_RESISTOR_PER_NODE};
freq_hop_sel_t selfcap_freq_hops[3u] = {DEF_SELFCAP_HOP_FREQS};
/**
* Self Cap Configuration structure provided as input to Touch Library.
*
* Note: Use the touch.h configuration header file to fill in
* the elements of this structure. DO NOT modify any of the input values
* directly in this structure.
*/
touch_selfcap_config_t selfcap_config = {
0, /* Self Cap number of channels. */
0, /* Self Cap number of sensors. */
DEF_SELFCAP_NUM_ROTORS_SLIDERS, /* Self Cap number of rotors and
* sliders. */
/* Self Cap GLOBAL SENSOR CONFIGURATION INFO. */
{
DEF_SELFCAP_DI, /* uint8_t di; Sensor detect
* integration (DI) limit. */
/* Interchanging Negative and Positive Drift rate, since Signal
* increases on Touch. */
DEF_SELFCAP_ATCH_DRIFT_RATE, /* uint8_t neg_drift_rate;
* Sensor
* negative drift rate. */
DEF_SELFCAP_TCH_DRIFT_RATE, /* uint8_t pos_drift_rate;
* Sensor
* positive drift rate. */
DEF_SELFCAP_MAX_ON_DURATION, /* uint8_t max_on_duration;
* Sensor
* maximum on duration. */
DEF_SELFCAP_DRIFT_HOLD_TIME, /* uint8_t drift_hold_time;
* Sensor
* drift hold time. */
DEF_SELFCAP_ATCH_RECAL_DELAY, /* uint8_t pos_recal_delay;
* Sensor positive recalibration
* delay. */
DEF_SELFCAP_CAL_SEQ1_COUNT,
DEF_SELFCAP_CAL_SEQ2_COUNT,
DEF_SELFCAP_ATCH_RECAL_THRESHOLD, /* recal_threshold_t
* recal_threshold; Sensor
* recalibration threshold. */
DEF_SELFCAP_TOUCH_POSTPROCESS_MODE,
DEF_SELFCAP_AUTO_OS_SIGNAL_STABILITY_LIMIT,
DEF_SELFCAP_FREQ_AUTO_TUNE_SIGNAL_STABILITY_LIMIT,
DEF_SELFCAP_FREQ_AUTO_TUNE_IN_CNT,
DEF_SELFCAP_NOISE_MEAS_SIGNAL_STABILITY_LIMIT, /* signal
*stability */
DEF_SELFCAP_NOISE_LIMIT,
DEF_SELFCAP_LOCKOUT_SEL,
DEF_SELFCAP_LOCKOUT_CNTDOWN,
#if ((SAMC20) || (SAMC21) || (SAML22))
DEF_SELF_CAP_CSD_VALUE,
#endif
},
{
selfcap_gain_per_node, /* Self Cap channel gain setting. */
DEF_SELFCAP_FREQ_MODE, /* Self Cap noise counter measure
* enable/disable. */
selfcap_prsc_per_node,
selfcap_resistor_per_node,
selfcap_boot_prsc_per_node,
selfcap_boot_resistor_per_node,
selfcap_freq_hops,
selfcap_filter_level_per_node, /* Self Cap filter level setting. */
selfcap_auto_os_per_node, /* Self Cap auto oversamples
*setting. */
},
selfcap_data_blk, /* Self Cap data block index. */
PRIV_SELFCAP_DATA_BLK_SIZE, /* Self Cap data block size. */
selfcap_y_nodes, /* Self Cap channel nodes. */
DEF_SELFCAP_QUICK_REBURST_ENABLE,
DEF_SELFCAP_FILTER_CALLBACK, /* Self Cap filter callback
* function
* pointer. */
DEF_SELFCAP_FREQ_AUTO_TUNE_ENABLE,
DEF_SELFCAP_NOISE_MEAS_ENABLE,
DEF_SELFCAP_NOISE_MEAS_BUFFER_CNT,
DEF_SELFCAP_MOIS_TOLERANCE_ENABLE,
DEF_SELFCAP_NUM_MOIS_GROUPS,
DEF_SELFCAP_MOIS_QUICK_REBURST_ENABLE,
DEF_SELFCAP_PTC_GPIO_STATE,
.tlib_feature_list ={
.lk_chk = DEF_LOCKOUT_FUNC,
.auto_os_init = DEF_AUTO_OS_FUNC,//auto_os_resolve,
.auto_tune_init = DEF_SELF_AUTO_TUNE_FUNC,// self_auto_tuning
.enable_aks = DEF_AKS_FUNC, /*AKS grouping function */
}
};
/**
* Touch Library input configuration structure.
*/
touch_config_t touch_config = {
NULL,
&selfcap_config, /* Pointer to Self Cap configuration
* structure. */
DEF_TOUCH_PTC_ISR_LVL, /* PTC interrupt level. */
};
touchio_touchin_obj_t *active_touchin_obj[DEF_SELFCAP_NUM_CHANNELS];
void common_hal_touchio_touchin_construct(touchio_touchin_obj_t* self,
const mcu_pin_obj_t *pin) {
if (!pin->has_touch) {
mp_raise_ValueError("Invalid pin");
}
claim_pin(pin);
if (selfcap_config.num_channels > 0) {
// Deinit the touch sensor, we're going to reinitialize it.
touch_selfcap_sensors_deinit();
// Remove holes in the active list from sensors that may have been
// disabled.
int first_hole = -1;
for (int i = 0; i < selfcap_config.num_channels; i++) {
if (active_touchin_obj[i] == NULL && first_hole == -1) {
first_hole = i;
} else if (active_touchin_obj[i] != NULL && first_hole != -1) {
active_touchin_obj[first_hole] = active_touchin_obj[i];
first_hole = i;
}
}
if (first_hole > -1) {
selfcap_config.num_channels = first_hole;
selfcap_config.num_sensors = first_hole;
}
}
// Add our sensor to the end of the list.
self->pin = pin;
selfcap_y_nodes[selfcap_config.num_channels++] = Y(pin->touch_y_line);
active_touchin_obj[selfcap_config.num_sensors++] = self;
/* Setup and enable generic clock source for PTC module. */
struct system_gclk_chan_config gclk_chan_conf;
system_gclk_chan_get_config_defaults(&gclk_chan_conf);
gclk_chan_conf.source_generator = GCLK_GENERATOR_3;
system_gclk_chan_set_config(PTC_GCLK_ID, &gclk_chan_conf);
system_gclk_chan_enable(PTC_GCLK_ID);
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_PTC);
touch_ret_t status;
/* Initialize touch library for Self Cap operation. */
status = touch_selfcap_sensors_init_with_rs_table(&touch_config,
PRIV_SELFCAP_RS_TABLE_INIT, PRIV_NM_TABLE_INIT,
PRIV_FREQ_AUTO_TUNE_CHK, PRIV_MOIS_TOLERANCE_CHK);
if (status != TOUCH_SUCCESS) {
mp_raise_OSError(MP_EIO);
}
for (int i = 0; i < selfcap_config.num_channels; i++) {
status = touch_selfcap_sensor_config(SENSOR_TYPE_KEY, i, i,
NO_AKS_GROUP, 10u, HYST_25, RES_8_BIT, &active_touchin_obj[i]->sensor_id);
if (status != TOUCH_SUCCESS) {
mp_raise_OSError(MP_EIO);
}
}
status = touch_selfcap_sensors_calibrate(AUTO_TUNE_RSEL);
if (status != TOUCH_SUCCESS) {
mp_raise_OSError(MP_EIO);
}
// Run a measurement to get calibrated.
common_hal_touchio_touchin_get_value(self);
}
// This may get called twice, once on exit or deinit and once from the finaliser
// (__del__). We need the finaliser to reduce the chance of us writing to memory
// we no longer own because we keep track of Python pointers in active_touchin_obj.
void common_hal_touchio_touchin_deinit(touchio_touchin_obj_t* self) {
touch_selfcap_sensor_disable(self->sensor_id);
// Remove ourselves from the list of active objects. We don't change the
// selfcap settings at all because we don't want to reinit now. Instead,
// we'll collect blank spots later on reinit.
for (int i = 0; i < DEF_SELFCAP_NUM_CHANNELS; i++) {
if (active_touchin_obj[i] == self) {
active_touchin_obj[i] = NULL;
break;
}
}
}
volatile bool touch_read_ready = false;
volatile touch_acq_status_t touch_acq_status;
void touch_selfcap_measure_complete_callback(void)
{
touch_read_ready = true;
touch_acq_status = p_selfcap_measure_data->acq_status;
}
bool common_hal_touchio_touchin_get_value(touchio_touchin_obj_t *self) {
if (p_selfcap_measure_data->acq_status & TOUCH_CC_CALIB_ERROR) {
mp_raise_RuntimeError("Touch calibration error");
}
touch_acq_status = TOUCH_BURST_AGAIN;
uint64_t start_ticks = ticks_ms;
while((touch_acq_status & TOUCH_BURST_AGAIN) && ticks_ms - start_ticks < 1000) {
touch_read_ready = false;
touch_ret_t touch_ret = touch_selfcap_sensors_measure(
ticks_ms,
NORMAL_ACQ_MODE,
touch_selfcap_measure_complete_callback);
if (touch_ret != TOUCH_SUCCESS) {
mp_raise_OSError(MP_EIO);
}
while(!touch_read_ready && ticks_ms - start_ticks < 1000) {
// wait
#ifdef MICROPY_VM_HOOK_LOOP
MICROPY_VM_HOOK_LOOP
#endif
}
}
if (touch_acq_status & TOUCH_BURST_AGAIN) {
mp_raise_OSError(MP_EIO);
}
return (p_selfcap_measure_data->p_sensor_states[self->sensor_id / 8] & (1 << (self->sensor_id % 8))) != 0;
}