Introduction: Capacitive Touch With PIC16F886 Microcontroller

About: Like geckos.

In this tutorial we will go over how you can use a PIC16F886 microcontroller to detect differances in capacitance, this can later be used to tell if a touch pad is being pressed. It's good to be familiar with pic microcontrollers prior to making this project.

Step 1: Wire Up Your Circuit

First, let's start by wiring up the circuit according to the schematic above. To make the touch pad you can fold aluminium foil into a square and tape on a wire. You can experiment around with different values for the 100k resistor, I found 100k worked well for me.

The RC4 pin is used to start charging/discharging the capacitance to be measured. C12IN0 is connected to the - side of an internal comparator and the C1IN pin is connected to the + side of the same comparator. The microcontroller sees the capacitance as fully charged when the C12IN0 voltage reaches above the C1IN voltage. The resistive voltage divider makes sure C1IN is close to 5 volts.

Since the touch pad depends of there being a significant capacitance between you and the circuit ground there is a possibility that a battery might not work.

Step 2: The Header File

Finished with all the connections? Good, we will proceed with the header file. We will be using the XC8 compiler and as the title suggests you shall now create a new header file in your project and copy-paste the following code. You can just as well copy-paste it above your main code without any header file.

#define TOUCH_SAMPLE 10 #define DISCHARGE_TIME 5

int count; int calibrationValue, maxCalibrationValue, minCalibrationValue;

int getChargeTime(){ int timerCount = 0; int overflowCount = 0; //discharge capacitance to be measured RC4 = 0; __delay_ms(DISCHARGE_TIME); //give enough delay to fully (almost fully actually) discharge the "capacitor" //clear the timer overflow flag T0IF = 0; //wait for timer to overflow, start count from 0 while(!T0IF); T0IF = 0; //start charging capacitance to be measured RC4 = 1; //wait for capacitance to charge up to the referance voltage while(C1OUT){ timerCount = TMR0; if(T0IF){ overflowCount++; T0IF = 0; } } count = (256 * overflowCount) + timerCount; //reset timerCount timerCount = 0; overflowCount = 0; return count; }

int isTouching(int tolerance){ //average of multiple samples double average = 0; for(int i = 0; i < TOUCH_SAMPLE; i++){ if(getChargeTime() > calibrationValue + tolerance) average++; } average /= TOUCH_SAMPLE; //average will be a number between 0 and 1 if(average > 0.2) return 1; return 0; }

void calibrate(){ int average = 0; int samples[CALIBRATION_SAMPLE]; //get average value for(int i = 0; i < CALIBRATION_SAMPLE; i++){ samples[i] = getChargeTime(); average += samples[i]; } average /= CALIBRATION_SAMPLE; calibrationValue = average; //get max/min values maxCalibrationValue = samples[0]; minCalibrationValue = samples[0]; for(int i = 0; i < CALIBRATION_SAMPLE; i++){ if(samples[i] > maxCalibrationValue) maxCalibrationValue = samples[i]; if(samples[i] < minCalibrationValue) minCalibrationValue = samples[i]; } }

void setupCapacitiveTouch(){ //setting charge/discharge pin as output, in this case it's RC4 TRISCbits.TRISC4 = 0; //setting up timer0 T0CS = 0; PSA = 1; //setting up comparator C1CH0 = 0; C1CH1 = 0; C1R = 0; C1ON = 1; C1POL = 0; //clearing count values count = 0; //clearing calibration values calibrationValue = 0; maxCalibrationValue = 0; minCalibrationValue = 0; //run calibration on start calibrate(); }

Step 3: Writing the Main Code

Starting with the main code, you'll need to include the header file created in the previous step. The following code is an example of how you can use the isTouching function as a switch. In my case i gave the header the name capacitiveTouch.h.

#include <xc.h> #include "capacitiveTouch.h"

// this variable tells if the button is or is not already pressed int lastState = 0;

void main(){ //setting RC5 as output TRISCbits.TRISC5 = 0; //you need to call this function up on start of the program setupCapacitiveTouch(); __delay_ms(1000); //calibrate after your exact setup calibrate(); while(1){ //checking if the button is getting pressed if(isTouching(15) && lastState == 0){ if(RC5) RC5 = 0; else RC5 = 1; lastState = 1; } //checking if button is getting released else if(lastState == 1 && !isTouching(15)) lastState = 0; __delay_ms(20); } }


When this function gets called the variables calibrationValue, maxCalibrationValue and minCalibrationValue will be updated. calibrationValue is used by the isTouching function. Keep in mind that the touch pad should be left alone during calibration.


Needs to be called in the beginning of your program. It sets up the necessary bits used by the other functions. It also runs a callibration. However I got better results by waiting a second and run the calibration again separately.


This function returns 1 if it detects an increase in capacitance on the C12IN0 and returns 0 if the capacitance is close to the one it was during calibration. Simply stated, if someone touches the pad the isTouching function will return 1. The function also wants a parameter as an input, this is the tolerance for it to trigger. The higher the tolerance value the less sensitive it gets. In my setup I found 15 worked well, but because this depends on ocsillator frequency and how much capacitance is added when you press it you should experiment around with this value until you find something that works for you.


When you want to know how long time it would take to charge up the capacitance to the CVREF voltage, this function will test it and return an integer. To get the time in seconds you use this formula:
(4 * getChargeTime) / oscillatorFrequency = chargeTimeInSeconds
This formula can also be used to get the tolerance input from the isTouching function to seconds.