Fehler im Code oder Hardware

Hallo,

ich hatte die Tage schon mal wegen Interruptproblemen beim MCP23017 gefragt, aber keine antworten erhalten, daher habe ich mich nochmal hingesetzt und den code auf das m.e. wesentliche reduziert:

#include <Wire.h>

#define Mcp_Reg_IODIRA			0x00  	// IO direction  (0 = output, 1 = input (Default))
#define Mcp_Reg_IODIRB			0x01
#define Mcp_Reg_IOPOLA			0x02  	// IO polarity   (0 = normal, 1 = inverse)
#define Mcp_Reg_IOPOLB   		0x03
#define Mcp_Reg_GPINTENA 		0x04  	// Interrupt on change (0 = disable, 1 = enable)
#define Mcp_Reg_GPINTENB 		0x05
#define Mcp_Reg_DEFVALA  		0x06  	// Default comparison for interrupt on change (interrupts on opposite)
#define Mcp_Reg_DEFVALB  		0x07
#define Mcp_Reg_INTCONA  		0x08  	// Interrupt control (0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL)
#define Mcp_Reg_INTCONB  		0x09
#define Mcp_Reg_IOCON    		0x0A  	// IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp
//#define Mcp_Reg_IOCON 		0x0B 		// same as 0x0A
#define Mcp_Reg_GPPUA    		0x0C  	// Pull-up resistor (0 = disabled, 1 = enabled)
#define Mcp_Reg_GPPUB    		0x0D
#define Mcp_Reg_INFTFA   		0x0E  	// Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt)
#define Mcp_Reg_INFTFB   		0x0F
#define Mcp_Reg_INTCAPA  		0x10  	// Interrupt capture (read only) : value of GPIO at time of last interrupt
#define Mcp_Reg_INTCAPB  		0x11
#define Mcp_Reg_GPIOA    		0x12  	// Port value. Write to change, read to obtain value
#define Mcp_Reg_GPIOB    		0x13
#define Mcp_Reg_OLLATA   		0x14   	// Output latch. Write to latch output.
#define Mcp_Reg_OLLATB   		0x15

#define mcp                             0x21

#define MCPResetPin			4		// hier hängt der MCP mit seinem Reset-Pin dran
#define MCPInterruptA	                0		// hier hängt der MCP mit seinem Interrupt-Pin 19 dran
#define MCPInterruptB    	        1		// hier hängt der MCP mit seinem Interrupt-Pin 19 dran
#define LEDOnBoard			13		// pin 13, LED aufm Arduino-Board

byte WireTransmitResult = 0;
boolean SwitchPressed = false;
boolean isInInit = false;
byte inputStateA = 0;
byte inputStateB = 0;


void setup()
{
  Serial.begin(9600);
  Serial.println("Start");
  Wire.begin();
  initiic();
  pinMode (LEDOnBoard, OUTPUT);
  digitalWrite (LEDOnBoard, LOW);
  attachInterrupt(MCPInterruptA, ISRSwitchPressed, FALLING);
  //  attachInterrupt(MCPInterruptB, ISRSwitchPressed, FALLING);
  Serial.println("Bereit");
}



void loop()
{
  if (SwitchPressed)
  {
    inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
    inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);

    Serial.print("LOOP inputStateA: ");
    Serial.println(inputStateA, BIN);
    Serial.print("LOOP inputStateB: ");
    Serial.println(inputStateB, BIN);

    digitalWrite (LEDOnBoard, LOW);
    SwitchPressed = false;
  }
}



void ISRSwitchPressed ()
{
  Serial.println("ISRSwitchPressed");
  digitalWrite (LEDOnBoard, HIGH);

  inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
  inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);

  Serial.print("ISR inputStateA: ");
  Serial.println(inputStateA, BIN);
  Serial.print("ISR inputStateB: ");
  Serial.println(inputStateB, BIN);

  if (SwitchPressed || isInInit)
  {
    /*
    Serial.println(inputState, BIN);
    Serial.print("ISR SP: ");
    Serial.println((String)isInInit);
    Serial.print("ISR iI: ");
    Serial.println((String)SwitchPressed);
    Serial.println("ISR returned");
    */
    digitalWrite (LEDOnBoard, LOW);
  }
  else
  {
    SwitchPressed = true;
  }
}



void writeiic(uint8_t adr, uint8_t port, byte value)
{
  Wire.beginTransmission( adr );
  Wire.write( port );
  Wire.write( value );
  WireTransmitResult = Wire.endTransmission();

  //  String t = "writeiic: ";
  //  t += " Adr: " + String(adr) + " Port: " + String(port) + " Value: " + String(value);
  //  Serial.println(t);
}



byte readiicbyte(uint8_t adr, uint8_t port)
{
  Serial.println("IIC Lesen");
  byte returnword = 0x00;

  Wire.beginTransmission(adr);
  Wire.write(port);
  Wire.endTransmission();
  Wire.requestFrom((int)adr, 1);

  int c = 0;

  if (Wire.available())
  {
    returnword = Wire.read();
  }
  return returnword;
}



