Channels ▼

Al Williams

Dr. Dobb's Bloggers

The Touch of a Button

December 19, 2014

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:

  1. There is no touch (button pressed quickly)
  2. There is a touch in the same zone as before (button pressed)
  3. 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());
    }
}

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

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.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video