Forum: Mikrocontroller und Digitale Elektronik Fehler im Code?


von La. W. (flyingeagle)


Lesenswert?

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:
1
#include <Wire.h>
2
3
#define Mcp_Reg_IODIRA      0x00    // IO direction  (0 = output, 1 = input (Default))
4
#define Mcp_Reg_IODIRB      0x01
5
#define Mcp_Reg_IOPOLA      0x02    // IO polarity   (0 = normal, 1 = inverse)
6
#define Mcp_Reg_IOPOLB       0x03
7
#define Mcp_Reg_GPINTENA     0x04    // Interrupt on change (0 = disable, 1 = enable)
8
#define Mcp_Reg_GPINTENB     0x05
9
#define Mcp_Reg_DEFVALA      0x06    // Default comparison for interrupt on change (interrupts on opposite)
10
#define Mcp_Reg_DEFVALB      0x07
11
#define Mcp_Reg_INTCONA      0x08    // Interrupt control (0 = interrupt on change from previous, 1 = interrupt on change from DEFVAL)
12
#define Mcp_Reg_INTCONB      0x09
13
#define Mcp_Reg_IOCON        0x0A    // IO Configuration: bank/mirror/seqop/disslw/haen/odr/intpol/notimp
14
//#define Mcp_Reg_IOCON     0x0B     // same as 0x0A
15
#define Mcp_Reg_GPPUA        0x0C    // Pull-up resistor (0 = disabled, 1 = enabled)
16
#define Mcp_Reg_GPPUB        0x0D
17
#define Mcp_Reg_INFTFA       0x0E    // Interrupt flag (read only) : (0 = no interrupt, 1 = pin caused interrupt)
18
#define Mcp_Reg_INFTFB       0x0F
19
#define Mcp_Reg_INTCAPA      0x10    // Interrupt capture (read only) : value of GPIO at time of last interrupt
20
#define Mcp_Reg_INTCAPB      0x11
21
#define Mcp_Reg_GPIOA        0x12    // Port value. Write to change, read to obtain value
22
#define Mcp_Reg_GPIOB        0x13
23
#define Mcp_Reg_OLLATA       0x14     // Output latch. Write to latch output.
24
#define Mcp_Reg_OLLATB       0x15
25
26
#define mcp                             0x21
27
28
#define MCPResetPin      4    // hier hängt der MCP mit seinem Reset-Pin dran
29
#define MCPInterruptA                  0    // hier hängt der MCP mit seinem Interrupt-Pin 19 dran
30
#define MCPInterruptB              1    // hier hängt der MCP mit seinem Interrupt-Pin 19 dran
31
#define LEDOnBoard      13    // pin 13, LED aufm Arduino-Board
32
33
byte WireTransmitResult = 0;
34
boolean SwitchPressed = false;
35
boolean isInInit = false;
36
byte inputStateA = 0;
37
byte inputStateB = 0;
38
39
40
void setup()
41
{
42
  Serial.begin(9600);
43
  Serial.println("Start");
44
  Wire.begin();
45
  initiic();
46
  pinMode (LEDOnBoard, OUTPUT);
47
  digitalWrite (LEDOnBoard, LOW);
48
  attachInterrupt(MCPInterruptA, ISRSwitchPressed, FALLING);
49
  //  attachInterrupt(MCPInterruptB, ISRSwitchPressed, FALLING);
50
  Serial.println("Bereit");
51
}
52
53
54
55
void loop()
56
{
57
  if (SwitchPressed)
58
  {
59
    inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
60
    inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
61
62
    Serial.print("LOOP inputStateA: ");
63
    Serial.println(inputStateA, BIN);
64
    Serial.print("LOOP inputStateB: ");
65
    Serial.println(inputStateB, BIN);
66
67
    digitalWrite (LEDOnBoard, LOW);
68
    SwitchPressed = false;
69
  }
70
}
71
72
73
74
void ISRSwitchPressed ()
75
{
76
  Serial.println("ISRSwitchPressed");
77
  digitalWrite (LEDOnBoard, HIGH);
78
79
  inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
80
  inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
81
82
  Serial.print("ISR inputStateA: ");
83
  Serial.println(inputStateA, BIN);
84
  Serial.print("ISR inputStateB: ");
85
  Serial.println(inputStateB, BIN);
86
87
  if (SwitchPressed || isInInit)
88
  {
89
    /*
90
    Serial.println(inputState, BIN);
91
    Serial.print("ISR SP: ");
92
    Serial.println((String)isInInit);
93
    Serial.print("ISR iI: ");
94
    Serial.println((String)SwitchPressed);
95
    Serial.println("ISR returned");
96
    */
97
    digitalWrite (LEDOnBoard, LOW);
98
  }
99
  else
100
  {
101
    SwitchPressed = true;
102
  }
103
}
104
105
106
107
void writeiic(uint8_t adr, uint8_t port, byte value)
108
{
109
  Wire.beginTransmission( adr );
110
  Wire.write( port );
111
  Wire.write( value );
112
  WireTransmitResult = Wire.endTransmission();
113
114
  //  String t = "writeiic: ";
115
  //  t += " Adr: " + String(adr) + " Port: " + String(port) + " Value: " + String(value);
116
  //  Serial.println(t);
117
}
118
119
120
121
byte readiicbyte(uint8_t adr, uint8_t port)
122
{
123
  Serial.println("IIC Lesen");
124
  byte returnword = 0x00;
125
126
  Wire.beginTransmission(adr);
127
  Wire.write(port);
128
  Wire.endTransmission();
129
  Wire.requestFrom((int)adr, 1);
130
131
  int c = 0;
132
133
  if (Wire.available())
134
  {
135
    returnword = Wire.read();
136
  }
137
  return returnword;
138
}
139
140
141
142
void initiic()
143
{
144
  Serial.println("Init Wire");
145
  isInInit = true;
146
  SwitchPressed = false;
147
148
  pinMode(MCPResetPin, OUTPUT);
149
  digitalWrite(MCPResetPin, LOW);
150
  delay(5);
151
  digitalWrite(MCPResetPin, HIGH);
152
153
  writeiic(mcp, Mcp_Reg_IODIRA,  0x00);        // Alles auf Output setzen
154
  writeiic(mcp, Mcp_Reg_GPIOA,  0x00);        // Alle Pins auf 0 setzen
155
  writeiic(mcp, Mcp_Reg_IODIRB,  0x00);        // Alles auf Output setzen
156
  writeiic(mcp, Mcp_Reg_GPIOB,  0x00);        // alle Pins auf 0 setzen
157
158
  byte inputMaskA = 0xFF;
159
  byte inputMaskB = 0xFF;
160
  byte polA = 0xFF;
161
  byte polB = 0xFF;
162
163
  writeiic(mcp, Mcp_Reg_IODIRA, inputMaskA);    // Richtung (Ein-/Ausgang) einstellen
164
  writeiic(mcp, Mcp_Reg_IODIRB, inputMaskB);    // Richtung (Ein-/Ausgang) einstellen
165
166
  writeiic(mcp, Mcp_Reg_GPPUA, inputMaskA);    // Pull-Up Widerstände einrichten
167
  writeiic(mcp, Mcp_Reg_GPPUB, inputMaskB);    // Pull-Up Widerstände einrichten
168
169
  writeiic(mcp, Mcp_Reg_IOPOLA, polA);      // Polarität einstellen (für Eingänge 0 oder 1)
170
  writeiic(mcp, Mcp_Reg_IOPOLB, polB);      // Polarität einstellen (für Eingänge 0 oder 1)
171
172
  writeiic(mcp, Mcp_Reg_GPINTENA, inputMaskA);          // Interrupt aktivieren
173
  writeiic(mcp, Mcp_Reg_GPINTENB, inputMaskB);          // Interrupt aktivieren
174
175
  writeiic(mcp, Mcp_Reg_DEFVALA, inputMaskA);    // Interrupt Default-Wert festlegen
176
  writeiic(mcp, Mcp_Reg_DEFVALB, inputMaskB);    // Interrupt Default-Wert festlegen
177
178
  writeiic(mcp, Mcp_Reg_INTCONA, inputMaskA);    // Interrupt Vergleich einstellen, 0 = Änderung zum Vorgänger, 1 = Änderung zum DEFVAL-Wert
179
  writeiic(mcp, Mcp_Reg_INTCONB, inputMaskB);    // Interrupt Vergleich einstellen, 0 = Änderung zum Vorgänger, 1 = Änderung zum DEFVAL-Wert
180
181
  readiicbyte(mcp, Mcp_Reg_INTCAPA);      // Interrupts lesen und dadurch löschen
182
  readiicbyte(mcp, Mcp_Reg_INTCAPB);      // Interrupts lesen und dadurch löschen
183
184
  Serial.println("Init Fertig");
185
  isInInit = false;
186
}

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

