Mega interrupts gone wild

This is a fork of my subject "Can't get multiple SoftwareSerial to run on the Mega" but I've narrowed my current problems down to specifically interrupts. Tell me how I can simplify the problem description any more or help me with what I've gotten.

Basically what I'm designing is a simplified two-wire interface with a separate pair of wires from a Master Mega to each Slave UNO. I can't (won't) use TWI on a bus since I'm a stubborn old Finn. Specifically, I can't (won't) put a serial# in each slave UNO to make them unique as TWI seems to require. I can't put more than one TWI on the Mega so I decided to write my own. Since the messages between the UNO and Mega have, at most 2 bits, I haven't expanded the protocol beyond one bit at a time. Right now I'm trying to use the 6 Mega hardware interrupts to set up a separate communication to each slave.

I've read Nick Gammon's "Gammon Forum : Electronics : Microprocessors : Interrupts" and everything I can find in the forums under "interrupts and Mega"

I thought last weeks problems were solved once I learned that detachInterrupt() only delays an interrupt execution (which then executes with the next attachInterrupt). I read all the discussion on "clearing" interrupt bits, which is very hard for me to follow, especially since it is different on the UNO and the Mega. I'm now using EIFR = 0xFF; in my sendDataBit() routine. I'm very unsure how to clear individual interrupts, but as suggested clearing them all might work.

I've included my complete code for the Master Mega, below. In it I've stripped it down to only 2 comm lines. Physically, I've stripped it down further to have only pin 2 for interrupt0 and pin 4 for the data line. Interrupt1 on pin 3 (and pin 5 for a data line) are not even physically connected at the moment and are held HIGH with INPUT_PULLUP. I swapped out the Mega (purchased from Mouser) with a Mega ADK and have the same behavior. I also swapped out the UNOs.

The current strange behavior is that when I send an interrupt out from slave0 (UNO out on pin 2 and 3, into Mega pins 2 and 4) I get an interrupt1 (from non-connected pin 3??) as well as the expected interrupt0. The sendDataBit() routine is the only one that fusses with anything that could be related to interrupt1. Do I misunderstand how to clear interrupts?

I'll post the Mega master code in a separate post. Please look at the setup and the function sendDataBit().

Here is a sample of the Monitor output on the Windows monitor that is connected to the Master Mega. I just booted both devices, connected both monitors and sent an authorization request from the UNO to the Mega. The UNO and Master are connected with 5v to 5v, ground to ground, pin2 to pin2 and UNO pin3 to Mega pin4. Both devices are getting power through USB from separate computers. The "Holy Smokes, Batman" just after loop 4 is from interrupt1 (slave1ISR() ), followed by the expected interrupt0 (slave0ISR() ) conversation "slave 0 being authorized..." Serial.println("Holy Smokes, Batman"); is the only thing I put into slave1ISR() because I never expected it to be executed. I have no idea what is kicking it off since it was set up with "attachInterrupt(1, slave1ISR, FALLING);" and Mega pin 3 is not connected. If I send a second request from the UNO, I still also get Interrupt1, so I don't think it is just an uncleared interrupt from the booting process. The unacceptable (and temporary) Serial.print() inside the ISR isn't the problem since it hasn't been executed yet when the unexpected interrupt1 is kicked off. What am I missing?

71M11Master_2013aug28E
Note: from monitor 1 = denied, 0 = authorized, 5 = print AR list 
      Only testing slave 0, for now...
end of setup... 
AR= 0 looping... 0
AR= 0 looping... 1
AR= 0 looping... 2
AR= 0 looping... 3
AR= 0 looping... 4
Holy smokes, Batman! slave1ISR...
Slave 0 being authorized  Slave 0 sent data 1
 slave 0 toys out 1 total toys out count now = 1 AR= 0
AR= 0 looping... 5
AR= 0 looping... 6
AR= 0 looping... 7
AR= 0 looping... 8
AR= 0 looping... 9
AR= 0 looping... 10
AR= 0 looping... 11

Here is the code in the Mega Master:

char sketch[] = "71M11Master_2013aug28Estripped";  // August 2013 by Rick Rantilla

const int TESTMODE = 1; // 1 or greater = test (print stuff), 0=normal (don't print it)                
                        
// Simple 2-wire master-slave comm sketch - by Rick Rantilla - August 2013
// includes Simple Servo by Rick Rantilla - February 2013

//  FUTURE - need to add/update more slave ISRs, adjust timing, remove test

const byte interruptPin[] = {2,3,21,20,19,18};
const byte dataPin[] = {4,5,31,32,33,34};
const byte SLAVEMAX = 6;  // this may change later with other protocol or cascading Megas

