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.

circuit

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.

scope

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值