Guten Tag zusammen,
ich bin derzeit damit beschäftigt Datenpakete eines externen Geräts an
meinem ATmega88 auszulesen.
Diese Datenpakete haben keine einheitliche Länge, Da die Daten
unterschiedlich lang sind. Daher befinden sich in diesem Datenpaket zwei
Bytes die die Länge der Daten angeben.
Nun wollte ich fragen ob es irgendwelche Tricks oder so gibt um die
Pakete möglichst einfach zu bearbeiten und temporär in Variablen zu
speichern. Eine Möglichkeit wäre nun bis zu den beiden Bytes in ein
neues Array auszulesen und abzuspeichern. Nach empfangen dieser beiden
Byte die Länge des Paketes bestimmen und einen Endwert festzulegen. Bei
erreichen des Endwertes dann in das Nächste Array (beispielsweise eines
Mehrdimensionalen Arrays wechseln) und in diesem wieder von vorne zu
beginnen.
Die Datenpakete werden am USART empfangen.
Aufbau der Datenpakete:
Start Delimiter 1 Byte | Packet Type identification 1 Byte | Op code 1
Byte | Data length 2 Byte | Checksum 1 Byte | Packet Data X Byte | End
delimiter 1Byte
Beispiel:
Array[0][0] Zeichen vom USART einlesen und speichern
Array[0][1] Zeichen vom USART einlesen und speichern
...
Array[0][5] Zeichen vom USART einlesen und speichern
Endwert festlegen aus Byte 4 und 5
...
Array[0][Endwert] Zeichen vom USART einlesen und speichern
zum nächsten Array wechseln
Array[1][0] Zeichen vom USART einlesen und speichern
Array[1][1] Zeichen vom USART einlesen und speichern
...
Array[1][5] Zeichen vom USART einlesen und speichern
Endwert festlegen aus Byte 4 und 5
...
Array[1][Endwert] Zeichen vom USART einlesen und speichern
zum nächsten Array wechseln
...
MfG Pat711
Am einfachsten wird es sein die Daten beim Empfang in einen Ringpuffer
zu speichern. Dabei kümmerst du dich nicht darum was du da eigentlich
empfangen hast, sondern schreibst es stumpf in den Ringpuffer rein.
Das schreiben in den Ringpuffer läuft im Empfangsinterrupt der UART
Schnittstele (Interrupt wenn ein Byte empfangen wurde).
Den Ringbuffer machst du dann bsw. 256Byte lang (oder eben so lang das
du zumindest mal 4-5 deiner Packete abspeichern kanst). So brauchst du
den Bytepointer einfach nur immer zu inkrementieren und fängst durch den
überlauf wieder bei 0 an.
Nun hast du das Datenpacket im Ringpuffer stehen (oder auch schon
mehrere) und über eine extra Funktion suchst du nun von deiner letzten
Lese-position (zu Beginn wird das 0 sein) den ersten auftauchenden
"Start Delimiter". Nun schreibst du jedes fogende Byte in einen
zwischenpuffer bis du zum "End delimiter" gelangst (dabei kontinuierlich
prüfen ob der Lese pointer nicht über den Schreibe-pointer hinausläuft).
Nun hast du in deinem Zwischenpuffer das Packet ohne Start/End delimiter
stehen. Du kannst nun je nach Packet identification und Op-code ein
struct erzeugen in dem du dann die Daten aus dem Zwischenpuffer
abspeicherst. (neu erzeugen der Checksum und vergleich mit der alten
natürlich nicht vergessen).
Um das ganze etwas leichter erweiterbar zu machen arbeite ich dabei
gerne mit Unions. Dazu definiere ich mir für jedes mögliche ankommende
datenpacket ein eigenes struct. All diese Structs fasse ich in einem
Union zusammen (bei definition der structs muss ggf. #pragma pack(1)
verwendet werden). Das erste byte in jedem struct enthält dabei einen
identificationscode um festzustellen was da eigentlich drinnen steht.
Bei dir wäre das dann wohl "Packet type identification" oder "Op Code".
Nun übergibst du an jede Funktion nur noch Pointer auf die Unions, liest
das erste Byte zur Identifikation aus und kannst anhand dessen
feststellen was zu amchen ist. So lässt sich der Code sehr leicht für
weitere mögliche Datenpackete erweitern.
Danke, deine Antwort ist schonmal sehr hilfreich ich werde mir nun mal
was du mit den union's und struct's meinst, welche mir unbekannt sind
;-). (wohl ne Art Maske, schätz ich mal)
Aber das mit dem Ringpuffer ist eine tolle Idee ^^ allerdings könnte ich
dann Probleme mit meinem Speicher bekommen. Vielleicht muss ich dann den
EEPROM zu hilfe nehmen.
MfG Pat711
Patrick R. schrieb:> Aber das mit dem Ringpuffer ist eine tolle Idee ^^ allerdings könnte ich> dann Probleme mit meinem Speicher bekommen.
weil? nicht genug Platz?
> Vielleicht muss ich dann den> EEPROM zu hilfe nehmen.
keine gute Idee, der lässt sich für den Anwendungsfall nicht oft genug
überschreiben
mal noch was zur Kärung:
* wie groß können die Packete maximal sein
* wie groß ist die minnimale Pause zwischen zwei Packeten
* ist der 'End delimiter' einmalig, oder kann der Wert dieses Bytes auch
im Rest des Packets enthalten sein
Sascha
//Auf ARM 7 macht er aus uint8_t sonst uint16_t weil er da besser/schneller zugreifen kann. Kostet aber mehr Platz. Daher das pragma welches dies verhindert
6
7
#pragma pack(1)
8
typedefstruct{
9
uint8_tu8OpCode;//enthält Zahl zur Identifikation des structs
10
uint8_tu8MyData1;
11
uint8_tu8MyData2;
12
}tdsMY_DATA_1;
13
14
#pragma pack(1)
15
typedefstruct{
16
uint8_tu8OpCode;//enthält Zahl zur Identifikation des structs
17
uint16_tu16MyData1;
18
char[5]cMyWord;
19
}tdsMY_DATA_2
20
21
typedefunion{
22
tdsMY_DATA_1tdsMyData1;
23
tdsMY_DATA_2tdsMyData2;
24
}tduMY_RECEIVE_DATA;
Nach füllen des Zwischenspeichers könnte man nun wie folgt vorgehen:
1
tduMY_RECEIVE_DATAgtduGetReceiveData(){
2
uint8_tu8Buffer[20];
3
tduMY_RECEIVE_DATAtduReceivedData;
4
5
...//erstmal zwischenspeicher füllen. Alles ausser delimiter enthalten
@Sascha:
Das mit dem Platz sollte schon irgendwie zu machen sein der ATmega88 hat
ja immerhin 1kByte Speicher.
Und noch zu deinen Fragen:
* Die Größe der Pakete ist im Datenblatt mit einem Maximum der
Datenlänge von 333 Byte also 7Byte + 333Datenbyte.
Dies ist allerdings nur voll ausgenutzt wenn der Controller Daten an den
Chip sendet. Im umgekehrten Fall habe ich bisher Pakete mit einer
maximalen Länge von 16 Byte empfangen. Viel längere werden nicht
gesendet werden.
* Diese Pause beträgt laut Logicanalyser 0,1sec also 100ms da der
Datenfluss sich aber mit RTS und CTS anhalten lassen sollte, sollte dies
kein Problem darstellen.
* Im Datenteil des Paketes kann der End delimiter auch vorkommen
MfG Pat711
Patrick R. schrieb:> @Sascha:> Das mit dem Platz sollte schon irgendwie zu machen sein der ATmega88 hat> ja immerhin 1kByte Speicher.
soviel ist das nicht
> Und noch zu deinen Fragen:> * Die Größe der Pakete ist im Datenblatt mit einem Maximum der> Datenlänge von 333 Byte also 7Byte + 333Datenbyte.
dann könnte man mit der Variante von 'Antwort' höchstens 1 Paket puffern
> Dies ist allerdings nur voll ausgenutzt wenn der Controller Daten an den> Chip sendet. Im umgekehrten Fall habe ich bisher Pakete mit einer> maximalen Länge von 16 Byte empfangen. Viel längere werden nicht> gesendet werden.
du empfängst also blos?
>> * Diese Pause beträgt laut Logicanalyser 0,1sec also 100ms da der> Datenfluss sich aber mit RTS und CTS anhalten lassen sollte, sollte dies> kein Problem darstellen.
liest du nur mit?
die 100ms sollten locker ausreichen um das empfange Paket noch im
Empfangspuffer zu verarbeiten.
mein Vorschlag:
*eingehende Daten Byteweise in einen Puffer ablegen (Array), dabei
Anzahl der Bytes mitzählen und Obergrenze überwachen (Obergrenze wird
erst mal auf die Größe des Arrays festgelegt)
*nach Empfang von Byte 5 (2.Byte der Länge) Paketlänge berechnen und
Obergrenze neu festlegen
*bei Erreichen von Obergrenze Flag setzten, das von Main aus das Paket
verarbeitet werden kann, wenn dabei Obergrenze=Arraygröße dann ist ein
Fehler aufgetreten (Daten haben nicht in das Array gepasst)
Sascha
Tag zusammen,
klar so viel ist 1kB nun auch wieder nicht aber es sollte reichen.
Und es wird außerdem auch gesendet, aber das ist ja wieder ein anderer
Part.
Und das mit den 100ms ist eben nicht so sicher, da der Controller
nebenher auch noch messwerte aufnehmen muss.
Die Vorgehensweise die du mir da beschreibst ist ja praktisch die aus
meinem ersten Post ein wenig erweitert, oder?
`
MfG Pat711
Patrick R. schrieb:> Die Vorgehensweise die du mir da beschreibst ist ja praktisch die aus> meinem ersten Post ein wenig erweitert, oder?
nicht ganz, in deiner Variante sieht es je eher so aus als ob du in dem
mehrdimensionalen Array alle Datenpakete speichern willst.
Nach meinem Vorschlag wird im Array immer nur ein Paket gespeichert,
und nach dessen vollständigem Empfang werden nur die Werte die du
brauchst in separaten Variablen abgelegt.
Sascha
while(PIND&(1<<RTS))// Warten, bis BT - Modul bereit zum Empfangen ist
66
{}
67
PORTD|=(1<<CTS);// Nicht empfangsbereit
68
BT_environment_inquiry();// Umgebung nach BT - Geräten absuchen
69
PORTD&=~(1<<CTS);// Empfangsbereit
70
sei();
71
lcd_clear();
72
while(1)
73
{
74
if(paket_ende==1)
75
{
76
count1=1;
77
while(count1>(lcd_zeichen-1))
78
{
79
lcd_clear();
80
charPuffer[20];// in diesem {} lokal
81
itoa(daten[count1],Puffer,10);
82
set_cursor(0,zeile);
83
lcd_string(Puffer);
84
count1++;
85
if(count1==15)
86
{
87
zeile=2;
88
}
89
}
90
}
91
if(paket_laenge>PAKET)// Falls Paketgröße die größe der Paketvariable übersteigt
92
{
93
set_cursor(0,2);
94
lcd_string("Err0:Paketlaenge");
95
while(1);
96
}
97
elseif(paket_laenge<7)// Fehler beim berechnen der Paketgröße
98
{
99
set_cursor(0,2);
100
lcd_string("Err1:Paketlaenge");
101
while(1);
102
}
103
if((daten[0]!=0x02)&&(count0>1))// Paketanfang ist nicht Start Delimiter!
104
{
105
lcd_clear();
106
set_cursor(0,2);
107
lcd_string("Err2:Paketanfang");
108
109
charPuffer[20];// in diesem {} lokal
110
itoa(daten[0],Puffer,10);
111
set_cursor(14,1);
112
lcd_string(Puffer);
113
114
while(1);
115
}
116
}
117
}
Ich habe nun mal versucht das ganze umzusetzen allerdings stimmt
irgendwas bei der Reihenfolge wie die Byte gespeichert werden nicht. Der
Start Delimiter fehlt des öfteren ( = 0).
Vielleicht sollte ich es doch mal mit dem Rinpuffer versuchen.
Die ANfrage an das Modul (BT_environmen_inqury) wird korrekt gesendet.
Die Antworten kommen aus am UART -Eingang an. (Logic Analyser)
MfG Pat711
schmeiß mal die ganzen LCD Ausgaben aus der ISR raus, das dauert dort
viel zu lange! Wenn du die Daten unbedingt ausgeben willst, dann setze
noch ein FLAG und mach die Ausgabe in Main.
UND definiere alle Variablen die in Main und der ISR verändert werden
als volatile!
Sascha
Guten Tag zusammen,
die LCD ausgaben habe ich erst nachträgich eingefügt, um den Fehler zu
finden. Nun habe ich sie wieder herausgenommen, aber es funktioniert
auch so nicht.
Es wird auf dem LCD immer Err2:Paketanfang angezeigt, was bedeutet, dass
mein Startdelimiter fehlerhaft ist. Die Variable data[0], welche neben
dem Fehler angezeigt wird hat immer einen der folgenden Werte: 14, 23,
105 oder 114.
Diese Werte kommen in der Antwort des Moduls gar nicht vor. Diese
Antwrot sieht folgendermasen aus:
0x02 Start Delimiter
0x69 Packet Type identification
0x01 Op Code
0x09 Data length
0x00 Data length
0x73 Checksum
0x72 Packet Data
0x15 Packet Data
0x07 Packet Data
0x0E Packet Data
0x19 Packet Data
0x00 Packet Data
0x04 Packet Data
0x01 Packet Data
0x3E Packet Data
0x03 End Delimiter
Im letzten Thread hatte ich einen kleinen Fehler, und zwar kommen die
Werte doch auch alle bis auf einen in der Antwort vor.
114 -> 0x72 Packet Data 1.Stelle
105 -> 0x69 Packet Type identification
23 -> 0x17 kommt nicht vor ???
14 -> 0x0E Packet Data 4.Stelle
heißt wohl dass mein Programm zu lansam arbeitet, oder?
und das mit den volatile habe ich auch noch vergessen die werde ich nun
verändern.
Edit: Macht keinen Unterschied
MfG Pat711
Patrick R. schrieb:> heißt wohl dass mein Programm zu lansam arbeitet, oder?
das würde erst mal heißen das das 1.Byte verschluckt wird, was schon
dadurch passieren kann wenn die Schnittstelle erst nach dem Einschalten
verbunden wird und dadurch eine Flanke am Eingang auftritt.
Das Problem was ich momentan noch sehe ist die fehlende Syncronisation
am Anfang. Wenn irgendwas aus dem Tritt kommt wird count0 ja erst wieder
auf null gesetzt wenn der Puffer voll ist, und auch dann werden
anschließend die Daten wieder falsch eingelesen. Wenn das Startbyte auch
innerhalb der Daten vorkommt bleibt zur Syncronisation nur ein Timeout,
also ein Reset des count0 wenn für z.B. 100ms keine Daten kommen, oder
wenn die Daten nur auf Anforderung gesendet werden dann am besten vor
dem senden den count0=0 setzen.
Sascha
Das mit dem Timer ist eine gute Idee. Aber ich vermute, dass ich mit
einem Ringpuffer besser arbeiten kann. Ich werde dies jetzt einmal
ausprobieren da ich vermite, dabei mehr chancen zu haben keine Bytes zu
verlieren.
Der Ringpuffer muss ja auch nicht 256 Byte lang sein.
MfG Pat711