Forum: Mikrocontroller und Digitale Elektronik Fehlersuche in Datenübertragung


von P. F. (pfuhsy)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

ich habe eine selbstentwickelte Alarmanlage die ich über meine 
Hausautomatisierung per USB alle paar Sekunden abfrage. Der Status wird 
mir mit einer 16stelligen Hex beantwortet z.B. "0000000000050207". 
Ändert sich ein Reed-Kontakt bekomme ich z.B. "0100800000050286".

Zum Verständniss es sind immer 2er Pakete, siehe haa_bus.h ab Zeile 23:
Erstes Paar 01 = Bit 1 von Byte 1 ist auf true
Zweites Paar 00 = alle Bits von Byte 2 sind auf false
drittes Paar 80 = letztes Bit von Byte 3 ist auf true
usw.

In den Fall sehe ich über die 01, dass es einen "Kontaktalarm" gibt und 
über die 80, welcher Kontakt es ist. Hier INP7, ist der 7te 
Reed-Kontakt. Jetzt ist es so, dass die Anlage ohne erkennbaren Grund 
zwischendruch den Datensatz "0000800000050287" übermittelt, obwohl sich 
in der Hardware nichts geändert hat. Ich bin mir sehr sicher, dass es an 
der Übermittlung des Datensatzes liegt und nicht an der Hardware, das 
begründe ich auch.

1. Das Erste HEX-Paar bleibt unverändert. Wenn ich die 7ten Reed-Kontakt 
per Hand ändere (also öffne) übermittelt mir die Anlage 
"0100800000050286", also mit 01 als erstes HEX-Paar, in den Fall aber 
nicht.
2. Wenn die Anlage scharf ist und dieser Kontakt wird geöffnet, geht der 
Alarm los. In den Fall passiert aber nichts weiter.
3. Im Code habe ich eine einzige Stelle an den ich den Bit manipuliere, 
siehe main.c Zeile 283
1
  _inputs[7] = In_7_offen() ? true : false;  //Auswertung, If-Kurzschreibweise

Wenn ich die Zeile immer mit Null beschreibe, taucht dieser Fehler 
trotzdem auf
1
  _inputs[7] = 0;

Ich hab verschiedene Baudraten versucht, 9600, 4800,19200, um zu sehen 
ob es ein Übertragungsfehler sein könnte. Selbes Ergebniss, selber 
fehlerhafte Datensatz.

In der Funktion
1
void IN0_byte_schreiben (bool *input)          //Zustände der Kontakte
2
{
3
/*  int wert = 0;
4
  
5
  for (int i=1; i <=128; i *= 2)      
6
  {
7
  wert += *input *i;        //Wertigkeit berechnen und dazu addieren
8
  *input++;      
9
  }  
10
    
11
  IN0 = wert;            //Wert übertragen
12
*/
13
  input[7] = 0 ;
14
  IN0 = (input[7] << 7) | (input[6] << 6) | (input[5]<< 5) | (input[4] << 4) | (input[3] << 3) | (input[2] << 2) | (input[1] << 1) | (input[0] << 0);
15
16
}
wo die Bits an die "richtig" Positionen geschoben werden, siehe 
haa_bus.c, Zeile 42, habe ich die Zeile
1
  input[7] = 0 ;
damit taucht der Fehler nicht mehr auf. Also muss er in der 
Verarbeitungsreihenfolge zwischen
1
  _inputs[7] = In_7_offen() ? true : false;  //Auswertung, If-Kurzschreibweise

und
1
  IN0 = (input[7] << 7) | (input[6] << 6) | (input[5]<< 5) | (input[4] << 4) | (input[3] << 3) | (input[2] << 2) | (input[1] << 1) | (input[0] << 0);
liegen. Ich finde ihn aber nicht.

Ihr seit doch ziemlich gut in versteckte Fehler finden, könnt Ihr da 
weiterhelfen ?

Gruss

von Stefan F. (Gast)


Lesenswert?

Du hast sehr wahrscheinlich einen Fehler in deinem Programm. Die USB 
Schnittstelle überträgt nicht einfach unaufgefordert irgendwelche Daten 
- schon gar keine falschen Daten.

Also würde ich Dir empfehlen, alles Ausgaben mit Begründung zusätzlich 
auf einem seriellen Port auszugeben, den du aufzeichnest. Und dann mal 
schauen, welche Begründung da dann steht.

von Stefan F. (Gast)


Lesenswert?

Ich finde seltsam, dass du einen "bool* input" hast und dort dann aber 
Bits hin und her schiebst. Ich dachte da seien nur die Werte 0x00 und 
0x01 definiert. Alles andere ist mehr oder weniger nicht vorgesehen. 
Aber vielleicht irre ich mich da.