byte maxToysOut = 1; // can be changed later by switches or bluetooth
volatile byte toysOut[] = {0,0,0,0,0,0};  // volatile for any variable updated within ISR
volatile byte totalToysOut = 0;
volatile byte authorizationRequest[]={0,0,0,0,0,0};  // set other than zero for testing

int count = 0; // use for looping message in test (may also help with timing)


void setup (void)
{ 
  Serial.begin (9600); Serial.println();
  Serial.println (sketch); // Rick - always start by identifying what's actually in the Arduino
  delay(50);  // why does this take 15 seconds???  
  //
  for(int slave = 0; slave < SLAVEMAX; slave++)
  {
    pinMode(interruptPin[slave], INPUT_PULLUP);  // can be pulled down by either end for attention
    pinMode(dataPin[slave], INPUT_PULLUP); // can be pulled down by either end to send data
  } // end for slave 
  attachInterrupt(0, slave0ISR, FALLING);  // interrupt number on Mega, not pin #
  attachInterrupt(1, slave1ISR, FALLING); 
  // eventually up to 6 interrupts
  Serial.println("Note: from monitor 1 = denied, 0 = authorized, 5 = print AR list ");
  Serial.println("      Only testing slave 0, for now...");
  Serial.println("end of setup... ");  
} // end setup


void loop (void)
{   
  for(int slave=0; slave <SLAVEMAX; slave++)
  {
    if (authorizationRequest[slave]==2) {sendNotAuthorizedAndFlash(slave);} // was set in ISR
    if (authorizationRequest[slave]==3) {sendAuthorization(slave);}  // was set in ISR
    // else 0 = no request
  }// end for slave  
  delay(100);
  if (TESTMODE>=1) {
    Serial.print("AR= ");Serial.print(authorizationRequest[0]);  // the only slave active this test
    Serial.print(" looping... ");
    Serial.println(count++);delay(1000);
  } 
  if(TESTMODE >=1) {readAndSendTestData();} // this is when monitor is attached to send test data 
} // end loop  

  
void sendDataBit(byte slave, boolean highLow)
// meaning (to slave) depends on context
// if slave does NOT have a pendingRequest, then HIGH = flash, LOW = simple request ack
// if slave HAS a pendingRequest, then LOW = denied (may also be followed by a flash)
//    and HIGH = OK to open another door0
{
  pinMode(dataPin[slave], OUTPUT);
  digitalWrite(dataPin[slave],highLow);  // drives slave's dataPin 
  detachInterrupt(slave); // so changing my own interruptPin doesn't screw it up
  pinMode(interruptPin[slave], OUTPUT); // so I can change it
  digitalWrite(interruptPin[slave], LOW); // gets Slave's attention, slave still at INPUT_PULLUP
  pinMode(interruptPin[slave], INPUT_PULLUP); // PULLUP holds this line high until someone grounds it
  pinMode(dataPin[slave], INPUT_PULLUP); // doesn't really matter, I suppose, I'll set it specifically next send  
  switch (slave) 
  {
    case 0:
      EIFR = 0xFF; // from this forum page http://forum.arduino.cc/index.php?topic=179020.30
      // which suggests you clear ALL the flags (they are different on Mega and UNO
      //EIFR = _BV (INTF0);  // clear flag for interrupt 0 that was set when Writing to interruptPin
      // It is unclear which INTF0 is what on UNO vs. Mega   
      attachInterrupt(0, slave0ISR, FALLING); // to start listening again
      break;
    case 1:
      EIFR = 0xFF;
      //EIFR = _BV (INTF1);  
      attachInterrupt(1, slave1ISR, FALLING); // to start listening again
      break;
    // FUTURE for all attachable modules/slaves
    default: 
      // if nothing else matches, do the default
      break;
  }// end switch case  
  if(TESTMODE>0) {Serial.print(" Slave ");Serial.print(slave);
    Serial.print(" sent data ");Serial.println(highLow);}
} // end sendDataBit  


void sendNotAuthorizedAndFlash(byte slave)  // normally one of the responses to a slave request
{
  if (TESTMODE>0) {Serial.print("AR= "); Serial.println(authorizationRequest[slave]); 
    Serial.println("Sending not authorized "); }
  sendDataBit(slave,LOW); // always do this (inside this routine) 
  //   to tell slave LOW not authorized (when slave has a pendingRequest)
  delay(10);  // not sure how long to delay to protect the same slave also having to flash another door
  // go find slaves with empty doors and flash each of them
  // since there were already too many toys out, I only need to check which slaves have any door unlocked  
  for (int i=0; i < SLAVEMAX; i++)
  {
    if (toysOut[i] > 0) // that is, does this slave have any toys out
    {
      if (TESTMODE>0) {Serial.print("Sending FLASH for slave ");Serial.println(i);  } 
      sendDataBit(i,HIGH);  // note: slave will figure which door(s) are empty
    } // end if toysOut
  } // end for slave
  authorizationRequest[slave]=0; // reset the flag that got you here
  if (TESTMODE>0) {Serial.print("AR= "); Serial.println(authorizationRequest[slave]); }  
}  // end sendNotAuthorizedAndFlash


