[CodeSys 2.3] Signal Overflow verhindern wenn ein Zähler überläuft

entenhausen_dd

Level-2
Beiträge
55
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Leute,

ich habe noch ein Problem mit einem Drehzahlsensor der überläuft also der Wert größer als 65535 wird.
Um das zu verhindern habe ich schon folgenden Code verwendet, wobei mw_Count das Signal vom Drehzahlsensor ist

(* Edge case overflow positive *)
IF mw_Count < 16384
AND mw_CountPrev > 49152 THEN
mi16_Delta := REAL_TO_INT(mw_Count + (65535 - mw_CountPrev));
END_IF

(* Edge case overflow negative *)
IF mw_Count > 49152
AND mw_CountPrev < 16384 THEN
mi16_Delta := - REAL_TO_INT(mw_CountPrev + (65535 - mw_Count));
END_IF

Wie kann ich nun den Overflow verhindern?

CodeSys 2.3

Vielen Dank für eure Hilfe!
 
Zuletzt bearbeitet von einem Moderator:
Hast Du vielleicht einfach nur einen falschen Datentyp verwendet? Vielleicht brauchst Du nur über UDINT anstatt INT rechnen.

Was für ein Signal liefert der "Drehzahlsensor" und wie erfasst Du das? Und was rechnest Du da heraus - eine Position oder einen Weg?


