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
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
voidIN0_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
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.
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.
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.
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ß
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.
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.
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.
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.
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.
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.
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.