TIA Ringpuffer in SCL mit Zeigern programmieren

Wurzel2

Level-1
Beiträge
16
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,


Ich programmiere in SCL mit TIA und möchte auf einen Ringpuffer mit Hilfe von Zeiigern zugreifen. Aus C kenne ich es, dass man Zeiger erstellen kann, die auf eine Struktur zeigen.

Ich habe zum Beispiel folgende Struktur, aus der ich mir einen Array mit z.B. 15 Elementen mache:

Code:
typedef struct bsp_struct
{    
    struct bsp_struct *forward_ptr;
    int messwert;
    int mess_nr;
}Bsp_Struct;

Dann erstelle ich mir von dieser Struktur einen Pointer

Code:
Bsp_Struct *struct_ptr;


Nun kann ich dem struct_ptr eine Adresse zuweisen, wo der Ringpuffer anfangen soll.

Code:
struct_ptr = (Bsp_Struct*)LIST_ORIGIN;


Nachdem ich dann meinen Ringpuffer initialisiert habe, kann ich mit Hilfe des Pfeil-Operators (->) auf die verschieden Elemente der Struktur zugreifen, also z.B.:

Code:
struct_ptr->messwert = 42;
struct_ptr->mess_nr = 2;

Wenn ich auf das nächste Element des Ringpuffers zugreifen will, lasse ich den struct pointer auf das nächste element zeigen, usw.

Code:
struct_ptr = struct_ptr->forward_ptr;


Gibt es bei Siemens auch die Möglichkeit in einer ähnlichen Art und Weise auf die Elemente einer Struct in einem Ringpuffer zuzugreifen oder geht das nur mit Offsets? Bei größeren Strukturen wird das nämlich schnell mal unübersichtlicht.
 
klar geht das. ich hab mal was getippt um einen Fehler zu finden.

Code:
FUNCTION_BLOCK "FB_DiagTuer" 

// In diesem FB werden Signale und Zustände mitgeloggt, die dazu
// führen könnten, dass die Hubtür geöffnet oder geschlossen wird.


// Bausteinparameter
VAR_INPUT
    // Eingangsparameter
    E_Tuer_offen : BOOL; // Endlage Tür offen
    E_Tuer_geschl : BOOL; // Endlage Tür geschlossen
    A_Tuer_oeffnen : BOOL; // Ansteuerung Tür öffnen
    A_Tuer_schliesen : BOOL; // Endlage Tür schließen
    Schrittnummer : INT;
    LS_Teil_vorhanden : BOOL;
    FIFO_P1_belegt : BOOL;
    FIFO_P2_belegt : BOOL;
    FIFO_P3_belegt : BOOL;
    FIFO_P4_belegt : BOOL;
    FIFO_P5_belegt : BOOL;
    FIFO_P6_belegt : BOOL;
    FIFO_Lack_Begonngen : BOOL;
    FIFO_Trocknung_iO : BOOL;
    Leerpos_takten : BOOL;
    Schaltleiste_betaetigt : BOOL;
    Tisch_takten : BOOL;
    Anzahl_Lackzyklen : int;
    Soll_Lackzyklen : int;
END_VAR

VAR_IN_OUT
    // Durchgangsparameter
END_VAR

VAR_OUTPUT
    // Ausgangsparameter

END_VAR
 
VAR_TEMP
    // temporäre Variablen
    tmp_int : INT;
END_VAR
VAR
    // statische Variablen
    FHV_Tuer_offen : BOOL;
    FHV_Tuer_geschl : BOOL;
    Zeiger_oeffnen : INT;
    Zeiger_schliesen : INT;
    Puffer_oeffnen : ARRAY [1..500] OF STRUCT
        Zeitstempel : DT;
        Schrittnummer : INT;
        A_Tuer_oeffnen : BOOL;
        A_Tuer_schliesen : BOOL;
        LS_Teil_vorhanden : BOOL;
        FIFO_P1_belegt : BOOL;
        FIFO_P2_belegt : BOOL;
        FIFO_P3_belegt : BOOL;
        FIFO_P4_belegt : BOOL;
        FIFO_P5_belegt : BOOL;
        FIFO_P6_belegt : BOOL;
        FIFO_Lack_Begonngen : BOOL;
        FIFO_Trocknung_iO : BOOL;
        Leerpos_takten : BOOL;
        Schaltleiste_betaetigt : BOOL;
        Anzahl_Lackzyklen : BYTE;
        Soll_Lackzyklen : BYTE;
    END_STRUCT;
    Puffer_schliesen : ARRAY[1..500] OF STRUCT
        Zeitstempel : DT;
        Schrittnummer : INT;
        A_Tuer_schliesen : BOOL;
        A_Tuer_oeffnen : BOOL;
        Tisch_takten : BOOL;
    END_STRUCT;
    
