Forum: Mikrocontroller und Digitale Elektronik I2C Pic zu Arduino Mega - Bytes gehen unterwegs verloren


von Deniz (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
erstmal möchte ich loswerden wie oft mir dieses Forum schon bei 
Problemen geholfen hat, vielen Dank dafür.

Zu meinem Problem:
Ich möchte regelmäßig ca. 25 Integer Werte von einem PIC16F707 an ein 
Arduino Mega 2560 über I2C versenden.
Als Compiler verwende ich den Hi-tech C, dort ist auch eine Art i2c 
Bibliothek mitgeliefert (Soft i2c).
der Code zum senden sieht folgendermaßen aus:
1
bit SendResults(char *data,char Address)
2
  {
3
  
4
  for(unsigned char i=0; i < PinCount; i++) {
5
         i2c_WriteTo(Address);
6
        i2c_PutByte(data[2*i]);
7
        i2c_PutByte(data[2*i+1]);
8
        i2c_Stop();
9
  }
10
  i2c_WriteTo(Address);
11
  i2c_PutByte(SeparatorLine);
12
  i2c_PutByte(SeparatorLine);
13
  i2c_Stop();
14
  //return Error;
15
}

Das Arduino wertet dies folgendermaßen aus:
1
void receiveEvent(int howMany)
2
{
3
  while(Wire.available()) 
4
  {
5
    b[0] = Wire.read();
6
    b[1] = Wire.read();
7
    p = b[1] * 256  + b[0];
8
    if (p == 0) {Serial.println("CR");}
9
    else {
10
    Serial.print(p);
11
    Serial.print("---");
12
    }
13
  }
14
}

Das ganze funktioniert auch ganz gut soweit, in dem angehängten Auszug 
aus dem Serial Monitor sieht man schön strukturierte Werte. Allerdings 
sieht man auch, das hin und wieder zwei Integer Werte (wenn dann Zwei 
hintereinander) unterwegs verschütt gehen. Dies geschieht scheinbar 
zufällig und ich bin mittlerweile echt ratlos. Habe auch schon 
verschiedene Pullups verwendet, Oszi drangehängt etc.. die Pegel 
scheinen in Ordnung.
Hat von euch schonmal jemand solche Erfahrungen gemacht oder weiß einen 
Rat?
Vielen Dank schonmal.

Liebe Grüße,

Deniz

von Kein Name (Gast)


Lesenswert?

Hast du schon das Serial.print() überprüft?

Möglicherweise blockiert der PC den Arduino, I2C Eingabepuffer läuft 
über und der Arduino wirft die korrekt eingelesenen Zeichen weg. Oder 
umgekehrt - das receiveEvent() wird ausgeführt, aber bei überlaufendem 
Ausgabepuffer wirft das Serial.print() die Ausgabe weg.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Bau den Empfänger so um, dass er nen Interrupt auslöst und das in nen 
FIFO schreibt.

Da geht dann so schnell nix verlorn.

Achja und progg den AVR lieber in C und nicht diesem Failduino Müll ;)

Ansonsten hier ne I2C Slave lib, damit verhält sich der AVR wie nen 
EEPROM und jeder Integer kommt an ne bestimmte Adresse:
http://www.jtronics.de/avr-projekte/library-i2c-twi-slave.html

von Andreas H. (ahz)


Lesenswert?

Hi Denniz

Aus den Logs kann man natürlich nicht erkennen, ob der Sender oder der 
Empfänger die Daten verbummelt. Da wirst Du etwas rumdebuggen müssen.

Kannst Du einen Pin am PIC als Output schalten (auf '0' initialisieren) 
und als ersten Befehl in SendResults() den Pin auf '1' und als letzten 
Befehl in SendResults() wieder auf '0' setzen ?
Am Scope kannst Du dann über die High-Time des Pins prüfen, ob immer die 
gleiche Byteanzahl geschickt wurde.


Kein Name schrieb:
> Hast du schon das Serial.print() überprüft?

Das hätte ich auch im Verdacht. Aber laut arduino.cc ist das nicht 
blockend. Und ein Fifoüberlauf, wo ordentlich immer ASCII Ausgaben von 
genau zwei Bytes fehlen ?

Wenn man das Serial.print() rausnimmt sieht man aber garnix mehr :/
Eine Möglichkeit wäre, vom PIC aus Pakete mit den Bytes 0x01 .. 0x19, 
gefolgt von 0x00 zu schicken. Die Bytes addiert der Arduino zusammen und 
gibt am Ende des Pakets die Summe aus.
Dann sind keine Serial.Print() mehr während eines Pakets am werkeln. Mit 
einer hinreichend grossen Pause ZWISCHEN den Paketen sollte das zum 
testen hinhauen.

