[SOLVED] CHANGE interrupt on A1..A3 not working

I am trying to get A1…A3 to call a CHANGE interrupt callback, but it isn’t working.

The schematics is quite simple: a 3-way DIP switch. Top part connected to +5V (ON position). Bottom (OFF) part of each switch connected to pins A1…A3 respectively, with a 33kOhm resistor connecting to GND. That and 2 LEDs on pins 5 and 6 with 270R resistors to GND.

This is the code that is supposed to detect the change:

#include <SoftwareSerial.h>
SoftwareSerial bluetooth(9, 10); // RX, TX

const byte DIP_BUZZER_PIN = A3; // DIP 3
const byte DIP_LED1_PIN   = A2; // DIP 2
const byte DIP_LED2_PIN   = A1; // DIP 1

const byte LED1_PIN = 6;   // Uses PWM
const byte LED2_PIN = 5;   // USes PWM

const byte BUZZER_BIT     = (1 << (DIP_BUZZER_PIN - 14)); //digital_pin_to_bit_mask_PGM[DIP_BUZZER_PIN]; 
const byte LED1_BIT       = (1 << (DIP_LED1_PIN - 14)); // digital_pin_to_bit_mask_PGM[DIP_LED_PIN]; 
const byte LED2_BIT       = (1 << (DIP_LED2_PIN - 14)); // digital_pin_to_bit_mask_PGM[DIP_BT_PIN];

boolean FLAG_BUZZER = false;
boolean FLAG_LED1 = false;
boolean FLAG_LED2 = false;

volatile boolean mustReadFlags = true;

void setup() {
   Serial.begin(57600);
   pinMode(DIP_BUZZER_PIN, INPUT);
   pinMode(DIP_LED1_PIN, INPUT);
   pinMode(DIP_LED2_PIN, INPUT);
   
   pinMode(LED1_PIN, OUTPUT);
   pinMode(LED2_PIN, OUTPUT);
   
   attachInterrupt(DIP_BUZZER_PIN, interrupt, CHANGE);
   attachInterrupt(DIP_LED1_PIN, interrupt, CHANGE);
   attachInterrupt(DIP_LED2_PIN, interrupt, CHANGE);
   Serial.println(F("Started"));
}

void interrupt() {
  mustReadFlags = true;
}


void loop() {
  if (mustReadFlags) {
    readFlags();
    applyFlags();
  }
}


void applyFlags() {
  analogWrite(LED1_PIN, ((FLAG_LED1) ? 50 : 0));
  analogWrite(LED2_PIN, ((FLAG_LED2) ? 50 : 0));
}

void readFlags() {
  byte Status = PINC;
  FLAG_BUZZER = (Status & BUZZER_BIT) > 0;
  FLAG_LED1 = (Status & LED1_BIT) > 0;
  FLAG_LED2 = (Status & LED2_BIT) > 0;
  mustReadFlags = false;
  Serial.print(F("FLAG_BUZZER: "));
  Serial.println(FLAG_BUZZER);
  Serial.print(F("FLAG_LED1: "));
  Serial.println(FLAG_LED1);
  Serial.print(F("FLAG_LED2: "));
  Serial.println(FLAG_LED2);
}

