I'm reading the book: AVR Programming: Learning to Write Software for Hardware by Elliot Williams. In the Interrupt section of the book, there's a small project to make a Capacitive touch sensor.


schematic diagram

   Capacitive touch sensor demo

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include "pinDefines.h"
#include "USART.h"

#define SENSE_TIME   50
#define THRESHOLD    12000

// -------  Global Variables ---------- //
volatile uint16_t chargeCycleCount;

// ------- Functions -------- //

void initPinChangeInterrupt(void) {
  PCICR |= (1 << PCIE1);    /* enable Pin-change interrupts 1 (bank C) */
  PCMSK1 |= (1 << PC1);   /* enable specific interrupt for our pin PC1 */

ISR(PCINT1_vect) {
  chargeCycleCount++;                             /* count this change */

  CAP_SENSOR_DDR |= (1 << CAP_SENSOR);                  /* output mode */
  _delay_us(1);                                      /* charging delay */

  CAP_SENSOR_DDR &= ~(1 << CAP_SENSOR);                /* set as input */
  PCIFR |= (1 << PCIF1);             /* clear the pin-change interrupt */

int main(void) {
  // -------- Inits --------- //
  clock_prescale_set(clock_div_1);                       /* full speed */
  printString("==[ Cap Sensor ]==\r\n\r\n");

  LED_DDR = 0xff;
  MCUCR |= (1 << PUD);                          /* disable all pullups */
  CAP_SENSOR_PORT |= (1 << CAP_SENSOR);    /* we can leave output high */


  // ------ Event loop ------ //
  while (1) {

    chargeCycleCount = 0;                             /* reset counter */
    CAP_SENSOR_DDR |= (1 << CAP_SENSOR);     /* start with cap charged */
    sei();                            /* start up interrupts, counting */
    cli();                                                     /* done */
    if (chargeCycleCount < THRESHOLD) {
      LED_PORT = 0xff;
    else {
      LED_PORT = 0;
    printWord(chargeCycleCount);                    /* for fine tuning */

  }                                                  /* End event loop */
  return 0;                            /* This line is never reached */


#define CAP_SENSOR              PC1                          
#define CAP_SENSOR_PORT         PORTC
#define CAP_SENSOR_PIN          PINC
#define CAP_SENSOR_DDR          DDRC

This is the event loop description of the program:

The event loop then resets the charge cycle counter, initially charges up the capacitor, and then enables interrupts. As soon as the voltage on the capacitor drops back down, the ISR will be called, which will add one to the counter and then charge the capacitor back up. This cycle loops around until the delay is up and the code again disables interrupts. Now we can test how many cycles happened during the delay time and decide whether a press happened or not.

When reading this description, the first question that came to mind was this: By setting


The capacitor starts charging, but as long as this pin (PC1) is HIGH, the capacitor stays charged, right? So how come it discharges and trigger the pin change interrupt?


You have to remember that this is a capacitive touch sensor, depicted in the circuit diagram as a standard capacitor with an annotation. The users finger is an implied part of the operation of the circuit.

There are a few ways to implement it, the most basic is that the sensor only including the bottom plate of the capacitor. When the users sticks their thumb on the top of the capacitor the thumb forms the top plate.

So when the capacitor is incomplete, the voltage is high. When the capacitor is completed by the thumb the voltage drops low, in practice the thumb won't build charge, so it will stay low.

The delay counting is a noise rejection setup, this is similar to a normal physical button debounce, but worse as the system is rather noise sensitive.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.