ADC baserat kapacitivt touch lib

Elektronik- och mekanikrelaterad mjukvara/litteratur. (T.ex schema-CAD, simulering, böcker, manualer mm. OS-problem hör inte hit!)
victor_passe
Inlägg: 2436
Blev medlem: 28 januari 2007, 18:45:40
Ort: Kungsbacka

ADC baserat kapacitivt touch lib

Inlägg av victor_passe »

Hej, jag har knåpat ihop ett fungerande lib för kapacitiva touch knappar, den kräver bara en ADC och (DAC eller en ledig I/O som har en analog kanal, vill man ha en I/O får man modda koden lite)
Det är 2 filer:

adcTouch.c

Kod: Markera allt

#include <xc.h>

#include "adcTouch.h"

#define MIN(x,y) (((x)>(y))?(y):(x))
#define MAX(x,y) (((x)<(y))?(y):(x))

#define X(adcMux, lowCode, outCode, inCode) 0,
static volatile char adcTouchButtonReadings[] = {adcTouchMacro};
#undef X
static volatile char adcTouchButtonState[(sizeof (adcTouchButtonReadings) / sizeof (adcTouchButtonReadings[0]))] = {0};

static void setChannel(unsigned char nr);
static inline void vddSampleCap();
static void groundChannel(unsigned char nr);
static inline unsigned short adcGetResult();
static void inputChannel(unsigned char nr);

void adcTouchInit() {
    //Set up touch pads as analog inputs
    ANSELB |= 0b00110011;

    //Set up DAC as VDD
    DACCON0 = 0b01000000;
    DACCON1 = 0b00011111;

    //Set up ADC
    ADCON0 = 0b00000001;
    ADCON1 = 0b10110000;

}

void adcTouchRun() {
    static unsigned char state = 1;
    static unsigned char channelIndex = 0;
    if (ADCON0bits.GO)
        return;

    switch (state) {
        case 1:
            vddSampleCap();
            groundChannel(channelIndex);
            inputChannel(channelIndex);
            setChannel(channelIndex);
            ADCON0bits.GO = 1;
            break;
        case 2:
            if (adcTouchButtonState[channelIndex]) {
                if (adcGetResult() > adcTouchThreshold)
                    adcTouchButtonReadings[channelIndex]++;
                else
                    adcTouchButtonReadings[channelIndex] = 0;
            } else {
                if (adcGetResult() < adcTouchThreshold)
                    adcTouchButtonReadings[channelIndex]++;
                else
                    adcTouchButtonReadings[channelIndex] = 0;
            }
            if (adcTouchButtonReadings[channelIndex] > adcDebounce)
                adcTouchButtonState[channelIndex] ^= 1;

            if (++channelIndex == (sizeof (adcTouchButtonReadings) / sizeof (adcTouchButtonReadings[0])))
                channelIndex = 0;
            state = 0;
            break;
    }
    state++;
}

unsigned char adcTouchGetButton(unsigned char channel) {
    return adcTouchButtonState[channel];
}

static void setChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, outCode, inCode) if(nr==i++){ adcMuxCode; }
    adcTouchMacro
#undef X
}

static void groundChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, outCode, inCode) if(nr==i++){ lowCode; outCode; }
    adcTouchMacro
#undef X
}

static void inputChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, outCode, inCode) if(nr==i++){ inCode; }
    adcTouchMacro
#undef X
}

static inline void vddSampleCap() {
    ADCON0bits.CHS = 0b11110;//Sample DAC
}

static inline unsigned short adcGetResult() {
    return (ADRESH << 8) | ADRESL;
}
adcTouch.h

Kod: Markera allt

#ifndef ADCTOUCH_H
#define	ADCTOUCH_H

void adcTouchInit();
void adcTouchRun();
unsigned char adcTouchGetButton(unsigned char channel);

//Set ADC-Mux Channel Code, Drive Pad Low Code, Pad Output Code, Pad Input Code
#define adcTouchMacro \
X(ADCON0bits.CHS=13, LATB5=0, TRISB5=0, TRISB5=1)\
X(ADCON0bits.CHS=11, LATB4=0, TRISB4=0, TRISB4=1)\
X(ADCON0bits.CHS=10, LATB1=0, TRISB1=0, TRISB1=1)\
X(ADCON0bits.CHS=12, LATB0=0, TRISB0=0, TRISB0=1)\

#define adcTouchThreshold 1020
#define adcDebounce 5

#endif	
Det finns 3 synliga funktioner:
void adcTouchInit();
Kör en gång i början för att initera det hela.

void adcTouchRun();
Kör på fix frekvens, exakt frekvens spelar ingen roll, mellan 10µs och 10ms ungefär, jag kör på 1ms.

unsigned char adcTouchGetButton(unsigned char channel);
Returnerar ett om knapp #channel är nedtryckt och noll annars

Sedan har vi inställningarna, man lägger in knappar i adcTouch.h, i exemplet är 4st inlaggda.

adcTouchThreshold Anger känsligheten, 1023 är max känslighet och kommer inte funka, en yta på ca 10mmx10mm med ett finger på och lödmask imellan koppar och finger ger ca 950 i värde, inget finger ger 1022 eller 1023, jag har känsligheten på 1020, det funkar bra för mig.

adcDebounce Anger hur många på varandra följande mätningar som måste "vara överens" för att en knapp ska bli släppt eller tryckt.

Exempel på användning:

Kod: Markera allt


#include "adcTouch.h"

static void interrupt isr(void) {
    if (TMR2IE && TMR2IF) {
        TMR2IF = 0;
        adcTouchRun();
    }
}

