Recently I've had the desire to add touch sensing to a project involving a Teensy 3.2 (or 3.6) but need the touch reads to be non-blocking due to timing constraints (each call to the main loop needs to complete in roughly 100 uSecs). However, a call to touchRead() can take milliseconds, if not longer, depending on the capacitance of the sensor. Perusing the prjc.com website and all the code in the libraries, I was not able to find any mention of non-blocking/asynchronous support for touch tensing. Surely I'm not the only one that has such a need!
So I borrowed the code from touch.c (from Paul's Teensy library) and made two routines: A startTouchRead() routine to initiate touch sensing on a given pin, and a touchReady() routine that checks to see if the sensing is complete. Both routines are non-blocking, so the sensing can take as long as needed, and you can simply add a test for completion in your main loop as you see fit.
While it appears possible to have interrupt-based touch sensing according to the NXP documentation, I chose not to go that route due to possible interference with some time-critical 10 kHz ADC sampling I had in my project, and didn't want the possibility of interrupting the ADC and all the complications that brings. (Yes I know ADC's can be restarted if interrupted.) Instead, I decided to initiate touch sensing in the ISR routine of the ADC at appropriate times, and then poll for touch sensing completion in my main loop. That seems far simpler and serves my needs.
I figured others might find the routines for asynchronous touch sensing useful. So here's the contents of the modified file that I call asynctouch.c. It was tested on a Teensy 3.2, and most of the code was left intact from the original touch.c file. I show an example of its usage in the file itself. CAVEAT: This code has not been tested much at all. I just got it working this morning!
So I borrowed the code from touch.c (from Paul's Teensy library) and made two routines: A startTouchRead() routine to initiate touch sensing on a given pin, and a touchReady() routine that checks to see if the sensing is complete. Both routines are non-blocking, so the sensing can take as long as needed, and you can simply add a test for completion in your main loop as you see fit.
While it appears possible to have interrupt-based touch sensing according to the NXP documentation, I chose not to go that route due to possible interference with some time-critical 10 kHz ADC sampling I had in my project, and didn't want the possibility of interrupting the ADC and all the complications that brings. (Yes I know ADC's can be restarted if interrupted.) Instead, I decided to initiate touch sensing in the ISR routine of the ADC at appropriate times, and then poll for touch sensing completion in my main loop. That seems far simpler and serves my needs.
I figured others might find the routines for asynchronous touch sensing useful. So here's the contents of the modified file that I call asynctouch.c. It was tested on a Teensy 3.2, and most of the code was left intact from the original touch.c file. I show an example of its usage in the file itself. CAVEAT: This code has not been tested much at all. I just got it working this morning!
Code:
/*
* Asynchronous (aka polling) touch read routines, as modified by Bryan F.
* (Bry) from the Teensy core library file touch.c. I've taken the functions
* from that file, and modified them to be non-blocking:
*
* The following non-blocking routine starts the read process for the given
* pin. It returns the TSI channel number for the touch pin, or 255 if the
* pin is invalid.
*
* uint8_t touchStartRead(uint8_t pin);
*
* The following non-blocking routine returns the touch value data for channel
* ch, or -1 if the touch data is not yet ready for that channel ch.
*
* int touchReady(uint8_t ch);
*
* Example usage:
*
* extern "C" uint8_t touchStartRead(uint8_t pin);
* extern "C" int touchReady(uint8_t ch);
*
* int touchPin = 22; // (A8 pin on Teensy 3.2)
* int state = 0; // 0: ready to start next read, 1: waiting to finish
* int ch; // TSI channel number
*
* void loop()
* {
* if (state == 0) // starting state
* {
* ch = touchStartRead(touchPin);
* if (ch == 255) Serial.println("Invalid touch pin");
* else state = 1; // ready to poll
* }
* else // non-blocking polling
* {
* int v = touchReady(ch); // Be sure to use the channel num, not pin num.
* if (v == -1)
* {
* // Not yet ready
* }
* else
* {
* Serial.print("touch val read: "); Serial.println(v);
* state = 0; // ready to initiate next read
* }
*
* delay(100); // Just for illustration purposes
* }
* }
*
*
*
* Old prjc.com comments:
*
* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2017 PJRC.COM, LLC.
*
* 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:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* 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.
*/
// Bry says: The following code is pretty much left intact from touch.c,
// except modified for non-blocking reads.
#include "core_pins.h"
//#include "HardwareSerial.h"
#if defined(HAS_KINETIS_TSI) || defined(HAS_KINETIS_TSI_LITE)
#if defined(__MK20DX128__) || defined(__MK20DX256__)
// These settings give approx 0.02 pF sensitivity and 1200 pF range
// Lower current, higher number of scans, and higher prescaler
// increase sensitivity, but the trade-off is longer measurement
// time and decreased range.
#define CURRENT 2 // 0 to 15 - current to use, value is 2*(current+1)
#define NSCAN 9 // number of times to scan, 0 to 31, value is nscan+1
#define PRESCALE 2 // prescaler, 0 to 7 - value is 2^(prescaler+1)
static const uint8_t pin2tsi[] = {
//0 1 2 3 4 5 6 7 8 9
9, 10, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 13, 0, 6, 8, 7,
255, 255, 14, 15, 255, 12, 255, 255, 255, 255,
255, 255, 11, 5
};
#elif defined(__MK66FX1M0__)
#define NSCAN 9
#define PRESCALE 2
static const uint8_t pin2tsi[] = {
//0 1 2 3 4 5 6 7 8 9
9, 10, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 13, 0, 6, 8, 7,
255, 255, 14, 15, 255, 255, 255, 255, 255, 11,
12, 255, 255, 255, 255, 255, 255, 255, 255, 255
};
#elif defined(__MKL26Z64__)
#define NSCAN 9
#define PRESCALE 2
static const uint8_t pin2tsi[] = {
//0 1 2 3 4 5 6 7 8 9
9, 10, 255, 2, 3, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 13, 0, 6, 8, 7,
255, 255, 14, 15, 255, 255, 255
};
#endif
// output is approx pF * 50
// time to measure 33 pF is approx 0.25 ms
// time to measure 1000 pF is approx 4.5 ms
// Bry: Used to return int touch value. Now returns channel num.
uint8_t touchStartRead(uint8_t pin)
{
uint32_t ch;
if (pin >= NUM_DIGITAL_PINS) return 255; // Bry: Indicates bad pin
ch = pin2tsi[pin];
if (ch == 255) return 255; // Bry: Indicates bad pin
*portConfigRegister(pin) = PORT_PCR_MUX(0);
SIM_SCGC5 |= SIM_SCGC5_TSI;
#if defined(HAS_KINETIS_TSI)
TSI0_GENCS = 0;
TSI0_PEN = (1 << ch);
TSI0_SCANC = TSI_SCANC_REFCHRG(3) | TSI_SCANC_EXTCHRG(CURRENT);
TSI0_GENCS = TSI_GENCS_NSCN(NSCAN) | TSI_GENCS_PS(PRESCALE) | TSI_GENCS_TSIEN | TSI_GENCS_SWTS;
delayMicroseconds(10);
// Bry removed: while (TSI0_GENCS & TSI_GENCS_SCNIP) ; // wait
// Bry removed: delayMicroseconds(1);
// Bry removed: return *((volatile uint16_t *)(&TSI0_CNTR1) + ch);
#elif defined(HAS_KINETIS_TSI_LITE)
TSI0_GENCS = TSI_GENCS_REFCHRG(4) | TSI_GENCS_EXTCHRG(3) | TSI_GENCS_PS(PRESCALE)
| TSI_GENCS_NSCN(NSCAN) | TSI_GENCS_TSIEN | TSI_GENCS_EOSF;
TSI0_DATA = TSI_DATA_TSICH(ch) | TSI_DATA_SWTS;
delayMicroseconds(10);
// Bry removed: while (TSI0_GENCS & TSI_GENCS_SCNIP) ; // wait
// Bry removed: delayMicroseconds(1);
// Bry removed: return TSI0_DATA & 0xFFFF;
#endif
return ch; // Bry: This is the parm to pass to touchReady()
}
// Bry Added this routine
int touchReady(uint8_t ch)
{
#if defined(HAS_KINETIS_TSI)
if (TSI0_GENCS & TSI_GENCS_SCNIP) return -1; // not ready
delayMicroseconds(1);
return *((volatile uint16_t *)(&TSI0_CNTR1) + ch);
#elif defined(HAS_KINETIS_TSI_LITE)
if (TSI0_GENCS & TSI_GENCS_SCNIP) return -1; // not ready
delayMicroseconds(1);
return TSI0_DATA & 0xFFFF;
#endif
}
#else
uint8_t touchStartRead(uint8_t pin)
{
return 255; // no Touch sensing :(
}
int touchReady(uint8_t ch)
{
return -1; // no Touch sensing :(
}
#endif