END_VAR

    // Zeiger Bereichsüberwachung
    IF Zeiger_oeffnen < 1 OR Zeiger_oeffnen > 500 THEN
        Zeiger_oeffnen := 1;
    END_IF;
    
    // Daten in Ringpuffer schreiben wenn Tür geöffnet wird
    IF NOT E_Tuer_geschl AND FHV_Tuer_geschl THEN
        tmp_int := READ_CLK(CDT := Puffer_oeffnen[Zeiger_oeffnen].Zeitstempel);
        Puffer_oeffnen[Zeiger_oeffnen].Schrittnummer := Schrittnummer;
        Puffer_oeffnen[Zeiger_oeffnen].A_Tuer_schliesen := A_Tuer_schliesen;  
        Puffer_oeffnen[Zeiger_oeffnen].A_Tuer_oeffnen := A_Tuer_oeffnen;  
        Puffer_oeffnen[Zeiger_oeffnen].LS_Teil_vorhanden := LS_Teil_vorhanden;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P1_belegt := FIFO_P1_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P2_belegt := FIFO_P2_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P3_belegt := FIFO_P3_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P4_belegt := FIFO_P4_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P5_belegt := FIFO_P5_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_P6_belegt := FIFO_P6_belegt;  
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_Lack_Begonngen := FIFO_Lack_Begonngen;
        Puffer_oeffnen[Zeiger_oeffnen].FIFO_Trocknung_iO := FIFO_Trocknung_iO;  
        Puffer_oeffnen[Zeiger_oeffnen].Leerpos_takten := Leerpos_takten;  
        Puffer_oeffnen[Zeiger_oeffnen].Schaltleiste_betaetigt := Schaltleiste_betaetigt;
        Puffer_oeffnen[Zeiger_oeffnen].Anzahl_Lackzyklen := INT_TO_BYTE(Anzahl_Lackzyklen);
        Puffer_oeffnen[Zeiger_oeffnen].Soll_Lackzyklen := INT_TO_BYTE(Soll_Lackzyklen);
         // Zeiger inkementieren
        Zeiger_oeffnen := Zeiger_oeffnen + 1;
    END_IF;
    // Flankenhilfsvariable sichern
    FHV_Tuer_geschl := E_Tuer_geschl;
    
        // Zeiger Bereichsüberwachung
    IF Zeiger_schliesen < 1 OR Zeiger_schliesen > 500 THEN
        Zeiger_schliesen := 1;
    END_IF;
    
    // Daten in Ringpuffer schreiben wenn Tür geöffnet wird
    IF NOT E_Tuer_offen AND FHV_Tuer_offen THEN
        tmp_int := READ_CLK(CDT := Puffer_schliesen[Zeiger_schliesen].Zeitstempel);
        Puffer_schliesen[Zeiger_schliesen].Schrittnummer := Schrittnummer;
        Puffer_schliesen[Zeiger_schliesen].A_Tuer_schliesen := A_Tuer_schliesen;  
        Puffer_schliesen[Zeiger_schliesen].A_Tuer_oeffnen := A_Tuer_oeffnen;  
        Puffer_schliesen[Zeiger_schliesen].Tisch_takten := Tisch_takten;  
         // Zeiger inkementieren
        Zeiger_schliesen := Zeiger_schliesen + 1;
    END_IF;
    // Flankenhilfsvariable sichern
    FHV_Tuer_offen := E_Tuer_offen;

END_FUNCTION_BLOCK

Kannst dir ja mal anschauen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Wurzel2,

das was Du da in C hast ist kein Ringpuffer sondern eine verkettete Liste.
Das sind zwei wesentlich verschiedene Varianten zum Speichern von Daten.
Welche meinst Du tatsächlich?


Datenstrukturen, welche dynamisch ihre Größe ändern, sind in SPS eher unerwünscht und werden in SCL so gut wie nicht unterstützt und sind nur mit hohem Aufwand "hintenrum" programmierbar. Typisierte Zeiger wie in C gibt es nicht. Auf Elemente einer Struktur mit dynamischer Anfangsadresse symbolisch zugreifen geht nur durch Umkopieren des Speicherbereiches auf eine Struktur mit beim Compilieren bekannter Anfangsadresse.

