Launchpad Capacitive Sensing

Here’s a demo of capacitive proximity sensing on the TI Launchpad. I’m hoping to use this in a project I’m planning.

The circuit (below), uses a 1M resistor and (optionally) a small (<100pF) capacitor connected between ground and a port pin (P1.7). The tin-foil acts as a capacitor. When your body comes near or into contact, the capacitance increases. If your Launchpad isn’t grounded (ie. running from battery power), add a second foil to ground and move your hand between them.

I’ve found that when using a small capacitor alongside the foil I see less background noise.

By charging the capacitor then timing the discharge with a known resistance, we can determine the capacitance.

t = RC

Several cycles are shown on the trace below. To charge the capacitor, a port pin is driven high (set to an output and set high). A timer is then started and the pin is set to an input with an interupt enabled to detect a high to low edge. The capacitor discharges via the 1M resistor. Once the voltage drops below the input threshold for the pin, the timer is stopped.

In order to detect a meaningful change in capacitance, it’s necessary to filter the values. The background noise is sampled for 1s on startup (pressing the Launchpad button also recalibrates). For a discharge time to indicate a touch, it must exceed the maximum found in the calibration period.

Once a touch is detected, the onboard LED is lit. It’s then turned off a short time later by the tick.

Download source and Makefile.

Source code:

#include <io.h>
#include <signal.h>
#include <stdbool.h>
 
/****************************************************/
 
#define     SWITCH_BIT            BIT3  // Launchpad switch on P1.3
#define     LED_BIT               BIT0  // Launchpad LED on P1.0
#define     CAP_BIT               BIT7  // RC circuit on P1.7
 
/****************************************************/
 
// WDT Timer providing a system tick
#define TICKS_PER_SEC (1000/32)
 
// Time to sample background noise level
#define SAMPLING_PERIOD (TICKS_PER_SEC * 1)
 
/****************************************************/
 
typedef enum
{
    TRIGGER_RECALIBRATE,  // start sampling background noise level
    TRIGGER_SAMPLING,     // sampling background noise
    TRIGGER_RUNNING,      // running normally
} state_t;
 
static state_t state = TRIGGER_RECALIBRATE;       // trigger state
static uint32_t min_sample;         // low noise value
static uint32_t max_sample;         // high noise value
static uint32_t sampling_timeout;   // tick counter for stopping sampling (SAMPLING_PERIOD)
 
/****************************************************/
 
static volatile uint32_t sys_ticks = 0; // global tick counter
 
static volatile uint32_t rc_discharge_time = 0; // last discharge time
static volatile bool measuring = false;         // is discharge being measured
 
/****************************************************/
 
static void cpu_init(void)
{
    // configure watchdog timer as 32ms interval timer
    IFG1 &=~WDTIFG;
    IE1 &=~WDTIE;
    WDTCTL = WDTPW + WDTHOLD;
    WDTCTL = WDT_MDLY_32;
    IE1 |= WDTIE;
 
    // configure system clock
    BCSCTL1 = CALBC1_1MHZ; // Set range
    DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz
}
 
// Watchdog interval timer
interrupt(WDT_VECTOR) WDT_ISR(void)
{
    sys_ticks++;
}
 
 
// Port1 ISR
interrupt(PORT1_VECTOR) P1_ISR(void)
{
    uint32_t tar = TAR; // store TAR as early as possible
 
    if((P1IFG & CAP_BIT) == CAP_BIT)    // cap discharged
    {
        rc_discharge_time = tar - rc_discharge_time;
        measuring = false;
        P1IE &= ~CAP_BIT;    // interrupt disable
    }
    else
    if((P1IFG & SWITCH_BIT) == SWITCH_BIT)    // switch pressed
    {
        state = TRIGGER_RECALIBRATE;
    }
 
    P1IFG = 0x00;   // clear interrupt flags
}
 
// wait a short time (for cap to charge)
void wait(void)
{
    volatile int i;
    for(i=0;i<32;i++);
}
 