von P. F. (pfuhsy)


Lesenswert?

Stefan ⛄ F. schrieb:
> Du hast sehr wahrscheinlich einen Fehler in deinem Programm. Die USB
> Schnittstelle überträgt nicht einfach unaufgefordert irgendwelche Daten
> - schon gar keine falschen Daten.

Hi Stefan, danke das du dir das anguckst. Ehrlich gesagt hilft mir die 
Aussage kein Stück weiter. Natürlich sendet die Schnittstelle nicht 
einfach so, ich frage den Datensatz an, s.o. Der Fehler liegt im Code, 
hast du selber geschrieben und da habe ich ja schon eingegrenzt. Ich 
denke nicht dass das vollkommen falsch war. Ich komme aber eben nicht 
weiter.

Stefan ⛄ F. schrieb:
> Also würde ich Dir empfehlen, alles Ausgaben mit Begründung zusätzlich
> auf einem seriellen Port auszugeben, den du aufzeichnest. Und dann mal
> schauen, welche Begründung da dann steht.

Habe ich das nicht getan, mit den Datensatz? Jeder Zustand wird exakt 
damit wieder gespiegelt. Ich hab die Anlage auch über Hyperterminal 
ausgewertet, mit dem selben Ergebnis. Also wieder ein Hinweis darauf, 
das es nicht an der Auswertung in der Automatisierung liegen kann.

von Frank (Gast)


Lesenswert?

Hi,
ohne jetzt groß in deinen Code eingestiegen zu sein:
Du definierst dein Array als:
bool _inputs[7]
D.h. es hat Platz für 7 Elemente (0..6).

Unten greifst du auf das 8. Element zu
_inputs[7] = In_7_offen() ? true : false;

Das kann nicht gehen.

Gruß

von Pandur S. (jetztnicht)


Lesenswert?

Und das Protokoll ?

Speziell bei USB werden keine Pakete generiert. Bei einer seriellen 
Schnittstelle kann vielleicht ein Byte oder so verloren gehen, bei USB , 
sollte das abgesichert sein.

Ich wuerde, da die Verbindung wahrscheinlich eh Serial ueber USB ist, 
auch ein Protokoll auf Serial auslegen.
Also [Header][Command][Daten][CRC]

Und dann auf dem System einen Tracebuffer halten, dass man zB Daten 
nochmals abrufen kann.

von Stefan F. (Gast)


Lesenswert?

Wenn dein Programm der Meinung ist, etwas senden zu müssen, dann eil 
eine bestimmte Bedingung zutrifft.

Alle Daten die zu einer solchen Bedingung führen solltest du 
protokollieren. Dann siehst du ob die Daten oder die Formulierung der 
Bedingung fehlerhaft ist.

Das mit dem Array-Index musst du natürlich auch korriegieren. Und ich 
würde auch davon absehen, einen Zeiger auf ein Boolean tatsächlich auf 
ein Array von Bytes zeigen zu lassen. Vielleicht verwirrt das nicht nur 
dich sondern auch den Compiler so sehr, dass er daraus fehlerhaften 
Maschinencode generiert.

von Frank (Gast)


Lesenswert?

In deiner ISR geht es weiter mit den Buffer overflows:
1
ISR (USART_RX_vect)    //Interrupt wird ausgelöst sobald neue Daten im USART-Empfangspuffer liegen
2
{
3
  _rxd_string[_rxd_count] = UDR0;        //in Array schreiben
4
  
5
  //Gesamtlänge wurde empfangen, gleichzeitig kann die max. Arraylänge nicht überschritten werden
6
  //UDR kann nur einmal gelesen werden, deshalb muss hier aus dem Array geprüft werden
7
  if(_rxd_count == _DATA_LEN -1)  //Wird einer gewissen Zeit automatisch zurückgesetzt, siehe RxD_Auswerten()
8
  {
9
    _rxd_string[_rxd_count + 1] = '\0';  //Abschlusszeichen anhängen falls nicht empfangen
10
    _rxd_count = 0;            //Zähler für das nächste Mal zurücksetzten
11
    _rxd_komplett = true;        //Bit setzten damit die Daten ausgewertet werden
12
  }
13
  
14
  else
15
  {
16
    _rxd_count++;            //Zähler für das nächste Mal hochzählen
17
  }
18
}

