tip for faster capacitive touch reads on teensy 3.6/3.5

Status
Not open for further replies.

mmalex

Member
there are a couple of old threads I found while investigating if I could speed up the time taken by touchRead() function on teensy 3.6.
a few threads noted that when moving from T3.2 to T3.6, the speed of the capacitive touch dropped significantly, which can be an issue for responsiveness, especially when time-multiplexing touch inputs. but there's good news!

the threads all contained useful snippets, and indicated that the 3.6 chip has a slightly more limited touch unit inside it, but none of them came to a firm conclusion in one place. so I just wanted to jot down my findings, drawn from those threads and just experimenting with the register settings, in case they are useful for others:

there's a simple change you can make to touchRead() to make it roughly an order of magnitude faster by increasing the drive current. it's a trivial change to a register configuration, but it's not provided by the arduino-compatible API, so I thought I'd jot it down here for anyone passing by here looking for a way to speed things up.

the bottom line is to extract the teensy 'TSI lite' specific code from the core touch.c, thus:
Code:
#define NSCAN     9
#define PRESCALE  2
void myTouchReadT36Start(uint8_t pin)
{
	uint32_t ch;
	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;
    // change TSI_GENCS_REFCHRG and TSI_GENCS_EXTCHRG to 7 for higher drive current & speed
	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;
}

int myTouchReadT36End()
{
	while (TSI0_GENCS & TSI_GENCS_SCNIP) ; // wait
	delayMicroseconds(1);
	return TSI0_DATA & 0xFFFF;
}

int myTouchReadT36(uint8_t pin) {
    myTouchReadT36Start(pin);
	delayMicroseconds(10);
    return myTouchReadT36End();
}

here I've renamed touchRead to myTouchReadT36, and split it into two halves - start and end.
in this way you can do a kind of non-blocking read if you want - call myTouchReadT36Start, do some other work (eg servicing some I2C, or reading an ADC, or whatever), and then myTouchReadT36End.
and the key to making it faster is simply to change the values in the line that sets the TSI0_GENCS register, in particular the TSI_GENCS_REFCHRG and TSI_GENCS_EXTCHRG settings.

you'll need to tune it for your electrodes, the defaults are as above (4, 3) - I found that I changed to 7,7 (maximum drive!) and it worked well for my electrodes with relatively high 'base' (untouched) capacitance - the default routine was returning around 4000, rising to 9000 when touched, taking over a millisecond.
with some tuning, I still got a wide range of values but times under 200 microseconds.

you can also tune the NSCAN and PRESCALE defines to configure how many averageing passes and how much prescales the counter (?) - but the key change is to increase the charge values. I think I ended up with NSCAN 7 and PRESCALE 3, tho this felt quite application dependent.

anyway I hope that by pulling together info that is already out there on this forum in one 'it works! you can make it faster!' post will be helpful to someone.
 
I’ve also found that another easy way to handle touchRead is through the use of TeensyThreads, have your touchRead on its own thread and because you can set how long each thread will run for it’s almost impossible for it to take too much time out of the rest of your program.
 
If you just want to use poll-based touch sensing, there is a way to do it without any "blocking" calls and without interrupts -- all it involves is first starting touch scans and then polling for particular flags being set/cleared that indicates the scan(s) are finished. These latter tests can be placed in your main loop code, and in effect allows you to do touch-based sensing with very little overhead in your main loop -- and without any tricky interrupt logic.

I developed two classes to do this: One for Teensy 3.2, and the other for Teensy3.6/LC. See this post: https://forum.pjrc.com/threads/54527-Asynchronous-(poll-based)-touch-sensing?highlight=asynchronous+touch Scroll to the bottom for the final code in the AsyncTouchMgr class (two versions). Due to the nature of the scanning hardware, the Teensy 3.6/LC version is a bit more convoluted than the Teensy 3.2 version, since with the Teensy3.6/LC you can't scan multiple sensors in parallel like you can with the Teensy 3.2. The AsyncTouchMgr class handles the convoluted logic for you.

This code also illustrates a different way to test for scan completion using the TSI_GENCS_EOSF flag instead of the TSI_GENCS_SCNIP flag, the latter being what is used in the "official" touch sensing code provided by PJRC, but that code requires several artificial delays (admittedly just ten or eleven microseconds) on each scan. The EOSF flag method allows you to get rid of those artificial delays and results in very little overhead in your main loop.

By using this method, it doesn't matter, in terms of blocking delays, what you set your touch sensing currents to. Instead, you can adjust those currents to be what your accuracy needs are. Note that no magic is taking place here. The scans take however long they take given sensor capacitance and current. All the aforementioned method does is prevent the main loop from being blocked during the scanning, so that you can be doing other things while waiting for the scans to finish.
 
Status
Not open for further replies.
Back
Top