void main() {
    TRISB = 0;
    LATB = 0b1000;

    OSCCON = 0b01110000;
    OPTION_REG = 0;

    adcTouchInit();

    //TMR2
    T2CON = 0b00000111;
    TMR2 = 0;
    PR2 = 125;

    //interrupt
    GIE = 1;
    PEIE = 1;
    TMR2IE = 1;

    while (1) {
        if (adcTouchGetButton(0))
            LED3 = 0; //Enable LED
        else
            LED3 = 1;//Disable LED
    }
}
Baserad på denna appnoten:
http://ww1.microchip.com/downloads/en/A ... 01298A.pdf
victor_passe
Inlägg: 2436
Blev medlem: 28 januari 2007, 18:45:40
Ort: Kungsbacka

Re: ADC baserat kapacitivt touch lib

Inlägg av victor_passe »

Har förbättrat koden en del, mycket känsligare och kräver inget förutom minst 2st touch knappar eller en knapp och en extra I/O med ADC.
adcTouch.c

Kod: Markera allt

#include <xc.h>

#include "adcTouch.h"


#define MIN(x,y) (((x)>(y))?(y):(x))

/*
 * Private variables
 */
#define X(adcMux, lowCode, highCode, outCode, inCode) 0,
static volatile unsigned char adcTouchButtonReadings[] = {adcTouchMacro};
#undef X
static volatile unsigned char adcTouchButtonState[(sizeof (adcTouchButtonReadings) / sizeof (adcTouchButtonReadings[0]))] = {0};

/*
 * Private function prototypes
 */
static void setChannel(unsigned char nr);
static void vddSampleCap(unsigned char nr);
static void groundChannel(unsigned char nr);
static unsigned short adcGetResult();
static void inputChannel(unsigned char nr);

/*
 * Init function
 */
void adcTouchInit() {
    //Set up touch pads as analog inputs
    ANSELB |= 0b00110011;

    //Set up ADC
    ADCON0 = 0b00000001; //Enable ADC
    ADCON1 = 0b10110000; //Internal clock
}

/*
 * Periodic task
 */
void adcTouchRun() {
    static unsigned char state = 1;
    static unsigned char channelIndex = 0;
    if (ADCON0bits.GO)//Ensure loop does not run too fast
        return;

    switch (state) {
        case 1:
            vddSampleCap(channelIndex);
            groundChannel(channelIndex);
            inputChannel(channelIndex);
            setChannel(channelIndex);
            ADCON0bits.GO = 1;
            break;
        case 2:
            if (adcTouchButtonState[channelIndex] ? (adcGetResult() > adcTouchThreshold) : (adcGetResult() < adcTouchThreshold))
                adcTouchButtonReadings[channelIndex] = MIN(adcTouchButtonReadings[channelIndex] + 1, 250);
            else
                adcTouchButtonReadings[channelIndex] = 0;

            if ((adcTouchButtonReadings[channelIndex] >= adcTouchOffDebounce) && adcTouchButtonState[channelIndex])
                adcTouchButtonState[channelIndex] = 0;
            else if ((adcTouchButtonReadings[channelIndex] >= adcTouchOnDebounce) && !adcTouchButtonState[channelIndex])
                adcTouchButtonState[channelIndex] = 1;

            if (++channelIndex == (sizeof (adcTouchButtonReadings) / sizeof (adcTouchButtonReadings[0])))
                channelIndex = 0;
            state = 0;
            break;
    }
    state++;
}

/*
 * Public function for reading button state
 */
unsigned char adcTouchGetButton(unsigned char channel) {
    return adcTouchButtonState[channel];
}

/*
 * Sets adc mux to pad
 */
static void setChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, highCode, outCode, inCode) if(nr==i++){ adcMuxCode; }
    adcTouchMacro
#undef X
}

/*
 * Set pad to 0V
 */
static void groundChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, highCode, outCode, inCode) if(nr==i++){ outCode; lowCode; }
    adcTouchMacro
#undef X
}

/*
 * Set pad to input
 */
static void inputChannel(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, highCode, outCode, inCode) if(nr==i++){ inCode; }
    adcTouchMacro
#undef X
}

/*
 * Charge next pad to Vdd and sample
 */
static void vddSampleCap(unsigned char nr) {
    unsigned char i = 0;
#define X(adcMuxCode, lowCode, highCode, outCode, inCode) if(nr==((++i)%(sizeof (adcTouchButtonReadings) / sizeof (adcTouchButtonReadings[0])))){ outCode; highCode; adcMuxCode;}
    adcTouchMacro
#undef X
}

/*
 * Get ADC result
 */
static unsigned short adcGetResult() {
    return (ADRESH << 8) | ADRESL;
}
adcTouch.h

Kod: Markera allt

#ifndef ADCTOUCH_H
#define	ADCTOUCH_H

void adcTouchInit();
void adcTouchRun();
unsigned char adcTouchGetButton(unsigned char channel);

//Set ADC-Mux Channel Code, Drive Pad Low Code, Drive Pad High Code, Pad Output Code, Pad Input Code
#define adcTouchMacro \
X(ADCON0bits.CHS=13, LATB5=0, LATB5=1, TRISB5=0, TRISB5=1)\
X(ADCON0bits.CHS=11, LATB4=0, LATB4=1, TRISB4=0, TRISB4=1)\
X(ADCON0bits.CHS=10, LATB1=0, LATB1=1, TRISB1=0, TRISB1=1)\
X(ADCON0bits.CHS=12, LATB0=0, LATB0=1, TRISB0=0, TRISB0=1)\

#define adcTouchThreshold 1020
#define adcTouchOnDebounce 1
#define adcTouchOffDebounce 10

#endif	
Kan ha en ca 1x1mm pad och detektera finger.

Man kanske egentligen borde ha sådant här i wikin och inte i en tråd.
Skriv svar