Direccionamiento indirecto en Step 7 (II) – Registros y áreas

En esta nueva entrada sobre direccionamiento indirecto es con la que terminamos el curso. En ella vamos a ver la segunda parte de cómo automatizar y recorrer zonas de memoria para trabajar con ellas de forma indirecta y recursiva. Lo primero que has de hacer es leer el direccionamiento indirecto en Step 7 (I)  si no lo has hecho ya.

Registro de direcciones

Como sabes, cuando se carga un valor, hay dos registros, el ACU1 y el ACU2. Pues también existen dos registros de direcciones, el AR1 y el AR2 para usarlos en el direccionamiento indirecto.

La forma de cargar y transferir los valores a los registros son de la siguientes 3 maneras:

  • Cargar directamente el puntero en el AR1
  • Cargar el puntero y transferirselo al AR1
  • Cargar el valor numérico de bits y transferirselo al AR1

direccionamiento indirecto

Cargar directamente el puntero

Es el más rápido si se sabe qué hay que cargar:

LAR1 P#1.0

Lo que cargará en el AR1 el puntero 1.0

Cargar el puntero y transferirlo al AR1

L P#1.0

LAR1

Al igual que el anterior, cargamos el puntero P#1.0 en el AR1

Cargar el valor numérico de bits y transferirlo al AR1

o, como dijimos en direccionamiento indirecto (I), podemos crear al puntero diciendo cuántos bits tiene la dirección

L 8

LAR1

Hará exactamente lo mismo que los casos anteriores. Hay que puntualizar que si queremos acceder a una dirección que tenga más de 32768 bits (16 bits) la carga debería ser del estilo:

L  L#80000

TAR1

De tal forma que cargaremos la dirección 10000.0

Un poco altita la dirección aunque esa sería la forma. A decir verdad, lo suyo es jugar siempre con números en doble palabra como ya veremos más adelante aunque hacerlo como enteros también es válido aunque limitado a la capacidad de una palabra frente a la doble.

 Direccionamiento Indirecto por Registro e Intraárea/Interárea

La diferencia fundamental entre el registro e Intraárea y registro e Interárea es:

  • Intraárea: No se especifica el área de memoria
  • Interárea: Vamos a especificar el área de memoria (el tipo, por así decirlo)

 

¿Y esto qué significa en cristiano?

Pues a la hora de la verdad, y para nosotros, no mucha cosa. En resumen, y que permitan los puristas, lo que vamos a hacer es o bien:

LAR1 P#50.0 // Cargar el puntero