I have to use attachInterrupt, because if I use ISR(PCINT1_vect) it causes a conflict with SoftwareSerial (that will be used for a Bluetooth module, although that part of the code isn’t implemented yet), with the message “multiple definition of `__vector_4’”.

So, what I look for is a way to either:
a) properly use attachInterrupt on A1…A3 for a CHANGE event
or
b) use ISR(PCINT1_vect) and still keep the ability to use SoftwareSerial.

Thanks in advance.

As far as I know, you may only have two interrupts at any given time, and they must be on digital pins 2 (interrupt 0) and 3 (interrupt 1).

CWashburn: As far as I know, you may only have two interrupts at any given time, and they must be on digital pins 2 (interrupt 0) and 3 (interrupt 1).

CHANGE interrupts can be set on any pin. If I use the code below, it works:

void InitializeInterrupt(){
  cli();       // switch interrupts off while messing with their settings  
  PCICR =0x02;          // Enable PCINT1 interrupt
  PCMSK1 = 0b00001110;
  sei();       // turn interrupts back on
}


ISR(PCINT1_vect) {    // Interrupt service routine. Every single PCINT8..14 (=ADC0..5) change
            // will generate an interrupt: but this will always be the same interrupt routine
  mustReadFlags = true;
}

then, in my setup() function, I call InitializeInterrupt() only once instead of calling attachInterrupt() 3 times. The rest of the code is almost unchanged (have to remove all references to SoftwareSerial). This code works and compiles properly if SoftwareSerial.h isn't included (and the bluetooth object isn't declared), but it isn't compatible with the SoftwareSerial library. With the SoftwareSerial library, this is what I get this when compiling:

SoftwareSerial\SoftwareSerial.cpp.o: In function `__vector_4':
C:\Arduino\arduino-1.0.6\libraries\SoftwareSerial/SoftwareSerial.cpp:312: multiple definition of `__vector_4'
InterruptTest.cpp.o:C:\Arduino\arduino-1.0.6/InterruptTest.ino:49: first defined here

Two points, the OP is NOT using pin change interrupts in his own code, he's using the external interrupts. So (on the Uno) he has two and they are on pins D2 and D3.

Second SoftwareSerial does use the pin change interrupts so at least without rewriting SoftwareSerial he can't use them as SoftwareSerial has already provided the pin change ISR.

Mark

I have a temporary solution, but commenting out ISR(PCINT1_vect) in SoftwareSerial.cpp (lines 311~316). It works fine, but it feels like a dirty hack.

Then I had my sketch like this (some basic bluetooth stuff was implemented just so I could test if my own interrupts were interefering with SoftwareSerial’s):

#include <SoftwareSerial.h>

SoftwareSerial bluetooth(9,10); // RX, TX

const byte DIP_BUZZER_PIN = A3; // DIP 3
const byte DIP_LED1_PIN   = A2; // DIP 2
const byte DIP_LED2_PIN   = A1; // DIP 1

const byte LED1_PIN = 6;   // Uses PWM
const byte LED2_PIN = 5;   // USes PWM

const byte BUZZER_BIT     = (1 << (DIP_BUZZER_PIN - 14)); //digital_pin_to_bit_mask_PGM[DIP_BUZZER_PIN]; 
const byte LED1_BIT       = (1 << (DIP_LED1_PIN - 14)); // digital_pin_to_bit_mask_PGM[DIP_LED_PIN]; 
const byte LED2_BIT       = (1 << (DIP_LED2_PIN - 14)); // digital_pin_to_bit_mask_PGM[DIP_BT_PIN];

boolean FLAG_BUZZER = false;
boolean FLAG_LED1 = false;
boolean FLAG_LED2 = false;

byte lastStatus = 0xFF;

volatile boolean mustReadFlags = true;

void setup() {
   Serial.begin(57600);
   bluetooth.begin(9600);
   pinMode(DIP_BUZZER_PIN, INPUT);
   pinMode(DIP_LED1_PIN, INPUT);
   pinMode(DIP_LED2_PIN, INPUT);
   
   pinMode(LED1_PIN, OUTPUT);
   pinMode(LED2_PIN, OUTPUT);
   
   Serial.println(F("Started"));
   InitializeInterrupt();
}


void InitializeInterrupt(){
  cli(); // switch interrupts off while messing with their settings  
  PCICR |= 0x02;          // Enable PCINT1 interrupt, while keeping the settings made by SoftwareSerial (0x01)
  PCMSK1 = 0b00001110;
  sei(); // turn interrupts back on
}

ISR(PCINT1_vect) {  
    mustReadFlags = true;
}

void loop() {
  if (mustReadFlags) {
    readFlags();
  }
  
  if (bluetooth.available()) 
    Serial.write(bluetooth.read());
  
}


void applyFlags() {
  analogWrite(LED1_PIN, ((FLAG_LED1) ? 50 : 0));
  analogWrite(LED2_PIN, ((FLAG_LED2) ? 50 : 0));
}

void readFlags() {
  byte Status = PINC;
  if (Status != lastStatus) {
    FLAG_BUZZER = (Status & BUZZER_BIT) > 0;
    FLAG_LED1 = (Status & LED1_BIT) > 0;
    FLAG_LED2 = (Status & LED2_BIT) > 0;
    mustReadFlags = false;
    Serial.print(F("FLAG_BUZZER: "));
    Serial.println(FLAG_BUZZER);
    Serial.print(F("FLAG_LED1: "));
    Serial.println(FLAG_LED1);
    Serial.print(F("FLAG_LED2: "));
    Serial.println(FLAG_LED2);
    lastStatus = Status;
    applyFlags();
  }
}

It’s still not what I wanted, because I wanted attachInterrupt to work, so I wouldn’t have to fiddle with SoftwareSerial.cpp, but the code above works perfectly.

I had to change some of the code around to work around the fact that DIP switches are a mechanical device and they bounce like a ping pong ball.

I am still open for suggestions using attachInterrupt and not changing SoftwareSerial.cpp, if there is one!

attachInterrupt

Only works for the external interrupts and they are not available on the pins you wish to use. READ THE DOCUMENTATION!

Mark

I am still open for suggestions using attachInterrupt and not changing SoftwareSerial.cpp, if there is one!

As Mark says. If attachInterrupt supported pin change interrupts, which it doesn't, then you would still have the issue of the conflicting ISR.

Are you using SoftwareSerial to send, or receive (or both)?

[quote author=Nick Gammon link=msg=2084616 date=1423542449] As Mark says. If attachInterrupt supported pin change interrupts, which it doesn't, then you would still have the issue of the conflicting ISR.

Are you using SoftwareSerial to send, or receive (or both)? [/quote]

Thanks, Nick.

Both. The bluetooth connection is a huge part of this project. Basically I'm using an Android app to send settings (via bluetooth) that are then saved in EEPROM. The same bluetooth app reads some stats from the device, like free mem, temperature, current settings (those previously saved on the EEPROM), actual Vcc voltage (so I know the health of the battery feeding the device), etc.

This code will be part of version 2.0 of this project: http://forum.arduino.cc/index.php?topic=295377

Anyhow, I consider the last code I posted as a solution, and I'll mark this thread as [SOLVED]. I'll leave the code here as a knowledge base for future reference.

Thanks again, Nick and Mark.

t starts by stating that on the Uno only pins 2 and 3 support interrupts, but in the example at the end of the page it uses attachInterrupt() on pin 0, and for a CHANGE interrupt. This example is what led me to believe it would also work on A1...A3.

Although I believe what you and Mark say (and it's proven by code!), the page above is wrong and leads to a false idea that attachInterrupt works with CHANGE on pins other than 2,3 on the UNO.

No - attachInterupt take and interrupt number NOT a pin number.

Mark

holmes4: No - attachInterupt take and interrupt number NOT a pin number.

Mark

Thanks... I feel quite dumb right now.