AVR Embedded Tutorial - GPIO-Interrupt

From Lazarus wiki

Deutsch (de) English (en)

GPIO interrupt

Introduction

How to control the GPIO and UART can be found here:

  • GPIO
  • UART UARTInit and UARTSendString(...

External interrupt

Interrupt pin assignment for the ATmega328:

  • INT0: PD2
  • INT1: PD3

Interrupt routine

This example shows how this works with INT0. You use EICRA to specify which interrupt is triggered. See table below. You can also activate INT0 and INT1 at the same time.

procedure Int0_Interrupt; public name 'INT0_ISR'; interrupt;
begin
  UARTSendString('INT0');  // Identify interrupt
end;

Activate interrupt

The parameters are described in the table below.

begin
  PORTD := %00001100;  // PullUp Pin 2 + 3

  // Initialize UART
  UARTInit ;

  // Activate INT0
  EICRA := %11;  // INT0 is triggered when the pin goes HIGH.
  EIMSK := %01;  // activate INT0.

  // Activate interrupt
  asm 
    Sei
  end;

  // Main loop
  repeat
  until 1 = 2;
end.

Register for the Atmega328p

The following registers are responsible for the activation of the ports/pins:

  • EICRA Function of the interrupt.
  • EIMSK Activate INT0 and/or INT1.

Table for EICRA which shows what kind of action on the pin triggers an interrupt.

INT1 INT0
Bit 3 2 1 0
0 0 0 0 Low Level on the pin
0 1 0 1 Every change
1 0 1 0 Pin goes LOW
1 1 1 1 Pin goes HIGH

INT1 interrupt

The INT1 interrupt must be set as follows:

  EICRA := %1100;  // INT1 is triggered when the pin goes HIGH.
  EIMSK := %10;    // activate INT1.

For the INT1 interrupt you have to use the ISR procedure for the INT1 (INT1_ISR).

procedure Int1_Interrupt; public name 'INT1_ISR'; interrupt;
begin
  // do something
end;

Pin change interrupt

This example is for an Arduino Nano (Atmega328p).

Terminal issue

Output the pin status on a terminal.

procedure WritePort(p : byte);
begin
  UARTSendString ('PB' + char(p + 48) + ':');

  if PinB and (1 shl p) = (1 shl p) then 
    begin
      UARTSendString('HIGH');
    end 
  else 
    begin
      UARTSendString('LOW');
    end;
end;

Interrupt routine

The interrupt fires when a pin changes.

procedure PC_Int0_Interrupt; public name 'PCINT0_ISR'; interrupt;
var
  i : byte;
begin
  UARTSendString(#27 '[0; 0H');  // Home the cursor

  for i := 0 to 3 do 
    begin
      WritePort(i);
    end;
end;

Declare pin change GPIO

Activate pins PB0, PB1, PB2 and BP3 for interrupts.

const
  inPortsB = %00001111;  // PCINT0 + PCINT1 + PCINT2 + PCINT3

Activate pin change interrupt

The parameters are described in the table below.

procedure PC_Int0_Interrupt; public name 'PCINT0_ISR'; interrupt;
begin
  DDRB  := 0;
  PORTB := inPortsB;  // PullUp

  // Initialize UART
  UARTInit;

  // Activate PCINT0_ISR
  PCICR  := %001;     // Only on PORTB
  PCMSK0 := inPortsB;

  // Activate interrupt
  asm 
    Sei
  end;

  // Main loop
  repeat
    // do something
  until 1 = 2;
end.

Registers for the Atmega328p

The following registers are responsible for the activation of the ports/pins:

  • PCICR Ports that trigger an interrupt.
  • PCMSKx The individual pins.

Table for PCICR:

Bit 2 1 0
PCICR PORTD PORTC PORTB

Table for the pins of PCMSKx :

Bit 7 6 5 4 3 2 1 0
PCMSK0 PCINT7 PCINT6 PCINT5 PCINT4 PCINT3 PCINT2 PCINT1 PCINT0
PCMSK1 PCINT14 PCINT13 PCINT12 PCINT11 PCINT10 PCINT9 PCINT8
PCMSK2 PCINT23 PCINT22 PCINT21 PCINT20 PCINT19 PCINT18 PCINT17 PCINT6

Example for PORT B and PORT C

// Activate PCINT0_ISR and PCINT1_ISR
PCICR  := %011;      // PORT B and PORT C
PCMSK0 := %11000000; // Pin 6 + 7 of PORT B
PCMSK1 := %00000011; // Pin 0 + 1 of PORT C

A second ISR procedure is added for the PCINT1_ISR. If you would like to use PORT D, then a procedure for the PCINT2_ISR would need to be added.

procedure PC_Int1_Interrupt; public name 'PCINT1_ISR'; interrupt;
begin
  // do something
end;

See also