L MW [AR1, P#10.0]  // Decir el operando (en este caso de carga), el tipo de área (marcas) y  la dirección.

O también podemos hacer:

LAR1 P#M50.0 // Cargar el puntero CON la zona de memoria

L W [AR1, P#10.0]  // Decir el operando   y  la dirección.

¿Resultado?

Pues básicamente el mismo. La diferencia es que en el direccionamiento de tipo intraárea, el contenido del AR1 va a tener la misma pinta que el direccionamiento por memoria que veíamos en el capítulo I. No tiene el área de memoria asignado (marcas en nuestro caso).

¿Qué se usa más?

Personalmente intraárea, pero supongo que es cuestión de gustos. Además imaginemos que mezclamos las cosas y hacemos lo siguiente:

LAR1 P#E50.0

L MW[AR1,P#10.0]

Como veis  hemos indicado el área en la carga del registro AR1 y luego hacemos una carga de MW… ¿vamos a mandar la CPU a Stop o crearemos un agujero espacio-tiempo?

Pues no. No sé si lamentablemente, pero no.

Simplemente del AR1 tomaremos la dirección, pero no la zona de memoria y ejecutaremos el operando de carga MW.

Por tanto, y a mi juicio, es mejor siempre usar el intraárea. Me parece más metódico ya que siempre vas a construir los punteros de la misma forma, independientemente del uso que le vayas a dar posteriormente.

Muy bien, me parece estupendo… pero me he perdido en el L MW [AR1, P#10.0]… ¿qué significa?

Muy sencillo. Lo que hay entre corchetes es la dirección inicial más un desplazamiento. De tal forma que en nuestro ejemplo, realmente estamos leyendo la MW60 (50 del AR1 +10 del offset por así decirlo).

¿Es obligatorio poner ese offset siempre?

Si. Siempre. Sin excepción. Pones un P#0.0 si no lo quieres y listo.

¿Qué pasa si cargo un P#50.0 en el AR1, cargo un offset P#0.1 y hago una lectura MB, MW o MD?

Pues que estarías provocando un bonito error de runtime. Ya que para hacer una lectura de byte, word o doble word, necesitas que el bit del puntero sea 0 (ojo, el bit, no el byte). No puedes crear un MW50.1 como puedes imaginar. Así que cuidadín con eso.

¿Qué más puedo hacer con los registros?

Como no sólamente del AR1 vive el hombre, existe también un segundo registro, el AR2 . Con esta pareja, podemos jugar indistintamente para cargar nuestros punteros e incluso combinarlos en las operaciones.

Así, para seguir con el ejemplo anterior, podríamos hacer:

LAR1 P#50.0

LAR2 P#10.0

L MW[AR1,AR2] : No se puede usar ambos AR para hacer este tipo de direccionamiento

L MW[AR1,p#0.0]

T MW[AR2,P#10.0]

¿Qué operaciones puedo hacer con los registros?

LARn : Cargar el contenido del ACU1 en el registro ARn, siendo n=1 o 2.

Offtopic: Y no, no es un 102, es un 1 O 2. A la RAE le ha parecido buena idea no tener que acentuar desde hace un tiempo la O entre números. Qué país.

TARn: Transferir desde el registro n, su contenido al ACU1.

+ARn: Sumar al contenido del ARn, el contenido del ACU1.

Pero como hemos visto, podemos hacer cargas y transferencias directas:

LARn <Dirección>: Cargamos la dirección al ARn. Por ejemolo LAR1 P#50.0

TARn <Dirección>: Cargamos el contenido del ARn en la dirección. Por ejemplo TAR1 MD50

+ARn <Puntero>: Añadimos al ARn el puntero que queramos. Por ejemplo, +AR1 P#10.0

Para rizar el rizo, podemos copiar la información entre los AR haciendo:

LAR1 AR2, con lo que cargaremos en el AR1 el contenido del AR2

TAR1 AR2, con lo que recuperamos el contenido del AR1 y se lo cargamos en el AR2.

Como ves las combinaciones son unas cuantas si bien se pueden resumir en asignación y suma.

 ¿Y todo esto, ya sirve para algo?

Pues aunque pueda parecer lo contrario, el direccionamiento indirecto sirve para muchas cosas, pero sobre todo para recorrer información y tratarla.

En el siguiente video os voy a mostrar cómo se usa el direccionamiento indirecto, especialmente con bucles LOOP (for-next), como ya vimos.

Recordarte finalmente que si estás interesado en formarte en Step 7, puede que te interese adquirir el libro “Cómo programar en Step 7 y no morir en el intento” donde puedes encontrar no sólamente el curso completo gratuito, sino muchas más entradas del blog que como sabes se irán actualizando para los que lo adquieran de forma gratuita. Si estás interesado, puedes echarle un vistazo a este enlace donde se explica qué es lo que contiene el libro, el precio y los métodos de pago.

Referencias:

Acerca de Iñigo Gútiez

Ingeniero industrial por la Escuela de Ingenieros Superiores de Bilbao. Trabajo como ingeniero de proyectos y automatización en Guardian Industries

Te puede interesar

multiinstancias en step 7

Multiinstancias en Step 7

Las multiinstancias en Step 7 fueron unas de las cosas que se quedaron en el …

24 Comentarios

  1. No me permite utilizar la instrucción “L MW[AR1,AR2]” (Error de sintaxis en AR2).

  2. Hola Carlos,
    Cuando lo he leído he pensado: “como que eso no se puede hacer”… lo que no me había fijado, es que lo había puesto como ejemplo. Se me debió ir la olla al poner ejemplos.

    Efectivamente el Offset no puede ser otra variable o registro, sino que tiene que ser fijo.

    Pero ya veo que estáis atentos jeje. Siento el error.

    Un saludo,

  3. Hola Íñigo:

    Ensayando el direccionamiento indirecto estaba preguntándome el como hacerlo a diferentes elementos dentro del mismo DB. Por ejemplo, quiero transferir un resultado que va a ir variando y cada vez, o cada cierto tiempo, quiero almacenarlo paulatinamente en diversos enteros que he puesto en un DB. En S7-1200 conozco la función “FieldWrite” para hacerlo, ¿pero cómo se haría con un S7-300?. He estado buscando informaciones, pero no encuentro nada. Gracias por anticipado por responder. Un saludo.

  4. Hola,

    No te sigo muy bien. Pon un ejemplo de qué querrías hacer please.

  5. Si, imagina que tengo una función que me va a dar un resultado diferente en un entero dependiendo de unas condiciones, y yo quiero tener almacenado en un DB los diversos resultados cada cierto tiempo (o cada vez que yo determine), o sea, imagina que tengo el resultado en una MW200 (Int) y quiero ponerlo paulatinamente en DB1.DBW0, luego en DB1.DBW2…3…4 y así paulatínamente, pero claro, lo que quiero saber es si se puede hacer el direccionamiento indirecto de esa última parte, del DB1.DBW… Ya que según veo no me deja hacer, por ejemplo:

    L 2
    T MW0
    L MW200
    T DB1.DBW[MW0]

    No me deja hacerlo y me da un error de sintaxis

    Por cierto, buenas noches 😉

  6. No se puede hacer ese tipo de direccionamiento.
    Una marca, la mw0 en este caso con un índice solo sirve para hacer referencia a la llamada de fc, fb, temporizadores o abrir db. Todo aquello que este indexado. Numerado.
    Tu quieres acceder a una posición de memoria.
    Tendrías que crear un puntero. Si en el db quieres cargarlo en la segunda posición tendrás que cargar algo asi
    L p#2.0
    T MD0 (no mw)
    L mw200
    Auf db1
    T dw[md0]
    A ver si se me ocurre un ejemplo y se lo dedico una semana.

    • Efectívamente.

      Esta noche te garantizo que voy a dormir más tranquilo :)

      No será por que no lo has explicado en las entradas sobre direccionamiento indirecto.

      Que sería de mi sin los ejemplos… No es lo mismo verlo…

      Bueno, como siempre, gracias.

  7. Me alegro haber resuelto la duda.
    El direccionamiento indirecto es realmente útil en ciertos casos, pero tampoco hay que abusar porque es difícil de seguir cuando quieres encontrar un problema o fallo.

    Saludos

  8. Hola de nuevo Íñigo:

    Veo mi comentario y es curioso, pero también voy a tener que depurar el comentario…

    Donde dije DB1.DBW2…3…4…, debía ser DB1.DBW2…4…6…

    Lo de

    L 2
    T MW0
    L MW200
    T DB1.DBW[MW0]

    MD0 en vez de MW0 era lo de menos, pese a que me alegro del patinazo, ya se sabe, no hay más para que no vuelvas a cometer un error que haberlo cometido ya…

    En todo caso la idea era saber como direccionar indirectamente a elementos dentro de un DB, eso me hizo descuidar todo lo demás. Por cierto ¿alguna explicación que merezca la molestia de darla de por qué no se puede hacer T DB1.DBW[MD0] y hay que hacerlo como has dicho OPN DB1 y T DW[MD0]?

    Saludos y gracias.

  9. No se puede hacer un T DB1.DBW[MD0]
    ¿La razón “tessnica”? Pues mas menos NPI. Entiendo que habrá que abrir previamente un área de memoria antes de posicionarse sobre él.

    ¿Absurdo? Pues sí. Pero lo cierto es que es un requerimiento previo.

    Lo cierto es que tienes que hacer una apertura del DB y luego hacer la carga o la lectura.

    Bien haciendo:

    AUF DB1
    T DW[MD0]

    o bien, usando

    AUF DI 1
    T DIW[MD0]

    Y sí, la única forma más fácil de no equivocarse es haberse equivocado con anterioridad. De eso no hay duda. En mi caso a veces, varias veces 😉

  10. Hola Iñigo, mi pregunta es pq es necesario utilizar el comando AUF , o cuando se utiliza y porque. Yo puedo acceder a los datos de un DB y no entiendo muy bien cuando utilizarlo.Gracias y felicidades por tu web

  11. Hola Adolfo,
    Hasta donde yo sé, creo que no hay forma de realizar un puntero a un DB de una forma directa, es decir, no se puede hacer algo del estilo:
    L DB1.DBX[MD0]
    Por tanto hay que realizar la apertura previamente a la lectura o escritura sobre el DB en cuestión.

    Cuando quieras hacer una lectura/escritura directa, lógicamente puedes hacer un
    L DB1.DBW8
    T DB1.DBW10

    Aunque también podrías hacer:
    AUF DB1
    L DBW8
    T DBW10

    Ambos casos realizarían lo mismo.

  12. Zeno Martorana

    Apreciado Iñigo.
    Creo ver un error en el direccionamiento en BITs, en el video anterior el bit 10.0, es en realidad el bit 81 y no el 80. Ya que esta relacion entre Byte y bits, se puede expresar como: Byte . bit = (Byte x 8) + (bit + 1).

    Por ejemplo. 10.0, donde Byte = 10 y bit = 0
    el bit correspondiente es: (10×8 ) + (0+1) = 80 + 1 = 81

    El bit correspondiente a la direccion: 9.7
    Seria: (9×8) + (7+1) = 72 + 8 = 80, este si corresponde al bit 80 del Ejemplo.

    Gracias.

    • Esta bien. Creo que no estas contando con el valor cero, que ya es el primer bit.. El 0.0. Por tanto 80 bits es el 10.0
      O de otra forma 80 unidades son 81 posiciones de memoria, porque hay que contar el cero.

      Si 0 es el 0.0 – >8 es el 1.0 y por tanto 80 el 10.0

      Saludos!

  13. Zeno Martorana

    Amigo Iñigo.
    Tengo un problema de direccionamiento indirecto.

    En OB1, cargue un FB, y hago un loop, donde comparo valores con una Variable interna del OB. La que coinciden quiero guardarlas en el DB de instancia del FB.

    No encuentro la manera de usar el “Indice” usado en el loop, para direccionar las memorias del DB donde deben ser ingresados los valores coincidentes.

    Las diferentes sintaxis que he probado me da error, podrias ayudarme a este tipo de direccionamiento ????

    Gracias.

    Zeno Martorana.

  14. Zeno Martorana

    Aclaratoria al anterior Comentario.

    Cuando los valores coinciden con la Variable de comparacion, hago que el valor de lavariable sea guardado en el DB de instancia. Usando para ello una instruccion MOVE a la direccioin DBxx.DBW [indice, P#0.0].

    Donde la variable indice se va incrementando en el loop y deberia direccionar la palabra de memoria donde debe ser ingresado el valor de la variable.

    Pero me da error de Sintaxis.

    • No hagas eso.

      Prueba a hacer esto, en vez de DBXX.DBW[Indice,P#0.0]…
      Suponiendo que el indice tenga el número de bits de la dirección…
      L #indice
      LAR1
      AUF DBXX
      L (Valor que quieras guardar)
      T DBW[AR1,P#0.0]

      O bien: Solución 2
      Supongamos que el índice como digo es la dirección y además es una DOBLE palabra.
      Entonces:
      AUF DBxx
      L (Valor que quiero Guardar)
      T DW[#indice]

      Esta segunda solución es más sencilla y compacta. Pero ambas son válidas e igual de funcionales.

      Saludos

  15. Zeno Martorana

    APRECIADO IÑIGO.

    TE AGRADEZCO MUCHO TU AYUDA. AQUI TE DOI EL RESULTADO DE HABER PROBADO LAS DOS SOLUCIONES QUE ME SUGERISTE.

    ESTA SOLUCION NO ME FUNCIONO……….

    AUF “DB82” //ABRO BLOQUE DB82

    L (#OB82_MDL_ADDR_WORD) //VALOR QUE SE QUIERE GUARDAR, ES LA
    VARIABLE INTERNA DEL OB82

    T DW[#PNTR] //EL INDICE ES LA VARIABLE INTERNA DEL
    DB, LLAMADA #PNTR

    EN LA INSTRUCCION LOAD, ME DICE QUE HACE FALTA UN OPERANDO ????? Y EN LA INSTRUCCION TRANSFER, ME DICE QUE HAY UN ERROR DE SINTAXIS EN #PNTR

    ESTA SOLUCION …….SI ME FUNCIONO PERFECTAMENTE.

    L #PNTR //CARGO EL REGISTRO AR1 CON EL VALOR
    DE #PNTR
    LAR1

    AUF DB82 // ABRO EL BLOQUE OB82

    L #OB82_MDL_ADDR_WORD //CARGO LA VARIABLE A SALVAR EN ACC1

    T DBW[AR1,P#0.0] //LA TRANSFIERO AL LUGAR DEL DB
    INDICADO POR EL AR1 CON UN OFF-SET DE
    P#0.0

    GRACIAS AMIGO …… SIEMPRE OPORTUNO.

    ZENO MARTORANA.

  16. Zeno Martorana

    Otra preguntita, para mantener el contacto …….

    Quiero cargar el ACC1, ACC2, AR1 o AR2 con un valor o una variable …… pero usando el lenguaje KOP ……. es posible hacerlo??????

    Gracias.

    Zeno Martorana

    • A ver, de lo que no te ha funcionado,

      En el Load, no pongas paréntesis, solo la variable

      L #OB82_MDL_ADDR_WORD

      y en el segundo como ya indiqué #PNTR tiene que ser un DOBLE word. Por tanto, si es un entero, vuelcalo a un dobleword antes de hacer la operación.

      Saludos

    • No. El direccionamiento indirecto no se puede hacer con FUP/KOP.

      Saludos

  17. Zeno Martorana

    Gracias mi amigo. Todo muy bien.

    Zeno.

  18. Muy bueno todos tus ejemplos y comentarios Iñigo, de verdad muchas gracias por ayudar a enseñar y a contribuir a hacer un pueblo culto entre los que programamos con Step 7. Saludos