void initiic()
{
  Serial.println("Init Wire");
  isInInit = true;
  SwitchPressed = false;

  pinMode(MCPResetPin, OUTPUT);
  digitalWrite(MCPResetPin, LOW);
  delay(5);
  digitalWrite(MCPResetPin, HIGH);

  writeiic(mcp, Mcp_Reg_IODIRA,	0x00);				// Alles auf Output setzen
  writeiic(mcp, Mcp_Reg_GPIOA,	0x00);				// Alle Pins auf 0 setzen
  writeiic(mcp, Mcp_Reg_IODIRB,	0x00);				// Alles auf Output setzen
  writeiic(mcp, Mcp_Reg_GPIOB,	0x00);				// alle Pins auf 0 setzen

  byte inputMaskA = 0xFF;
  byte inputMaskB = 0xFF;
  byte polA = 0xFF;
  byte polB = 0xFF;

  writeiic(mcp, Mcp_Reg_IODIRA, inputMaskA);		// Richtung (Ein-/Ausgang) einstellen
  writeiic(mcp, Mcp_Reg_IODIRB, inputMaskB);		// Richtung (Ein-/Ausgang) einstellen

  writeiic(mcp, Mcp_Reg_GPPUA, inputMaskA);		// Pull-Up Widerstände einrichten
  writeiic(mcp, Mcp_Reg_GPPUB, inputMaskB);		// Pull-Up Widerstände einrichten

  writeiic(mcp, Mcp_Reg_IOPOLA, polA);			// Polarität einstellen (für Eingänge 0 oder 1)
  writeiic(mcp, Mcp_Reg_IOPOLB, polB);			// Polarität einstellen (für Eingänge 0 oder 1)

  writeiic(mcp, Mcp_Reg_GPINTENA, inputMaskA);	        // Interrupt aktivieren
  writeiic(mcp, Mcp_Reg_GPINTENB, inputMaskB);	        // Interrupt aktivieren

  writeiic(mcp, Mcp_Reg_DEFVALA, inputMaskA);		// Interrupt Default-Wert festlegen
  writeiic(mcp, Mcp_Reg_DEFVALB, inputMaskB);		// Interrupt Default-Wert festlegen

  writeiic(mcp, Mcp_Reg_INTCONA, inputMaskA);		// Interrupt Vergleich einstellen, 0 = Änderung zum Vorgänger, 1 = Änderung zum DEFVAL-Wert
  writeiic(mcp, Mcp_Reg_INTCONB, inputMaskB);		// Interrupt Vergleich einstellen, 0 = Änderung zum Vorgänger, 1 = Änderung zum DEFVAL-Wert

  readiicbyte(mcp, Mcp_Reg_INTCAPA);			// Interrupts lesen und dadurch löschen
  readiicbyte(mcp, Mcp_Reg_INTCAPB);			// Interrupts lesen und dadurch löschen

  Serial.println("Init Fertig");
  isInInit = false;
}

Leider tuts das nicht, alle Serial-angaben sind nur zu debugzwecken drin!

Schon beim Start gehts los, es kommt gerade noch das "I" von "Init fertig" und dann passiert nix mehr.

Lasse ich die Zeile: attachInterrupt(MCPInterruptA, ISRSwitchPressed, FALLING); weg startet er wie gewünscht, klar logisch, ISR ist dann nicht mehr.

schaltung ist nicht spannend, spannungsversorgung an den mcp, adress-pins (die stimmen), reset und interrupts an die im code beschriebenen pins.

gedanke ist (später) beliebigen port als input/output schalten, interrupts auf a und/oder b legen, dass ich nur einen abfragen muss.
pull-ups aktiviert bei input. hier im test hängt nix weiter dran, daher alle pins auf input und pull-up.

plattform: arduino uno r3
das ist mittlerweile der 4 mcp den ich teste, alle anderen gehen aber noch!

jemand ne idee?

danke & grüße

Serial Ausgaben haben in Interrupts nichts verloren!

Außerdem müssen alle Variablen die innerhalb und außerhalb von ISRs verwendet werden als volatile deklariert werden.

Wenn du Serial Ausgaben durch Interrupts auslösen willst, setzte in der ISR ein Flag und frage darauf in loop() ab

An welche Pin des Arduino hast Du die Interruptausgänge des MCP angeschlossen?
Grüße Uwe

plattform: arduino uno r3

#define MCPInterruptA 0 // hier hängt der MCP mit seinem Interrupt-Pin 19 dran
#define MCPInterruptB 1 // hier hängt der MCP mit seinem Interrupt-Pin 19 dran

Pin 0 + 1 sind schon von der seriellen Schnittstelle belegt.

Eine Doppelbelegung dieser Art bereitet garantiert Probleme.