Martin Wende schrieb:
> Bau den Empfänger so um, dass er nen Interrupt auslöst und das in nen
> FIFO schreibt.
Wenn es der Arduino ist, dann ist das eine sehr gute Idee.

>
> Achja und progg den AVR lieber in C und nicht diesem Failduino Müll ;)
Solltest Du für diese Idee nicht erst mal wissen, warum er es so macht ?

Viele Grüße & Viel Erfolg beim Debuggen
Andreas

von Peter (Gast)


Lesenswert?

Was sind denn das für Werte in Deinem Log? Wie korrespondieren die mit 
den tatsächlichen übertragenen 9 Bit auf der Leitung?

von Peter D. (peda)


Lesenswert?

Deniz schrieb:
> for(unsigned char i=0; i < PinCount; i++) {
>          i2c_WriteTo(Address);
>         i2c_PutByte(data[2*i]);
>         i2c_PutByte(data[2*i+1]);
>         i2c_Stop();

Du kannst nicht einfach so ins Blaue hinein schreiben. Nach jedem Byte 
mußt Du erst prüfen, ob Du auch ein ACK empfangen hast.

Und der Master muß natürlich das Clock Stretching unterstützen, siehe:

http://www.i2c-bus.org/clock-generation-stretching-arbitration/


Peter

von Andreas H. (ahz)


Lesenswert?

Peter Dannegger schrieb:
> Und der Master muß natürlich das Clock Stretching unterstützen, siehe:
>
> http://www.i2c-bus.org/clock-generation-stretching...

Und der Peter muss natürlich noch lernen, die richtigen Dokumente zu 
lesen (just kidding ;-)

Clock stretching ist ein optionales Feature.

"UM10204 - I2C-bus specification and user manual - Rev. 5 — 9 October 
2012", Page 8, Table 2.
(Und die sollten es wissen - die haben I2C erfunden. Bzw. damals war es 
ja noch Philips)

Und damit der Peter nicht lange suchen muss:
http://www.nxp.com/documents/user_manual/UM10204.pdf

Viele Grüße
Andreas

von Peter D. (peda)


Lesenswert?

Andreas H. schrieb:
> Und der Peter muss natürlich noch lernen, die richtigen Dokumente zu
> lesen (just kidding ;-)

Nö.
Das richtige Dokument ist das AVR Datenblatt!
Als Slave zieht der AVR solange SCL auf low, bis der Interrupt behandelt 
ist.
Und wenn der Master nicht darauf wartet, dann geht es eben gegen die 
Wand.
Und das machen nicht nur die AVRs so, sondern auch andere MCs (z.B. 
8051, ARM).

Nur bei den dummen I2C-Slaves (ADC, EEPROM, IO-Port usw.) ist der SCL 
nur ein Eingang.


Peter

von Deniz (Gast)


Lesenswert?

Vielen Dank für die Antworten.

Peter Dannegger schrieb:
> Du kannst nicht einfach so ins Blaue hinein schreiben. Nach jedem Byte
> mußt Du erst prüfen, ob Du auch ein ACK empfangen hast.

Hab ich mittlerweile umgesetzt, leider ohne erfolg:
1
for(unsigned char i=0; i < PinCount; i++) {
2
i2c_WriteTo(Address);
3
while (-1 == i2c_PutByte(data[2*i])){};
4
while (-1 == i2c_PutByte(data[2*i+1])){};
5
//i2c_PutByte(SeparatorNumber);
6
i2c_Stop();
7
}

Peter schrieb:
> Was sind denn das für Werte in Deinem Log? Wie korrespondieren die mit
> den tatsächlichen übertragenen 9 Bit auf der Leitung?

Das sind Kapazitätsmesswerte eines Touchpanels (Anzahl Ladezyklen) in 
Integer. Vor dem Senden werden sie in Highbyte und Lowbyte geteilt und 
nach dem Empfang wieder zusammengesetzt - das sind die Werte im Log.

Andreas H. schrieb:
> Und ein Fifoüberlauf, wo ordentlich immer ASCII Ausgaben von
> genau zwei Bytes fehlen ?

Korrektur: Es fehlen immer genau zwei Integer Werte, sprich vier Bytes.

Andreas H. schrieb:
> Wenn man das Serial.print() rausnimmt sieht man aber garnix mehr :/
> Eine Möglichkeit wäre, vom PIC aus Pakete mit den Bytes 0x01 .. 0x19,
> gefolgt von 0x00 zu schicken. Die Bytes addiert der Arduino zusammen und
> gibt am Ende des Pakets die Summe aus.
> Dann sind keine Serial.Print() mehr während eines Pakets am werkeln. Mit
> einer hinreichend grossen Pause ZWISCHEN den Paketen sollte das zum
> testen hinhauen.

Das werde ich als erstes ausprobieren.

Martin Wende schrieb:
> Ansonsten hier ne I2C Slave lib, damit verhält sich der AVR wie nen
> EEPROM und jeder Integer kommt an ne bestimmte Adresse:
> http://www.jtronics.de/avr-projekte/library-i2c-tw...

Und das als zweites. Anschließend werde ich hier berichten wie es 
ausgegangen ist. Vielen Dank!

von Deniz (Gast)


Angehängte Dateien:

Lesenswert?

So ich habs ausprobiert und es scheint nicht am Empfänger zu liegen:

Sendercode:
1
for(unsigned char i=1; i < PinCount; i=i+2) {
2
i2c_WriteTo(Address);
3
while (-1 == i2c_PutByte(i/*data[2*i]*/)){};
4
while (-1 == i2c_PutByte(i+1/*data[2*i+1]*/)){};
5
//i2c_PutByte(SeparatorNumber);
6
i2c_Stop();
7
}

Empfängercode:
1
while(Wire.available()) 
2
{
3
MyByte = Wire.read();
4
if (MyByte == 0) {
5
Serial.println(i);
6
i = 0;
7
}
8
else{i=i+Wire.read();}

Es ist wieder ein Auszug aus dem Serial Monitor angehängt. Hier kann man 
schön sehen, dass wieder nach genau dem gleichen Schema Bytes verloren 
gehen.
Auch das Trennzeichen (0x00) geht ab und zu verloren, daher die Werte 
größer 156.

So langsam wird das ganze echt mysteriös. Einen Hardwarefehler kann man 
auch fast ausschließen, da ein solcher Fehler (evtl. falsche Pull Ups) 
keine so zufälligen aber doch gleichmäßigen Symptome erzeugen würde.
Liegt es evtl. an der Sender Software, sprich der Hi-tech c lib für i2c?

von Peter (Gast)


Lesenswert?

>Es ist wieder ein Auszug aus dem Serial Monitor angehängt. Hier kann man
>schön sehen, dass wieder nach genau dem gleichen Schema Bytes verloren
>gehen.
Irgendwann spielt Dein Slave nicht mit, warum kann man nur sagen, wenn 
man die Pegel sieht.
Probier mal etwas langsamer zu senden - mach Warteschleifen dazwischen. 
Was passiert dann?

von Andreas H. (ahz)


Lesenswert?

Huhu
Sry, bin schon im Urlaub, darum etwas lazy.

Da ich den Fehler grade beim Basteln hatte: Deine Watchdogs hast Du 
disabled ?

Grüße
Andreas

von Deniz (Gast)


Lesenswert?

Hallo,

Andreas H. schrieb:
> Da ich den Fehler grade beim Basteln hatte: Deine Watchdogs hast Du
> disabled ?

Ja habe ich, weiß noch nicht genau was ich damit anfangen soll. Soweit 
ich weiß sind die zur Laufzeitüberwachung? Inwiefern können die Fehler 
verursachen?

Peter schrieb:
> Probier mal etwas langsamer zu senden - mach Warteschleifen dazwischen.
> Was passiert dann?

Danke, werd ich gleich mal versuchen.

LG,

Deniz

von Peter D. (peda)


Lesenswert?

Deniz schrieb:
> Hab ich mittlerweile umgesetzt, leider ohne erfolg:for(unsigned char i=0; i < 
PinCount; i++) {
> i2c_WriteTo(Address);
> while (-1 == i2c_PutByte(data[2*i])){};
> while (-1 == i2c_PutByte(data[2*i+1])){};
> //i2c_PutByte(SeparatorNumber);
> i2c_Stop();
> }

Das ist kein Test auf ACK, sonst hängst Du ja in der While-Schleife 
fest. Also wird -1 nie auftreten.

NACK heißt, niemand ist auf dem Bus. Nach dem NACK muß daher ein STOP 
gesendet werden und dann wieder START, Adresse usw.
Das ACK muß auch nach der Adresse geprüft werden.
Lies Dir mal die Beschreibung des I2C in Ruhe durch.


Peter

von Andreas H. (ahz)


Lesenswert?

Deniz schrieb:
> Ja habe ich, weiß noch nicht genau was ich damit anfangen soll. Soweit
>
> ich weiß sind die zur Laufzeitüberwachung? Inwiefern können die Fehler
>
> verursachen?

Huhu

Watchdogs sind Counter, die von einem Resetwert bis 0 runterzählen. Wenn 
sie bei 0 ankommen dann lösen sie einen Reset des Prozessors aus. Darum 
muss man sie im Programm immer mal wieder auf ihren Resetwert 
zurücksetzen.
Die Prozessoren haben dafür meist einen eigenen Befehl (wdt).

Sinn der Sache ist sicherzustellen, dass das Programm des Prozessors 
immer ausgeführt wird und sich nicht, z.B. aufgrund einer externen 
Störung, in einer Endlosschleife aufhängt (denn hier würde der WD ja 
nicht mehr reseted werden).

Nachteil: Wenn der WD nicht reseted wird, dann reseted er den Chip, der 
dann wieder das Programm NEU (!) ausführt. Das sieht man aber unter 
Umständen nicht sofort.
Wenn z.B. bei einer seriellen Übertragung im Empfänger ein WDT auftritt, 
dann geht der Receiver nach der Initialisierung innerhalb weniger us 
wieder in die Empfangsschleife und es fehlen nur ein/zwei Byte. Würde 
also evtl. zu Deinem Fehlerbild passen.


Evtl. kann man den WD auch nicht per Programm ausschalten, sondern nur 
über Fuses. Damit soll verhindert werden, dass ein Nut-laufendes Program 
den WD disabled, was ja die Idee ad absurdum führen würde.

Einfache Debugmaßnahme: Beim Programstart einen (freien) Outputpin 100us 
auf '1' ziehen und danach permanent auf '0' halten. Siehst Du auf dem 
Scope regelmäßig Pulse an diesem Pin, dann schlägt der WD zu.

Viele Grüße
Andreas

von Peter D. (peda)


Lesenswert?

Während der Entwicklung bleibt der Watchdog grundsätzlich aus!
Er würde sonst die Fehler verstecken bzw. die Fehlersuche erschweren.

Wenn, dann wird der Watchdog immer als letztes implementiert, nachdem 
alles einwandfrei funktioniert.


Peter

von Deniz (Gast)


Lesenswert?

Der Watchdog Timer ist per fuses ausgestellt.
Eine zeitliche Pause zwischen den Übertragungen auch leider auch keine 
Besserung gebracht!
Zurzeit beschäftige ich mich mit den ACKs, aber ich glaube das ganze ist 
schon in den i2c Methoden implementiert.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Genau da ist das Problem beim Failduino, man weis ned was die Libs 
machen ;)
Schreib die I2C Routinen doch mal selber, dann weiste zu 100% was da 
abgeht.
Im Datenblatt steht alles nötige dazu.

von Deniz (Gast)


Lesenswert?

Ich meinte eigentlich die i2c Methoden vom PIC C Compiler. Aber 
grundsätzlich bin ich deiner Meinung. Aber man kann nicht immer das Rad 
neu Erfinden, sonst kommt man nicht voran :)

