Step 7 AWL mit Zähler und Pointer in DB schreiben

tommylik

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

Hier ein Screenshot wie es in KOP aussehen würde.
Die Adressen habe ich wegen der Einfachheit extra so ausgewählt.

Anhang anzeigen 48779

Der Zähler kann bis 30 variieren. Das würde sehr lange Netzwerke geben.
Mit einer steigenden Flanke vom E13.4 soll der Wert im MD 600 (enthält immer eine andere 6 stellige Zahl)
in den DB3 kopiert werden.

Ist_Zaehler = 1 --> MD600 --> DB3.DBD0
Ist_Zaehler = 2 --> MD600 --> DB3.DBD4
usw.

Das geht bestimmt mit einem Pointer.
Ich habe da auch schon mal was Angefangen aber nach dem AUF DB [#DB_Nr] weiß ich nicht weiter.


Code:
 L     P##Zeiger_DB

      LAR1  
      L     W [AR1,P#0.0]
      T     #DB_Nr                      // Speichern im Adressregister
      L     D [AR1,P#2.0]
      LAR1  

      U     #E13.4
      SPBN  ende

      L     #zaehler
      L     4                           //da Doppelwortzugriff
      *I                                //akku enthält nun die adresse
      SLD   3                           //pointer erzeugen
      LAR1  

      AUF   DB [#DB_Nr]                 // Öffne DB

Wie in den DB schreiben da hackt es bei mir jetzt


 L     #zaehler
      L     1
      +I    
      T     #zaehler

      L     #zaehler
      L     #Anzahl                     // deine Länge
      ==I   
      SPBN  ende

      L     0
      T     #zaehler

ende: NOP   0

Könnte mir einer von Euch sagen wie das Aussehen müsste?

Ich wäre sehr Dankbar dafür.

Mfg Tom
 
Hallo

Dein Anhang funktioniert nicht. Wir können das Bild nicht sehen. Kannst Du das Bild nochmal anhängen oder beschreiben was da drauf zu sehen ist?

Was ist #Zeiger_DB? Ein Baustein-Input? Was hat der mit Deiner Aufgabe zu tun? Warum entnimmst Du die DB-Nummer aus dem Pointer, wenn Du doch immer in DB3 schreiben willst?
Vergiss nicht daß Du nur bei steigender Flanke des E13.4 schreiben wolltest.

Code:
      L     #zaehler
      L     4
      *I
      SLD   3
      LAR1  

      AUF   DB [#DB_Nr]                 //oder: AUF DB 3

      L     MD600
      T     DBD [AR1,P#0.0]             //bereichsintern indirekt in DB schreiben

siehe Hilfe zu AWL > Index > Indirekte Adressierung

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

Vielen Dank für deine Antwort.

Hier nochmal das Bild.

Sps_Screenshot.JPG


Ja der Code ist sehr schlecht und ich bekomme es nicht hin den Pointer
wirklich zu verstehen. Es ist viel copy and Paste um ans Ziel zu kommen.

Ich wollte eigentlich das dann in einen FC packen und den Eingang, MB, Zähler und die DB Nr. von außen an parametrieren.
Ich dachte deshalb brauche ich das. (#Zeiger_DB = Input Pointer )

Vielen Dank noch mal

Mfg Tom
 
Ich wollte eigentlich das dann in einen FC packen und den Eingang, MB, Zähler und die DB Nr. von außen an parametrieren.
Du kannst die DB-Nummer als Block_DB übergeben - das ist gut für die Referenzdaten.
Code:
                     +-----------------+
  E13.4    M13.4     | "FC_MOVE_in_DB" |
---| |------(P)------|EN            ENO|-
                     |                 |
       "Ist_Zaehler"-|Zaehler          |
                     |                 |
                 DB3-|DBNr             |
                     |                 |
               MD600-|diWert           |
                     +-----------------+
oder nicht so schön die DB-Nummer als Zahl (Int) übergeben - dann ist in den Referenzdaten nicht zu sehen in welchen DB geschrieben wird
Code:
                     +-----------------+
  E13.4    M13.4     | "FC_MOVE_in_DB" |
---| |------(P)------|EN            ENO|-
                     |                 |
       "Ist_Zaehler"-|Zaehler          |
                     |                 |
                   3-|DBNr             |
                     |                 |
               MD600-|diWert           |
                     +-----------------+
Die Flankenerkennung am einfachsten außerhalb des FC machen, weil ein FC kann sich nichts merken bis zum nächsten Aufruf, müsste sich aber für jeden Aufruf getrennt den Flankenmerker merken, falls er auch die Flankenerkennung machen soll (oder per IN_OUT noch zusätzlich den Flankenmerker übergeben).

Code:
      L     30
      L     #Zaehler
      >=D   
      SPB   M001
      CLR                   [COLOR="#008000"]//unzulässig! Zaehler ist < 0 oder > 30[/COLOR]
      SAVE                  [COLOR="#008000"]//ENO = FALSE zurückmelden[/COLOR]
      BE                    [COLOR="#008000"]//beenden ohne schreiben[/COLOR]

M001: L     4
      *I    
      SLD   3
      LAR1  

[COLOR="#008000"]//Variante 1: Input DBNr als Block_DB - besser für Referenzdaten[/COLOR]
      AUF   #DBNr

[COLOR="#008000"]//oder Variante 2: Input DBNr als Int[/COLOR]
      L     #DBNr
      T     #temp_DBNr
      AUF   DB [#temp_DBNr]

[COLOR="#008000"]//Wert in den DB übertragen[/COLOR]
      L     #diWert
      T     DBD [AR1,P#0.0]

Harald
 
Hallo Harald,

Vielen Dank für deine Antwort und Mühe.

Funktioniert fast so wie ich es wollte.
Der 1. Wert wird ab DBD 4 eingetragen und nicht in DBD 0.

Woran kann das noch liegen? das AR1 beginnt ja bei 0

Muss beim 1. Durchgang der Zähler mit -1 Multipliziert werden?

Vielen Dank noch mal für die Hilfe.

Mfg Tom
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Funktioniert fast so wie ich es wollte.
Der 1. Wert wird ab DBD 4 eingetragen und nicht in DBD 0.
Dann ist beim ersten Aufruf des FC der Wert von "Ist_Zaehler" nicht 0 sondern schon 1. Dann mußt Du wohl immer 1 abziehen, z.B. so:
Code:
      L     30
      L     #Zaehler
      +     -1
      >=D
      SPB   M001
...

Mit welchen Werten von "Ist_Zaehler" wird der FC denn aufgerufen? 1..31 ?

Harald
 
Hallo Harald,

Vielen Dank für deine Antwort.

Funktioniert bestens.

Mit welchen Werten von "Ist_Zaehler" wird der FC denn aufgerufen? 1..31 ?

Harald

Diese Frage verstehe ich nicht so richtig. Meinst du den Datentyp?
Es sind Zahlen von 1 - 30 die ich aus einem anderen DB beziehe.

Code:
      L     #Soll
      L     #Ist
      +     -1
      >=D   
      SPB   M001
      CLR                               //unzulässig! Zaehler ist < 0 oder > 30
      SAVE                              //ENO = FALSE zurückmelden
      BE                                //beenden ohne schreiben


M001: L     4
      *I    
      SLD   3
      LAR1  


      AUF   #DBNr


      L     #diWert
      T     DBD [AR1,P#0.0]             //Wert in den DB übertragen

Ich habe noch ein INOUT eingebaut um zu verhinder das höher gezählt werden kann als Eiintrage im DB vorhanden sind.

Code:
      L     #Soll
      L     #Ist
      ==D   
      SPBN  M002
      =     #Soll_err

M002: NOP   0


Vielen Dank noch mal für deine Hilfe.

Mfg Tom
 
Hallo Harald,

Was müsste ich am AWL Code ändern wenn ich Variable Startadressen bräuchte?

Also DB3 bleibt aber ich vergrößere ihn und z. B. der erste Bereich beginnt bei DBD 0 bis DBD 116 (30DW)

der Nächste Bereich beginnt dann bei DBD 120.

Das ist eine neue Überlegung wenn ich den FC mehrmals aufrufe aber den gleichen DB benutzen möchte.

Vielen Dank nochmal für deine tolle Hilfe.

Mfg Tom
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Es sind Zahlen von 1 - 30 die ich aus einem anderen DB beziehe.
Oh, mein Fehler, daß ich gedacht habe der Zähler liefert 0..30. Im #1 hast Du eindeutig geschrieben, daß er von 1..30 geht.
Also: vor der Adressberechnung 1 abziehen: 1..30 --> 0..29. Und prüfen, ob der Wert danach 0..29 ist --> im Code das ">=D" zu ">D" ändern
Formel für die Adressberechnung: Pointer := (Zaehler - 1) * 4 * 8;


Ich habe noch ein INOUT eingebaut um zu verhinder das höher gezählt werden kann als Eiintrage im DB vorhanden sind.

Code:
      L     #Soll
      L     #Ist
      ==D   
      SPBN  M002
      =     #Soll_err

M002: NOP   0
Wäre es nicht besser den Zähler außerhalb des FC auf 1..30 zu begrenzen?
Der FC prüft schon fest mit 30, ob der Zähler einen unzulässigen Wert hat und signalisiert den Fehler über den ENO-Ausgang des FC. Natürlich kann man auch mit einem zusätzlichen Output den Fehler signalisieren.
Oder warum willst Du den Zähler-Maxwert als Variable (#Soll) übergeben? Soll die Bereichsgröße nicht fest 30, sondern flexibel sein?
Warum nimmst Du einen IN_OUT für #Soll_err? Warum wird im OK-Fall dem #Soll_err nichts zugewiesen? Hinweis: besser auf "#Ist >= #Soll" prüfen als nur auf "#Ist == #Soll".


Was müsste ich am AWL Code ändern wenn ich Variable Startadressen bräuchte?

Also DB3 bleibt aber ich vergrößere ihn und z. B. der erste Bereich beginnt bei DBD 0 bis DBD 116 (30DW)

der Nächste Bereich beginnt dann bei DBD 120.
Da gibt es viele Möglichkeiten. Du müsstest zunächst überlegen/festlegen wie Du die zusätzliche Information an den FC übergeben willst:
- als Bereichsnummer: (0), 1, 2, 3 ...
- als Array-Index: 0, 30, 60, 90 ...
- als Byte-Offset: 0, 120, 240 ...

Danach richtet sich, wie im FC der Offset (Bereichsstartadresse) vor der Addition auf den Pointer berechnet wird.
Hier ein Beispiel mit Bereichsnummer 1, 2 oder 3:
Code:
[COLOR="#008000"]//Bereich 1: --> DBnr.Array[    0..(Max-1)  ] (bei Max=30:   DBD0..DBD116)
//Bereich 2: --> DBnr.Array[  Max..(2*Max-1)] (bei Max=30: DBD120..DBD236)
//Bereich 3: --> DBnr.Array[2*Max..(3*Max-1)] (bei Max=30: DBD240..DBD356)[/COLOR]

[COLOR="#008000"]//Zähler auf 1..Max prüfen[/COLOR]
      L     #Max
      L     #Zaehler                    [COLOR="#008000"]//zulässig 1..Max[/COLOR]
      +     -1                          [COLOR="#008000"]//--> 0..Max-1[/COLOR]
      <=D   
      SPB   MERR                        [COLOR="#008000"]//Fehler! #Zaehler ist < 1 oder > Max[/COLOR]
      T     #Index                      [COLOR="#008000"]//normierter Zählerwert 0..(Max-1) merken[/COLOR]

[COLOR="#008000"]//Bereichsnummer auf 1..3 prüfen[/COLOR]
      L     3
      L     #Bereich                    [COLOR="#008000"]//zulässig 1..3[/COLOR]
      +     -1                          [COLOR="#008000"]//--> 0..2[/COLOR]
      <=D   
      SPB   MERR                        [COLOR="#008000"]//Fehler! #Bereich ist < 1 oder > 3[/COLOR]

[COLOR="#008000"]//Pointer berechnen, hier im AKKU1 die normierte Bereichsnummer 0..2[/COLOR]
      L     #Max                        [COLOR="#008000"]//Anzahl Einträge je Bereich[/COLOR]
      *I    
      L     #Index                      [COLOR="#008000"]//normierter Zählerwert dazu[/COLOR]
      +I    
      SLD   5                           [COLOR="#008000"]//*4*8 zusammengefasst (*4 wegen DINT, *8 für Pointer)[/COLOR]
      LAR1                              [COLOR="#008000"]//Adresse im DB (P#x.y ohne Bereichskennung)[/COLOR]

[COLOR="#008000"]//Wert in den DB übertragen[/COLOR]
      AUF   #DBNr
      L     #diWert
      T     DBD [AR1,P#0.0]             [COLOR="#008000"]//bereichsintern indirekt in DB schreiben[/COLOR]

      CLR                               [COLOR="#008000"]//kein Fehler[/COLOR]
      SPA   END                         [COLOR="#008000"]//Alles Gut :-)[/COLOR]

[COLOR="#008000"]//Fehler oder OK zurückgeben[/COLOR]
MERR: SET                               [COLOR="#008000"]//hierher Sprung bei Parameterfehler[/COLOR]
END:  =     #Error                      [COLOR="#008000"]//Fehler-Output: 1=Fehler[/COLOR]
      NOT   
      SAVE                              [COLOR="#008000"]//ENO: 0=Fehler[/COLOR]
Die Parameter des FC:
Code:
FUNCTION "MOVE_in_DB" : VOID

VAR_INPUT
  Zaehler : INT ;   [COLOR="#008000"]//1..Max[/COLOR]
  Max : INT ;       [COLOR="#008000"]//Anzahl Einträge je Bereich[/COLOR]
  Bereich : INT ;   [COLOR="#008000"]//1..3[/COLOR]
  DBNr : BLOCK_DB ; [COLOR="#008000"]//DB mit ARRAY[0..3*Max-1] OF DINT oder ARRAY[0..2, 0..Max-1] OF DINT[/COLOR]
  diWert : DINT ;   [COLOR="#008000"]//einzutragender Wert[/COLOR]
END_VAR
VAR_OUTPUT
  Error : BOOL ;    [COLOR="#008000"]//1=Parameterfehler[/COLOR]
END_VAR
VAR_TEMP
  Index : INT ;     [COLOR="#008000"]//normierter Zählerwert 0..Max-1[/COLOR]
END_VAR

Harald
 
Hallo Harald,

Vielen Dank für deine Antwort und Mühe.

Wäre es nicht besser den Zähler außerhalb des FC auf 1..30 zu begrenzen?

Ja habe ich auch schon umgesetzt.

Oder warum willst Du den Zähler-Maxwert als Variable (#Soll) übergeben? Soll die Bereichsgröße nicht fest 30, sondern flexibel sein?

Der Soll ist immer verschieden daher habe ich auch den #Ist bis auf 30 hoch genommen vielleicht reicht auch 20 das muß ich dann für den DB3 noch prüfen.
Den #Ist und #Soll bekomme ich fertig aus einem anderen DB als DBB was ich vorher nicht gewusst hatte. Ich dachte ich brauche INT als Input.

Warum nimmst Du einen IN_OUT für #Soll_err?

Das war ein Fehler da habe ich nicht aufgepasst. Aber ich habe es jetzt sowieso gelöscht.

Da gibt es viele Möglichkeiten. Du müsstest zunächst überlegen/festlegen wie Du die zusätzliche Information an den FC übergeben willst:
- als Bereichsnummer: (0), 1, 2, 3 ...
- als Array-Index: 0, 30, 60, 90 ...
- als Byte-Offset: 0, 120, 240 ...

Schade du warst mit dem Offset schneller.

Ich habe es mit einem Array-Index gelöst.

Code:
       L     4                                      // Doppelwortzugriff
      *D    
      L     #Offset
      +D    
      SLD   3                                    // Pointerformat erstellen
      LAR1  


      AUF   #DBNr                           // DB Aufrufen
      L     #diWert
      T     DBD [AR1,P#0.0]             //Wert in den DB übertragen

Funktioniert gut.

Hier ein Beispiel mit Bereichsnummer 1, 2 oder 3:

Das mit den Bereichsnummern werde ich mir noch mal genauer anschauen.

So, vielen Dank nochmal für deine tolle Hilfe. Habe einiges dazu gelernt.

Ich hatte mir auch erhofft dieses mit einem AAA--Haaaa zu verstehen.

Code:
       L     4                                      // Ist klar
     *D                                            // Ist klar
      L     #Offset                             // Habe ich sogar selber heraus gefunden
      +D                                          // Ist Klar 
      SLD   3                                    // Warum geschoben werden muss - Keine Ahnung
      LAR1                                       // Klar wenn ich bereichsintern indirekt in DB schreiben will 

      AUF   #DBNr                           
      L     #diWert
      T     DBD [AR1,P#0.0]             // Am meisten verstehe ich das nicht P#0.0

Dieses Halbwissen über den Pointer ist so frustrierend.

Schönes Wochenende

Mfg Tom
 
Wie bereits in #2 empfohlen: siehe Hilfe zu AWL > Index > Indirekte Adressierung
Ein Pointer adressiert eine Bitadresse x.y. Mit dem *4 berechnet man aus dem Index die Byteadresse x. Nun muss man noch *8 rechnen (SLD 3) um die Bitadresse x.0 zu erhalten.
Weil bei der Berechnung keine Bereichskennung zugefügt wurde (in AR1 steht lediglich P#x.0) muss der Speicherbereich in der Operation angegeben werden: DBD...
Bereichsübergreifende indirekte Adressierung ist so nicht möglich, dafür müsste in AR1 noch eine Bereichskennung vorhanden sein.

Harald
 
Zurück
Oben