Bekomme FB_IOLRead nicht zum laufen.

Robi123

Level-2
Beiträge
41
Reaktionspunkte
6
Hallo zusammen,
habe mir aktuell einen Baustein um den FB_IOLRead gebaut um diesen einfacher verwenden zu können. Das Selbe habe ich bereits für den FB_IOLWrite gemacht, da klappt soweit alles. Leider funktioniert es mit dem Read gar nicht. Egal was ich mache, es kommt immer "ADSERR_DEVICE_INVALIDPARM" als Fehler. Die Parameter sind dabei fast 100% identisch zum schreiben. Ich will zum testen erstmal nur lesen, was ich gerade geschrieben habe. Hat jemand ne idee was ich falsch mache?
Hey guys,

Code:
METHOD FB_Init: BOOL
VAR_INPUT
    bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
    bInCopyCode: BOOL;  // TRUE: the instance will be copied to the copy code afterward (online change)
    // IO-Link specific inputs
    _amsnetid_EL6224 : I_AMSNETID_Input;
    _connected_IOLink_port : E_IOLPort;
END_VAR

amsnetid_EL6224 := _amsnetid_EL6224;
connected_IOLink_port := _connected_IOLink_port;

FUNCTION_BLOCK ABSTRACT FB_IOLink_Base
VAR
    // IO-Link Terminal specific values
    amsnetid_EL6224 : I_AMSNETID_Input;
    connected_IOLink_port : E_IOLPort;

    // Reading
    IOLink_read : FB_IOLRead;
    eading_state : (IDLE, READ, WAIT_FOR_SUCCESSFUL_READ);
    execute_read : R_TRIG;
END_VAR

METHOD Read_IOLink_BYTE_Parameters : BYTE
VAR_INPUT
    execute : BOOL := FALSE; // Must be set TRUE to write parameters
    IOLink_index : WORD; // Provide the processed data as dec
    IOLink_subindex : BYTE; // Provide subindex fitting to index
END_VAR
VAR
    IOLink_value : BYTE; // Provide the actual value that should be written
END_VAR

Read_IOLink_BYTE_Parameters := 0;

execute_read(CLK:=execute);
IF execute_read.Q THEN
    reading_state := READ;
END_IF

CASE reading_state OF
    IDLE:
        // Do nothing as long there is no edge on execute

    READ:
        // Do the actual reading with the provided input parameters
        //IOLink_read(bExecute:=FALSE);
        IOLink_read.bExecute:=FALSE;
        IOLink_read.sNetId := F_CreateAmsNetId(amsnetid_EL6224.State);
        IOLink_read.nIolPort := connected_IOLink_port;
        IOLink_read.pDSTBuf := ADR(IOLink_value);
        IOLink_read.cbBufLen := SIZEOF(IOLink_value);
        IOLink_read.nIndex := 16#128;
        //IOLink_read.nIndex := IOLink_index;
        IOLink_read.nSubindex := 0;
        //IOLink_read.nSubindex := IOLink_subindex;
        IOLink_read.bExecute := TRUE;

        reading_state := WAIT_FOR_SUCCESSFUL_READ;

    WAIT_FOR_SUCCESSFUL_READ:
        IF IOLink_read.bDone THEN
            IOLink_read.bExecute := FALSE;
            reading_state := IDLE;
            Read_IOLink_BYTE_Parameters := IOLink_value;// Signal success by returning TRUE
        END_IF
END_CASE

IOLink_read();

Stehe hier etwas auf dem Schlauch
 
Der Iolink_read Baustein wird nur für einen Zyklus aufgerufen.ich würde diesen Baustein außerhalb der case schleife aufrufen
 
Ich würde mir mal diese Zeile näher ansehen, die ergibt für mich keinen Sinn:

Code:
IOLink_read.sNetId := F_CreateAmsNetId(amsnetid_EL6224.State);

Ich kenne zwar den Datentyp I_AMSNETID_Input nicht, aber die Eigenschaft State als AMS-NetId zu nutzen erscheint mir suspekt.

Ein paar Tipps:
Der Code

Code:
execute_read(CLK:=execute);
IF execute_read.Q THEN
    reading_state := READ;
END_IF

führt dazu, dass die Schrittkette unkontrolliert durch die Variable execute zurückgesetzt werden kann, auch wenn das aktuelle Lesen noch nicht zu Ende ist.

Besser wäre
Code:
execute_read(CLK:=execute AND_THEN reading_state = IDLE);
IF execute_read.Q THEN
    reading_state := READ;
END_IF

Oder noch besser, wenn Du ein richtiges Handshake baust mit

Code:
IDLE:
    IF excecute THEN    
        reading_state := READ;
    END_IF

........