jemand ne idee?

danke & grüße

von asdfasd (Gast)


Lesenswert?

In Interrupt-Routinen gehört nichts, was länger als ein paar Takte 
dauert, insbesondere keine I2C-Transaktionen oder Serial-Port-Ausgaben. 
Auch müssen Zugriffe auf Resourcen (Speicher, Register, I/O-Ports, etc), 
die sowohl von ISR als auch vom normalen Programmcode benutzt werden, 
synchronisiert werden.  Stell dir z.B. nur mal vor was passiert, wenn 
während deine Mainloop gerade auf dem Serial-Port oder dem I2C-Bus 
rumwuselt, mitten drin von einem Interrupt unterbrochen wird, der das 
gleiche machen will ...

von La. W. (flyingeagle)


Lesenswert?

ok, ich schrieb aber bereits das alles was Serial ist, nur debug ist, 
das einerseits, andererseits, der code ist das ergebniss von einigem 
probieren.
das readbyte hatte ich ursprünglich da nicht drin, da man ja aber die 
register lesen muss bevor ein neuer interupt auftreten kann, hab ich es 
da rein gemacht.
was genau meinst du mit synchronisieren?

von Wolfgang (Gast)


Lesenswert?

Aber es gibt doch keinen Grund, den ganzen Kram in die 
ISRSwitchPressed() zu packen. Setze in der ISR nur das Flag und rufe das 
alles aus dem Hauptprogramm auf.

