Created
January 17, 2012 18:40
-
-
Save mrmekon/1628041 to your computer and use it in GitHub Desktop.
Capacitive touch sensor via ADC (no external components)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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) | |
{ | |
} | |
//------------------------------------------------------------------------------ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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