Aber ich weiß mittlerweile woran es liegt:
Es liegt am Sender und zwar an der Übertragung der Adresse.
1
for(unsigned char i=0; i < PinCount; i++) {
2
         if (i2c_WriteTo(Address)) {
3
            i2c_WriteTo(Address);
4
            i2c_PutByte(SeparatorLine);
5
            i2c_PutByte(SeparatorLine);
6
            i2c_Stop();}
7
        i2c_PutByte(data[2*i]);
8
        i2c_PutByte(data[2*i+1]);
9
        //i2c_PutByte(SeparatorNumber);
10
        i2c_Stop();
11
  }
i2c_writeto liefert TRUE zurück wenn kein Ack zurückkommt. Der 
Einfachheit halber lasse ich in diesem Falle zwei nullBytes schicken. 
Und siehe da, nun erscheint jedes mal ein Zeilenumbruch (Empfänger ist 
dementsprechend programmiert) wenn einer dieser Fehler auftritt. Ich 
werde der Sache mal genauer nachgehen.
Vielen Dank schonmal für Eure Hilfe!

von Andreas H. (ahz)


Lesenswert?

Peter Dannegger schrieb:
> Während der Entwicklung bleibt der Watchdog grundsätzlich aus!
>
> Er würde sonst die Fehler verstecken bzw. die Fehlersuche erschweren.
>
>
>
> Wenn, dann wird der Watchdog immer als letztes implementiert, nachdem
>
> alles einwandfrei funktioniert.

Nette Theorie.
Ich hatte schon ASICs bei denen wir den WD auf expliziten Wunsch des 
Kunden nicht abschaltbar implementiert haben.

Am Ende des Tages ist es auch egal. Die CFA musst Du sowieso immer 
machen.

Grüße
Andreas

von Peter D. (peda)


Lesenswert?

Andreas H. schrieb:
> Ich hatte schon ASICs bei denen wir den WD auf expliziten Wunsch des
> Kunden nicht abschaltbar implementiert haben.

Das schließt aber doch nicht aus, daß man es eben als letzten 
Entwicklungsschritt macht.


Peter

von Andreas H. (ahz)


Lesenswert?

Peter Dannegger schrieb:
> Das schließt aber doch nicht aus, daß man es eben als letzten
> Entwicklungsschritt macht.

Bei einem ASIC ? Wie soll das den gehen ?

Grüße
Andreas

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.