Es kommt selten vor, dass elektronische Bauteile ausfallen, aber es kommt vor.
Durch Störung oder Ausfall eines I²C-Slave ist es möglich, dass der Buspegel auf einer oder beiden Leitungen auf low - zumindest von high weg - gezogen wird. Bei bekannten I²C-Slave-Routinen kann dadurch der Master in der Abfrage
Code:
unsigned char i2c_start(unsigned char address)
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // wait until transmission completed
while (!(TWCR & (1<<TWINT)));
...
hängen bleiben und in der Folge hängt die ganze Ausrüstung.
Anmerkung: eine denkbare Störung, die leicht simuliert werden kann, ist ein Slave an der Busleitung, aber ohne Spannungsversorgung.
Hängt der Bus wegen eines Teilnehmers (I²C-Adressaten), dann ist auch die Kommunikation mit anderen I²C-Baugruppen nicht möglich. Wäre es also vergebliche Mühe einen solchen Deadlock als Problem anzusehen und eine Lösung zu überlegen?
Ich meine schon. Wenn der Master hängt, dann hängt ja schließlich die ganze Apparatur - egal welche es auch ist. Wenn ein solches Hängenbleiben in der I²C-Routine verhindert werden kann, dann könnte wenigstens noch eine Störungsmeldung oder Ähnliches vom Master ausgegeben werden, es liegt ja nach der Abfrage ein entsprechendes 0/1-Signal vor. Daher die Frage ob es eine Lösung gibt und wie die aussieht?
Ja, es gibt sie, sie ist einfach, eine Möglichkeit folgt jetzt.
Die while-Schleife wird ersetzt durch eine Schleife
Code:
#define nrep 3000 //
...
for (uint16_t m = 0; m<=nrep; m++)
{ if ((TWCR & (1<<TWINT))) break; } //
Wenn eine positive Antwort existiert, wird die Schleife sofort abgebrochen - es ist also zu der while-Variante kein Nachteil (signifikant nach Codelänge oder Zeitbedarf) entstanden. Damit wird die Abfrage aber in ihrer Anzahl begrenzt, sie kann nicht endlos ausgedehnt werden und der Master erhält AUF JEDEN FALL eine Antwort - selbst wenn es nur eine negative ist. Der Zähler nrep soll dazu dienen evtl. auftretende Stretches und evtl. auftretende andere Signale (Multimaster o.ä.) abzufangen. Theoretisch reichen an die 150 Wiederholungen (bei einem 20 MHz-CPU-Takt) um in die zeitliche Spezifikation eines sehr langsamen Taktes - 10 kHz - zu kommen. Allerdings ist der I²C-Bus nach der offiziellen Darstellung, siehe
UM10204 I2C-bus specification and user manual
Rev. 4 — 13 February 2012 von NXP
Seite 32 unten
bis auf 0 Hz festgelegt. Der hier genannte Wert von 3000 dürfte für fast alle praktisch vorkommenden Fälle ausreichend sein. Werden deutlich langsamere Antworten erwartet, kann der Wert nrep entsprechend höher genommen werden oder eine Verlängerung der Art
Code:
for (uint16_t m = 0; m<=60000; m++)
{ if ((TWCR & (1<<TWINT))) break; // I2C-Device hat sich gemeldet
for (uint8_t n = 0; n<= tttt; n++) // wenn nicht
{ tggl = 29 + 2; } // => Zeit schinden
}
eingebaut werden. Dabei bringt die Übernahme von TWBR in die Zeitschleife eine Anpassung an unterschiedlich gewählte I²C-Takte. Weitere Anpassungen sind natürlich noch möglich. So nutze ich diese Variante beispielsweise, um zu Beginn der Arbeit mit einem I²C-Bussystem alle möglichen Adressen abzufragen und nachzusehen, ob und welche Adressen besetzt sind. Der Vergleich mit einer vorgegebenen Liste ist dabei natürlich möglich.
Danke Sternthaler für die fruchtbare Diskussion.
Lesezeichen