Letztes Update: 08.12.2002
| Repeat Wenn interner Messzyklus erreicht Dann Scanne alle Sensoren Wenn Hauptmesszyklus erreicht Dann Speicher alle Datensätze Wenn Befehl vom PC vorhanden Dann Bearbeite Befehl Wenn Aktiver Sensor sich meldet Dann Speicher Datensatz Wenn Datenübertragung aktiv Dann Sende Datensatz Until forever |
Jeder Kanal wird in einem definierten Zeitinterval gemessen = SubTime
Jeder Kanal wird in einem definierten Zeitinterval aufgezeichnet = MeasTime
Es gilt: MeasTime >= SubTime, wobei die MeasTime immer ein ganzes vielfaches der SubTime ist.
Die SubTime kann z.B. bei 10 Sekunden und die MeasTime bei 5 Minuten liegen, d.h. ein Meßwert besteht dann aus einem Mittelwert von 30 Messungen. Über das Meßinterval wird der größte und der kleinste Wert festgehalten. Es sollte immer bedacht werden, das sich die meisten Wettergrößen innerhalb kurzer Zeit nicht stark ändern. Die Änderungen erfolgen in der Regel in Stunden und nicht in Sekunden. Sollte also bei der Temperatur oder beim Luftdruck der Min- oder Maxwert stark vom Mittelwert abweichen, dann kann ein defekter Sensor oder eine defekte Elektronik vermutet werden. Durch die Mittelwertbildung mit der Min/Max Speicherung kann also eine geringe Aussage über die Qualität des Meßwertes gemacht werden kann.
Jeder Meßwert (im PC) besteht also aus vier Meßgrößen = DataSet
Für den Datenspeicher ist eine Kapazität von 16 MB in Form einer MultiMediaCard geplant. .
Geplant sind für die Wetterstation 64 Kanäle, die sich aus realen und berechneten Werten zusammensetzen.
Aufbau eines DataSets |
|||
| Byte-Pos | Bytes | Inhalt | Bemerkung |
| $0000..$0005 | 6 |
Zeit | MeasTime: Aktuelle Zeit im BCD Format (YYMMDDhhmmss) |
| $0006 | 1 |
Kanal | Kanalnummer |
| $0007 | 2 |
n | Anzahl der Messungen im Intervall MeasTime |
| $0009..$000B | 3 |
Integer | Summer aller Meßwerte im Intervall MeasTime |
| $000C..$000D | 2 |
Integer | Kleinster Wert im Intervall MeasTime |
| $000E..$000F | 2 |
Integer | Größter Wert im Intervall MeasTime |
Type TDataSet = Record
Time: Array [0..5] of Char;
Chn: Byte;
Cnt: Word;
RawVal: Integer;
MinVal: Word;
MaxVal: Word;
End;
Damit bei RawVal kein Überlauf auftritt, muß gelten: MeasTime / SubTime < 4096.
Der minimale Aufzeichnungszeitraum berechnet sich aus folgenden Angaben:
Wird alle 5 Minuten ein DataSet gespeichert kann ein Zeitraum von 16384 * 5 min = 81920 min aufgezeichnet werden:
Das sind 56 Tage 21 Stunden 20 Minuten
Wird alle 10 Minuten ein DataSet gespeichert kann ein Zeitraum von 16384 * 10 min = 163840 min aufgezeichnet werden:
Das sind ungefähr 113 Tage 18 Stunden 40 Minuten
Der Aufzeichnungszeitraum verkürzt sich, wenn Kanäle einen höheren Meßzyklus haben und er erhöht sich, wenn Kanäle einen kleineren Meßzyklus und/oder weniger Kanäle gemessen werden. Wird 1 Kanal alle 10 Minuten gemessen, kann also fast 20 Jahre aufgezeichnet werden.
Speicherorganisation
Der gesamte Speicher ist als Ringspeicher organisiert, d.h. der Speicher wird von Anfang bis Ende beschrieben. Wenn der Speicher voll ist, dann wird der DataSet, der zuerst gespeichert wurde durch den Letzten ersetzt. Verwaltet wird das ganze durch zwei Pointer:
In Bild 1 zeigt Grün den Startpointer und Rot den Endpointer aus Sicht des Programmes, welches Daten Lesen will. Wenn Start- und Endpointer identisch sind, ist der Ringspeicher Leer.
Wird ein DataSet ausgelesen, dann immer von der Position Startpointer. Danach wird der Pointer erhöht bis er mit dem Endpointer identisch ist.
Ein Datenset wird zuerst an der Position EndPointer+1 eingeschrieben, dann wird erst der Endpointer erhöht. Sollte der Endpointer jetzt identisch sein mit dem Startpointer, dann wird auch der Startpointer erhöht.
Wird der End-/Startpointer erhöht und es ist das Speicherende erreicht, dann wird der Pointer auf den Speicheranfang gesetzt (Ringspeicher).
Ein Datensatz ist dann vorhanden, wenn StartPointer und EndPointer verschieden sind. Sind sie identisch ist der Ringspeicher Leer. Der Ringspeicher ist voll, wenn der StartPointer um 1 größer ist als der EndPointer.
Bild 1
0. Leerer Ringspeicher
1. Fünf DataSets (1 bis 5) wurden in den Ringspeicher geschrieben.
2. DataSet 6 wurde eingeschrieben und 1 und 2 ausgelesen.
3. DataSet 7 und 8 wurden eingeschrieben. Da das Ende des Speicherbereiches erreicht wurde, ist DataSet 8 an den Anfang geschrieben worden.
4. DataSet 3 wurde gelesen und 9 und 10 eingeschrieben. Der Speicher ist jetzt voll.
5. DataSet 11 wurde eingeschrieben. Dazu wurden der Start- und Endpointer erhöht und der DataSet beim Endpointer eingeschrieben. Der letzte DataSet (Nummer 4), auf den der alte Startpointer zeigte geht verloren.
Folgendes ist zu beachten:
Wird ein DataSet gelesen, dann wird er in einen Zwischenspeicher kopiert und zum PC gesendet. Kommt vom PC eine positive Quittierung wird der nächste Datensatz in den Zwischenspeicher kopiert und übertragen. Kommt eine negative Quittierung wird noch einmal übertragen.
Wird ein DataSet eingeschrieben, dann wird erst der Endpointer erhöht und dann eingeschrieben. Wird der Endpointer erhöht, wird vor dem Einschreiben geprüft, ob er identisch mit dem Startpointer ist, wenn ja, dann wird auch der Startpointer erhöht. Der DataSet, auf den der Startpointer zeigte ist dann verloren.
Der Befehlssatz unterteilt sich in funf Gruppen:
T - Time Parameter
C - Channel Parameter
D - Datatransfer Parameter
F - Format Parameter
S - Sonstige Parameter
Hinweis: Am Ende einiger Kommandos oder Antworten steht ein cr. Dies ist die Abkürzung für ein Carriage Return Zeichen = 13 = $0D. Das Beispiel besteht immer aus den zwei Zeilen Eingabe und Ausgabe.
Alle Ein- und Ausgaben, bis auf die Zeit, erfolgen als Hexadezimale Zahl.
| Befehl | Datum und Uhrzeit setzen |
| Syntax | T1 x1 cr |
| Parameter | x1 = 12stellig im Format YYMMDDhhmmss |
| Antwort | cr |
| Beispiel | T1 021123123456cr cr |
| Befehl | Datum und Uhrzeit auslesen |
| Syntax | T2 cr |
| Parameter | - |
| Antwort | 12stellig im Format YYMMDDhhmmss |
| Beispiel | T2cr 021123123456cr |
| Befehl | SubTime in Sekunden für alle Kanäle setzen |
| Syntax | T3 x1 cr |
| Parameter | x1 = 4stellige Zeit in Sekunden |
| Antwort | cr |
| Beispiel | T3 000Acr cr |
| Befehl | SubTime in Sekunden für alle Kanäle auslesen |
| Syntax | T4cr |
| Parameter | - |
| Antwort | 4stellig |
| Beispiel | T4cr 000Acr |
| Befehl | Messwertinterrupt sperren oder freigeben |
| Syntax | T5 x1cr |
| Parameter | x1 = 0 = Messzyklus sperren x1 = 1 = Messzyklus freigeben |
| Antwort | cr |
| Beispiel | T5 0cr cr |
| Befehl | Bitmaske für einen/alle Kanäle setzen |
| Syntax | C1 x1 x2cr |
| Parameter | x1 = 2stellige Kanalnummer 01..40 = Kanalnummer 00 = alle Kanäle x2 = ASCII Zeichen mit 0 oder 1 0 = Kanal wird nicht gemessen 1 = Kanal wird gemessen |
| Antwort | cr |
| Beispiel | C1 05 1cr cr |
| Befehl | Bitmaske für einen/alle Kanäle auslesen |
| Syntax | C2 x1cr |
| Parameter | x1 = 2stellige Kanalnummer 01..40 = Kanalnummer 00 = alle Kanäle |
| Antwort | Wenn x1 = 00 dann String aus 64 Zeichen mit 0 oder 1 Wenn x1 = 01..40 dann 0 oder 1 |
| Beispiel | C2 05cr 1cr |
| Befehl | MeasTime in Sekunden für einen/alle Kanäle setzen |
| Syntax | C3 x1 x2cr |
| Parameter | x1 = 2stellige Kanalnummer 01..40 = Kanalnummer 00 = alle Kanäle x2 = 4stellige Zykluszeit in Sekunden |
| Antwort | cr |
| Beispiel | C3 05 0258cr (= 600 Sekunden) cr |
| Befehl | MeasTime in Sekunden für einen Kanal auslesen |
| Syntax | C4 x1cr |
| Parameter | x1 = 2stellige Kanalnummer 01..40 = Kanalnummer |
| Antwort | String aus 4 Zeichen |
| Beispiel | C4 05cr 0258cr |
| Befehl | Rohwert eines Kanals auslesen |
| Syntax | D1 x1cr |
| Parameter | x1 = 2stellige Kanalnummer 01..40 = Kanalnummer 00 = alle Kanäle |
| Antwort | Wenn x1 = 01..40 dann Kanalnummer und Meßwert aus 5 Zeichen Wenn x1 = 00 dann 64 Zeilen mit Kanalnummer und Meßwert |
| Beispiel | D1 05cr 05 03A5cr Der ADC arbeitet mit 12Bit Auflösung und einer Referenzspannung von 4,096Volt. Das ergibt eine Auflösung von 1mV/Bit. Die Ausgabe 03A5 (=933) entspricht also einer Spannung von 0,933Volt. |
| Befehl | Übertrage den Status des Datenspeichers |
| Syntax | D2cr |
| Parameter | - |
| Antwort | Drei 4stellige Hexadezimale Zahlen Anzahl der Datensätze Schreibpointer Lesepointer |
| Beispiel | D2cr 0091 012B 01BCcr Es sind 145 Datensätze vorhanden. Im Ringspeicher liegt der älteste Datensatz an Position 299 und der neueste Datensatz an Position 444. Es ist zu beachten, das der Schreibpointer immer auf die nächste freie Position zeigt. |
| Befehl | Übertrage den nächsten Datensatz aus dem Datenspeicher. Sollte kein Datensatz vorhanden sein, wird nur mit einem cr geantwortet. |
| Syntax | D3cr |
| Parameter | - |
| Antwort | Siehe Daten-Protokoll-Format |
| Beispiel | D3cr 021123123456 03 003C 014820 04D2 0654cr
1. Zahl: Datum = 23.11.02 12:34:56 |
| Befehl | Übertrage den gleichen Datensatz noch einmal. |
| Syntax | D4cr |
| Parameter | - |
| Antwort | Siehe Daten-Protokoll-Format |
| Beispiel | D4cr 021123123456 03 003C 014820 04D2 0654cr
1. Zahl: Datum = 23.11.02 12:34:56 |
| Befehl | Lösche den Datenspeicher |
| Syntax | D5cr |
| Parameter | - |
| Antwort | cr |
| Beispiel | D4cr cr |
| Befehl | Arbeitsmodus setzen |
| Syntax | F1 x1cr |
| Parameter | x1 = Übetragungsmodus 0 = Die Messwerte werden intern im Massenspeicher abgelegt, bis sie mit dem Befehl D3 abgeholt werden. 1 = Nach jedem Messzyklus werden die gemessenen Werte über die Schnittstelle gesendet. Im Format wie bei D3 2 = Nach jeder Messung werden die Werte über die Schnittstelle gesendet. Im Format wie bei D1. |
| Antwort | cr |
| Beispiel | F1 1cr cr |
| Befehl | Arbeitsmodus auslesen |
| Syntax | F2cr |
| Parameter | - |
| Antwort | 0 = Die Messwerte werden intern im Massenspeicher abgelegt. 1 = Nach jedem Messzyklus werden die gemessenen Werte über die Schnittstelle gesendet. 2 = Nach jeder Messung werden die Werte über die Schnittstelle gesendet. |
| Beispiel | F2cr 1cr |
| Befehl | Übertragungsformat für die Messwerte eines Datensatzes setzen | ||||||||||||||||||
| Syntax | F3 x1cr | ||||||||||||||||||
| Parameter | x1 = Folge von 8 Bits (immer alle 8 Bits angeben)
|
||||||||||||||||||
| Antwort | cr | ||||||||||||||||||
| Beispiel | F3 00000001cr cr |
| Befehl | Übertragungsformat für die Messwerte eines Datensatzes auslesen | ||||||||||||||||||
| Syntax | F4cr | ||||||||||||||||||
| Parameter | - | ||||||||||||||||||
| Antwort | Format = Folge von 8 Bits (immer alle 8 Bits angeben)
|
||||||||||||||||||
| Beispiel | F4cr 00000001cr |
| Befehl | Alle Variablen neu initialisieren |
| Syntax | S1cr |
| Parameter | - |
| Antwort | Versionsangaben cr |
| Beispiel | S1cr WUDU2 / V1.00 / 2002-11-24 22:00:00cr |
| Befehl | Software neu Starten durch Software Reset |
| Syntax | S2cr |
| Parameter | - |
| Antwort | Versionsangaben cr |
| Beispiel | S2cr WUDU2 / V1.00 / 2002-11-24 22:00:00cr |
| Befehl | Versionsinformation auslesen |
| Syntax | S3cr |
| Parameter | - |
| Antwort | Versionsangaben cr |
| Beispiel | S3cr WUDU2 / V1.00 / 2002-11-24 22:00:00cr |
| [STX=$02] | MeasTime | Daten | [ETX=$03] | CR=$0D | [LF=$0A] |
Wenn im ASCII Modus alle Kanäle auf einmal übertragen werden, kann es bei einigen PC's passieren, das der komplette String nicht in einem Rutsch an den Eingangsbuffer der seriellen Schnittstelle übergeben wird, d.h. der Eingangsbuffer muß nach dem Stringanfang und -ende gescannt werden. Das STX und das ETX Zeichen sollen diesen Vorgang vereinfachen. Sollte der PC aber immer schnell genug sein, ist diese Kennung nicht notwendig und kann über eines der DF Flags ausgeschaltet werden.
Die Uhrzeit im Standard Format: YYMMDDhhmmss
Mit einem Space als Separator.
Jede Zahl wird in seinem zugehörigen Format übertragen und durch ein Space getrennt.
1 Byte = 3stellig
2 Byte = 5stellig
3 Byte = 8stellig
Es werden nur die Kanäle übertragen, die auch gemessen werden, als DataSet oder nur der RawValue (abhängig von einem der F4 Flags).
Wenn Daten übertragen werden, kann es bei einigen PC's passieren, das der komplette String nicht in einem Rutsch an den Eingangsbuffer der seriellen Schnittstelle übergeben wird, d.h. der Eingangsbuffer muß nach dem Stringanfang und -ende gescannt werden. Das STX und das ETX Zeichen sollen diesen Vorgang vereinfachen. Sollte der PC aber immer schnell genug sein, ist diese Kennung nicht notwendig und kann über eines der F4 Flags ausgeschaltet werden.
Jede Zeile im ASCII Modus wird mit einem CR und LF abgeschlossen. Ist nur ein CR notwendig, kann das LF über eines der DF Flags ausgeschaltet werden.
Kanal 3 und 32 mit vollem DataSet und allen Optionen:
S = STX, E = ETX, C = CR, L = LF
S021123123456 03 003C 014820 04D2 0654ECL
S021123123456 04 003C 014820 04D2 0654ECL
Das sind 82 Bytes. Bei einer Übertragungsrate von 19200 Baud dauert das Senden 42,7ms.
Die eigentlichen Daten werden in einer HDLC ähnlichen Struktur verpackt:
| $7F | Daten | CRC-Checksumme | $7E |
Zeichen, die nicht auf der Schnittstelle auftauchen dürfen, werden durch ein Markierungsbyte gekennzeichnet und beim eigentlichen Zeichen wird das sechste Bit invertiert. Zu den Zeichen gehören:
| Zeichen | wird zu |
Bedeutung |
$11 |
$7D $31 | X-on, wird für das Software-Handshake auf der seriellen Schnittstelle gebraucht |
$13 |
$7D $33 | X-off, wird für das Software-Handshake auf der seriellen Schnittstelle gebraucht |
$7D |
$7D $5D | Das Markierungsbyte selbst muß natürlich auch gekennzeichnet werden |
$7E |
$7D $5E | Das Ende Zeichen darf auch nicht zu früh erkannt werden |
$7F |
$7D $5F | Das gleiche gilt für das Startzeichen |
Das Zeichen $7F kennzeichnet den Anfang des Datenprotokolls.
Es wird immer der komplette DataSet übertragen.
T1 K1 C1 F1 F2 F3 |
||
| Bytes | Bemerkung | |
| T1 | 1 |
Jahr = 0..99 |
1 |
Monat = 1..12 | |
1 |
Tag = 1..31 | |
1 |
Stunde = 0..23 | |
1 |
Minute = 0..59 | |
1 |
Sekunde = 0..59 | |
K1 |
1 |
Kanalnummer |
| C1 | 2 |
Anzahl der Messung im Mittelungsintervall |
F1 |
3 |
Summe der Messwerte im Hexadezimal Format |
F2 |
2 |
Minimumwert im Hexadezimal Format |
F3 |
2 |
Maximumwert im Hexadezimal Format |
Zur Generierung des 16 Bit CRC wird das Polynom nach CCITT Empfehlung verwendet:
Hier die Pascal Kodierung:
Const Crc16Tab : Array[0..255] of Word = (
$0000, $1021, $2042, $3063, $4084, $50a5, $60c6, $70e7,
$8108, $9129, $a14a, $b16b, $c18c, $d1ad, $e1ce, $f1ef,
$1231, $0210, $3273, $2252, $52b5, $4294, $72f7, $62d6,
$9339, $8318, $b37b, $a35a, $d3bd, $c39c, $f3ff, $e3de,
$2462, $3443, $0420, $1401, $64e6, $74c7, $44a4, $5485,
$a56a, $b54b, $8528, $9509, $e5ee, $f5cf, $c5ac, $d58d,
$3653, $2672, $1611, $0630, $76d7, $66f6, $5695, $46b4,
$b75b, $a77a, $9719, $8738, $f7df, $e7fe, $d79d, $c7bc,
$48c4, $58e5, $6886, $78a7, $0840, $1861, $2802, $3823,
$c9cc, $d9ed, $e98e, $f9af, $8948, $9969, $a90a, $b92b,
$5af5, $4ad4, $7ab7, $6a96, $1a71, $0a50, $3a33, $2a12,
$dbfd, $cbdc, $fbbf, $eb9e, $9b79, $8b58, $bb3b, $ab1a,
$6ca6, $7c87, $4ce4, $5cc5, $2c22, $3c03, $0c60, $1c41,
$edae, $fd8f, $cdec, $ddcd, $ad2a, $bd0b, $8d68, $9d49,
$7e97, $6eb6, $5ed5, $4ef4, $3e13, $2e32, $1e51, $0e70,
$ff9f, $efbe, $dfdd, $cffc, $bf1b, $af3a, $9f59, $8f78,
$9188, $81a9, $b1ca, $a1eb, $d10c, $c12d, $f14e, $e16f,
$1080, $00a1, $30c2, $20e3, $5004, $4025, $7046, $6067,
$83b9, $9398, $a3fb, $b3da, $c33d, $d31c, $e37f, $f35e,
$02b1, $1290, $22f3, $32d2, $4235, $5214, $6277, $7256,
$b5ea, $a5cb, $95a8, $8589, $f56e, $e54f, $d52c, $c50d,
$34e2, $24c3, $14a0, $0481, $7466, $6447, $5424, $4405,
$a7db, $b7fa, $8799, $97b8, $e75f, $f77e, $c71d, $d73c,
$26d3, $36f2, $0691, $16b0, $6657, $7676, $4615, $5634,
$d94c, $c96d, $f90e, $e92f, $99c8, $89e9, $b98a, $a9ab,
$5844, $4865, $7806, $6827, $18c0, $08e1, $3882, $28a3,
$cb7d, $db5c, $eb3f, $fb1e, $8bf9, $9bd8, $abbb, $bb9a,
$4a75, $5a54, $6a37, $7a16, $0af1, $1ad0, $2ab3, $3a92,
$fd2e, $ed0f, $dd6c, $cd4d, $bdaa, $ad8b, $9de8, $8dc9,
$7c26, $6c07, $5c64, $4c45, $3ca2, $2c83, $1ce0, $0cc1,
$ef1f, $ff3e, $cf5d, $df7c, $af9b, $bfba, $8fd9, $9ff8,
$6e17, $7e36, $4e55, $5e74, $2e93, $3eb2, $0ed1, $1ef0
);
Function GenerateCRC16(Var s1:String):Word;
Var w1,w2,crc16:Word; i:Integer;
begin
crc16 := 0;
For i := 1 to Length(s1) do Begin
w1 := Crc16 shr 8;
w1 := w1 xor Ord(s1[i]);
w1 := w1 and $00FF;
w1 := Crc16Tab[w1];
w2 := (Crc16 shl 8) and $FFFF;
Crc16 := w1 xor w2;
End;
Result := crc16;
end;
Beim HDLC Rahmen wird das Ende eines Protokollrahmens ebenfalls durch ein $7F gekennzeichnet. Es ist jedoch wesentlich einfacher ein anderes Zeichen als Ende zu erkennen. Deshalb wird hier ein $7E benutzt.
Benutzt werden die gleichen Werte wie im Beispiel zum ASCII Modus.
7F 02 7D 31 23 12 34 56 03 00 3C 01 48 20 04 D2 06 54 xx xx 7E
7F 02 7D 31 23 12 34 56 04 00 3C 01 48 20 04 D2 06 54 xx xx 7E
Das sind 42 Bytes. Bei einer Übertragungsrate von 19200 Baud dauert das Senden 21,9ms, also fast doppelt so schnell wie ASCI Übertragung.