Was ist eigentlich, wenn dein UART keine Zahl oder A-F empfängt?
So wie ich das sehe, wird das ohne Prüfung in Char_in_int(..) 
verarbeitet.
Wenn dann ein falsches Zeichen in zeichen_nb (kein ASCII und bit 3 
gesetzt) vorliegt,
ist in der Funktion RxD_Auswerten _baskom != 0. Und das ist doch die 
Bedingung etwas über den UART zu schicken.

von P. F. (pfuhsy)


Lesenswert?

Frank schrieb:
> Hi,
> ohne jetzt groß in deinen Code eingestiegen zu sein:
> Du definierst dein Array als:
> bool _inputs[7]
> D.h. es hat Platz für 7 Elemente (0..6).
>
> Unten greifst du auf das 8. Element zu
> _inputs[7] = In_7_offen() ? true : false;
>
> Das kann nicht gehen.
>
> Gruß

Den hab ich übersehen. Jetzt klappt es. Danke!
Oh man manchmal sind es die kleinen Dinge die das Ganze zum wackeln 
bringen.

von P. F. (pfuhsy)


Lesenswert?

Frank schrieb:
> Was ist eigentlich, wenn dein UART keine Zahl oder A-F empfängt?
> So wie ich das sehe, wird das ohne Prüfung in Char_in_int(..)
> verarbeitet.
> Wenn dann ein falsches Zeichen in zeichen_nb (kein ASCII und bit 3
> gesetzt) vorliegt,
> ist in der Funktion RxD_Auswerten _baskom != 0. Und das ist doch die
> Bedingung etwas über den UART zu schicken.

Guter Einwand. Ich hab das mal geprüft. Er schickt dann einfach den 
Status raus als hätte ich ihn angefragt.

von Heinz (Gast)


Lesenswert?

Hallo,

deine USART-Empfangs- und Verarbeitungsroutine ist aus meiner
Sicht nicht zuverlässig:

In ISR (USART_RX_vect) liest du eine gewisse Menge Bytes in das Array 
_rxd_string und setzt _rxd_komplett = true wenn du die erwartete Anzahl 
von Bytes empfangen hast. In der Funktion RxD_Auswerten() greifst du 
dann entsprechend auf _rxd_string zu und wertest die Daten aus.

Problem: Deine Auswerte-Routine und die Interrupt Service Routine 
greifen parallel auf _rxd_string zu. Die Konsistenz deiner Kommandos ist 
nicht sichergestellt. Möglicherweise funktioniert es nur, weil gewisse 
Timings gerade "günstig" sind. Das könnte sich bei zukünftig ändern?

Im Normalfall würde man in etwa wie folgt vorgehen, um eine robuste 
Kommunikation zu bauen:
Die ISR liest solange Bytes, bis ein Kommando komplett empfangen wurde 
und stellt es dann in einen Ringbuffer. Die Funktion RxD_Auswerten prüft 
lediglich, ob ein Kommando im Ringbuffer steht und bearbeitet es.

von P. F. (pfuhsy)


Lesenswert?

Heinz schrieb:
> In ISR (USART_RX_vect) liest du eine gewisse Menge Bytes in das Array
> _rxd_string und setzt _rxd_komplett = true wenn du die erwartete Anzahl
> von Bytes empfangen hast. In der Funktion RxD_Auswerten() greifst du
> dann entsprechend auf _rxd_string zu und wertest die Daten aus.

Hab ich jetzt ich geändert.
1
  _rxd_string[_rxd_count] = UDR0;        //in Array schreiben
2
  
3
  //Gesamtlänge wurde empfangen, gleichzeitig kann die max. Arraylänge nicht überschritten werden
4
  //UDR kann nur einmal gelesen werden, deshalb muss hier aus dem Array geprüft werden
5
  
6
  if(_rxd_string[_rxd_count] == '\0')  //_rxd_count wird einer gewissen Zeit automatisch zurückgesetzt, siehe RxD_Auswerten()
7
  {
8
    _rxd_count = 0;            //Zähler für das nächste Mal zurücksetzten
9
    _rxd_komplett = true;        //Bit setzten damit die Daten ausgewertet werden
10
  }
11
  
12
  else
13
  {
14
    _rxd_count++;            //Zähler für das nächste Mal hochzählen
15
  }

Heinz schrieb:
> Problem: Deine Auswerte-Routine und die Interrupt Service Routine
> greifen parallel auf _rxd_string zu. Die Konsistenz deiner Kommandos ist
> nicht sichergestellt. Möglicherweise funktioniert es nur, weil gewisse
> Timings gerade "günstig" sind. Das könnte sich bei zukünftig ändern?

Bis jetzt nicht aufgefallen, könnte aber passieren. Ok, muss ich 
anpassen, Danke.

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.