I'm here because I got totally confused about those names.... like was questioning why "PCINT0" was seen all over the place in unrelated things - like bit names in a register, and an interrupt request name. Thanks to westfw for that golden write-up of post #30. I reckon every single tutorial for pin change interrupt should include westfw's post, which is #30 of this thread. And have it at the BEGINNING of every tutorial. Also, the people at ATMEL shouldn't ever pull this sort of two-timing naming stunt ever again.
I'll upload my working code that I modified from existing online code..... that works on the UNO .... detects pin changes on pins (labelled on UNO board) 8, 9 and 10; and pins A0, A1 and A2.
Monitor the activity in Serial Monitor at 115200 bit per second.
Just put a jumper wire into a GND socket of the UNO, then touch the other end of the ground wire to any of these pins labelled on the UNO board..... 8, 9, 10, A0, A1, and A2.
Thanks westfw.
EDIT: code below updated to avoid race condition (thanks to important advice from forum member "Coding Badly"). Avoid reading PINB and PINC registers more than one time within a interrupt service routine, since the register values can change at any time. So, immediately read the register (eg. PINB) at the beginning of the service routine, and don't read it again within that routine.
#include <avr/io.h>
#include <stdint.h> // has to be added to use uint8_t
#include <avr/interrupt.h> // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF; // default is high because the pull-up
volatile uint8_t portchistory = 0xFF; // default is high because the pull-up
void setup()
{
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin // Clearing (setting to zero) these pins is the code for setting the pins to be INPUT pins.
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up //This is an unintuitive operation, which is a method for ACTIVATING internal PULLUP mode for particular UNO pins associated with 'PORTB'. In this case, PB0, PB1 and PB2 are pins 8, 9 and 10 on the UNO.
// PB0, PB1 and PB2 are now inputs with pull-up enabled //NOTE: this pullup configuration method only works when a pin has been configured as an INPUT pin (and configuration of portB pins is done with data direction register DDRB)
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan //When the bit called PCIE0 in the pin change interrupt register (PCICR) is "set" (to 1), it enables monitoring of the PCMSK0 register bits (that include pins PCINT0, PCINT1 and PCINT2 - which are pins 8, 9 and 10 on the UNO).
//PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
//PCMSK0 |= (1 << PCINT1); // set PCINT1 to trigger an interrupt on state change
//PCMSK0 |= (1 << PCINT2); // set PCINT2 to trigger an interrupt on state change
PCMSK0 |= (1 << PCINT0) | (1 << PCINT1) | (1 << PCINT2); //alternative way of setting these bits all at once.
DDRC &= ~((1 << DDC0) | (1 << DDC1) | (1 << DDC2)); // Clear the PC0, PC1, PC2 pin // Clearing (setting to zero) these pins is the code for setting the pins to be INPUT pins.
// PC0,PC1,PC2 (PCINT8, PCINT9, PCINT10 pin) are now inputs
PORTC |= ((1 << PORTC0) | (1 << PORTC1) | (1 << PORTC2)); // turn On the Pull-up //This is an unintuitive operation, which is a method for ACTIVATING internal PULLUP mode for particular UNO pins associated with 'PORTC'. In this case, PC0, PC1 and PC2 are pins A0, A1 and A2 on the UNO (which are unlabeled digital pins 14, 15, 16).
// PC0, PC1 and PC2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE1); // set PCIE1 to enable PCMSK1 scan //When the bit called PCIE1 in the pin change interrupt register (PCICR) is "set" (to 1), it enables monitoring of the PCMSK1 register bits (that include pins PCINT8, PCINT9 and PCINT10 - which are pins A0, A1 and A2 on the UNO - ie. unlabeled digital pins 14, 15, 16).
//PCMSK1 |= (1 << PCINT8); // set PCINT8 to trigger an interrupt on state change
//PCMSK1 |= (1 << PCINT9); // set PCINT9 to trigger an interrupt on state change
//PCMSK1 |= (1 << PCINT10); // set PCINT10 to trigger an interrupt on state change
PCMSK1 |= (1 << PCINT8) | (1 << PCINT9) | (1 << PCINT10); //alternative way of setting these bits all at once.
//This example handles a subset of pins associate with PORTB and PORTC
//If pins associated with other ports are needed, such as PORTD, then would need to focus on DDRD, PORTD, and to set (to 1) the 'bit' called PCIE2 in the PCICR register, which would enable monitoring of the PCMSK2 register bits (that include pins PCINT16, PCINT17, PCINT18 etc - which are pins 0, 1, 2 on the UNO.
Serial.begin(115200);
Serial.println("READY");
}
void loop()
{
while(1)
{
/*main program loop here */
}
}
ISR (PCINT0_vect) //PCINT0_vect is the interrupt service routine NAME (ISR name) for PORTB. Unintuitive, but that's the way it is. Also, the name PCINT0 is confusing, because this name is used not only as an interrupt request name, but also happens to be used as a bit name. Totally unrelated things.
{
uint8_t changedbits;
uint8_t PINB_store;
PINB_store = PINB; //temporary storage of PINB register value to avoid RACE condition. This means - PINB register values can change at any time. So we simply read PINB once only in this service routine, and store it.
changedbits = PINB_store ^ portbhistory; //NOTE: PINB is a register that contains the values of the digital bits of PORTB pins - only relevant when PORTB pins are INPUT pins.
portbhistory = PINB_store;
if(changedbits & (1 << PINB0))
{
/* PCINT0 changed */
Serial.println(" PINB0"); //Pin 8 on UNO
}
if(changedbits & (1 << PINB1))
{
/* PCINT1 changed */
Serial.println(" PINB1"); //Pin 9 on UNO
}
if(changedbits & (1 << PINB2))
{
/* PCINT2 changed */
Serial.println(" PINB2"); //Pin 10 on UNO
}
}
ISR (PCINT1_vect)
{
uint8_t changedbits;
uint8_t PINC_store;
PINC_store = PINC;
changedbits = PINC_store ^ portchistory;
portchistory = PINC_store;
if(changedbits & (1 << PINC0))
{
/* PCINT8 changed */
Serial.println(" PINC0"); //Pin A0 on UNO
}
if(changedbits & (1 << PINC1))
{
/* PCINT9 changed */
Serial.println(" PINC1"); //Pin A1 on UNO
}
if(changedbits & (1 << PINC2))
{
/* PCINT10 changed */
Serial.println(" PINC2"); //Pin A2 on UNO
}
}