{**************************************************************}
program WS2300_RAM;
//S.Taborek
//Modul-RAMSlave für Datenlogger
//Stand: 20.05.2005
//Stand: 02.06.2005 Datenstrukturänderung
//Stand: 11.06.2005 MW_Status eingeführt
{**************************************************************}
{$NOSHADOW}
Device = mega8, VCC=4.8;
Define_Fuses //negative Logik beachten
override_fuses;
LockBits0 = [];
FuseBits0 = [CKSEL0, CKSEL1, CKSEL3, SUT1, BODEN]; //8MHz int. Osz
FuseBits1 = [];
ProgMode = SPI;
Import SysTick, {RTclock,} TWInet;
From System Import LongWord, Processes;
Define
ProcClock = 8000000; {Hertz}
SysTick = 10; {msec}
StackSize = $0064, iData;
FrameSize = $0064, iData;
TWInetMode = Slave;
TWInode = 04; {default address in slave mode}
TWIpresc = TWI_BR100; {Bitrate}
TWIframe = 54, iData; {buffer/packet size}
TWIframeBC = 4; {Broadcast Frame size}
// RTClock = iData, DateTime;{Time, DateTime}
Scheduler = iData;
//uses Unit_Types;
Implementation
{$IDATA}
{--------------------------------------------------------------}
{ Type Declarations }
type
TReply = array[0..31]of Byte;
TRAMDATENRec = Record
Rec_jj : byte; //1
Rec_mm : byte; //2
Rec_dd : byte; //3
Rec_hh : byte; //4
Rec_mi : byte; //5
//..........................
TempInn : Integer; //6..7 1
TempOut : Integer; //8..9 2
HumidityOut : Word; //10..11 3
WindSpeed : word; //12..13 4
WindDir : Word; //14..15 5
AirPressRel : Word; //16..17 6
DewPoint : Word; //18..19 7
RainTotal : Word; //20..21 8
OkBits : Byte; //22 für Werte 1..8
//...........................
CRS : Byte; //23
end;
// folgener Record muss in Größe gleich dem TRAMSaveRec sein!
TRAMContrRec = Record
RecStart_jj : byte; //1
RecStart_mm : byte; //2
RecStart_dd : byte; //3
RecStart_st : byte; //4
RecStart_mi : byte; //5
RecSize : byte; //6
MAX_DevNr : byte; //7
SaveZyk : byte; //8
Akt_WrSnr : word; //9..10
Last_RdPC : word; //11..12 letzter DS an PC
Last_AnzPC : word; //13..14
IdentCode : word; //15..16
voll : boolean; //17
d : byte; //18
MasterStatus : byte; //19
WSS_SlvStatus : byte; //20
RAM_SlvStatus : byte; //21
LCD_Slvstatus : byte; //22
CRS : byte; //23
end;
TRAM_DS = array[1..sizeOf(TRAMDATENRec)] of byte;
// da RAM-Wetterdaten und Ctrl-Daten gleich groß sind,
// wird TRAM_DS auch als TRAM_CDS verwendet
TTime1 = array[1..3{4}] of byte;
//.................................................
// folgender Record darf die Größe von TWIframe nicht übersteigen!!
// Der Record enthält alle Messwerte der Wetterstation - auch jene,
// die nicht im RAM archiviert werden.
TWSTSlaveDS = Record
cmd : byte; //1
Time : TTime1; //2..4 4
Date : TTime1; //5..7
TempInnen : Integer; //8..9
TempAussen : Integer; //10..11
HumidityInn : word; //12..13
HumidityOut : word; //14..15
WindFlag : byte; //16
WindCode : Byte; //17
WindSpeed : word; //18..19
WindDir : array[1..7]of byte;//20..26
AirPressRel : Word; //27..28
Rain1H : word; //29..30
RainTotal : word; //31..32
DewPoint : word; //33..34
Reserve : byte; //35
WSSensorExt : byte; //36
MW_Status : word; //37..38 für alle MW
WSConnErr : word; //39..40
Last_RdPC : word; //41..42
MasterStatus : byte; //43 >> LED s
WSS_Slv_Status : byte; //44
RAM_Slv_Status : byte; //45
LCD_Slv_status : byte; //46
RAMCapacity : longword; //47..50
r1 : longword;
end;
// folgender Record darf die Größe von TWIframe nicht übersteigen!!
TRAMSlaveRec = Record
cmd : byte; //1
qui : byte; //2
Akt_WrSnr : word; //3..4
CtrlDS : TRAMContrRec; //Controldaten 5..28
WetterDS : TRAMDATENRec; //Wetterdaten 29..51
r1 : longword;
end;
{--------------------------------------------------------------}
{ Const Declarations }
const
BroadCNode : byte = $00;
MasterNode : byte = $02;
WSTSlvNode : byte = $03;
RAMSlvNode : byte = $04;
LCDSlvNode : byte = $05;
BatSlvNode : byte = $06;
const
FM24C_Adr : byte = $50; // Moduladr. im Datenblatt: 10100000b
SlvTimeOut : byte = 20; // 20x SysTick = 200msec
TxFrameSize : byte = sizeOf(TWSTSlaveDS);
TxLCDFrameSize : byte = SizeOf(TWSTSlaveDS);
TimeoutLimit : byte = 230; // für Timeoutüberwachung der ser.Schnittst.
CRAM_ZykTime : byte = 10; // Anzahl Minuten bis Speicherung
const
RAMCtrlCode : word = $abcd;
RAM_MAX_DevNr : byte = 3; // 0..3 für Anzahl FM24C256
RAMDevCapacit : word = 32768; // Bytes pro Device
RAMRecSize : byte = sizeOf(TRAMDATENRec);
RAMDS_proDev : word = RAMDevCapacit div word(RAMRecSize);
RAM_MAX_Snr : word = ((RAM_MAX_DevNr + 1)* RAMDS_proDev) -1; //
RAMContrDSAdr : word = 0; // Datensatzadresse für Control-Daten
//-----------------------------
ClearBuffer : byte = $01; // broadcast for Slv to clear Rx state
cmd_01 : byte = $01;
cmd_02 : byte = $04;
cmd_03 : byte = $15;
cmd_04 : byte = $17;
cmd_05 : byte = $19;
cmd_06 : byte = $31;
cmd_07 : byte = $43;
cmd_08 : byte = $07;
cmd_09 : byte = $13;
qui_01 : byte = $A1;
qui_02 : byte = $A4;
qui_03 : byte = $A7;
qui_04 : byte = $A9;
kenng1 : byte = $37;
kenng2 : byte = $32;
kenng3 : byte = $31;
kenng4 : byte = $33;
kenng5 : byte = $35;
kenng6 : byte = $36;
kenng8 : byte = $38;
kenng9 : byte = $A2;
kenngA : byte = $AA;
kenngB : byte = $BB;
kenngC : byte = $CC;
// es folgen Messwert-Kennzeichnungen:
CTempInn : word = 1;
CTempOut : word = 2;
CHumyInn : word = 4;
CHumyOut : word = 8;
CWindFlag : word = 16;
CWindCode : word = 32;
CWindSpd : word = 64;
CWindDir : word = 128;
CAirPress : word = 256;
CRain1H : word = 512;
CRainTot : word = 1024;
CDewPoint : word = 2048;
CWSSensExt : word = 4096;
//--------end des Unit_Types---Ersatzes
type
TMW = array[1..4] of longint;
//...............................................
TMiWerteRec = Record
TempInn :TMW; //1
TempOut :TMW; //2
HumidityOut :TMW; //3
WindSpeed :TMW; //4
WindDir :TMW; //5
AirPressRel :TMW; //6
DewPoint :TMW; //7
RainTotal :TMW; //8
end;
{--------------------------------------------------------------}
{ Const Declarations }
//const
{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}
var
NetTxRec[@TWITXBUFF] : TRAMSlaveRec;
NetRxRec[@TWIRXBUFF] : TWSTSlaveDS; //
WstDaten : TWSTSlaveDS;
//die folgende Struktur wird nur für die Übermittlung des
//RAM_Ctrl-DS vom Master an den RAM_Slave benötigt:
RAM_Dat_Rx[@TWIRXBUFF] : TRAMSlaveRec; // Empfangsdaten wie Sendedaten
//
WDS_MiW : TMiWerteRec; //für Mittelwerte
RAM_WDS_A : TRAM_DS; // nur für Controlsum-Proc
RAM_RecDS[@RAM_WDS_A] : TRAMDATENRe c;
DS_Saved : boolean;
RAM_Wr_Snr : word; // Schreibadresse
RAM_DS_sent : boolean;
DRec_erstellt : boolean;
RAMSlvStatus : byte;
MW_MittOkBits : byte;
MW_TempOkBits : byte;
h23_request : boolean;
tick_std : boolean;
sk : byte;
ret : boolean;
w : word;
lw : longint;
iw : integer;
c1,c2,i,j,h : byte;
wadd : integer;
// Ma_con_cnt : word;
Sl_prs_err : word;
{--------------------------------------------------------------}
{ functions }
{--------------------------------------------------------------}
function GetSumCode(RAM_DS_:TRAM_DS):byte;
{--------------------------------------------------------------}
var ps:word;
begin
ps := 0;
for i := 1 to (RAMRecSize-1) do // letztes Byte = prs
ps := ps + word(RAM_DS_[i]);
endfor;
return(hi(ps) xor lo(ps));
end;
{--------------------------------------------------------------}
function TestSumCode(DS:TRAM_DS):boolean;
{--------------------------------------------------------------}
begin
return(GetSumCode(DS) = DS[RAMRecSize]);
end;
{--------------------------------------------------------------}
function MesswertMitteln(mw:TMW):TMW;
{--------------------------------------------------------------}
// mw[4] enthält den letzten aktuellen Wert
// mw[3] enthält den letzten Mittelwert
// mw[2] enthält die Summe der letzten Messwerte
// mw[1] enthält die Anzahl der letzten Messwerte
begin
if mw[1] > 0 then // Divisor und Zähler
if mw[3] <> 0 then
inc(mw[1]);
mw[2] := mw[2] + mw[3];
endif;
mw[3] := mw[2] div mw[1]; // neuer Mittelwert
endif;
mw[2] := 0; // Summenspeicher
mw[1] := 0; // Divisor
return(mw);
end;
{--------------------------------------------------------------}
function WindDirMittelWert(mw:TMW):longint;
{--------------------------------------------------------------}
begin
//Kat:
//Hier wird der letzte Mittelwert mit dem aktuellen Wert gemittelt
// mw[3] enthält den letzten Mittelwert
// mw[2] enthält den letzten Messwert
// mw[1] wird als Hilfswert benötigt
//
if abs(mw[2] - mw[3]) > 180 then
mw[1] := (mw[2] + mw[3] - 360) div 2;
if mw[1] < 0 then
mw[1] := mw[1] + 360;
endif;
else
mw[1] := (mw[2] + mw[3]) div 2;
endif;
return(mw[1]);
end;
{--------------------------------------------------------------}
procedure DatenMittelwerteBerechnen;
{--------------------------------------------------------------}
begin
with WDS_MiW do
TempInn := MesswertMitteln(TempInn);
TempOut := MesswertMitteln(TempOut);
HumidityOut := MesswertMitteln(HumidityOut);
WindSpeed := MesswertMitteln(WindSpeed);
//WindDir separat
WDS_MiW.WindDir[1] := 0;
//Die Rücksetzung des Max-Wertes der Windgeschwindigkeit
//erfolgt in der procedure RAMDatenRecErstellen(mittelwerte:boolean);
AirPressRel := MesswertMitteln(AirPressRel);
DewPoint := MesswertMitteln(DewPoint);
RainTotal := MesswertMitteln(RainTotal);
endwith;
end;
{--------------------------------------------------------------}
procedure DatenInMittelwertArrayEintragen;
{--------------------------------------------------------------}
//Die Mittelwerteintragung erfolgt mit jedem neuen Datensatz
//erst bei Anforderung des RAM-Datensatzes vom Master werden
//die Mittelwerte übernommen.
// TMiWerteRec = Record
// TempInnen :Integer; //1
// TempAussen :Integer; //2
// FeuchteOut :Integer; //3
// WindSpeed :Integer; //4
// WindDir :Integer; //5
// AirPressRel :Integer; //6
// Reserve :Integer; //7
// RainTotal :Integer; //8
// end;
//
//Bei der Übernahme eines ungstörten Wertes wird das Ok-bit
//gesetzt. Das gilt bis zur Übergabe an den Master,
//wonach alle Okbits wieder zurückgesetzt werden. Die Okbits
//werden vom low-bit gezählt, wobei Bit0 für den ersten Wert
//steht.
var MWStatus : word;
begin
// setzt die OKBits
// WstDaten ist vom Typ TWS2300SlaveDS
//---Wert 1----------------------
MWStatus := WStDaten.MW_Status; // siehe WS_Slave
iw := WStDaten.TempInnen; // Wert ist mit 10 multipliziert
if (iw < 450)and(iw > -120) then
if (MWStatus and CTempInn) > 0 then //Wert wurde neu gemessen
WDS_MiW.TempInn[2] := WDS_MiW.TempInn[2] + longint(iw);
inc(WDS_MiW.TempInn[1]); // Divisor
MW_MittOkBits := MW_MittOkBits or 1;
endif;
WDS_MiW.TempInn[4] := longint(iw);
MW_TempOkBits := MW_TempOkBits or 1;
else
MW_TempOKBits := MW_TempOKBits and %11111110;
endif;
//---Wert 2----------------------
iw := WStDaten.Tempaussen; //Wert ist mit 10 multipliziert
if (iw < 600)and(iw > -500) then
if (MWStatus and CTempOut) > 0 then
WDS_MiW.TempOut[2] := WDS_MiW.TempOut[2] + longint(iw);
inc(WDS_MiW.TempOut[1]);
MW_MittOkBits := MW_MittOkBits or %00000010;
endif;
WDS_MiW.TempOut[4] := longint(iw);
MW_TempOkBits := MW_TempOkBits or %00000010;
else
MW_TempOKBits := MW_TempOKBits and %11111101;
endif;
//---Wert 3----------------------
w := WStDaten.HumidityOut; //Wert ist mit 10 multipliziert
if (w < 1001)and(w > 0) then
if (MWStatus and CHumyOut) > 0 then
WDS_MiW.HumidityOut[2] := WDS_MiW.HumidityOut[2] + longint(w);
inc(WDS_MiW.HumidityOut[1]);
MW_MittOkBits := MW_MittOkBits or %00000100;
endif;
WDS_MiW.HumidityOut[4] := longint(w);
MW_TempOkBits := MW_TempOkBits or %00000100;
else
MW_TempOKBits := MW_TempOKBits and %11111011;
endif;
//---Windgeschwindigkeit 4-----------------------
//keine MIttelwertbildung unterstützen, sondern Maximum suchen
if WStDaten.WindFlag = 0 then
WDS_MiW.WindSpeed[4] := longint(WStDaten.WindSpeed); //momentan
if (MWStatus and CWindSpd) > 0 then
if WDS_MiW.WindSpeed[2] < WDS_MiW.WindSpeed[4] then //max suchen
WDS_MiW.WindSpeed[2] := WDS_MiW.WindSpeed[4]; //neuer max
endif;
WDS_MiW.WindSpeed[1] := 1; //div immer 1
MW_MittOkBits := MW_MittOkBits or %00001000;
endif;
MW_TempOkBits := MW_TempOkBits or %00001000;
else
MW_TempOKBits := MW_TempOKBits and %11110111;
endif;
//---Windrichtung 5------------xx---------
if WStDaten.WindFlag = 0 then
// Windrichtungswertebereich: 0..15
// muss mit 22.5 multipliziert werden.
// Hier wird der aktuelle Wert mit dem jew. letzten Wert gemittelt,
// da eine einfache Mittelwertbildung der Werte keinen Sinn hat.
if ((sk mod 3) = 0) then
wadd := wadd *(-1); //leichter Spin
endif;
lw := ((longint(WStDaten.WindDir[1]) * 225) div 10) +longint(wadd);
if lw < 0 then
lw := lw + 360;
endif;
if (MWStatus and CWindDir) > 0 then
WDS_MiW.WindDir[2] := lw;
WDS_MiW.WindDir[3] := WindDirMittelWert(WDS_MiW.WindDir);
inc(WDS_MiW.WindDir[1]);
MW_MittOkBits := MW_MittOkBits or %00010000;
endif;
WDS_MiW.WindDir[4] := lw;
MW_TempOkBits := MW_TempOkBits or %00010000;
else
MW_TempOKBits := MW_TempOKBits and %11101111;
endif;
//---Wert 6----------------------
w := WStDaten.AirPressRel; //Wert ist mit 10 multipliziert
if (w > 8500)and(w < 12000) then
if (MWStatus and CAirPress) > 0 then
WDS_MiW.AirPressRel[2] := WDS_MiW.AirPressRel[2] + longint(w);
inc(WDS_MiW.AirPressRel[1]);
MW_MittOkBits := MW_MittOkBits or %00100000;
endif;
WDS_MiW.AirPressRel[4] := longint(w);
MW_TempOkBits := MW_TempOkBits or %00100000;
else
MW_TempOKBits := MW_TempOKBits and %11011111;
endif;
//---Wert 7----------------------
iw := integer(WStDaten.DewPoint div 10); //
if (iw > -10000)and(iw < 12000) then //????
if (MWStatus and CDewPoint) > 0 then
WDS_MiW.DewPoint[2] := WDS_MiW.DewPoint[2] + longint(iw);
inc(WDS_MiW.DewPoint[1]);
MW_MittOkBits := MW_MittOkBits or %01000000;
endif;
WDS_MiW.DewPoint[4] := longint(iw);
MW_TempOkBits := MW_TempOkBits or %01000000;
else
MW_TempOKBits := MW_TempOKBits and %10111111;
endif;
//---Wert 8----------------------
if (WStDaten.RainTotal < 51000) then // Wert ist mit 100 multipliziert
//keine Mittelwertbildung sinnvoll
WDS_MiW.RainTotal[3] := longint(WStDaten.RainTotal);
WDS_MiW.RainTotal[2] := longint(WStDaten.RainTotal); { stehen lassen}
WDS_MiW.RainTotal[4] := longint(WStDaten.RainTotal); {}
WDS_MiW.RainTotal[1] := 1;
MW_MittOkBits := MW_MittOkBits or %10000000;
MW_TempOkBits := MW_TempOkBits or %10000000;
else
MW_TempOKBits := MW_TempOKBits and %01111111;
endif;
end;
{--------------------------------------------------------------}
procedure RAMDatenRecErstellen(mittelwerte:boolean);
{--------------------------------------------------------------}
// Daten werden immer ab Snr 1 bis RAM_MAX_Snr gespeichert und dann
// wird RAM_voll gesetzt. Die Datensicherung läuft jedoch weiter
// und überschreibt nicht abgeholte Daten. Die Speichernazeige
// LCDbuf.RAMCapacity bleibt jedoch auf 100% stehen bis PC Daten
// übernimmt.
// da jeder DS seine Zeitstempel bekommt, kann PC archivieren
// ind = 4 liest die Momentanwerte aus
// ind = 3 liest die berechneten Mittelwerte aus
var ind :byte;
begin
//
if mittelwerte then
DatenMittelwerteBerechnen;
RAM_RecDS.OkBits := MW_MittOKBits;
ind := 3; //3 liest die berechneten Mittelwerte aus
else
RAM_RecDS.OkBits := MW_TempOKBits;
ind := 4; //4 liest die aktuellen Werte aus
endif;
//....................................
//jetzt den Zeitstempel in den aktuellen DS eintragen:
RAM_RecDS.Rec_jj := WstDaten.Date[3];
RAM_RecDS.Rec_mm := WstDaten.Date[2];
RAM_RecDS.Rec_dd := WstDaten.Date[1];
RAM_RecDS.Rec_hh := WstDaten.Time[1];
RAM_RecDS.Rec_mi := WstDaten.Time[2];
//....................................
//
RAM_RecDS.TempInn := integer(WDS_MiW.TempInn[ind]);
RAM_RecDS.TempOut := integer(WDS_MiW.TempOut[ind]);
RAM_RecDS.HumidityOut := word(WDS_MiW.HumidityOut[ind]);
RAM_RecDS.AirPressRel := word(WDS_MiW.AirPressRel[ind]);
RAM_RecDS.WindSpeed := word(WDS_MiW.WindSpeed[ind]);
RAM_RecDS.WindDir := word(WDS_MiW.WindDir[ind]);
RAM_RecDS.DewPoint := Word(WDS_MiW.DewPoint[ind]);
RAM_RecDS.RainTotal := word(WDS_MiW.RainTotal[ind]); {ist kein Mittelwert!!}
// RAM_RecDS.OKBits wurden in DatenMittelwerteBerechnen gesetzt
RAM_RecDS.crs := GetSumCode(RAM_WDS_A); //RAM_Rec overlays RAM_WDS
//
// Daten in den Sendepuffer eintragen:
NetTxRec.WetterDS := RAM_RecDS;
//
if ind = 3 then
WDS_MiW.WindSpeed[3] := 0; // wegen neuer Maximumbildung
endif;
//
end;
{==============================================================}
Process RxMasterFrame(40, 80 : iData);
{==============================================================}
var err : byte;
begin
WaitSema(TWIrxSEMA);
case NetRxRec.cmd of // 1.Byte wie in RAM_Dat_Rx
cmd_01:
WstDaten := NetRxRec; // Datenübernahme Wetterdaten + Zeit
if not RAM_DS_sent then
DatenInMittelwertArrayEintragen; // Daten vom Master übernehmen
if not DRec_erstellt then
RAMDatenRecErstellen(true);
//Rücksetzen aller OK-Bits nur an dieser Stelle
RAM_RecDS.OkBits := 0; // jeder MW wird ok=1 oder ok=0 gesetzt
//für die temporären Messwerte über cmd_02 gilt zusätzlich:
MW_TempOKBits := $FF; // jeder MW wird ok=1 oder ok=0 gesetzt
DRec_erstellt := true;
endif;
endif;
mdelay(100);
NetTxRec.cmd := cmd_07; // Quittung für kompl RAM_DS
if TWItxFrame(02, byte(sizeOf(TRAMSlaveRec))) then // Senden des RAM_DS
RAM_DS_sent := true; // siehe unter cmd_01
DRec_erstellt := false;
endif;
|
cmd_02: //Datenübernahme Wetterdaten + Zeit wie bei cmd_01, mit
// dem Unterschied, dass hier nur eine temporärer DS zurück-
//gesendet wird, der für Anzeigezwecke verwendet werden kann.
WstDaten := NetRxRec;
RAM_DS_sent := false;
DatenInMittelwertArrayEintragen; //Daten vom Master übernehmen
RAMDatenRecErstellen(false); //false= keine Mittelwerte bilden
MW_TempOKBits := $FF; //jeder MW wird ok=1 oder ok=0 gesetzt
//mdelay(5);
NetTxRec.cmd:= cmd_01; // Quittung für temp. RAM_DS
TWItxFrame(02, byte(sizeOf(TRAMSlaveRec))); //Senden des RAM_DS
|
endcase;
//
mdelay(50);
TWIRXCLEAR; // !!!
end;
{==============================================================}
Process RxBroadcastFrame(40, 80 : iData);
{==============================================================}
begin
WaitSema(TWIbroadcastSema);
case TWIbroadcastCMD of
ClearBuffer : TWIrxClear; // remove deadlocks
TWItxClear;
|
endcase;
end;
{--------------------------------------------------------------}
procedure InitPorts;
{--------------------------------------------------------------}
begin
DDRB:= %00000110;
end InitPorts;
{--------------------------------------------------------------}
procedure InitVar;
{--------------------------------------------------------------}
begin
h23_request := false;
tick_std := true;
//Ma_con_cnt := 0;
Sl_prs_err := 0;
DS_Saved := false;
RAM_DS_sent := false;
DRec_erstellt := false;
RAMSlvStatus := 0;
MW_TempOKBits := $FF;
wadd := -1;
end;
{==============================================================}
{ Main Program WS2300-Datenlogger-Master}
{$IDATA}
begin
InitPorts;
InitVar;
EnableInts;
//---------------------------------
TWIadrMask:= $FF;
Priority(main_proc, 2);
Start_Processes;
TWIrxClear; //erst nach Start_Processes !
TWItxClear;
//==================Hauptschleife===================
loop
//
mdelay(810);
if sk > 59 then
sk := 0;
endif;
inc(sk);
//---------------
DisableInts;
if (WstDaten.Time[1] = 23) and (h23_request) then
h23_request := false;
endif;
if WstDaten.Time[1] = 22 then // 22 Uhr
h23_request := true;
endif;
EnableInts;
endloop;
end WS2300_RAM.