Das sind nicht die Pins, sondern die Interrupt Nummern:
http://arduino.cc/en/Reference/AttachInterrupt

ich hatte mal ein ähnliches Problem. ich wollte in der interrupt Routine alle halbe Sekunde einen Ausgang des mcp toggeln. ich glaube, das ging nicht, weil irgend ein Timer dann mehrfach verwendet wird. ich bin mir aber nicht sicher, ob es in der Wire Bibliothek war, oder in der in der mcp Bibliothek.

sinn des Ganzen sollte sein, einfach eine LED mit konstanter Frequenz blinken zu lassen, die nicht vom Ablauf des restlichen Programmes beeinflusst wird.

auf einem normalen Ausgang Hat es richtig funktioniert, auf einem mcp Ausgang eben nicht.

Es kann auch gut sein, dass Wire genau wie Serial nicht richtig in ISRs funktioniert. Einfach weil ISRs die Interrupts global deaktivieren. Die Low Level I2C Schreibmethode aktiviert irgendwelche Interrupts am TWI Bus. Und Empfangen wird sowieso mit Interrupts laufen.

Auch da ist die Lösung das Senden/Empfangen außerhalb der ISR zu machen.

Bezüglich der Interrupt Pins gibt es neuerdings übrigens das digitalPinToInterrupt() Makro, das eine Arduino Pin Nummer auf eine Interrupt Nummer umsetzt.

Serenifly:
Das sind nicht die Pins, sondern die Interrupt Nummern:
http://arduino.cc/en/Reference/AttachInterrupt

Da hast du natürlich wahr!

hab das zwischenzeitlich mal so umgebaut

... [schnipp]

volatile boolean SwitchPressed = false;


void loop()
{
  if (SwitchPressed || (millis() - lastRead >= 4000))
  {
    digitalWrite (LEDOnBoard, HIGH);

    //    inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
    //    inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
    //    inputStateA = readiicbyte(mcp, Mcp_Reg_GPIOA);
    //    inputStateB = readiicbyte(mcp, Mcp_Reg_GPIOB);
    inputStateA = readiicbyte(mcp, Mcp_Reg_OLLATA);
    inputStateB = readiicbyte(mcp, Mcp_Reg_OLLATB);

    Serial.print("LOOP inputStateA: ");
    Serial.println(inputStateA, BIN);
    Serial.print("LOOP inputStateB: ");
    Serial.println(inputStateB, BIN);

    digitalWrite (LEDOnBoard, LOW);

    lastRead = millis();
    SwitchPressed = false;
  }
}



void ISRSwitchPressed ()
{
  if (!SwitchPressed && !isInInit)
  {
    SwitchPressed = true;
  }
}



void writeiic(uint8_t adr, uint8_t port, byte value)
{
  ...
}



byte readiicbyte(uint8_t adr, uint8_t port)
{
  ...
}



void initiic()
{
  ...

  //  readiicbyte(mcp, Mcp_Reg_INTCAPA);      // Interrupts lesen und dadurch löschen
  //  readiicbyte(mcp, Mcp_Reg_INTCAPB);      // Interrupts lesen und dadurch löschen
  inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
  inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
  inputStateA = readiicbyte(mcp, Mcp_Reg_GPIOA);
  inputStateB = readiicbyte(mcp, Mcp_Reg_GPIOB);
  inputStateA = readiicbyte(mcp, Mcp_Reg_OLLATA);
  inputStateB = readiicbyte(mcp, Mcp_Reg_OLLATB);

  Serial.println("Init Fertig");
  isInInit = false;
}

die ISR funktioniert nicht, zumindest kommt mehr oder weniger direkt
nach dem schließen des kontaktes (gnd auf pin am mcp) nichts.
daher habe ich zusätzlich in dem code ein pollen alle 4 sekunden
reingemacht, da wird auch gelesen, allerdings "rennt" der code nach
einer (un)bestimmten zeit einfach los, grad so als als wenn
millis/lastRead dann nicht mehr gesetzt wird.
ich hatte das lastRead = millis dann mal in die ISR gemacht, aber auch
das ändert am verhalten nichts.

um den code etwas kürzer zu halten, habe ich unveränderte dinge
"geschnippt".

Pins: die ersten beiden frei, da serial, und dann der/die nächste(n). das sind lt. refrence digitalpin 2 & 3 aber innerhalb attachinterrupt 0 und 1.

interrupt ein-/ausschalten halte ich auch für schwierig, wenn serial und wire welche nutzen (würden).

Niemand mehr ne idee?
Verwendet denn keiner diesen chip?

Wird schon verwendet, aber wahrscheinlich meistens ohne Interrupts.

Ich nehme mal an das kennst du:

ja das kenne ich, danach habe ich vieles in meinem code umgesetzt.
kann doch nicht sein, dass den chip hier noch keiner verwendet hat, oder zumindest den 8port ... der hat m.e. doch auch interrupt - oder nicht?
und pollen ist doch auch irgendwie unschön.