# Programmierwettbewerb, 2. Aufgabe



## LargoD (23 Juni 2011)

Aufgabe ist, die Ausgangssignale eines Inkrementalgebers auszuwerten und zu überwachen.

Kanal_1: __----____----____--
Kanal_2: ____----____----____

Positive Flanke auf Kanal_1 wenn Kanal_2 Null bedeutet positive Zählrichtung.

Der Geber liefert zwei um eine Viertel-Periode (90 Grad) zueinander versetzte Signale. Hieraus soll die Richtung der Bewegung erkannt werden und ein DINT-Zähler mit best möglicher Auflösung bei höchst möglicher Gebergeschwindigkeit bei gegebenem Aufrufintervall aufwärts bzw. abwärts getaktet werden. Nicht plausible Signalfolgen  (wenn der Geber zu schnell bewegt wird, z. 10,01) sollen erkannt, ignoriert und gemeldet werden. Der Fehler muss nicht gespeichert werden.
Ein zusätzliches Eingangssignal soll eine dauerhafte Umkehrung der Zählrichtungen erlauben.

Bemerkungen:
Es gibt eine bitparallele Lösung, die wahrscheinlich bei mehreren Gebern deutliche Laufzeitvorteile hat. Hier soll die laufzeitoptimale Lösung für einen Geber gefunden werden, egal ob bitparallel oder nicht.
Wenn der Geber viel zu schnell bewegt wird, können wieder plausible, aber falsche Zählimpulse entstehen. Das kann per Software nicht mehr erkannt werden.


Vorschlag für die Schnittstelle
(kann individuell erweitert werden)

```
FUNCTION_BLOCK VR-Logik
TITLE =Vorwärts/Rückwärts-Logik
VERSION : 0.1
VAR_INPUT
  Kanal_1 : BOOL ;	//Kanal 1 vom Geber
  Kanal_2 : BOOL ;	//Kanal 2 vom Geber
  Richtung : BOOL ;	//Bei 1 Zählrichtungen umkehren
END_VAR
VAR_OUTPUT
  Zaehler : DINT ;	//Zählerwert
  Fehler : BOOL ;	//Fehler, Kanäle nicht plausibel
END_VAR
BEGIN
NETWORK
TITLE =

END_FUNCTION_BLOCK
```

Viel Vergnügen
Gruß
Erich


----------



## Larry Laffer (24 Juni 2011)

Hallo Erich,
dazu hätte ich noch etwas anzumerken :

- Es gibt aus meiner Sicht keine unplausiblen Bitfolgen, da aufgrund von Richtungsänderungen (Zittern) jede neue Bitfolge gegenüber der vorangegangenen pausibel ist. Also würde z.B. eine Signalfolge am E1 die da wäre 0-1-0 während E2=0 ist nur bewirken (müssen), dass der Zähler mit der einen Flanke hochgezählt und mit der anderen Flanke wiederheruntergezählt wird.
Entsprechend ist auf diese Weise ein Übertakten des FB's überhaupt nicht registrierbar (außer man sagt, das man nicht mehr mitspielt wenn die Impulszeiten z.B. kleiner 5 ms werden).

- Welche Form der Flankenauswertung soll es denn werden ? 1fach, 2fach oder 4fach ? Oder ggf. durch eine E-Beschaltung sogar wählbar ?

Gruß
Larry

PS:
Leider kann ich mangels Programmiergerät und damit PLC-Sim dazu im Augenblick sonst nichts beitragen außer ggf. einem Quick-and-Dirty-Code ...


----------



## Solaris (24 Juni 2011)

Larry Laffer schrieb:


> ...
> 
> - Welche Form der Flankenauswertung soll es denn werden ? 1fach, 2fach oder 4fach ? Oder ggf. durch eine E-Beschaltung sogar wählbar ?


 
ist das nicht schon Teil der Aufgabe?



LargoD schrieb:


> ... mit best möglicher Auflösung bei höchst möglicher Gebergeschwindigkeit


----------



## LargoD (24 Juni 2011)

@Larry
Was Du beschreibst ist natürlich eine plausible Bitfolge. So lange sich die Signale nicht schneller als das Ausleseintervall ändern, ergeben sich nur plausible Bitfolgen und die müssen natürlich korrekt ausgewertet werden. Wenn die Signale sich schneller ändern, ergeben sich Bitfolgen die im Normalbetrieb nicht vorkommen, z. B. E1:0,1 und E2:1,0.
Gruß
Erich


----------



## StructuredTrash (24 Juni 2011)

Der Inkremental-Drehimpulsgeber, seit Jahren eines meiner Lieblingsthemen. Leider reicht mein Schulabschluss nicht für Siemens, kann nur TwinCAT/CoDeSys. Meinen Beitrag daher ausser Konkurrenz laufen lassen, auch wenn die Aufgabe relativ systemunabhängig ist.

