A time bounded variant of touchRead()
Greetings all,
First off, I'm quite impressed with the resolution of touchRead measurements. 1/20 of a pF is way more sensitivity than I would have expected!
Due to my control-systems perspective, I'm wary a measurement that can take a variable time to return. So, I decided to code up a time bounded variant of Paul's touchRead() function. TouchReadTimeLim() takes an additional parameter, a maximum number of microseconds for the measurement. If the measurement reaches the max time before completion, it returns a negative value, usually -1, but occasionally the negative of a somewhat sane looking capacitance measurement. I've left all the other capacitance measuring parameters exactly as in touchRead().
@Paul,
I don't know what your preferred form is for patches; so please forgive me if pasting in longish chunks of code in a forum post isn't what you want. Please let me know if you'd like me to send you the modified files.
I modified two files, touch.c and core_pins.h and wrote a simple test script, TestTouchReadTimeLim. I'll paste the mods and the test code at the end of this post. I'm using the beta12 software on a Windows7 machine.
For testing, I put a short wire on a pin that does touchRead plus a 22 pF cap to ground (in addition to the capacitance from a solderless breadboard.) Here's a test run using 250 uSec for the timeout parameter:
1931 -- 22 pF cap + wire
1940
1939
1963
1932
1942
2217 -- 22 pF cap + wire + me touching wire's insulation
2219
2284
2307
2233
2287
-1 -- 22 pF cap + wire + me touching wire itself adds enough capacitance to trigger the timeout
-1
-1
-1
-1
====================
Code added to the end of touch.c:
/* **************************************************************
* touchReadTimeLim: a time-bounded version of Paul's touchRead()
* Larry Pfeffer, ursine @t gmail d0t c0m
* 27Jan2012: tested OK with sketch touchReadTestTimeBound
*
* To make it accessable, I've added a function
* prototype for touchReadTimeLim(...)to file core_pins.h,
* below the prototype for touchRead() itself:
*
* int touchReadTimeLim(uint8_t pin, unsigned Tmax); // Experimental LEP
*
************************************************************** */
#include <limits.h> /* for ULONG_MAX. Eventually move to top of
*file with other includes */
#include <stdlib.h> /* For abs(), if not included elsewhere.
*Ditto re moving to top of file */
unsigned long unwrapMicros(unsigned long curr, unsigned long prev)
/* Utility function to calculate the delta T between results
* of two calls to micros(). Robust to one rollover of the
* micros() counter.
*
* Eventually this function could be moved into some file/lib
* separate from touchRead* and renamed unwrapUL(?)
*/
{
if (curr >= prev) {
return (curr - prev);
} else {
/* the casts below may not be absolutely neccessary,
* but using them is a bit of extra insurance. */
return (unsigned long)( (ULONG_MAX - (unsigned long long)prev)
+ ((unsigned long long)curr + 1) );
} // ends if/else
}
int touchReadTimeLim(uint8_t pin, unsigned Tmax) // Tmax in uSec
// Like touchRead, but always returns within Tmax microseconds
{
uint32_t ch;
unsigned long Tstart;
Tstart = micros(); // First thing, record start time
if (pin >= NUM_DIGITAL_PINS) return 0;
ch = pin2tsi[pin];
if (ch == 255) return 0;
*portConfigRegister(pin) = PORT_PCR_MUX(0);
SIM_SCGC5 |= SIM_SCGC5_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);
/* Must unwrap the result from micros()
* to avoid getting wrong results when -- rarely but possibly --
* micros() rolls over during the measurment.
*/
/* while we haven't yet reached the timeout:
* note that the unwrap gives a deltaT,
* so must compare to Tmax, not to (Tstart + Tmax) */
while (unwrapMicros(micros(), Tstart) < (Tmax))
{
if (TSI0_GENCS & TSI_GENCS_SCNIP) { // if not ready
delayMicroseconds(1); // wait
} else {
delayMicroseconds(1); // not sure delay still necc.
// Normal return
return *((volatile uint16_t *)(&TSI0_CNTR1) + ch);
}
} // ends while micros()
// If we timeout, return negative of probably garbage value
// note extra -1, so timeout result is neg even if register
// reads zero when read before the measurment completes.
return -1 - abs(*((volatile uint16_t *)(&TSI0_CNTR1) + ch));
}
====================
One line of code added to core_pins.h, just below the prototype for touchRead():
int touchReadTimeLim(uint8_t pin, unsigned Tmax); // Experimental LEP
====================
The test sketch:
/* TestTouchReadTimeLim: a sketch to test function touchReadTimeLim, which
* is a time-bounded variant of touchRead.
#include <limits.h>
/* Note: for this test, core_pins now has declaration of touchReadTimeLim" */
//int touchReadTimeLim(uint8_t pin, unsigned long int Tmax); // should go into a header file when working
#define CAP_PIN (A8) /* change to whatever pin you use */
#define TOUCH_MAX_USEC (250) /* Paul's touchRead comment says 250 uSec for approx 33 pF */
void setup() {
Serial.begin(115200);
}
int touchReadAvg(int pin, int nSamp, long int maxT)
{
int i;
int runningSum = 0;
int cap;
for(i=0; i<nSamp; i++) {
cap = touchReadTimeLim(pin, maxT);
if (cap >= 0) {
runningSum = runningSum + cap;
} else {
// Serial.print("Timeout in touchReadTimeLim: ");
// Serial.println(cap);
return -1; // error return if any sample times out
}
}
return runningSum/nSamp;
}
void loop()
{
//Serial.println(touchReadAvg(CAP_PIN, 32, TOUCH_MAX_USEC)); // with averaging
Serial.println(touchReadTimeLim(CAP_PIN, TOUCH_MAX_USEC)); // without averaging
delay(500);
}
====================