von La. W. (flyingeagle)


Lesenswert?

hab das zwischenzeitlich mal so umgebaut
1
... [schnipp]
2
3
volatile boolean SwitchPressed = false;
4
5
6
void loop()
7
{
8
  if (SwitchPressed || (millis() - lastRead >= 4000))
9
  {
10
    digitalWrite (LEDOnBoard, HIGH);
11
12
    //    inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
13
    //    inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
14
    //    inputStateA = readiicbyte(mcp, Mcp_Reg_GPIOA);
15
    //    inputStateB = readiicbyte(mcp, Mcp_Reg_GPIOB);
16
    inputStateA = readiicbyte(mcp, Mcp_Reg_OLLATA);
17
    inputStateB = readiicbyte(mcp, Mcp_Reg_OLLATB);
18
19
    Serial.print("LOOP inputStateA: ");
20
    Serial.println(inputStateA, BIN);
21
    Serial.print("LOOP inputStateB: ");
22
    Serial.println(inputStateB, BIN);
23
24
    digitalWrite (LEDOnBoard, LOW);
25
26
    lastRead = millis();
27
    SwitchPressed = false;
28
  }
29
}
30
31
32
33
void ISRSwitchPressed ()
34
{
35
  if (!SwitchPressed && !isInInit)
36
  {
37
    SwitchPressed = true;
38
  }
39
}
40
41
42
43
void writeiic(uint8_t adr, uint8_t port, byte value)
44
{
45
  ...
46
}
47
48
49
50
byte readiicbyte(uint8_t adr, uint8_t port)
51
{
52
  ...
53
}
54
55
56
57
void initiic()
58
{
59
  ...
60
61
  //  readiicbyte(mcp, Mcp_Reg_INTCAPA);      // Interrupts lesen und dadurch löschen
62
  //  readiicbyte(mcp, Mcp_Reg_INTCAPB);      // Interrupts lesen und dadurch löschen
63
  inputStateA = readiicbyte(mcp, Mcp_Reg_INTCAPA);
64
  inputStateB = readiicbyte(mcp, Mcp_Reg_INTCAPB);
65
  inputStateA = readiicbyte(mcp, Mcp_Reg_GPIOA);
66
  inputStateB = readiicbyte(mcp, Mcp_Reg_GPIOB);
67
  inputStateA = readiicbyte(mcp, Mcp_Reg_OLLATA);
68
  inputStateB = readiicbyte(mcp, Mcp_Reg_OLLATB);
69
70
  Serial.println("Init Fertig");
71
  isInInit = false;
72
}

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".

: Bearbeitet durch User
von Dumdi D. (dumdidum)


Lesenswert?

Vielleicht meinst Du && statt || ?

von La. W. (flyingeagle)


Lesenswert?

nein, soll ja gelesen werden wenn ISR ausgelöst wurde oder die 
"Poll-Zeit" abgelaufen ist.

von La. W. (flyingeagle)


Lesenswert?

Niemand mehr ne idee?
Hardwareprobleme kann ich , denk ich, ausschließen, habe nochmal neue 
MCPs und den 3. Arduino verwendet.

von dunno.. (Gast)


Lesenswert?

ich hab ja keine ahnung von arduinos, aber wenn ich code mit gcc für 
meine arms schreibe, deklariere ich eine isr als isr.
1
 void foo(void) __attribute__ ((interrupt));

muss man das bei arduino nicht..?

von La. W. (flyingeagle)


Lesenswert?

das geht da mit attachInterrupt ... man kann bestimmt das auch anders 
machen. so generell.
vielmehr drängt es sich  mir hier auch auf, ob ich hardwaretechnisch ein 
problem habe oder softwaretechnisch.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.