```
FUNCTION_BLOCK fb_VR_logik
VAR_INPUT
    Kanal_1:BOOL;
    Kanal_2:BOOL;
    Richtung:BOOL;
END_VAR
VAR_OUTPUT
    Zaehler:DINT;
    Fehler:BOOL;
END_VAR
VAR
    Flanke_1:BOOL;
    Flanke_2:BOOL;
    Speicher_1:BOOL;
    Speicher_2:BOOL;
END_VAR

Flanke_1:=Kanal_1 XOR Speicher_1;
Flanke_2:=Kanal_2 XOR Speicher_2;
Fehler:=Flanke_1 AND Flanke_2;
IF Flanke_1 XOR Flanke_2
THEN
    IF Flanke_1
    THEN
        IF (Richtung XOR Kanal_2) XOR Kanal_1
        THEN
            Zaehler:=Zaehler+1;
        ELSE
            Zaehler:=Zaehler-1;
        END_IF;
    ELSE
        IF (Richtung XOR Kanal_1) XOR Kanal_2
        THEN
            Zaehler:=Zaehler-1;
        ELSE
            Zaehler:=Zaehler+1;
        END_IF;
    END_IF;
END_IF;
Speicher_1:=Kanal_1;
Speicher_2:=Kanal_2;
```
Habe mehr Hilfsvariablen als eigentlich notwendig verwendet, um den Code auch ohne Kommentare verständlich zu halten.


----------



## SPSKILLER (24 Juni 2011)

Hi,

ich raffs gar nicht!
Hatte solche Teile bisher nur am Profibus. 
Da kommt der Zählwert fertig raus 
Stundenlang googeln will ich jetzt aber auch nicht...

Also mal für Noobs wie mich:

Der Ausgang wird um 1 hochgezählt wenn:
- Pos. Flanke an Kanal 1
- Kanal 2 ist dabei 0

Frage:
Was muss anstehen, wenn wieder um 1 hochgezählt werden soll???
Das gleiche?
Wen dem so ist habe ich die Signalfolge in #1 nicht kapiert!
Dort gibt es den Zustand das beide 0 haben doch gar nicht. 

Kann das mal bitte jemand erläutern?

Micha


----------



## Zottel (25 Juni 2011)

```
L     EB     0                    // Geber an A->E0.2, B->E0.0
      L     W#16#5                   // 1 an Positionen der Geberanschlüsse
      UW                                // nur Geberanschlüsse, andere Bits löschen
      T     #AZ5                        // aktueller Zustand
      L     #VZ5                        // enthält 0000 x0x0, die alten Zustände der Geberanschlüsse

      OW                                // kombiniert alte und neue Zustände. Das resultierende Byte enthält die komplette

// Das resultierende Byte enthält die komplette
// Information über vorigen und jetzigen Zustand 
// Für jede Kombination gibt es jetzt ein Sprungziel

      SPL   MEND

      SPA   M00                         // 00 00 00 00, Zustände unverändert
      SPA   M0N                         // 00 00 00 01, Spur B 0->1 bei A=0                                                                            
      SPA   M0P                         // 00 00 00 10, Spur B 1->0 bei A=0                                                                           
      SPA   M00                         // 00 00 00 11, Zustände unverändert                                                                           
      SPA   M0P                         // 00 00 01 00, Spur A 0->1 bei B=0                                                                           
      SPA   M0E                         // 00 00 01 01, Spur A 0->1 UND Spur B 0->1, Fehler!                                                                           
      SPA   M0E                         // 00 00 01 10, Spur A 0->1 UND Spur B 1->0, Fehler!                                                                           
      SPA   M0N                         // 00 00 01 11, Spur A 0->1 bei B=1                                                                           
      SPA   M0N                         // 00 00 10 00, Spur A 1->0 bei B=0
      SPA   M0E                         // 00 00 10 01, Spur A 1->0 UND Spur B 0->1, Fehler!
      SPA   M0E                         // 00 00 10 10, Spur A 1->0 UND Spur B 1->0, Fehler!
      SPA   M0P                         // 00 00 10 11, Geber 2 Spur A 1->0 bei B=1
      SPA   M00                         // 00 00 11 00, Zustände unverändert                                                                           
      SPA   M0P                         // 00 00 11 01, Geber 2 Spur B 0->1 bei A=1                                                                            
      SPA   M0N                         // 00 00 11 10, Geber 2 Spur B 1->0 bei A=1                                                                            
      SPA   M00                         // 00 00 11 11, Zustände unverändert                                                                           

MEND: SPA   M00


// Geber Fehler
M0E:  L     #errors
      L     1
      +I    
      T     #errors
      SPA   M00

M0N:  L     #count
      L     L#1
      -D    
      T     #count
      SPA   M00

M0P:  L     #count
      L     L#1
      +D    
      T     #count

// aktuellen Zustand für Flankenerkennung speichern

M00:  L     #AZ5
      SLW   1
      T     #VZ5
      BE
```
Das habe ich mal  so ähnlich auf einem Mikrocontroller programmiert. Dabei sind an dem Eingangsbyte 4 Geber angeschlossen: 31314242
Je 2 Geber werden gleichzeitig ausgewertet: 
Die Sprungtabelle hat dann 256 Einträge, für alle möglichen Bitkombinationen
nach maskieren mit L#16#55  : 01010202
werden zuerst zwei Geber bearbeitet und dann
nach maskieren mit L#16#AA  : 30304040
die anderen.
Es werden die "alten" Zustände aus dem vorigen Zyklus an die Stelle der Nullen kopiert. 
Diese Kombination dient dann zur Auswahl des Sprungziels.  

