Electronic – How to use the CTMU (Charge Time Measurement Unit) for a capacitive touch sensor on PIC24F

capsensepic

I'm trying to implement a basic touch sensor using the CTMU on my PIC24F04KA200. Reading the datasheet and application notes provided by Microchip I was expecting this to be an easy endeavor. In this question I will just explain what I'm doing, post my C code and hopefully someone can point me in the right direction.

I have connected my sensor which is a copper plate on top of PCB board material about the size of a postcard. The kind of thing you buy to DIY your own circuit board. I have this connected this to AN4(RA2) pin. I have an LED and current limiting resistor connected to RA4. The only other circuit items I have are a decoupling cap .1uF on power pins and the PicKit3 powering the circuit (I've also tried with separate power supply).

All I've tried to do is follow the code example given by Microchip and essentially turn on the current source to allow my copper pad to charge and measure the voltage with A/D (as explained in the App Note) and when I touch the pad and the capacitance increases it would follow that the voltage would be lower and it would detect the touch.

My problem is that I get nothing out of the pin and it's not charging; my ADC value is always zero and on my scope it just shows 0 volts. I assume that my CTMU is not turning on, as the ADC cap should at least charge with it. I've searched the internet and Microchip forums and haven't found many people having problems so I think I must be making a simple mistake.

#define CLK 8000000L //8MHZ
#define FCY (CLK/2)

#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <libpic30.h>
//#include "p24Fxxxx.h"
#include "uart.h"
#include "p24F04KA200.h"

#pragma config MCLRE = ON, GCP = OFF, FNOSC = FRC, FWDTEN = OFF, ICS = PGx2, PWRTEN = ON, OSCIOFNC = ON

void setup()
{
    //setup CTMU
    CTMUCONbits.CTMUEN = 0; //make sure CTMU is disabled
    CTMUCONbits.CTMUSIDL = 0; //CTMU continues to run in idle mode
    CTMUCONbits.TGEN = 0; //disable edge delay generation mode of the CTMU
    CTMUCONbits.EDGEN = 0; //edges are blocked
    CTMUCONbits.EDGSEQEN = 0; //edge sequence not needed
    CTMUCONbits.IDISSEN = 0; //Do not ground the current source
    CTMUCONbits.CTTRIG = 0; //Trigger Output is disabled
    CTMUCONbits.EDG2POL = 0;
    CTMUCONbits.EDG2SEL = 0x3; //Edge2 Src = OC1 (don?t care)
    CTMUCONbits.EDG1POL = 1;
    CTMUCONbits.EDG1SEL = 0x3; //Edge1 Src = Timer1 (don?t care)
    //CTMUICON
    CTMUICONbits.ITRIM = 0x3F; //Maximum Positive Adjustment
    CTMUICONbits.IRNG = 0x3; //55 uA
    CTMUCONbits.CTMUEN = 1; //Enable CTMU

    //setup A/D converter
    AD1PCFG = 0x0000;
    AD1CON1 = 0x0000;
    AD1CON1bits.FORM = 0x0; //Unsigned fractional format
    AD1CON2 = 0x0000;
    AD1CON3 = 0x0000; //bits.ADRC=0;
    AD1CON3bits.SAMC = 0xB; //12 TAD
    AD1CON3bits.ADCS = 0x60; //Used formula for ADCS with 12 TAD
    AD1CHS = 0x04; //select the analog channel 4
    AD1CSSL= 0x0000;
    AD1CON1bits.ADON = 1; //Turn On A/D
}

unsigned short runCTMU()
{
    AD1CON1bits.SAMP = 1; //Manual sampling start
    CTMUCONbits.IDISSEN = 1; //drain charge on the circuit
    __delay_us(125); //wait 125us
    CTMUCONbits.IDISSEN = 0; //end drain of circuit
    CTMUCONbits.EDG1STAT = 1; //Begin charging the circuit using CTMU current source
    __delay_us(125); //wait for 125us
    CTMUCONbits.EDG1STAT = 0; //Stop charging circuit
    IFS0bits.AD1IF = 0; //make sure A/D Int not set
    AD1CON1bits.SAMP = 0; //and begin A/D conv.
    while(!IFS0bits.AD1IF); //Wait for A/D convert complete
    AD1CON1bits.DONE = 0;

    return ADC1BUF0;
}

unsigned short getVbaseline()
{
    unsigned short vRead = 0;

    for(k = 0; k < 50; k++)
    {
        vRead = vRead + runCTMU();
    }

    return (vRead/50);
}

void main()
{
    unsigned short vRead;
    unsigned short vBaseline;

    setup();

    //Set up sensor pin
    TRISA = 0x2;
    AD1CHS = 0x4;   //select AN04 as input
    AD1PCFG = ~(0x4);

    //get untouched value on start up
    vBaseline = getVbaseline();

    while(1)
    {   
        vRead = runCTMU();

        //Touch sensed
        if (vRead < vBaseline)
        {
            LATAbits.LATA4 = 1; //output LED

            //Send ADC value over UART
            UART1PutChar(vRead >> 1);
            UART1PutChar(vRead);
        }
        else //Touch not sensed
        {
            LATAbits.LATA4 = 0;
        }

    }
}

I've used this Application Note for code examples.

Best Answer

I just ran across this very old (and unanswered) question during a Google search.

The problem is that during setup, the CTMU is disabled, but it is never re-enabled:

CTMUCONbits.CTMUEN = 0; //make sure CTMU is disabled

At the very beginning of your runCTMU function, you should have:

CTMUCONbits.CTMUEN = 1; //make sure CTMU is enabled

And then disable it again just before your return in that function.