<PinChangeInt.h>< SoftwareSerial.h> clash

Hi

I have just struck this problem with the attached sketch. I have introduced PinChangeInt.h as i need to read from an RC signal. I have done this many times before, but now the two clash. I see this is not a new problem. Is there a workaround? I am using a Uno. I note someone said in one forum to go for a Leonardo. So everyone can see the problem, but there isn’t an answer?

Advice welcome

Cheers

John

newBoat.ino (4.85 KB)

you might try AltSoftSerial.h rather than SoftwareSerial.h
found at AltSoftSerial Library, for an extra serial port
It only uses pins 9,10 on the uno leaving most of the other interrupts alone.

I had a similar problem with reading other interrupts at the same time
z

Thanks

I tried that and it seems to use a lot of memory. It runs the program fine, but when I add pininterupt I have the same probs. Tried to only use pins 10 and 9 also

John

An alternative.... Possibly. I couldn't tell what is using the software serial but I am guessing the GPS does. Because you are using hardware serial for your debug output. With that in mind: Can you use hardest serial rx for your GPS and hardware serial tx for your debugging? GPS after it is configured usually doesn't need any serial input... Then you won't need software serial

Z

Write your own SoftwareSerial library and dump the PinChangeInt library. You don’t say what your errors are but I assume that they relate to the interrupt vectors.

The below will fix the errors for interrupt_on_change on the pins 0…7 and SoftwareSerial on any other pin.

Starting with SoftwareSerial.

Copy the files C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.cpp and C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.h to your sketch directory.
2)
Rename both files to e.g. mySoftwareSerial.cpp and mySoftwareSerial.h.
3)
Edit both files and replace all occurrences of SoftwareSerial by mySoftwareSerial.
4)
In mySoftwareSerial.cpp, find the below section (around line 220 or so)

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  mySoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect));
#endif

Just before that section, add an #undef

#undef PCINT2_vect

#if defined(PCINT0_vect)
ISR(PCINT0_vect)
{
  mySoftwareSerial::handle_interrupt();
}
#endif

#if defined(PCINT1_vect)
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT2_vect)
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#if defined(PCINT3_vect)
ISR(PCINT3_vect, ISR_ALIASOF(PCINT0_vect));
#endif

This will remove the duplicate __vector_5.

The editing can be done in the IDE or using any other editor. After the editing, close (if open) and open the IDE and open your sketch.

Next step is writing your own interrupt handler for the pinchange interrupt. The code is based on Simple Pin Change Interrupt on all pins

Add the pciSetup(byte pin) function to your sketch (line 3…8). Add the ISR (PCINT2_vect) (lines 21…25) to your sketch but leave it empty (remove the digitalWrite, line 24); we will fill it in later.

Use the following in setup (please replace the pin numbers with the names).

void setup()

  pinMode(2, INPUT_PULLUP);
  pciSetup(2);
  pinMode(3, INPUT_PULLUP);
  pciSetup(3);
  pinMode(4, INPUT_PULLUP);
  pciSetup(4);
}

For debugging, I have added HardwareSerial to print to the serial monitor. I.ve also added mySoftwareSerial to test compilation.

The full framework to start with (with only pins 2 and 3 as I did not have a switch on any other pin)

#include "mySoftwareSerial.h"
mySoftwareSerial mySerial(10, 11); // RX, TX


void pciSetup(byte pin)
{
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

void setup()
{
  mySerial.begin(9600);
  Serial.begin(115200);

  pinMode(2, INPUT_PULLUP);
  pciSetup(2);
  pinMode(3, INPUT_PULLUP);
  pciSetup(3);
}

void loop()
{
}

ISR (PCINT2_vect) // handle pin change interrupt for D0 to D7 here
{
}

Please note the way that mySoftwareSerial is included using double quotes. This will compile.

Next the fun part starts. Create 2 variables that will hold the old values of the pins and the new values.

#include "mySoftwareSerial.h"
mySoftwareSerial mySerial(10, 11); // RX, TX

// for the pin change detection
volatile byte pinD = 0;
volatile byte oldD = 0;
byte mask;
...
...

and write the ‘interrupt handlers’ for the pinchange of each pin

#include "mySoftwareSerial.h"
mySoftwareSerial mySerial(10, 11); // RX, TX

// for the pin change detection
volatile byte pinD = 0;
volatile byte oldD = 0;

// interrupt handlers
void pin2handler()
{
  Serial.print("2");
}

void pin3handler()
{
  Serial.print("3");
}

The handlers use Serial.print which should not pose a big problem as it only prints one character.

Next we create an array of pointers to those functions. The array has 8 entries, one for each pin 0…7.

#include "mySoftwareSerial.h"
mySoftwareSerial mySerial(10, 11); // RX, TX

// for the pin change detection
volatile byte pinD = 0;
volatile byte oldD = 0;

// interrupt handlers
void pin2handler()
{
  Serial.print("2");
}

void pin3handler()
{
  Serial.print("3");
}

void (*handlers[])() =
{
  NULL,
  NULL,
  pin2handler,
  pin3handler,
  NULL,
  NULL,
  NULL,
  NULL,
};

The beauty of an array of function pointers is that we don’t need a complex it/else to determine which handler to execute. We will see that later when we fill in the ISR. NULL indicates that no handler for a pinchange interrupt on that pin is specified; in the example we only have pinchange handlers for pins 2 and 3.

Next we add the initial status of the pins on portD (0…7) in the setup() function.

#include "mySoftwareSerial.h"
mySoftwareSerial mySerial(10, 11); // RX, TX

// for the pin change detection
volatile byte pinD = 0;
volatile byte oldD = 0;

void pin2handler()
{
  Serial.print("2");
}

void pin3handler()
{
  Serial.print("3");
}

void (*handlers[])() =
{
  NULL,
  NULL,
  pin2handler,
  pin3handler,
  NULL,
  NULL,
  NULL,
  NULL,
};

void pciSetup(byte pin)
{
  *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}

void setup()
{
  mySerial.begin(9600);
  Serial.begin(115200);

  pinMode(2, INPUT_PULLUP);
  pciSetup(2);
  pinMode(3, INPUT_PULLUP);
  pciSetup(3);

  // generate the initial values for the interrupts
  for (int cnt = 0; cnt < 8; cnt++)
  {
    if (handlers[cnt] != NULL)
    {
      oldD |= (1 << cnt);
      mask = oldD;
    }
  }
}

void loop()
{
}

Next we can fill in the ISR.

ISR (PCINT2_vect) // handle pin change interrupt for D0 to D7 here
{
  // read the pins on port D
  pinD = PIND & mask;

  // compare with the old value
  if (pinD != oldD)
  {
    // loop through the handlers
    for (int cnt = 0; cnt < 8; cnt++)
    {
      // fi a handler specified
      if (handlers[cnt] != NULL)
      {
        // compare current status of pins with old status
        if ((pinD & (1 << cnt)) != (oldD & (1 << cnt)))
        {
          // call the handler
          handlers[cnt]();
        }
      }
    }
    // remember the status of the pins
    oldD = pinD;
  }
}

I can not test the software serial so can’t guarantee that it works. I suggest that you create a small test project to play with it.

Thanks so much. Working week staring this sens so I will try when I get time but that looks very helpful.

Cheers

John