Im Fehlerfall wird nicht nur ein Fehlerbit gesetzt, sondern die Fehler werden gezählt.


----------



## Toki0604 (7 Juli 2011)

Hi @ all,

für mich ist diese Aufgabe etwas schwer verdaulich. Kann das Thema zwar grob nachvollziehen, aber nicht mitmischen...
Wann kommt denn eine Entscheidung bzw. eine neue Aufgabe?

Gruß
Toki


----------



## StructuredTrash (8 Juli 2011)

So schwierig ist das doch gar nicht. Der Geber liefert diese Signalfolge:
Kanal_1:  0  1  1  0
Kanal_2:  0  0  1  1
Von links nach rechts gelesen ist als Vorwärtsrichtung definiert, solange der Richtungseingang=False ist. Tritt eine Signalflanke auf einem der Eingänge auf, muss man mit den bisherigen Signalzuständen vergleichen, um die Zählrichtung zu bestimmen. Bei gleichzeitiger Flanke von Kanal_1 und Kanal_2 war der Geber zu schnell für den SPS-Zyklus, so dass eine Signalkombination übersprungen wurde.


----------



## LargoD (8 Juli 2011)

Die Aufgabe war doch etwas zu leicht ;-)
Deshalb haben sich nur wenige herausgefordert gefühlt
Bringen wir die Runde also zu Ende.
Die Lösung von Zottel zeigt eine etwas ungewöhnliche, aber sehr schöne Anwendung des SPL-Befehls, die außerdem
zu extrem kurzer Laufzeit führt, weil immer nur ein kleiner Teil des Codes ausgeführt wird.
Sie ist deshalb mein Kandidat.
Am Samstag Abend ist Ende der Abstimmung.

Um die Fragen nach der Auswertelogik zu erklären, habe ich das Verfahren mal angehängt.
(StructuredTrash hat das in Hochsprache abgeliefert)
Mein Code ist bewusst nicht optimiert, damit man in FUP die schöne Symetrie der Vierfach-Auswertung erkennen kann.
Also, nach dem Einlesen in FUP umschalten.


```
FUNCTION_BLOCK "VR_Logik"
TITLE =Vorwärts/Rückwärts-Logik in FUP
VERSION : 0.1


VAR_INPUT
  Kanal_1 : BOOL ;    //Kanal 1 vom Geber
  Kanal_2 : BOOL ;    //Kanal 2 vom Geber
  Richtung : BOOL ;    //Bei 1 Zählrichtungen umkehren
END_VAR
VAR_OUTPUT
  Zaehler : DINT ;    //Zählerwert
  Fehler : BOOL ;    //Fehler, Kanäle nicht plausibel
END_VAR
VAR
  Kanal_1_old : BOOL ;    //Kanal 1 vom letzten Zyklus
  Kanal_2_old : BOOL ;    //Kanal 2 vom letzten Zyklus
END_VAR
VAR_TEMP
  K1_Flanke : BOOL ;    //Flanke auf Kanal 1
  K2_Flanke : BOOL ;    //Flanke auf Kanal 2
END_VAR
BEGIN
NETWORK
TITLE =Flanke auf Kanal 1

      U     #Kanal_1;
      =     L      1.0;
      X     #Kanal_1_old;
      X     L      1.0;
      =     #K1_Flanke;
      U     L      1.0;
      BLD   102;
      =     #Kanal_1_old;
NETWORK
TITLE =Flanke auf Kanal 2

      U     #Kanal_2;
      =     L      1.0;
      X     #Kanal_2_old;
      X     L      1.0;
      =     #K2_Flanke;
      U     L      1.0;
      BLD   102;
      =     #Kanal_2_old;
NETWORK
TITLE =Fehlererkennung

      U     #K1_Flanke;
      U     #K2_Flanke;
      =     #Fehler;
NETWORK
TITLE =Aufwärts zählen, vierfach Auswertung

      U(    ;
      U     #K1_Flanke;
      U(    ;
      X     #Kanal_1;
      X     #Kanal_2;
      X     #Richtung;
      )     ;
      O     ;
      U     #K2_Flanke;
      U(    ;
      U(    ;
      X     #Kanal_1;
      X     #Kanal_2;
      X     #Richtung;
      )     ;
      NOT   ;
      )     ;
      )     ;
      UN    #Fehler;
      SPBNB _001;
      L     #Zaehler;
      L     1;
      +D    ;
      T     #Zaehler;
_001: NOP   0;
NETWORK
TITLE =Abwärts zählen, vierfach Auswertung

      U(    ;
      U     #K1_Flanke;
      U(    ;
      U(    ;
      X     #Kanal_1;
      X     #Kanal_2;
      X     #Richtung;
      )     ;
      NOT   ;
      )     ;
      O     ;
      U     #K2_Flanke;
      U(    ;
      X     #Kanal_1;
      X     #Kanal_2;
      X     #Richtung;
      )     ;
      )     ;
      UN    #Fehler;
      SPBNB _002;
      L     #Zaehler;
      L     1;
      -D    ;
      T     #Zaehler;
_002: NOP   0;
END_FUNCTION_BLOCK
```
Gruß aus Hessen
Erich


