Wednesday, April 27, 2016

Simple Cap Sense Example for PIC 12F1840 in XC8

My wife wanted a simple touch switch battery powered sconce light... sounded like a great fit for a PIC 12F1840... small 8 pin microcontroller with a built in CSM (Cap Sense Module) also sometimes referred to as a CPS module. Attempting to research this was incredibly frustrating! There is just no SIMPLE example out there in either assembler or C! Everywhere I looked was only examples of using the mTouch library, which although a nice library is just far too much over-kill for my needs. Other research from forums suggested that the peripheral is just too complex and tweeky to be of much use. So off on my own... I dug into the manuals and put this simple example of using the CSM module on a PIC12F1840 (and probably the 12F1822 as well) written in XC8 free edition. Here it is in operation.

The PIC12F1840 CSM module is simply an R/C oscillator feeding a frequency counter! You select one of the four available pins as the capacitor input pin for the oscillator, setup timer 0 as the frequency counter time base, and timer 1 as the counter to count the R/C oscillator. The time base (Timer 0) is set to interrupt the PIC at specific intervals and it also gates the R/C pulses going to Timer 1. That interval should be long enough to capture a large count in Timer 1 from the R/C oscillator. The system clock is configured for internal 4MHz and Timer 0 is configured for a prescaling of 256. This gives a count of around 33,000 in Timer 1 (16 bit counter) or about half way through. So for every time interval of Timer 0 interrupting the PIC, Timer 1 will have a count of about 33,000 in it. Now in the interrupt routine you just read Timer 1 into a global variable, reset Timer 1 to zero, and re-enable interrupts for the next read. In the main body of code you just loop and watch the Timer 1 value stored in the global variable (touch in my case) and act on that value when it changes. Simple! If there is any change in the R/C oscillator's cap sense pin (like a finger, or other larger object), that will change the capacitance on that pin and shift the oscillator down ... Timer 1 will reflect that with a smaller count. Now just have your code watch for the change to a smaller count and you have a touch switch.

Here are the XC8 files out of the MPLABX IDE:

/***********************************/
/* Simple PIC 12F1840 Touch Switch Example */
/* main.c - Tim Stoddard, 2016  */
/***********************************/

#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */

#include "system.h"        /* System funct/params, like osc/peripheral config */
#include "user.h"          /* User funct/params, such as InitApp */

/***********************************/
/* User Global Variable Declaration */
/***********************************/

/* i.e. uint8_t <variable_name>; */

/***********************************/
/* Main Program  */
/***********************************/
void main(void)
{
    /* Configure the oscillator for the device */
    ConfigureOscillator();

    /* Initialize I/O and Peripherals for application */
    InitApp();

   /* Flash the LAMP a few times */
    for(i=0;i<5;i++){
        LAMP=1;
        __delay_ms(50);
        LAMP=0;
        __delay_ms(200);
    }
    /* grab the latest count from Timer 1 (touch), subtract a threshold amount,
        and save it (touch_cal) */
    touch_cal=touch-1000;

    /* MAIN LOOP */
    while(1)
    {
        if(touch<touch_cal){// sensor is touched
            LAMP=1;// turn it on
        }else{// otherwise ...
            LAMP=0;// turn it off
        }
    }

}


/***********************************/
/* user.c */
/***********************************/

#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>         /* For uint8_t definition */
#include <stdbool.h>        /* For true/false definition */

#include "user.h"
/***********************************/
/* User Functions */
/***********************************/

void InitApp(void)
{
    /* Setup analog functionality and port direction */
       TRISA = 0b00111011; // RA2 is output
       ANSA4=1;
    /* Initialize peripherals */
       CM1CON1=0;
       CPSCON0bits.CPSRM=0;// 1=variable voltage ref, 0=fixed
       CPSCON0bits.CPSRNG=0b11;// high current
       CPSCON0bits.CPSON=1;
       CPSCON1bits.CPSCH=0b11;//CPS3 selected
       //Timer 0 is the time base for CPS
       TMR0CS=0;//Timer 0 used as time base for CPS select Fosc/4
       OPTION_REGbits.PS=0b111;
       OPTION_REGbits.PSA=0;
       // Timer 1 is the freq counter for CPS
       T1CON=0b11000101; // TMR1 capacitive sensing osc, prescaler 1/1, dedicated osc disabled,no synch,timer1 enabled
       T1GSEL=0b01;// set timer 1 gate for TMR0 overflow
    /* Enable interrupts */
       TMR0IE=1;// enable TMR0 interrupts
       PEIE=1;//enable Peripheral interrupts
       GIE=1;// enable global interrupts
}


/***********************************/
/*interrupts.c */
/***********************************/

#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>         /* For uint8_t definition */
#include <stdbool.h>        /* For true/false definition */
#include "user.h"
/***********************************/
/* Interrupt Routines */
/***********************************/

#ifndef _PIC12

void interrupt isr(void)
{
 #if 1

    GIE=0;// disable all interrupts

    if(T0IF) // Timer 0 interrupt?
    {
        TMR1ON=0;// turn off timer 1
        touch=TMR1L+(unsigned int)(TMR1H << 8);// read timer 1
        TMR1H=0;// clear timer 1
        TMR1L=0;
        TMR1ON=1;// enable timer 1
        T0IF=0;
    }else{
        /* Unhandled interrupts */
    }
    GIE=1;// enable all interrupts

#endif

}
#endif


/***********************************/
/* user.h */
/***********************************/
#define _XTAL_FREQ  4000000

#define LAMP RA2

void InitApp(void);         /* I/O and Peripheral Initialization */

/* declare variables */
unsigned int touch;
unsigned int touch_cal;
unsigned int i;


/***********************************/
/* configuration_bits.c */
/***********************************/

#if defined(__XC)
    #include <xc.h>         /* XC8 General Include File */
#elif defined(HI_TECH_C)
    #include <htc.h>        /* HiTech General Include File */
#endif


// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

/***********************************/
/* system.c */
/***********************************/

#include <xc.h>         /* XC8 General Include File */
#include <stdint.h>        /* For uint8_t definition */
#include <stdbool.h>       /* For true/false definition */

#include "system.h"

void ConfigureOscillator(void)
{
    while (!HFIOFS) continue;// wait for stable osc

#if 0

    OSCCAL=_READ_OSCCAL_DATA(); /* _READ_OSCCAL_DATA macro unloads cal memory */

#endif
}

/***********************************/
/* system.h */
/***********************************/

/* TODO Define system operating frequency */

/* Microcontroller MIPs (FCY) */
#define SYS_FREQ        4000000L
#define FCY             SYS_FREQ/4

/***********************************/
/* System Function Prototypes */
/***********************************/

void ConfigureOscillator(void); /* Handles clock switching/osc initialization */