void slave1ISR (void) // means pin 3 went low, look on pin 5 for data - FUTURE
// won't get here again until pin 3 goes high again and then driven low later
{
  Serial.println("Holy smokes, Batman! slave1ISR...");
  // I already have an INPUT_PULLUP resistor on pin 3 and currently nothing connected to it
  // do nothing until I've debugged interrupt 0
} // end slave0ISR


void sendAuthorization(byte slave)
{
  if (TESTMODE>0) {Serial.print("Slave ");Serial.print(slave);Serial.print(" being authorized "); } 
  
  sendDataBit(slave,HIGH); // in the slave context of a pendingRequest, HIGH means AUHORIZED
  
  toysOut[slave]++;  // I'm not sure I'm going to use this
  totalToysOut++;  
  authorizationRequest[slave]=0;  // reset the flag that got me here
  
  if (TESTMODE>0) {
  Serial.print(" slave "); Serial.print(slave);
  Serial.print(" toys out "); Serial.print(toysOut[slave]);
  Serial.print(" total toys out count now = "); Serial.print(totalToysOut); 
  Serial.print(" AR= "); Serial.println(authorizationRequest[slave]);
  }  
} // end sendAuthorization


void slave0ISR (void) // means pin 2 went low, look on pin 4 for data
// won't get here again until pin 2 goes high again and then slave drives low later
//ISR routines are not themselves interrupt-able. Not unless you turn interrupts on inside, 
//which I strongly suggest you do not do. - Nick Gammon
{
  volatile int slave0 = 0;  // this must match slave # in routine name
  // the only time a slave will call is when they need authorization
  // or they put a toy back 
  if (dataPin[slave0]==LOW) // means slave telling me it put a toy back
  {
    toysOut[slave0]--; // indicating a toy was put back
    totalToysOut--; 
    // no response or other action is required
  }  // end if HIGH
  else  // that is, data pin was HIGH means this is a request for authorization in taking a toy out
  { // need to decide and set a flag (the flag will allow Master to send response outside of ISR)
    if (totalToysOut <= maxToysOut) 
      {authorizationRequest[slave0]=3;}  // flag for slave is authorized to take a toy out
    else
      {authorizationRequest[slave0]=2;} // flag for slave is NOT authorized to take a toy out 
  } // end else 
} // end slave0ISR


void readAndSendTestData() // this is when monitor is attached to send test data
{
  if (Serial.available() > 0) 
  {  
    // read the incoming byte:
    byte incomingByte = Serial.read();  // reads one byte only
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC); // expect a slave/module integer in ASCII
    if (incomingByte == 48+1)
    {
      Serial.println("Sending Not Authorized and Flash. "); 
      sendNotAuthorizedAndFlash(0);
      Serial.print("Returned to test from sendNotAuthorizedAndFlash, AR= ");
      Serial.println(authorizationRequest[0]);
    } // end if incomingByte
    else if (incomingByte == 48+0)
    {
      Serial.println("Sending Authorization"); 
      sendAuthorization(0);
      Serial.println("Returned from sendingAuthorization ");
    } // end else
  } // end if Serial.available
} // end readAndSendTestData
void slave1ISR (void) // means pin 3 went low, look on pin 5 for data - FUTURE
// won't get here again until pin 3 goes high again and then driven low later
{
  Serial.println("Holy smokes, Batman! slave1ISR...");
  // I already have an INPUT_PULLUP resistor on pin 3 and currently nothing connected to it
  // do nothing until I've debugged interrupt 0
} // end slave0ISR

Interrupt driven code in an ISR, where interrupts are disabled, seems like a bad idea.

Useless comments need to be accurate. The } is not the end of slave0ISR.

  volatile int slave0 = 0;  // this must match slave # in routine name

Local variables do not need to be volatile. The volatile keyword tells the compiler that each time the variable is accessed it must be re-read, because it might have been changed by an interrupt. Your ISR is not interruptable.

  if (dataPin[slave0]==LOW) // means slave telling me it put a toy back

Shouldn't you be READING the state of dataPin[clave0] and comparing that value to LOW?

Thanks, Paul.

I apologize for the useless comments. I had copied from the other ISR and didn't update the comments.

I had put the Serial.print() line into slave1ISR as a quick check. Just to be on the safe side I've now set a flag and done the Serial.print in the main loop. ... No change in behavior.

Yes, that was overkill on "volatile". I've taken it out. ... No change in behavior.

Good catch on "READING the state of dataPin[slave0]", that would have caused me other problems, later. I've fixed it but there was no change in the program's behavior.