----------



## LargoD (13 Juli 2011)

Für mein langes Nicht-Schreiben muss ich mich entschuldigen. Ein Projekt ging vor, direkt nach dem Urlaub.
Da keine weiteren Meinungen eingegangen sind, entscheide ich einfach nach abgegebenen <DANKE>.
Damit ist Zottel der Gewinner und darf die nächste Aufgabe stellen.

Gruß aus Hessen
Erich


----------



## Onkel Dagobert (20 Juli 2011)

Was ist denn eigentlich aus dem Programmierwettbewerb geworden? Ich habe aus privaten Gründen hier im Forum leider ein bisschen den Faden verloren. Geht das irgendwo weiter?

Gruß, Onkel


----------



## Lipperlandstern (20 Juli 2011)

LargoD schrieb:


> Damit ist Zottel der Gewinner und darf die nächste Aufgabe stellen.
> 
> Gruß aus Hessen
> Erich


 

Alle warten auf Zottel


----------



## stift (2 August 2011)

ganz coole aufgaben gibts übrigens hier: 
http://projecteuler.net/index.php?section=problems

1 bis 14 hab ich in c gelöst... 
das ganze ist halt sps-technisch wenig interessant - wär aber ne herausvorderung - vor allem in fup


----------



## Perfektionist (2 August 2011)

hmm, ist denn keiner auf die Idee gekommen, dass auch Doppelschrittzählung möglich ist, wenn beide Eingänge ihren Zustand ändern? Wenn man unterstellt, dass sich die Drehgeschwindigkeit zwischen zwei Auslesezyklen nicht massiv verändern kann und zudem auch die Abtastung einem Maximalzyklus unterliegt (ohne diese Annahme gehts ohnehin nie), so kann man anhand der zuvor ermittelten Drehrichtung auch in Zweierschritten weiterzählen, wenn sich beide Eingänge im gleichen Zyklus ändern.

Soweit bin ich in der Praxis bereits gegangen.

Wenn nun die Zykluszeit des Auswerteprozesses hinreichend stabil ist und die Abtastung wenig Jitter aufweist, kann man die Sache noch weiter stricken:

vom Stillstand ausgehend ermittelt man zuersteinmal die Drehrichtung. Während Einzelschrittzählung können folgende Ereignisse auftreten: Übergang zu Stillstand (weil Drehung langsam) oder Übergang zu Doppelschritten, weil Bewegung schneller wird als die Abtastfähigkeit (Zykluszeit) der Auswerteeinheit. Während Doppelschrittzählung können folgende Übergänge passieren: Übergang zu Einzelschritten, weil Drehung langsamer wird. Oder Übergang zu Dreischrittzählung, die zwar scheinbar rückwärts läuft, aber aufgrund des Wissens darüber, dass zuvor eine Doppelschrittzählung erfolgte, diese scheinbare Rückwärtsbewegung als tatsächlich dreifache Vorwärtsbewegung gewertet werden kann. usw ...

Ein Fehler wird angezeigt, wenn eine Schrittigkeit übersprungen wird. Also z.B. eine Vierschrittzählung direkt in eine Sechsschrittzählung übergeht, die ja genausogut eine Zweischrittzählung sein könnte.


----------