WAIT_FOR_SUCCESSFUL_READ:
        IF IOLink_read.bDone THEN
            IOLink_read.bExecute := FALSE;
            reading_state := WAIT_FOR_RELEASE_EXCECUTE;
            Read_IOLink_BYTE_Parameters := IOLink_value;// Signal success by returning TRUE
        END_IF
WAIT_FOR_RELEASE_EXCECUTE:    
        IF NOT excecute THEN    
            reading_state := IDLE;
        END_IF

Und lässt dann das Flanken-gedöns weg.

Und dann kannst Du noch überlegen, ob Du die AMS-Adresse wirklich über die Bausteinschnittstelle holen muss. Die kannst Du auch als PDI-Variable deklarieren und direkt mit der Hardware verknüpfen:

Code:
    stI_AmsAdr                    AT%I*    : AMSADDR;

und dann hier dran:

1780225935825.png
Das spart einiges an Code.
 
Zuletzt bearbeitet:
Ich würde mir mal diese Zeile näher ansehen, die ergibt für mich keinen Sinn:

Code:
IOLink_read.sNetId := F_CreateAmsNetId(amsnetid_EL6224.State);

Ich kenne zwar den Datentyp I_AMSNETID_Input nicht, aber die Eigenschaft State als AMS-NetId zu nutzen erscheint mir suspekt.
Wir nutzen für alle Variablen die gemapped werden können eine art Hardware Abstraction Layer. Für die AMSNETID sieht das so aus:

Code:
FUNCTION_BLOCK FB_AMSNETID_Input_Channel IMPLEMENTS I_AMSNETID_Input
VAR
    physical_input AT %I* : AMSNETID;
END_VAR

PROPERTY
VAR
END_VAR
State := physical_input;

Dadurch haben wir sehr flexibels Handling im code später. Ich gebe dir aber recht bei der AMSNETID ist State vielleicht nicht der perfekte Begriff, da dass aber bei allen anderen Ein-/Ausgängen gut passt habe ich das einfach übernommen.

Hier steckt aber der Wurm nicht drin. Der Write Baustein funktioniert ja schon, da ist die Umsetzung genau gleich, ich habe das ganze aber trotzdem auch schon probiert, indem ich die AMSNETID hart reingeschrieben habe.
Ich habe ansonsten aber echt kein Plan woran es liegen könnte, das ganze ist eigentlich zu simpel um nicht zu funktionieren. Ist aber nicht so schlimm, wäre eher ein nice to have gewesen.

Besser wäre
Code:
execute_read(CLK:=execute AND_THEN reading_state = IDLE);
IF execute_read.Q THEN
    reading_state := READ;
END_IF

Das gefällt mir, werde ich so umsetzen.

Oder noch besser, wenn Du ein richtiges Handshake baust mit

Code:
IDLE:
    IF excecute THEN   
        reading_state := READ;
    END_IF

........

WAIT_FOR_SUCCESSFUL_READ:
        IF IOLink_read.bDone THEN
            IOLink_read.bExecute := FALSE;
            reading_state := WAIT_FOR_RELEASE_EXCECUTE;
            Read_IOLink_BYTE_Parameters := IOLink_value;// Signal success by returning TRUE
        END_IF
WAIT_FOR_RELEASE_EXCECUTE:   
        IF NOT excecute THEN   
            reading_state := IDLE;
        END_IF

Und lässt dann das Flanken-gedöns weg.
Auch eine schöne Lösung.

Und dann kannst Du noch überlegen, ob Du die AMS-Adresse wirklich über die Bausteinschnittstelle holen muss. Die kannst Du auch als PDI-Variable deklarieren und direkt mit der Hardware verknüpfen:

Code:
    stI_AmsAdr                    AT%I*    : AMSADDR;

und dann hier dran:

Anhang anzeigen 96214
Das spart einiges an Code.
Das mache ich quasi auch so nur eben wird nicht direkt gemapped
 
Was für eine Hardware spricht Du denn an? Es ist denkbar, dass dieser Parameter vielleicht nur writeonly ist. Was sag die Dokumentation? Oder passt der Datentyp von der Größe her nicht - aber der IO-Link-Master sendet trotzdem ADSERR_DEVICE_INVALIDPARM statt ADSERR_DEVICE_INVALIDSIZE... Das würde dazu passen, das nur Schreiben geht. Kleiner in größer geht aber größer in kleiner nicht, weil kein Platz.
Hast Du mal sicher lesbare Parameter, z.B. Index 16#10 / Subindex 0 probiert?

Das mit dem Hardware Abstraction Layer würde ich euch ausreden. Weil das macht schon der System-Manager und ihr macht euch damit nur unnötige Arbeit. Das ist nämlich schon seit Jahrzehnten der Vorsprung von TwinCAT gegenüber allen anderen SPS-Systemen.
 
Was für eine Hardware spricht Du denn an? Es ist denkbar, dass dieser Parameter vielleicht nur writeonly ist. Was sag die Dokumentation? Oder passt der Datentyp von der Größe her nicht - aber der IO-Link-Master sendet trotzdem ADSERR_DEVICE_INVALIDPARM statt ADSERR_DEVICE_INVALIDSIZE... Das würde dazu passen, das nur Schreiben geht. Kleiner in größer geht aber größer in kleiner nicht, weil kein Platz.
Hast Du mal sicher lesbare Parameter, z.B. Index 16#10 / Subindex 0 probiert?

Das mit dem Hardware Abstraction Layer würde ich euch ausreden. Weil das macht schon der System-Manager und ihr macht euch damit nur unnötige Arbeit. Das ist nämlich schon seit Jahrzehnten der Vorsprung von TwinCAT gegenüber allen anderen SPS-Systemen.
Es handelt sich um einen Sick Farbsensor. Hier speziell der Wert hinter index 296, Measurements Color Space. Im PDF dazu steht jedenfalls read/write.
Über das im IO-Link Reiter verfügbare Feld habe ich den Index auch nicht auslesen können. Vielleicht ist die Doku falsch und er ist doch nur write only.
Andere Indexe, aber nicht alle konnte ich darüber lesen.

Das mit dem HAL hat sich eher daraus ergeben, dass es für uns ohne ungeschickt war. Wir kommen damit bisher super zurecht.
 
Ist dieser Parameter irgendwo im CoE-Verzeichnis aufgelistet? Wie sieht der online aus. Kannst Du da mal einen Screenshot machen?
Wenn Du aber im IO-Link-Reiter auch Probleme hast, dann scheint das sich um Kompatibilitätsprobleme zu handeln. Eventuell gibt es eine aktualisierte Konfigurationsdatei.
 
Zuletzt bearbeitet:
Ich hab gerade mal nachgeschaut, wie ich es denn mache:

Gibt ja mehrere Bibliotheken und Bausteine. Ich nutze den FB_IolStdVarSlave aus der Tc3_IoLink:
1780297594596.png

dem muss man natürlich die NetId und Port setzen:
1780297660469.png

kann dann aber recht komfortabel Werte abfragen (hier auch von einem Sick-Sensor):
1780297694998.png

Was mir aber aufgefallen ist:
Ich habe die AoeNetId verknüpft, nicht die AdsAdrr:
1780297751990.png
Was der Unterschied zwischen den beiden ist kann ich dir so auf Anhieb nicht sagen, Experten wie asci25 können uns da sicher weiterhelfen.

Vorteil von diesem Baustein wäre, dass er direkt Methoden hat um Standardparameter zu lesen, wie z.B. die Seriennummer. Damit kannst du schon mal prüfen, ob die Kommunikation grundsätzlich geht.
 
Ich hab gerade mal nachgeschaut, wie ich es denn mache:

Gibt ja mehrere Bibliotheken und Bausteine. Ich nutze den FB_IolStdVarSlave aus der Tc3_IoLink:
Anhang anzeigen 96220

dem muss man natürlich die NetId und Port setzen:
Anhang anzeigen 96221

kann dann aber recht komfortabel Werte abfragen (hier auch von einem Sick-Sensor):
Anhang anzeigen 96222

Was mir aber aufgefallen ist:
Ich habe die AoeNetId verknüpft, nicht die AdsAdrr:
Anhang anzeigen 96223
Was der Unterschied zwischen den beiden ist kann ich dir so auf Anhieb nicht sagen, Experten wie asci25 können uns da sicher weiterhelfen.

Vorteil von diesem Baustein wäre, dass er direkt Methoden hat um Standardparameter zu lesen, wie z.B. die Seriennummer. Damit kannst du schon mal prüfen, ob die Kommunikation grundsätzlich geht.
Das werde ich bei nächster Gelgenheit auf jeden Fall mal testen. Hab jetzt leider keine Zeit mehr weiter zu suchen und da das erstmal nur nice to have ist auch nicht schlimm. Ggf. melde ich mich dann zu einem späteren Zeitpunkt hier nochmal.
 
Ansatz zur Fehlersuche vom anderen Ende: AmsMonitor mitlaufen lassen und die Dataen im Read und den Write-Zugriff abgleichen.
Ein Klassiker wäre dann noch das Litte-Big-Endian-Spiel wobei das bei dir eigentlich nicht der Fall sein sollte. Du schreibst ja nur 1 Byte.
ABER... wenn es auf Sick-Ebene doch ein Wort-wäre....

AmsNetID: Adresse des Slaves, generalisiert d.h. Typ-unspezifisch.
AoE: ApplicationOverEtherCAT: Du realisiert etwas Typspezifisches. Wird bei spezielleren Klemmen genutzt um dort spezielle Parameter zu orchestrieren. Gibt es bei vielen Gateway-Klemmen um auf die Parameter der hintere Seite des Gateways zuzugreifen.
 
Zurück
Oben