pinChange.h

Allow callback routines to be attached to IOPins that are called when the pin changes.

This relies upon hardware interrupts and is not provided by the ATMega8 or ATMega32. The ATMega168, ATMeag328P, ATMega640 and ATMega2560 provide the feature on 24 of the pins. The ATMega2561 only proves the feature on 8 pins. Check the datasheet for pins labelled PCINTnn. Trying to use this file when compiling for devices that do not provide the feature will result in a compilation error: "This device does not support Pin Change interrupts".

Note: it does not support the alternative INT pins - only the PCINT pins.

The interrupts occur whenever the pin changes from high to low or from low to high and will happen whether the pin is an input pin or an output pin. This allows us to monitor incoming pulses and is used to support other library functions such as Sensors/Encoder/quadrature.h.

A simple logging routine could be created that sets up callbacks for each I/O pin and logs each pin change over the UART to a PC. This could be done without changing anything in your main program and could be removed at a later date once your program is debugged.

Each pin can only have one callback routine attached at a time and will be called via an interrupt so it should be as fast as possible.

An error is generated if the IOPin doesn't have a hardware pin change interrupt associated with it or if there is already a callback attached.

The first parameter is the IOPin to attach the callback to.

The second parameter is the address of your callback routine.

The third parameter is a user defined pointer to some data that can be passed into the routine when it is called. If you don't need it then set it to 'null'.

The callback routine itself should have the following signature:

void myCallback(const IOPin* io,boolean val, volatile void* data)

Where the first parameter is the pin that has changed, the second parameter is the new value for the pin (TRUE=high, FALSE=low), and the last parameter is the 'data' value you specified when attaching the callback.

The same callback routine can be attached to as many pins as you like.

Example - to create a callback that is called when B4 changes:

#include "pinChange.h"

void myCallback(const IOPin* io,boolean val, volatile void* data){

... the pin has changed ...

}

// Set up the callback in my initialisation code

void appInitHardware(){

pin_change_attach(B4, &myCallback, null);

}

Practical pin change example

Assume that we have two push button switches on B4 and B5 and we want to count how many times each one has been pushed or released.

All of the following solutions will need the buttons to be defined and initialised - so they will all start with this code snippet:

#include "switch.h"

#include "iopin.h"

#include "pinChange.h"

SWITCH button1 = MAKE_SWITCH(B4);

SWITCH button2 = MAKE_SWITCH(B5);

volatile int count1; // Changes for button1

volatile int count2; // Changes for button2

void appInitHardware(){

SWITCH_init(&button1);

SWITCH_init(&button2);

count1 = 0;

count2 = 0;

}

Solution 1 - have a call back for each switch:-

void myCallback1(const IOPin* io,boolean val, volatile void* data){

count1 = count1 + 1;

}

void myCallback2(const IOPin* io,boolean val, volatile void* data){

count2 = count2 + 1;

}

TICK_COUNT appInitSoftware(TICK_COUNT loopStart){

pin_change_attach(button1.pin, &myCallback1, null);

pin_change_attach(button2.pin, &myCallback2, null);

}

Solution 2. Wait a minute! The call backs are doing the same thing but to a different variable. So we could just have one call back.

void myCallback(const IOPin* io,boolean val, volatile void* data){

if( io == button1.pin){

count1 = count1 + 1;

}else{

count2 = count2 + 1;

}

}

TICK_COUNT appInitSoftware(TICK_COUNT loopStart){

pin_change_attach(button1.pin, &myCallback, null);

pin_change_attach(button2.pin, &myCallback, null);

}

Solution 3. The above code is messy in that if we add more switches then we have to remember to change appInitSoftware and the call back routine to add the new switches. So this solution will make use of the last parameter to pin_change_attach to tell the call back routine which variable should be changed:-

void myCallback(const IOPin* io,boolean val, volatile void* data){

// We know that the 'data' is a pointer to an 'int' variable

int* ptr = (int*)data;

// Increment the integer

*ptr = *ptr + 1;

}

TICK_COUNT appInitSoftware(TICK_COUNT loopStart){

pin_change_attach(button1.pin, &myCallback, &count1);

pin_change_attach(button2.pin, &myCallback, &count2);

}

This is easier to maintain as we can add as many new switches as we like without having to change the call back routine.