Skip to content

Instantly share code, notes, and snippets.

@mrmekon
Created January 17, 2012 18:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrmekon/1628041 to your computer and use it in GitHub Desktop.
Save mrmekon/1628041 to your computer and use it in GitHub Desktop.
Capacitive touch sensor via ADC (no external components)
/*
* Capacitive touch sensor via ADC
* Trevor Bentley, January 2012
*
* Uses ADC on MSP430 to approximate a capacitive touch sensor.
* No external components necessary. Simply hook a wire to P1.3.
*
*
* Theory:
* Based on Microchip AN1298 app note 'Capacitive Touch Using Only and ADC'
*
* ADC has an internal capacitor in the Sample And Hold circuit that is
* shared between all ADC inputs.
*
* Set ADC to read Vref input to charge internal cap. Rapidly switch to
* reading a pin (P1.3) and perform sampling before internal cap is fully
* discharged.
*
* The rate of discharge will vary with total capacitance on the input,
* so a finger touching P1.3 changes the value read.
*
* This program uses two moving averages, one over many samples and one
* over just a few samples. If the short average differs from the long
* average, a touch is predicted.
*
* It works.. okay.
*/
#include <msp430x22x2.h>
#include <msp430/adc10.h>
#include <signal.h>
#include <string.h>
//------------------------------------------------------------------------------
// Hardware-related definitions
//------------------------------------------------------------------------------
#define UART_TXD 0x02 // TXD on P1.1 (Timer0_A.OUT0)
#define UART_RXD 0x04 // RXD on P1.2 (Timer0_A.CCI1A)
#define ADC_PIN 0x08 // LDPump detect P1.4
#define LED1_PIN 0x01
#define LED2_PIN 0x40
//------------------------------------------------------------------------------
// Conditions for 9600 Baud SW UART, SMCLK = 1MHz
//------------------------------------------------------------------------------
#define UART_TBIT_DIV_2 (1000000U / (9600 * 2))
#define UART_TBIT (1000000U / 9600)
//------------------------------------------------------------------------------
// Global variables used for full-duplex UART communication
//------------------------------------------------------------------------------
unsigned int txData = 0; // UART internal variable for TX
unsigned char rxBuffer = 0; // Received UART character
static volatile unsigned char adc_done = 1;
static volatile unsigned int adc_word = 0;
#define AVERAGE_SHIFT 6
static unsigned short average = 0x1C0 << AVERAGE_SHIFT;
#define SHORT_SHIFT 2
static unsigned short short_average = 0x1C0 << SHORT_SHIFT;
#define CALIBRATION_COUNT 200
//------------------------------------------------------------------------------
// Function prototypes
//------------------------------------------------------------------------------
void Timer_A0_ISR(void);
static void __inline__ delay(register unsigned int n);
void adc_read(void);
void print_int(unsigned short val);
void uart_isr(void);
void TimerA_UART_init(void);
void TimerA_UART_tx(unsigned char byte);
void TimerA_UART_print(char *string);
void TimerB_ISR(void);
unsigned char hexvals[] = {
'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F',
};
//------------------------------------------------------------------------------
// main()
//------------------------------------------------------------------------------
void main(void)
{
unsigned short last_avg = 0;
unsigned short count = 0;
unsigned char calibrated = 0;
// Configure clocks
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
DCOCTL = 0x00; // Set DCOCLK to 1MHz
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
// Configure Port 1
P1OUT = 0x00; // Initialize all GPIO
P1DIR = 0xFF & ~(UART_RXD | ADC_PIN); // Set all pins but RXD to output
P1SEL = UART_TXD | UART_RXD; // Timer function for TXD/RXD pins
P1REN = ADC_PIN; // enable pull-up on ADC
eint(); // Enable interrupts
__bis_SR_register(GIE);
TimerA_UART_init();
TimerA_UART_print("\r\nCAPTOUCH_ADC\r\n");
// Take a long moving average and a short moving average.
// If the short average is different from the long average,
// count it as a touch. No debouncing.
//
// Red LED lit after calibration (getting averages correct) P1.0
// Green LED lit on touch P1.6
for (;;)
{
if (adc_done) {
adc_read();
last_avg = average;
average = average - (average>>AVERAGE_SHIFT) + adc_word;
short_average = short_average - (short_average>>SHORT_SHIFT) + adc_word;
if (!calibrated && count++ < CALIBRATION_COUNT) {
continue;
}
else if (!calibrated && count >= CALIBRATION_COUNT) {
calibrated = 1;
P1OUT |= LED1_PIN;
print_int(average>>AVERAGE_SHIFT);
TimerA_UART_print("calibrated.\r\n");
}
if (short_average>>SHORT_SHIFT != average>>AVERAGE_SHIFT) {
P1OUT |= LED2_PIN;
TimerA_UART_print("TOUCH ");
print_int(short_average>>SHORT_SHIFT);
TimerA_UART_print(" != ");
print_int(average>>AVERAGE_SHIFT);
TimerA_UART_print("\r\n");
}
delay(3000);
P1OUT &= ~LED2_PIN;
}
}
}
void print_int(unsigned short val) {
unsigned char mstr[10];
TimerA_UART_init();
mstr[0] = hexvals[val >> 12 & 0xf];
mstr[1] = hexvals[val >> 8 & 0xF];
mstr[2] = hexvals[val >> 4 & 0xF];
mstr[3] = hexvals[val & 0xF];
mstr[4] = 0;
TimerA_UART_print(mstr);
}
static void __inline__ delay(register unsigned int n)
{
__asm__ __volatile__ (
"1: \n"
" dec %[n] \n"
" jne 1b \n"
: [n] "+r"(n));
}
void adc_read(void) {
ADC10CTL0 = 0;
ADC10AE0 = 0;
// Drive output high to charge caps
P1DIR |= ADC_PIN;
P1OUT |= ADC_PIN;
delay(2000); // allow caps to charge
// Reset back to input
P1DIR &= ~(ADC_PIN);
P1OUT &= ~(ADC_PIN);
// Set pin to ADC mode
ADC10AE0 = ADC_PIN;
adc_done = 0;
// Configure ADC
#if 0
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE + ADC10SR;
ADC10CTL1 = ADC10SSEL_3 + INCH_3;
#else
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10SR + REFOUT + REF2_5V + REFON;
ADC10CTL1 = ADC10SSEL_3 + 0x1000;
ADC10CTL0 |= ENC + ADC10SC;
delay(10000);
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE + ADC10SR;
ADC10CTL1 = ADC10SSEL_3 + INCH_3;
ADC10CTL0 |= ENC + ADC10SC;
#endif
}
enablenested interrupt (ADC10_VECTOR) adc_isr(void) {
adc_word = ADC10MEM;
adc_done = 1;
}
//------------------------------------------------------------------------------
// Timer_A UART - Transmit Interrupt Handler
//------------------------------------------------------------------------------
enablenested interrupt (TIMERA0_VECTOR) Timer_A0_ISR(void)
{
static unsigned char txBitCnt = 10;
uart:
TACCR0 += UART_TBIT; // Add Offset to CCRx
if (txBitCnt == 0) { // All bits TXed?
TACCTL0 &= ~CCIE; // All bits TXed, disable interrupt
txBitCnt = 10; // Re-load bit counter
}
else {
if (txData & 0x01) {
TACCTL0 &= ~OUTMOD2; // TX Mark '1'
}
else {
TACCTL0 |= OUTMOD2; // TX Space '0'
}
txData >>= 1;
txBitCnt--;
}
}
//------------------------------------------------------------------------------
// Function configures Timer_A for full-duplex UART operation
//------------------------------------------------------------------------------
void TimerA_UART_init(void)
{
TACTL = MC_0; // Disable timer
TACCTL0 = OUT; // Set TXD Idle as Mark = '1'
TACCTL1 = SCS + CM1 + CAP + CCIE; // Sync, Neg Edge, Capture, Int
TACTL = TASSEL_2 + MC_2 + TACLR; // SMCLK, start in continuous mode
}
//------------------------------------------------------------------------------
// Outputs one byte using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_tx(unsigned char byte)
{
while (TACCTL0 & CCIE); // Ensure last char got TX'd
TACCR0 = TAR; // Current state of TA counter
TACCR0 += UART_TBIT; // One bit time till first bit
TACCTL0 = OUTMOD0 + CCIE; // Set TXD on EQU0, Int
txData = byte; // Load global variable
txData |= 0x100; // Add mark stop bit to TXData
txData <<= 1; // Add space start bit
}
//------------------------------------------------------------------------------
// Prints a string over using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_print(char *string)
{
while (*string) {
TimerA_UART_tx(*string++);
}
}
//------------------------------------------------------------------------------
// Timer_A UART - Receive Interrupt Handler
//------------------------------------------------------------------------------
interrupt (TIMERA1_VECTOR) Timer_A1_ISR(void)
{
}
//------------------------------------------------------------------------------
all: default run
default:
msp430-gcc -I/usr/local/msp430-gcc-4.4.3/msp430/include/ captouch.c -save-temps -mendup-at=main -mmcu=msp430x2111 -Os
run:
mspdebug rf2500 "prog a.out"
dump:
mspdebug rf2500 "md 0x1000 256"
erase_info:
mspdebug rf2500 "erase segment 0x1000"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment