/* Ebene 2: Regulatoren (Zustands-Automaten) mit Hardwarezugriff über Treiber */ #include #include #include #include "system.h" #include "board.h" #include "uart.h" #include "data.h" // Treiber #include "device.h" // Formatierfunktionen #include "report.h" #include "reg.h" // A-Regler #define MAXONIVTIME 14400L // Ölwechselintervall //#define MINRUNTIME 7200 // Warmlaufzeit #define TMAXSEC 600L // 10min Wartezeit für Tmax-Bestimmung #define ONIVSTARTSEC 3600L // Abkühlzeit bis Starter funktioniert #define ONIVMINT 50000L // zeugt nach 1h von warmen Verhältnissen #define RESTARTSEC 3720L // Neustart Zeitpunkt #define COOLSEC 10000L // nach 3h Benässen #define ADRYSEC 20000L // nach 6h Trockenblasen #define MAXRETRY 3 // max. Startversuche // M-Regler #define F_M_AN 1800L // rpm warmwerden //#define F_M_AUS 600L // rpm definitiv MAUS #define F_G_AN 800L // rpm #define F_G_AUS 700L // rpm #define F_W_AN 1500L // rpm #define F_W_AUS 1400L // rpm #define MROTSEC 20L // Zeit bis Drehbeginn #define MGASSEC 20L // Zeit bis Durchstarten // O-Regler #define OZSEC 15 // Zirkulatonszeit (ab gewisser Zeit öffnet Slave im Stillstand das Ventil) #define OZGSEC 2 // wreg() Gelegenheit bieten Wasserpumpe anzulassen #define OZPSEC 1800 // Zirkulationsperiode #define OFSECMAX 20//15 // kaltes Tanköl braucht länger #define OFSECMIN 7 #define OCSECMAX 25//13 // heisses Motoröl geht schneller #define OCSECMIN 5//6 #define ONSSEC 60 // 5 Wartezeit bis Motor dreht #define OEQISEC 30 // Wartezeit vor Füllen im Niveauausgleich //#define ONIVSEC 7200L // 4h Zeit ohne Niveaureglung, >2fach -> Zwangsabschaltung // H-Regler #define HZSEC 120L // 2min Heizungszirkulation einschalten #define HZDEFROST 1200L // defrost -> 20min Periode #define HZPTEST 86400L // Pumpentest -> 24h Periode #define HNACHSEC 60 // Nachlaufverzögerung beim Ausschalten #define HZFROST 20000L // Frostgefahrgrenze 20deg #define HZTOUT 86400L // 1 Tag lang nach Motorabschaltung kurze Periode (Winterurlaub) // W-Regler #define WEINZS 15 // x0.1s Einschaltzeit bei kaltem Motor, 20mitBypass #define WAUSZS 10 // x0.1s Pausenzeit zum Entgasen und Austasten #define TKR2K 68000L // mC bis dahin wird ausgetastet #define WPERMAXZS 1800L // max. Regelperiode - wenn länger, dann nie // G-Regler #define GVORSEC 5 // Einschaltverzögerung beim Starten #define GNACHSEC 3 // Ausschaltverzögerung beim Stoppen, trocken aber nicht zu heiss wegen Spannungen //#define GANTOUT 5 // danach wird reguliert long areg(int act){ // Automatik mit Ölwechsel static long sta=AAUS,amo=AUS,w=MAXRETRY,t0,osec;// Reg-Zustand, Phasenzeit, AutoModus,Versuche übrig static int si=0; // Drückzeit [loops] static long sebo; // Motorstart mit Loslassen des SEB-Tasters innerhalb 2-4s long rval=AAUS,sec,seb=val(SEB); // Rückgabewert,Sekunden,SEB-Status,Phasenzeit int oact,osta,arm,st; // alter act-Wert, alter Status, Start-Scharf static long tkmax,tpmax; do{ sec=(gti()-t0)/TPS; // Phasensekunden osta=sta; switch (oact=act){ case AAN: if (w>0) {sta=act;send("AAN ");mreg(MAN);w--;} // Versuche übrig? -> Start break; case AMAUS: sta=act;send("AMAUS ");mreg(MAUS); // Stop>Tmax>ONIV>STEV>WAN1S tkmax=0L;tpmax=0L; // T-Reset break; case AAUS: sta=act;w=MAXRETRY;mreg(MAUS);send("AAUS "); break; // Ruhezustand, neues Glück case AHAND: amo=AUS;send("AAN=AUS"); break; // Modus kein Autostart case AAUTO: amo=AN; send("AAN=AN"); break; // Modus Autostart case AUPD: if (sec>osec){st=1;osec=sec;} else st=0; // Sekundentrigger setzen switch (sta){ // abhängig vom Status case AAN: // Motor läuft if(mreg(MRSTA)==MAUS){ amo=AUS; act=AAUS; break;} // Motor ausgegangen? > kein Neustart erlauben if(oreg(ONSEC)>MAXONIVTIME){ act=AMAUS; break;} // Ölwechsel fällig > ggf. Neustart break; // case AMAUS: case AAUS: // Motor steht if ((si>=20)&&(si<40)) {arm=1; if(si==20) hp(AN); } // MANSTART Scharf else { arm=0; if(si==40) hp(AUS);} // Unscharf if (seb==1){ if (si<100) si++;} // Drückzeitzähler else { si=0; // oder Reset if (arm==1) {send("MANSTART");act=AAN;} // Scharf? -> Start if(sebo==1) hp(AUS); // 1>0 Flanke -> LED AUS } if (sta==AMAUS){ // nur AMAUS if ((sec<=TMAXSEC)&st){ // Ttmax und Tpmax ermitteln if (val(TKR)>tkmax) tkmax=val(TKR); if (val(TPW)>tpmax) tpmax=val(TPW); if (sec==TMAXSEC){ // und drucken uputs(pform("Tkmax ",1,tkmax, "")); // Max-Krümmertemperatur uputs(pform("Tpmax ",1,tpmax, ""));uputs("\n");// Max-Abgastemperatur nach PWT -> TBOX } } if ((sec==ONIVSTARTSEC)&(st)&(val(TKR)>ONIVMINT)){// 1h und noch warm -> verzögerter if (oreg(ONSEC)>MAXONIVTIME) oreg(ONIV);} // autom. Niveauzyklus if ((sec==RESTARTSEC)&st){ // 1h2min if (amo==AN) act=AAN; // autom. Neustart else startstop(STEV); // oder EV öffnen } if ((sec==COOLSEC)&st){ // 3h if (startstop(STSTA)==STDONE) // STEV geklappt? wreg(WAN1S); // etwas Wasser } if ((sec==ADRYSEC)&st){ // 3h if (startstop(STSTA)==STDONE) // STEV geklappt? startstop(STGO); // Trockenblasen act=AAUS; // Grundzustand } }// end AMAUS break;// end Kombi-Case }// end switch sta break;// end AUPD case ASEC: rval=sec; break; // Phasensekunden case ARSTA: rval=sta; break; // Regulator Status case AMODE: rval=amo; break; // Automode Status }// end switch if (osta!=sta) {t0=gti();osec=0;} // Zustandsänderung -> neu zählen } // end do while (oact!=act); // bis alles erledigt sebo=seb; // MANSTART Taster-Zustand sichern return rval; } long mreg(int act){ // Motorzustandssteuerung AUS->DREHT->GAS->AN->DRY->AUS static long sta=MAUS,t0,osec; // Reg-Zustand,Zwischenzeit,Ölzeit,Zwischensekunden long rval=sta,sec; // Rückgabewert, Phasensekunden int oact,osta,i,st; // alter act-Wert, Versuche übrig, Sekundentrigger if ((act==MAN)&(sta!=MAUS)) return sta; // externes MAN während temporärer Zurstände ignorieren do{ sec=(gti()-t0)/TPS; // Phasensekunden osta=sta;; switch (oact=act){ case MAN: // Im Sinne von hochschalten switch (sta){ // abhängig vom Status case MAUS: // Motor AUS if (oreg(ORSTA)!=OAUS) send(lab(oreg(ORSTA),OREG)); // Keine Öl-Aktivitäten und else if (ala()==0){ // ohne Alarme -> Start if (val(FMO)==0) {startstop(STGO);sta=MROT;} // regulär else send("Startversuch bei drehendem Motor verhindert");// da Wäre etwas faul } break; case MROT: // dreht hoch greg(GANG); // Gas verzögert AN sta=MGAS; // break; case MGAS: // Dreht mit > F_M_AN , Restarts reset min(FMO,F_M_AN); // FMO > 500rpm oreg(OZAUS); // Öl und Heizung AN setpar(STARTS,1L+getpar(STARTS)); // Starts inkrementieren _delay_ms(100); // EEPROM schreiben sta=MAN; // neuer Status break; } // Ende MAN break;// Ende Hochschalten case MAUS: // Im Sinne von Runterschalten min(FMO,0L); // 0 rpm erlaubt if (startstop(STSTA)!=STDONE) startstop(STSTP); // Stop aktiv durchreichen switch (sta){ case MAN: // Er läuft gerade drucke(LAUFZEIT); // Laufzeit drucken if ((lim(FMO)==1)|(lim(SEB)==-1)) // Überdreht? oder Not-AUS? greg(GAUS); else greg(GAUSG); // Gas gleich AUS oder später oreg(OAUS); // wreg schaut aufs Gas setpar(RUNSEC,sec+getpar(RUNSEC)); // Laufzeit sichern _delay_ms(100); // Schreibzeit sta=MDRY; break; // gehe in Trockenlauf case MDRY: // nach Trockenlauf max(TPW,TPWSMA);max(TKR,TKRSMA); // TPW,TKR unscharf sta=MAUS; break; default: sta=MAUS; break; // Gas,Wasser,Heizung folgen autom. } break;// Ende Runterschalten case MUPD: // alle 100ms Dinge testen if (sec>osec){st=1;osec=sec;} else st=0; // Sekundentrigger setzen rval=sta; switch(sta){ case MROT: // Anlassen i=startstop(STSTA); // Starterzustand? if (val(FMO)>F_G_AN) act=MAN; // Gas AN if (i==STSTP) act=MAUS; // Starterfehler if(sec>MROTSEC) act=MAUS; // im Kriechmodus länger break; case MGAS: // Gas geben if (val(FMO)>F_M_AN) act=MAN; // Motor AN if (startstop(STSTA)==STSTP) act=MAUS; // Zu lange -> AUS if (sec>MGASSEC){uputs("ST-ERROR ");act=MAUS;} // Fehler -> AUS break; case MAN: // Motor läuft if ((sec==10L)&(st==1)){ uputs("\n");drucke(WERTE);} // nach 10s Werte if ((sec==60L)&(st==1)){ max(TPW,TPWRMA);max(TKR,TKRRMA);}// nach 60s TPW,TKR scharf if((ala()>0)||(oreg(ORSTA)==OERR)) act=MAUS; // Fehler (Öl,Ladeende, Ausgehen) break; case MDRY: // Trockenlauf if (sec<20L){if (st==1) uputs("*");} // 20 x Sekundentakt warten else act=MAUS; // bis Kondensator voll break; case MAUS: t0=gti(); // Bei MAUS steht Zeit break; }// Ende switch(sta) break; case MRSTA: rval=sta; break; // Status case MSEC: rval=sec; break; // Phasenzeit default: rval=sta; break; // unschädlich } if (osta!=sta){t0=gti();osec=0L; // Zustandsänderung -> neu zählen switch(sta){ // und berichten case MROT: uputs("MROT "); break; case MGAS: uputs("MGAS "); break; case MAN: uputs("MAN "); break; case MDRY: uputs("MDRY "); break; case MAUS: /*if (act!=MAN) */ send( "MAUS "); break; // } } } while (oact!=act); return rval; } long oreg(int act){ // Ölmodi starten und durchführen, benutzt opump(), multizyklisch static long sta=OAUS,t0,osec,nsec,nm=AUS;// Reg-Zustand,Zeit - nach erstem Wechsel gültig,old sec, ONIV-Mode long rval=sta,sec; // Phasensekunden int oact,osta,st; // Sekundentrigger if( (sta==OERR)&((act==OZAN)|(act==OZAUS)|(act==ONIV)|(act==OCLR)|(act==OFIL)) ) return(rval); // OERR nur mit OAUS beendbar do { // kreiseln bis durch sec=(gti()-t0)/TPS; // Phasensekunden osta=sta; // Ausgangszustand switch (oact=act){ case OERR: sta=act;opump(PCAL); uputs("OERR "); break; // Einrasten im Fehlerzustand - raus nur mit OAUS case OAUS: sta=act;opump(PAUS); uputs("OAUS "); break; // Pumpe AUS case OZANG:sta=act; uputs("OZANG "); break; // Pumpe verzögert vorwärts und AN case OZAN: sta=act;opump(PZIR); uputs("OZAN "); break; // Pumpe vorwärts und AN case OZAUS:sta=act;opump(PAUS); uputs("OZAUS "); break; // Pumpe vorwärts und AN case ONIV: if (mreg(MRSTA)==MAUS){ uputs("ONIV "); // Stillstand? startstop(STGO); // Drehen lassen sta=act; opump(PAUS);nm=AN; // Niveliermodus aktivieren } break; // case OCLR: if (mreg(MRSTA)==MAUS){ uputs("OCLR "); // Stillstand? sta=act;opump(PCLR);} break; // Entleeren case OEQI: sta=act;opump(PAUS);uputs("OEQI "); break; // Warten bis Ausgleich case OFIL: if (mreg(MRSTA)==MAUS){ uputs("OFIL "); // Stillstand? sta=act;opump(PFIL);} break; // Füllen case ORSTA:rval=sta; break; // Reglerstatus case OPSTA:rval=opump(PSTA); break; // Pumpenstatus case OSSTA:rval=val(SOS); break; // Sensorstatus case OVSTA:rval=ov(POS); break; // Ventilstatus case OSEC: rval=sec; break; // sec schon in sta case ONSEC:rval=nsec; break; // sec schon in sta case OMOV: rval=opump(PMOV); break; // aktuelle Ölmenge case OUPD: if (sec>osec){st=1;osec=sec;}else st=0; // Sekundentrigger setzen if( (sta!=OERR)&(sec>1) ){ // nach Einschaltphase if(lim(IOP) !=0){send("IOP->OERR"); act=OERR;} // Pumpenstromüberwachung if (act==OERR) drucke(ODEBUG); // Werte drucken } if (st&(mreg(MRSTA)==MAN)) nsec++; // ONIV-Zeit hochzählen switch (sta){ // und ggf Aktionen case OAUS: break; case OZANG: if (sec>=OZGSEC) act=OZAN; break; // verzögert AN case OZAN: if (sec>OZSEC) act=OZAUS; break; // intervallweise AUS case OZAUS: if (sec>OZPSEC) act=OZANG; break; // und AN case ONIV: if (val(FMO)>F_G_AN) act=OCLR; // Leeren wenn dreht if (sec>ONSSEC) act=OERR; break; // oder Fehler wenn nicht case OCLR: if (st==1) drucke(ODEBUG); // Sekundentakt -> drucken if((sec>OCSECMAX)|(opump(PSTA)==PAUS)){ // Leeren startstop(STSTP); // Starter anhalten uputs(pform("",0,sec*1000L,"s ")); // Leerungszeit notieren if( (sec>OCSECMAX)|(secZeitfenster oder DIOP uputs("OCSEC|MOV->OERR ");act=OERR; // Fehler ausgeben } else{if (nm==AN) act=OEQI;else act=OAUS;} // Niveliermodus? } break; // Ende CLR -> RTX case OEQI: if (sec==OEQISEC) {act=OFIL;nm=AUS; } break; // Equilibierphase, dann Leeren case OFIL: if (st==1) drucke(ODEBUG); // Sekundentakt -> drucken if( (sec>OFSECMAX)|(opump(PSTA)==PAUS) ){ // Füllen uputs(pform("",0,sec*1000L,"s ")); // Füllzeit notieren if( (sec>OFSECMAX)|(secOERR ");} // Fehler ausgeben else {act=OAUS;nsec=0; } // ONIV-Zeit reset } break; case OERR: break; // nix tun ausser Zeit inc }// end switch(sta) break; }// end switch (act) if (osta!=sta) {t0=gti();osec=0L;} // Zustandsänderung -> neu zählen } while (act!=oact); // kreiseln bis durch return rval; } long greg(int act){ // Gasventilsteuerung mit Vor- und Nachlaufzeit static long sta=GAUS,t0; // Reg-Zustand,Zeit long rval=sta,sec; // /ticks per second int oact,osta; do{ sec=(gti()-t0)/TPS ; // /ticks per second osta=sta; switch (oact=act){ case GAN: uputs("GAN "); sta=GAN; gv(AN); break; // sofort AN case GAUS: uputs("GAUS "); sta=GAUS;gv(AUS); break; // sofort AUS case GANG: uputs("GANG "); sta=GANG; break; // verzögert AN case GAUSG:uputs("GAUSG ");sta=GAUSG; break; // verzögert AUS case GUPD: // Beginn Update if ((mreg(MRSTA)==MAUS)&(sta!=GAUS)) {act=GAUS; break;} // z.B. irregulärer Stop switch(sta){ case GAUSG: if (sec>GNACHSEC) act=GAUS; break; case GANG: if (sec>GVORSEC) act=GAN; break; case GAN: if(val(FMO)GANTOUT){ if(val(FMO) neu zählen } while (oact!=act); return rval; } long wreg(int act){ // Wasserpumpenregler mit Entblasung und Austastung static long sta=WAUS,t0,wti,wtzs,wzs; // Status, Statusstartzeitpunkt,Tastperioden, Zeiten für WTAN/WAN Tastgrad long rval=sta,zs,fmo,grsta; // Zehntelsec, int oact,osta; fmo=val(FMO);grsta=greg(GRSTA); // vorher lesen, wegen Debugmodus if (dm==DMWAN){fmo=F_W_AN+1;grsta=GAN;} // im debugmode fmo und grst manipulieren, damit Wasser läuft do{ zs=((gti()-t0)*10L)/TPS; // Zehntelsekunden seit Statusänderung osta=sta; switch (oact=act){ case WAUS: sta=act;wp(AUS);uputs("WAUS ");wzs=0;wtzs=0;break; // AUS case WTAN: sta=act;wp(AN); /*uputs("WTAN ");*/ break; // Temporär AN case WTAUS: sta=act;wp(AUS); /*uputs("WTAUS ");*/ break; // Temporär AUS case WAN: sta=act;wp(AN); /*uputs("WAN ");*/ break; // AN case WAN1S: if(sta==WAUS) {sta=act;wp(AN);uputs("WAN1S ");} break; // bei Stillstand 1s nass machen case WUPD: switch(sta){ case WAN1S: if (zs>WEINZS) act=WAUS; break; // 1.5s AN, dann AUS case WAUS: if ((fmo>F_W_AN)&(grsta==GAN)) act=WAN; // rpm+Dauergas -> AN break; case WTAN: if ((fmo AUS if (val(TKR)<(TKR2K+100L)){ // kühl genug if( (zs>WEINZS) && (oreg(ORSTA)!=OZANG) && (oreg(ORSTA)!=OZAN))// nach 1.5s {act=WTAUS;wti++;} // und OZIR=AUS weitertasten } else {wtzs=wti*(WEINZS+WAUSZS);wti=0;act=WAN;} // Sonst -> Dauermodus break; case WTAUS: if (zs>WAUSZS) act=WTAN; // Temporär-AUS, Entblasung break; case WAN: if ((fmo AUS if (val(TKR)<(TKR2K-100L)){wzs=zs; act=WTAN;} // zu kalt -> Tastmodus break; } break; case WRSTA: rval=sta; break; case WTAST: // Prozent Tastgrad if ((wzs+wtzs)>30) rval=(100L*wzs)/(wzs+wtzs); // Kurzphase ausblenden, allg. Rechnung, aber .... else rval=0; if (wti>WPERMAXZS/(WEINZS+WAUSZS)) rval=0; // dauerhaft in WTAN/WTAUS if ((sta==WAN)&(zs>WPERMAXZS)) rval=100; // dauerhaft in WAN break; } // end switch(act) if (osta!=sta) t0=gti(); // Statusänderung -> neu zählen } while (oact!=act); // noch mehr Aktionismus? return rval; // Status } long hreg(int act){ // Voll und Intervallmodus und Nachlaufzeit static int sta=HINTAN; // Zirkulieren nach Reset ist wichtig - nach 20min static long t0=0,t1=0; // Zeitpunkte: Phasenwechsel, HAN-Zeitpunkt long rval=sta,sec,sec1,lval; // int oact,osta; do{ sec=(gti()-t0)/TPS; // /ticks per second sec1=(gti()-t1)/TPS; // /ticks per second osta=sta; switch (oact=act){ case HAUS: sta=act;hp(AUS);uputs("HAUS "); break; // Pumpe AUS - i.d.R. nicht verwendet case HINTAN: sta=act;hp(AN); /*uputs("HINTAN");*/ break; // Intervall-AN-Phase case HINTAUS: sta=act;hp(AUS); break; // Intervall-AUS-Phase case HINTG: sta=act;hp(AN); uputs("HINTG "); break; // Nachlaufzeitphase vor Intervall case HAN: sta=act;hp(AN); uputs("HAN "); t1=gti(); break; // Pumpe AN, wenn Motor läuft case HUPD: if ((val(TKR)HZSEC) act=HINTAUS; break; // nach Pumpzeit AUS case HINTAUS: if (sec>lval) act=HINTAN; // nach Periodenzeit AN if (mreg(MRSTA)==MAN) act=HAN; break; // Motor AN -> Pumpe dauernd AN case HINTG: if (sec>HNACHSEC) act=HINTAN; break; // nach Nachlaufzeit INT case HAN: if (mreg(MRSTA)==MAUS) act=HINTG; break; // Motor AUS -> Pumpe runterfahren } break; case HRSTA: rval=sta; break; case HPSTA: rval=hp(POS); break; } if (osta!=sta) t0=gti(); // Zustandsänderung -> neu zählen } while (oact!=act); return rval; }