In the world of microcontroller programming, efficient event handling is crucial for real-time applications, where timely responses to external stimuli are essential. One powerful feature offered by many microcontrollers is Pin-Change Interrupts, which allow the microcontroller to react swiftly to changes in the state of input pins. This article aims to provide a detailed exploration of Pin-Change Interrupts, their functionality, implementation, and practical applications in microcontroller projects.
Hardware interrupt can be the external interrupt (int) we have described before:
What is Interrupt
How Interrupt Works
Understanding Arduino Interrupts
---
Present topic is another type of hardware interrupt – pin change interrupt (pcint). Unlike external interrupt (int), pin change interrupt (pcint) is not restricted to few GPIO pins, it is usually available to all the pins.
Understanding Pin-Change Interrupts
Pin-Change Interrupts (PCINTs) are a type of interrupt mechanism available in many microcontrollers, including popular platforms like Arduino and AVR. Unlike traditional external interrupts, which are triggered by specific hardware events (e.g., a rising or falling edge on a dedicated pin), Pin-Change Interrupts are triggered by changes in the state of any GPIO (General Purpose Input/Output) pin.
Pin change interrupts are used for a whole port and only can detect any change for a whole port. Any change on the pin trigger an interrupt. Each pin row (0-7, 8-13, A0-A5) represents a port. If an interrupt (ISR) occurs on one pin of a port it is still unclear what pin of the port caused this interrupt. Therefore software library saves the state of the whole port and compares with the last state.
As because the total thing requires more lines of coding, there is overhead, resulting slowness and not that reliable.
Key Features of Pin-Change Interrupts
Pin-Change Interrupts allow the microcontroller to detect changes in the state of individual GPIO pins, providing granular event detection capabilities. Most microcontrollers with Pin-Change Interrupt support can handle interrupts from multiple GPIO pins simultaneously, making them versatile for various input configurations.
Pin-Change Interrupts offer low latency response times, enabling rapid reaction to external stimuli such as button presses, sensor readings, or signal changes.
Pin-Change Interrupts operate asynchronously to the main program execution, allowing the microcontroller to respond to events in real-time without disrupting ongoing tasks.
Implementation of Pin-Change Interrupts
Configure the GPIO pins that will trigger Pin-Change Interrupts as inputs. Ensure that the pins are set to the appropriate mode (e.g., INPUT or INPUT_PULLUP) based on the application requirements.
Enable Pin-Change Interrupts for the selected GPIO pins in the microcontroller’s control registers. This step may involve configuring specific registers related to interrupt control and pin change detection.
Define an Interrupt Service Routine (ISR) to handle the Pin-Change Interrupt events. The ISR is a function that executes when a Pin-Change Interrupt is triggered, allowing the microcontroller to respond to the event.
In some microcontroller architectures, you may need to set up interrupt vectors or register ISR functions with the appropriate interrupt vectors to ensure that the microcontroller executes the ISR when a Pin-Change Interrupt occurs.
Within the ISR, implement the necessary event handling logic to respond to the Pin-Change Interrupt. This may involve reading the state of the GPIO pins, performing specific actions based on the detected event, and clearing interrupt flags as needed.
Practical Applications of Pin-Change Interrupts
Pin-Change Interrupts find applications in various microcontroller projects. Pin-Change Interrupts are commonly used to detect button presses and switch changes in user interface applications, such as digital input panels or control interfaces.
Pin-Change Interrupts can be utilized to monitor changes in sensor readings, such as motion sensors, proximity sensors, or environmental sensors, enabling real-time data acquisition and processing.
Communication Protocols
Pin-Change Interrupts can play a role in implementing communication protocols, such as UART (Universal Asynchronous Receiver-Transmitter) or I2C (Inter-Integrated Circuit), by triggering interrupts upon receiving data on GPIO pins.
Pin-Change Interrupts enable microcontrollers to react to external events or signals, such as interrupts from external devices, pulse signals, or signal transitions from other electronic components.
Example of Pin Change Interrupt for Arduino
Reference libraries:
1 2 | https://www.arduino.cc/reference/en/libraries/pinchangeinterrupt/ https://github.com/NicoHood/PinChangeInterrupt |
You need to know hardware details to calculate the values of PCICR, PCMSK2 (see below) etc. PCMSK0 is used to enable each PCINT pins 0 to 7; PCMSK1 for PCINT pins 8 to 14; and PCMSK2 for PCINT pins 16 to 23.
You need two set of charts for the calculation. One is for PCINT number:

Another chart for PCICR – Pin Change Interrupt Control Register:

Obviously, the manufacturers provide documentation:
1 | https://onlinedocs.microchip.com/pr/GUID-C2E04B22-568F-490C-92A5-D4BF82B089A4-en-US-3/index.html?GUID-2DDA6605-4F41-4F7B-92C6-97FA126CD912 |
If we want to set the PCMSK bit for pin A0, the pin is PCINT8 and thus belongs to PCMSK1. If you have to activate/enable interrupt on that port and trigger interrupt on that pin (bit manipulation).
1 2 3 4 5 6 7 8 | void setup() { pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT_PULLUP); PCICR |= B00000100; // port D PCMSK2 |= B10000000; // pin 7 } |
Or we can write in this way:
1 2 3 | // Enable Pin-Change Interrupt for the interrupt pin (PCINT2 for PB2) PCICR |= (1 << PCIE0); // Enable Pin-Change Interrupt Control Register PCMSK0 |= (1 << PCINT2); // Enable Pin-Change Interrupt Mask Register for PB2 |
Here is an example sketch for Arduino IDE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <PinChangeInt.h> const byte ledPin = 13; const byte buttonPin = 7; volatile bool togglestate = false; void setup() { pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT_PULLUP); PCICR |= B00000100; // port D PCMSK2 |= B10000000; // pin 7 } void loop() { } ISR (PCINT2_vect) { // Interrupt for Port D // Invert toggle state togglestate = !togglestate; // Indicate state on LED digitalWrite(ledPin, togglestate); } |
Example for AVR-GCC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // Pin-Change Interrupt example for AVR microcontrollers #include <avr/io.h> #include <avr/interrupt.h> // Define the GPIO pin number to be used for the interrupt #define INTERRUPT_PIN PB2 // Interrupt Service Routine (ISR) for handling the Pin-Change Interrupt ISR(PCINT0_vect) { // Perform actions in response to the interrupt // For example, toggle an LED or execute specific tasks } int main(void) { // Set the interrupt pin as an input DDRB &= ~(1 << INTERRUPT_PIN); // Enable Pin-Change Interrupt for the interrupt pin (PCINT2 for PB2) PCICR |= (1 << PCIE0); // Enable Pin-Change Interrupt Control Register PCMSK0 |= (1 << PCINT2); // Enable Pin-Change Interrupt Mask Register for PB2 // Enable global interrupts sei(); // Your main program logic goes here while (1) { // Main program loop } return 0; } |
Conclusion
Pin-Change Interrupts offer a powerful mechanism for efficient event handling in microcontroller applications, allowing rapid response to changes in the state of GPIO pins. By leveraging Pin-Change Interrupts, developers can design responsive and interactive systems that effectively interact with the surrounding environment. Whether in user interfaces, sensor networks, communication systems, or other applications, Pin-Change Interrupts play a crucial role in enhancing the functionality and real-time.
Tagged With headed8pg