Harald
 
Hi,
danke für die Antworten. Schade, dass es typisierte Zeiger nicht gibt. Das wollte ich wissen.
Was ich im Prinzip haben will, ist ein Array mit einer festen Anzahl an Elementen, die nacheinander beschrieben werden sollen. Wenn das letzte Element des Arrays beschrieben wurde, soll als nächstes das erste Element wieder beschrieben werden, usw. Momentan mache ich das so ähnlich wie in Aventinus' Vorschlag. Nur wäre es für mich praktisch über einen Pointer auf die Daten zuzugreifen und nicht über den Index. So könnte ich mir einiges an Umkopieren von Daten sparen. Aber gut, wenn es das nicht gibt, dann muss ich wohl so auskommen
 
Zuletzt bearbeitet:
Pointer gibt es in Step 7 schon. Aber wie PN/DP schrieb keine verkettete Listen wie in c. Dynamische Array gibt es in Step 7 auch nicht. Daher muss die Größe der betreffenden Struktur vorgegeben sein. Daher ist es eigentlich egal ob man dann über Index oder Pointer arbeitet. Das einzige was man machen kann ist einen neuen DB erzeugen zur Datenspeicherung durch das SPS Programm.
Aber das habe ich in der Praxis noch nicht gesehen. Wenn halt dann wieder jemand an der Anlage arbeitet hat der halt Unterschiede beim Offline/ Online Bausteinvergleich.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi

es gibt in der kleinen Welt der Simatic keine Zeiger und keine dynamische Speicherverwaltung. Kein new, Kein delete. Aber so wie Aventinus vorschlägt, mache ein Feld und verwende die Indices wie Zeiger.

Jetzt gibt es noch einen Freiheitsgrad. Legst du das Feld statisch im FB an oder global in einem DB?

Statisch im FB hat den Vorteil, dass du davon mehrere Instanzen haben kannst und jede für sich im Ring herum rotiert. Dadurch, dass du alle Zugriffe im FB kapselst, kommt der Puffer nicht so leicht durcheinander. Nachteil, du musst die Struktur rein/raus kopieren, was entsprechend Zeit kostet, wenn die Struktur groß ist.

Global im DB hat den Vorteil, dass du jetzt von überall in den Puffer langen kannst, es muss nicht mehr kopiert werden. Aber jetzt wird es schwierig mehrere Puffer zu verwalten. Das erfordert Konvention und noch mehr Disziplin -- geht also in einem Projekt wo mehrere dran arbeiten praktisch schief.

So Wurzel2 und jetzt kommt es drauf an: Welche CPU soll denn verwendet werden. Bei AS300/400 gibt es so gruselige Sachen wie POINTER und ANY. Mit denen kann ein AWL-Freak tausend tolle Kunststücke machen ... durch brennende Reifen springen und gleichzeitig dem Baby die Windeln wechseln. Bei 1200/1500 kommt statt dessen VARIANT. Auch ein ganz fürchterliches Ding. Auf der einen Seite kann man mit VARIANT mehr machen als mit ANY, auf der anderen Seite darf man es nicht global ablegen.

Der POINTER ist ein Zeiger, der nicht weiß auf was er zeigt. Also in C sowas wie void*.
Der ANY ist ein Zeiger, der manchmal weiß auf was er zeigt. Versorgt man einen ANY mit einem ARRAY[ 1..99 ] of BYTE, dann weiß er, dass er auf 99 Bytes zeigt. Aber gerade bei Strukturen vergisst er den Strukturtyp. Versorgt man einen ANY mit einem ARRAY[ 1..99 ] of UDT21, dann weiß er, dass er auf n Bytes zeigt. Wobei n := 99*sizeof(UDT21).
Der VARIANT ist ein Zeiger, der genau weiß worauf er zeigt. Aber das nutzt so wenig, denn so richtig damit umgehen lassen sie uns nicht. Statt eine vernünftigen Syntax, die sauber zwischen Referenz (C: &) und Dereferenz (C: *) unterscheidet muss man mit einem seltsamen Sack von Funktionen hantieren (die allerdings alle keine Bausteinaufrufe sind sondern vom Compiler in Befehlsfolgen abgebildet werden) und manches funktioniert irritierend implizit.

Wo soll die Reise denn hingehen?

'n schön' Tach auch
HB
 
Zurück
Oben