I changed interrupt1 to interrupt2 (on pin 21), added in slave2ISR(), etc. and do not have any unusual behavior. When I put interrupt1 back in (as well as interrupt2) my strange behavior returns. That got me to thinking it might be noise on pin3 (interrupt1) so I put an additional 10k PULLUP resistor on pin3 but no improvement.

Any other ideas?

So now I've tried a few other things.

I moved the slave physically from pins 2 and 4 to pins 3 and 5 (now interrupt 1 instead of interrupt 0). ... Both interrupt 0 and 1 fire! (Holy Smokes, Batman!).

So I moved the slave physically to pins 21 and 31 (interrupt 2). ... I get only the proper interrupt 2 firing.

I added in interrupt3 and connected the slave to those pins. ... Proper only interrupt 3 firing.

Now I'm going to build another slave (or mock-up) and try them in various combinations of 2 slaves actually connected.

Progress, of sorts.

Up until now I've had both the UNO slave and the Master Mega each powered from a separate computer via USB. I've had a common ground between the Uno and the Mega. In some cases I also had the 5v pins on the Mega and Uno connected. Either of these configurations generates the unexpected call to the wrong ISR as long as I have only interrupts 0 and 1 attached.

When I attached a second slave (with power from the Mega, not USB) the problem went away. I also tried running the single slave only from the 5v on the Mega (which is harder to get all the test signals out since the slave then doesn't have a monitor through the USB) but the double interrupt went away.

My production configuration will be to run the Mega and all Unos from a single 2 amp regulated 5v power supply, with no USB. So maybe I'll be all right.

I'm uncomfortable, but I'm moving on since I seem to have a work-around. (Or from another perspective, the problem may only exist in my test environment.) I've got other problems looming on the horizon as I more thoroughly test my new communication protocol.

If other interrupt issues come up, I plan to fuss with putting the interrupt into the Mega and the interrupt out to each slave on separate pins, even though it would required an additional wire between each device.

Thanks again, Paul, for the help and to Nick and the others on my earlier thread. You guys are great! Try getting help like this on a Windows or Apple product.

Well, I'm back.

I've been making good progress with my own UNO to Mega communications using the 6 Mega standard Interrupts. I've physically built 3 slaves and they work well when hooked to any of the first 4 interrupts ( pins 2, 3, 21 and 20) on the Mega. I haven't tried interrupt 4 and 5, yet.

I ran into another problem when adding EEPROM to store some configurations on the UNOs. (EEPROM is probably coincidental, since I tried commenting it out completely and had the same behavior. ) The slave program in the UNOs compile and upload fine, but when running, they can't get out of the setup routine. I've been trying to find the offending line and depending on which line I comment out I get various late night bizarre behaviors. Sometimes I get into the second line of Serial.print in setup and it hangs, or it keeps looping back to the start of setup or I've lost track of all the other random behaviors.

I've compiled and uploaded using two different Windows 7 computers (Arduino version 1.0.5) and several different UNOs, some have already had EEPROM written and some are virgin. Oddly, when I compile and upload the same program to a Mega, it works fine. I don't think it is a size problem since the compiled version is about 10.4kbytes (my UNOs have 32k). However it might be some kind of page boundary problem.

I cut back a handful of the subroutines to just stubs (for example test subroutines that would never get called without keyboard/monitor input) and the program starts working properly. But then I don't have any hardware testing ability, and WORSE I don't know when the problem will return.

I just pulled another virgin UNO R3 out of it's bag, with no hardware hooked to it, compiled and uploaded the attached sketch and am getting random looping behavior, never apparently getting out of setup(). This goes on for as long as I've had patience. However, when I close the monitor window on my computer, the TX,RX LEDs on the UNO stop flashing after about 2 minutes.

Anyone had a similar problem and how might I best cope with it?

TC71M12Slave_2013sep16V.ino (28.8 KB)

Oddly, when I compile and upload the same program to a Mega, it works fine. I don't think it is a size problem since the compiled version is about 10.4kbytes (my UNOs have 32k). However it might be some kind of page boundary problem.

It could be an SRAM problem. Go through your code and use the F() function to keep those static strings in program memory instead of copying them to SRAM before using them. Here are some examples from your setup() function:

  Serial.println(F("Note: from monitor 1 = denied, 0 = authorized, 5 = print AR list "));
  Serial.println(F("      Only testing slave 0, for now..."));
  Serial.println(F("end of setup... "));  
  // and do all the rest of the static strings like that.

SurferTim, I love you! This seems to have solved my problem. Now I can get back to hardware problems.

Now why hadn't I heard of casting strings before?

Thanks.

Now why hadn't I heard of casting strings before?

Perhaps because there is no casting going on. Casting tells the compiler to treat one data type as another. That is not what F() does.