PS: für das Posten von Programmcode bitte die [CODE]-Tags verwenden (der #-Button im Beitragseditor). Dann wird Code besser lesbar:
Code:
(* Edge case overflow positive *)
IF mw_Count < 16384
AND mw_CountPrev > 49152 THEN
    mi16_Delta := REAL_TO_INT(mw_Count + (65535 - mw_CountPrev));
END_IF

(* Edge case overflow negative *)
IF mw_Count > 49152 
AND mw_CountPrev < 16384 THEN
    mi16_Delta := - REAL_TO_INT(mw_CountPrev + (65535 - mw_Count));
END_IF
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
IF mw_Count < 16384 AND mw_CountPrev > 49152 THEN // Edge case overflow positive
    Cnt := Cnt + 1 ;
ELSIF mw_Count > 49152 AND mw_CountPrev < 16384 THEN // Edge case overflow negative
    Cnt := Cnt - 1 ;
END_IF
mw_CountPrev := mw_Count ;
dCountCorr := mw_Count + Cnt * 65536 ;
Aber wodurch den Zähler Cnt auf auf 0 setzen!?
 
Der Drehzahlsensor zählt einfach die Schritte hoch und das Signal ist im Format WORD. Im Endeffekt möchte ich die Drehzahl darüber ermitteln die anliegt
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was hältst Du von dieser Alternative?
Code:
IF mw_Count < 16384 AND mw_CountPrev > 49152 THEN // Edge case overflow positive
    dAdd := 65536 ;
ELSIF mw_Count > 49152 AND mw_CountPrev < 16384 THEN // Edge case overflow negative
    dAdd := 0 ;
END_IF
mw_CountPrev := mw_Count ;
dCountCorr := mw_Count + dAdd ;
Damit könntest leben, wenn Deine max. Drehzahl < 2 * 65536 bleibt.
Aber das Problem, aus Deiner Folge von DrehzahlWerten darauf zu schliessen, auf welcher Seite des Überlaufs man sich befindet, bleibt.
Hast Du ein weiteres Kriterium dafür, ob die Drehzahl z.B. tatsächlich 0 ist, mit dem sich die Mimik "synchronisieren" liesse?

P.S.:
Habe ich mich vielleicht durch Deinen Begriff "DrehzahlSensor" irritieren lassen?
Ist der Wert, den er Dir liefert, tatsächlich die Drehzahl (solange die tatsächliche Drehzahl < 65536 / min)? Über welche ZeitDauer zählt denn der Sensor? ZeitDauer halbieren sollte schon helfen.

Oder ist das z.B. die WinkelPosition von irgendwas und Du versuchst daraus, die Drehzahl zu ermitteln?
 
Zuletzt bearbeitet:
Der Eingang des Drehzahlsensors läuft über einen Counter. Über den Counter ermittle ich dann die Drehzahl. Evtl. gibt es einen anderen Code um das zu realisieren
Die Drehzahl beträgt maximal 2000rpm die ich messen möchte.

Was ich von dir verstanden habe, wenn der Wert über 65536 ist, dann läuft er über und startet wieder bei 0 und dieses überlaufen bewirkt ein falsches Signal vom Code
 
D.h. die Differenz Deiner ZählerStände ist das Mass für die Drehzahl (bei gleichbleibenden ZeitAbständen zwischen den Auswertungen). Was passiert denn mit der Differenz, wenn ein Überlauf stattfindet?
Wenn Du den Zählerstand statt als UINT als INT interpretierst?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Du brauchst (und solltest) den Zähler nie auf 0 setzen.
Zählt der Zähler immer vorwärts? Dann berechne Deine Zählerstands-Differenz mit dieser einfachen Formel :cool: und das Ergebnis ist immer positiv:
Code:
mw_Diff := (mw_Count - mw_CountPrev + 32768) MOD 32768;

Die Differenz muß in Zeitabständen gebildet werden, bevor der Zähler mehr als 32767 Pulse weitergezählt hat.

Harald
 
Der Zählerstand, m16_Delta, habe ich als INT probiert, dann als DINT, beides hat nichts gebracht.

Wenn ein Überlauf stattfindet, rasselt die berechnete Drehzahl fast auf 0 und dann geht sie gleich wieder hoch
 
Hallo Harald,

das Problem hier ist, dass ich auch die Drehzahl rückwärts messen möchte damit. Rückwärts geschieht aber erst dann, wenn die Drehzahl auf 0 geht, der Counter zählt halt dann rückwärts runter wo er zuvor stehen geblieben ist
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn es rückwärts dreht, dann willst Du eine negative Drehzahl anzeigen? Das geht mit WORD so nicht, irgendwo mußt Du dann den Schritt zu einem Datentyp machen, der auch negative Werte haben kann. Also z.B. INT oder DINT.
Was macht Dein Zähler, wenn beim Zählerstand 0 ein Rückwärts-Zählpuls kommt?

Wieviele Pulse können in z.B. 1 Sekunde maximal kommen? In welchem Zeitabstand willst Du die Drehzahl neu berechnen? In welcher Einheit und wie hoch aufgelöst willst Du die Drehzahl berechnen?

Harald
 
(* Edge case overflow positive *)
IF mw_Count < 16384
AND mw_CountPrev > 49152 THEN
mi16_Delta := REAL_TO_INT(mw_Count + (65535 - mw_CountPrev));
END_IF
Warum schreibst Du in dem Code REAL_TO_INT(...)? In dem Code ist doch gar kein REAL beteiligt?? Oder wie sind Deine Variablen deklariert?

Harald
 
Ich weiß jetzt nicht, was in Codesys-ST "MOD" bei negativen Werten macht, doch probiere mal so:
Code:
iCountNow  := iCount; [COLOR="#008000"]//asynchronen Zähler (-32768 .. +32767) nur einmal abfragen[/COLOR]
iDiff      := DINT_TO_INT(INT_TO_DINT(iCountNow - iCountPrev) MOD 32768);
iCountPrev := iCountNow;
alle Variablen sind INT

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Leider verstehe ich (noch) nicht, wie bzw. ob die MOD-Variante mit Vorwärts- UND RückwärtsZählen funktioniert.
Deshalb würde ich's auf diese aufwändige (und gänzlich ungetestete) Weise versuchen.
Code:
// mw_Count        WORD   // Zählerstand 0..65535 (vorzeichenlos)
// i_mw_Count      INT    // Zählerstand -32768 .. 32767
// i_mw_CountPrev  INT    // (stat) Zählerstand im vorherigen Durchlauf
// i_Diff          INT    // optional "Zuwachs" des Zählerstandes
// d_Diff          DINT   // "Zuwachs" des Zählerstandes
// "Zuwachs" skalieren ==> Drehzahl. 

i_mw_Count := WORD_TO_INT(mw_Count) ; // sollte hoffentlich als TypeCast funktionieren?

d_Diff := INT_TO_DINT(i_mw_Count - i_mw_CountPrev) ;
i_mw_CountPrev := i_mw_Count ; // VergleichsZählerstand für nächsten Durchlauf
IF d_Diff < -32768 THEN
    d_Diff := d_Diff + 65536 ; // DINT, weil '< -32768', '> 32767', '+65536' und '-65536' jenseits von INT
ELSIF d_Diff > 32767 THEN
    d_Diff := d_Diff - 65536 ;
END_IF ;

i_Diff := DINT_TO_INT(d_Diff) ; // entfällt, wenn "Zuwachs" nicht als INT benötigt wird

P.S.:
alle Variablen sind INT
Die Variablen wohl, aber die Konstante (der Divisor) ist > INT, Harald!?
 
Zuletzt bearbeitet:
alle Variablen sind INT
Die Variablen wohl, aber die Konstante (der Divisor) ist > INT, Harald!?
Jawohl Heinrich, deshalb soll das MOD als DINT ausgeführt werden: die INT-Differenz wird zu DINT erweitert, dann das MOD 32768 als DINT gerechnet, und dann kann das Ergebnis (-32767..+32767) wieder in INT gespeichert werden.

Eventuell würde auch ein AND 16#7FFF mit vorher Retten des Bit31 und nachher wieder herstellen funktionieren... hmm klingt eigentlich nach überflüssigem Quatsch :confused: ... vielleicht braucht man auch gar kein MOD oder AND weil uns das geniale Zweierkomplement bei Verwendung von signed! INT/DINT schon die ganze Arbeit abnimmt? Ich habe das heute mal nicht so genau durchdacht, und hoffe daß der TE das testet.

Harald
 
So, ich habe das jetzt mal getestet und wie schon vermutet braucht man bei den Zählerüberläufen gar nichts machen, wenn man im richtigen Datentyp rechnet. :D :cool:

Wenn der Zähler als 16 Bit INT zählt: 0,1,2...32767,-32768,-32767,-32766...-1,0,1,2... vorwärts oder auch rückwärts, dann kann man die Differenz Wert_jetzt zu Wert_vorher ganz einfach so bilden:
Code:
iCountNow  := iCount; [COLOR="#008000"]//asynchronen Zähler (-32768 .. +32767) nur einmal abfragen[/COLOR]
[COLOR="#0000FF"]iDiff      := iCountNow - iCountPrev;[/COLOR]
iCountPrev := iCountNow;
und braucht dabei keine Vorwärts- oder Rückwärts-Zählerüberläufe beachten. Die relative Differenz ist Dank Zweierkomplement immer vorzeichenrichtig und korrekt. Beim Zweierkomplement zählt man ja quasi im Zahlenkreis. Man darf natürlich nicht so lange warten, bis seit iCountPrev mehr als 32767 Pulse in einer Richtung aufgetreten sind, weil dann kann man nicht mehr unterscheiden ob der Zähler vorwärts oder rückwärts gezählt hat. Wenn man mehr als 15 Bit Zählerdifferenz auswerten will, dann muß man einen 32 Bit Zähler (DINT) verwenden.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Morgen,

vielen Dank für die Hilfe, wenn ich das jetzt alles zusammen setze müsste der Code wie folgt aussehen
Code:
// mw_Count WORD // Signal vom Drehzahlsensor 0 - 65535
// i_mw_Count      INT    // Zählerstand -32768 .. 32767
// i_mw_CountPrev  INT    // (stat) Zählerstand im vorherigen Durchlauf
// i_Diff          INT    // optional "Zuwachs" des Zählerstandes
// d_Diff          DINT   // "Zuwachs" des Zählerstandes
// "Zuwachs" skalieren ==> Drehzahl. 

i_mw_Count := WORD_TO_INT(mw_Count) ;

d_Diff := INT_TO_DINT(i_mw_Count - i_mw_CountPrev) ;
i_mw_CountPrev := i_mw_Count ;
IF d_Diff < -32768 THEN
    d_Diff := d_Diff + 65536 ;
ELSIF d_Diff > 32767 THEN
    d_Diff := d_Diff - 65536 ;
END_IF ;
i_Diff := DINT_TO_INT(d_Diff) ;[COLOR=#333333][FONT=Courier]
[/FONT][/COLOR]

Wird jetzt nun das i_Diff benötigt?
 
Wird jetzt nun das i_Diff benötigt?
Die berechnete Differenz zwischen zwei aufeinanderfolgenden Lesevorgängen von Zählerständen ist zwar proportional zur Drehzahl, die Du errechnen möchtest, aber vermutlich wirst Du die Differenz noch skalieren müssen, um den DrehzahlWert zu berechnen.
Ob Du dafür den DifferenzWert lieber als INT oder als DINT "benötigst", oder als REAL, das liegt bei Dir.

PS:
Es liegt auch bei Dir, in welchen ZeitAbständen Du das Progrämmchen durchläufst. Der ZeitTakt, in dem das geschehen muss, bestimmt natürlich, wie gross die ZählerstandsDifferenzen werden, bei gleichbleibender Drehzahl.

PPS:
Wie fast immer hat wahrscheinlich auch diesmal Harald damit Recht, dass der ganze Aufwand gar nicht nötig ist.
Ich konnte das Thema nur mit Excel und dem WindowsRechner im ProgrammiererModus "simulieren" (und bin so zu meinem Ergebnis gekommen), habe aber aus meiner aktiven Zeit als SPS-Programmierer auch in Erinnerung, dass die Eigenschaften der ZweierKomplementDarstellung einen recht einfachen Lösungsweg ermöglichen. Man darf einfach nicht Probleme sehen, wo gar keine sind! :D

PPPS:
Ja, Harald hat Recht! Hab's mir gerade nochmal mit dem WindowsRechner vorgenommen und es stimmt! Warum habe ich das gestern nicht gesehen bzw. übersehen? :oops:
 
Zuletzt bearbeitet:
Zurück
Oben