Al Williams

Dr. Dobb's Bloggers

The Touch of a Button

December 19, 2014

Of course, I wasn't satisfied with the simple approach, so I added a bit of code to sense swipes much as you would find on a touchscreen phone.

I've noticed a lot of development boards now have touch sensors on board. The STM32L053 discovery board has one as well as the Freescale FRDM-KL25Z board I've talked about before. The typical way to use these touch sensors is for controlling something "knob-like" (perhaps a volume control). However, I often find these sensors useful for emulating buttons. Especially since many of these boards don't have many (if any) mechanical switches.

The concept is simple. You divide the touch sensor into multiple zones and then detect touch events and categorize them. Of course, I wasn't satisfied with the simple approach, so I added a bit of code to sense swipes much as you would find on a touchscreen phone.

I broke out the KL25Z board again to develop a simple library. The touch sensor operates by sensing your finger's capacitance, but thanks to the mbed libraries, you don't really need to understand that. You simply instantiate a TSIAnalogSlider object. Then you can call the readPercentage method of the object.

The mbed libraries seem to enjoy using floating point numbers, and readPercentage returns a number between 0.0 (no touch) to 1.0 (a touch at the right hand side). It seems like integers would be more efficient, but the ARM processor on the board handles floating point with no real problem, so it doesn't matter — at least not for this simple example.

The code implements a very simple state machine. There are three global variables shared between the main API call (getbtn) and a function scheduled using an mbed Timeout (checkbtn). The variables are:

candidate — A potential button number (0-3)

realbtn — An actual button press (0-3 or 256/512 for swipes)

hold — Set when the state machine is waiting for a button press or swipe to complete

Note two of these are volatile because they are accessed from both the main line code and the Timeout code. You can think of a Timeout as a thread that gets executed sometime in the future. Unlike a Ticker object, the Timeout occurs exactly once per call to attach. That is, a call to attach causes the attached function to execute after the specified interval, but it won’t execute again unless there is another call to attach later.

When the program detects a finger press, it schedules the timeout function. The timeout function examines the state of the sensor. There are three possibilities:

There is no touch (button pressed quickly)

There is a touch in the same zone as before (button pressed)

There is a touch in a different zone (swipe)

In the case of a swipe, the program can tell the difference between a right and a left swipe by determining if the new zone is a higher or lower number than the original zone.

You can find the code below. There are several enhancements you could try to add. For example, regular button presses could repeat at some programmable rate. You could also implement the idea of a "long press" if you hold a button down for a certain length of time.

I doubt this is what the designer had in mind for the touch sensor, but it works well enough. A good example of adapting your design to the tools at hand to avoid having to customize or alter the existing hardware.

Listing:

#include "mbed.h"
#include "tsi_sensor.h"
// Simple button/swipe library for FRDM-KL25Z
// Williams -- DDJ
/* This defines will be replaced by PinNames soon */
#if defined (TARGET_KL25Z) || defined (TARGET_KL46Z)
#define ELEC0 9
#define ELEC1 10
#elif defined (TARGET_KL05Z)
#define ELEC0 9
#define ELEC1 8
#else
#error TARGET NOT DEFINED
#endif
// Define the "zones" considered to be buttons
// 4 is about the limit and 3 might be even better
// You could also equal space them automatically pretty easily
// (e.g., specify 25% and fill in BTNMAX programmatically
float BTNMAX[]= {0.25, 0.50, 0.75, 1.00 };
#define SWIPETIME .350 // seconds to wait before sensing a button push or swipe
#define SWIPE_R 256 // virtual button code for right swipe
#define SWIPE_L 512 // virtual button code for left swipe
Serial pc(USBTX, USBRX); // tx, rx for debugging
TSIAnalogSlider tsi(ELEC0, ELEC1, 40); // The Analog slider
Timeout scan;
int candidate=-1; // possible button push (-1 is none)
volatile int realbtn=-1; // actual button push (-1 is none)
volatile int hold=0; // waiting for button release when 1
// internal function to get raw button state
int getrawbtn()
{
float v=tsi.readPercentage(); // read slider
if (v==0.0) return -1; // no button at all
for (int i=0;i<sizeof(BTNMAX)/sizeof(BTNMAX[0]);i++) // classify by zone
if (v<BTNMAX[i]) return i;
return -1; // what?
}
// This is called by the timeout to
// either see there is no swipe
// see that there is a swipe
// or see that there is a button release
void checkbtn(void)
{
int newbtn=getrawbtn();
if (hold!=0 && newbtn==-1) // wait for key release
{
hold=0; // released so
return; // don't reschedule me
}
// reschedule us for next swipetime
scan.attach(checkbtn,SWIPETIME);
if (hold) return; // still waiting for release
hold=1; // we will be waiting for release from now on
if (newbtn==-1||newbtn==candidate) // if no touch or button is the same, the candidate is the button
{
realbtn=candidate;
return;
}
// Otherwise we are swiping either left or right
if (candidate<newbtn) realbtn=SWIPE_L; else realbtn=SWIPE_R;
return;
}
// This is the main API
// Call it to get a button code (0-3 or 256 or 512)
// You can block or not (default is to block)
int getbtn(bool kwait=true)
{
while (hold) // if holding, either wait or return
{
if (!kwait) return -1;
}
realbtn=-1; // mark that we don't know
do
{
candidate=getrawbtn(); // get a candidate (or return if not waiting)
} while (candidate==-1 || !kwait);
if (candidate==-1) return -1;
scan.attach(checkbtn,SWIPETIME); // schedule the checkbtn routine
while (realbtn==-1); // wait for realbtn to get set
return realbtn; // return it
}
// Simple test program
int main(void) {
while (true) {
pc.printf("Button %d\r\n",getbtn());
}
}

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!