// test if an RC discharge time looks like a trigger or not
static bool isTrigger(uint32_t sample)
{
    switch(state)
    {
        case TRIGGER_RECALIBRATE:
            // Sample background noise for SAMPLING_PERIOD
            sampling_timeout = sys_ticks + SAMPLING_PERIOD;
            min_sample = 0xFFFFFFFF;
            max_sample = 0x00000000;
            state = TRIGGER_SAMPLING;
 
        // DROP THROUGH
 
        case TRIGGER_SAMPLING:
            if (sample < min_sample)
                min_sample = sample-1;
            if (sample > max_sample)
                max_sample = sample+1;
 
            if (sys_ticks >= sampling_timeout)  // sampling time over
                state = TRIGGER_RUNNING;
        break;
 
        case TRIGGER_RUNNING:
            if (sample < min_sample)
                state = TRIGGER_RECALIBRATE;     // the environment has changed, recalibrate
            else
            {
                if (sample > max_sample)
                    return true;
            }
        break;
    }
 
    return false;
}
 
// Begin measuring RC discharge time
static void start_measuring(void)
{
    measuring = true;
 
    // SMCLK continuous
    TACTL = TASSEL_2 | MC_2;
    TACCR0 = 0x00;
 
    // drive capacitor high, charge it up
    P1DIR |= CAP_BIT;
    P1OUT |= CAP_BIT;
    wait(); // wait for cap to charge
 
    // store start count
    TAR = 0;
    rc_discharge_time = TAR;
 
    // interrupt when capacitor drops below pin's "high" voltage
    P1IES |= CAP_BIT;   // interrupt on hi to lo edge
    P1IE |= CAP_BIT;    // interrupt enable
 
    // flip pin to input, capacitor will begin discharging
    P1DIR &= ~CAP_BIT; // set to input
}
 
int main(void)
{
    uint32_t led_timer = 0; // timed latch for LED
 
    cpu_init();
 
    // setup LED pins
    P1DIR |= LED_BIT;   // All LED pins as outputs
    P1OUT &= ~LED_BIT;  // Turn off LED
 
    // setup switch interrupt
    P1DIR &= ~SWITCH_BIT;  // as input
    P1IES |= 0x01;          // interrupt on falling edge
    P1IE |= SWITCH_BIT;    // interrupt enable
 
    // start measuring discharge time
    start_measuring();
    eint();
 
    while(1)
    {
        if (!measuring)
        {
            // test discharge time to see if it looks like a trigger
            if (isTrigger(rc_discharge_time))
            {
                P1OUT |= LED_BIT;       // turn on LED
                led_timer = sys_ticks;  // record current time
            }
            start_measuring();  // measure again
        }
        // if 0.5s has elapsed since trigger, turn off LED
        if (led_timer != 0 && sys_ticks > led_timer + (TICKS_PER_SEC/2))
        {
            P1OUT &= ~LED_BIT;          // turn off LED
            led_timer = 0;              // clear timer
        }
    }
}

Makefile:

TARGET=captouch
 
CC=msp430-gcc
SIZE=msp430-size
STRIP=msp430-strip
 
CFLAGS=-Os -Wall -g -mmcu=msp430x2013 -ffunction-sections -fdata-sections -fno-inline-small-functions
 
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += -Wl,--relax
LDFLAGS += -Wl,--gc-sections
 
OBJS=$(TARGET).o
 
all: $(TARGET).elf
 
$(TARGET).elf: $(OBJS)
	$(CC) $(CFLAGS) -o $(TARGET).elf $(OBJS) $(LDFLAGS)
	$(STRIP) $(TARGET).elf
	$(SIZE) --format=sysv $(TARGET).elf
 
program: $(TARGET).elf
	mspdebug rf2500 "prog $(TARGET).elf"
 
%.o: %.c
	$(CC) $(CFLAGS) -c $&lt;
 
clean:
	rm -rf $(TARGET).elf $(OBJS) $(TARGET).map

你可能感兴趣的:(Launchpad Capacitive Sensing)