Operaciones de programación en PLC

Las operaciones de programación son el conjunto de instrucciones que dotan al autómata de la capacidad de tomar decisiones, realizar cálculos, gestionar tiempos y eventos, y manipular datos. Según la norma IEC 61131-3, estas operaciones se clasifican en categorías bien definidas: lógicas, aritméticas, de comparación, de temporización/conteo y de transferencia de datos. Su correcta combinación permite implementar desde sencillos automatismos hasta complejos algoritmos de control (PID, recetas, diagnosis).

Ejemplo industrial: en una planta embotelladora, se usan operaciones lógicas para detectar presencia de botella y tapón, operaciones aritméticas para calcular la velocidad de la cinta en función de la producción deseada, comparaciones para activar alarmas si la temperatura del sellado excede un límite, temporizadores para controlar el tiempo de llenado, y transferencia de datos para cargar recetas de producto desde una interfaz HMI.

Carga y transferencia de datos

Las operaciones de carga y transferencia (también llamadas MOV, CPY, XFR según el fabricante) permiten copiar valores entre diferentes áreas de memoria del PLC: entradas, salidas, marcas internas, registros, bloques de datos y constantes. Son la base para inicializar variables, gestionar recetas, intercambiar información entre bloques funcionales y preparar datos para operaciones aritméticas o de comunicación.

Instrucciones típicas

  • MOV Mover: copia el contenido de la fuente al destino. Ejemplo: MOV 100, MW10 (carga el valor 100 en la marca de palabra MW10).
  • BLKMOV Transferencia de bloques: copia un rango de datos (array, estructura) de un origen a un destino. Usado para recetas o buffers.
  • XFR Intercambio: intercambia los valores de dos operandos.
  • ROL / ROR (rotación) y SHL / SHR (desplazamiento): operaciones de bits, también consideradas de transferencia/desplazamiento.

Aplicación práctica: en un sistema de control de temperatura, se lee un valor analógico (por ejemplo, 0-10V) que se convierte a digital (0-27648) y se transfiere a una variable interna temp_actual. Luego, mediante comparaciones, se decide si activar la calefacción. La transferencia se realiza con una instrucción MOV del registro de entrada analógica a la variable.

Datos técnicos: las operaciones de transferencia suelen ejecutarse en un ciclo de scan y no afectan los flags de estado (aunque algunas variantes sí pueden modificar el flag de desbordamiento si se usa aritmética). En PLC Siemens S7-1500, la instrucción MOVE puede copiar desde un byte (8 bits) hasta un bloque de 64 KB en una sola operación.

Editor Ladder de TIA Portal V17 mostrando la Red 1 con un contacto normalmente abierto Enable_Carga (%M0.0, TRUE) en serie con el bloque MOVE: pin EN activo, valor de entrada IN=100 (INT) y salida OUT1 conectada a la variable simbólica temp_set (%MW10). El flujo de energía (línea azul) es activo; ENO=TRUE (línea verde) indica transferencia correcta. Comentario de red: 'Cargar consigna de temperatura inicial'.
Figura 5: Instrucción de transferencia MOVE en el editor Ladder de TIA Portal V17. Un contacto normalmente abierto (Enable_Carga, %M0.0) habilita el bloque MOVE que copia la constante 100 a la variable temp_set (%MW10), usada como consigna de temperatura. El pin ENO activo confirma la ejecución sin errores. Fuente: Elaboración propia. / Licencia: CC BY‑NC 4.0

Ejemplo en lista de instrucciones (IL)

LD      100      (* cargar constante 100 *)
ST      MW10     (* almacenar en marca de palabra MW10 *)
        

En lenguaje estructurado (ST): MW10 := 100;

Importante: la transferencia puede ser condicional (solo si se cumple una condición) o incondicional. La correcta gestión de tipos de datos (INT, REAL, WORD, etc.) evita errores de conversión implícita.

🐍 Simulación de transferencia de datos estilo PLC en Python
# transferencia_datos.py - Ejemplo de operaciones MOV y BLKMOV en Python
# Ideal para entender la copia de valores en PLC (por valor, no referencia)

import copy

class PlcMemory:
    """ Simula la memoria de un PLC: entradas, salidas, marcas y bloques """
    def __init__(self):
        self.inputs = {'I0.0': False, 'IW0': 0}          # entradas digitales y analógicas
        self.outputs = {'Q0.0': False, 'QW0': 0}
        self.marks = {'MW0': 0, 'MW2': 0, 'MD4': 0.0}    # marcas (word, double)
        self.data_blocks = {'DB1': [0]*10}               # bloque de datos con array

    def mov(self, source, dest):
        """ Simula MOV: copia el valor de source a dest (por valor) """
        # Si source es un nombre de variable, obtener su valor
        if isinstance(source, str):
            val = self.get_value(source)
        else:
            val = source   # constante

        # Asignar a destino
        self.set_value(dest, val)
        print(f"MOV {source} -> {dest}  (valor={val})")

    def blkmov(self, src_block, src_start, dest_block, dest_start, count):
        """ Copia un bloque de datos (lista) de una posición a otra """
        src = self.data_blocks[src_block]
        dest = self.data_blocks[dest_block]
        for i in range(count):
            dest[dest_start + i] = src[src_start + i]
        print(f"BLKMOV: {count} elementos de {src_block}[{src_start}] a {dest_block}[{dest_start}]")

    def get_value(self, var):
        # Búsqueda simple en inputs, outputs, marks
        if var in self.inputs:
            return self.inputs[var]
        elif var in self.outputs:
            return self.outputs[var]
        elif var in self.marks:
            return self.marks[var]
        else:
            raise KeyError(f"Variable {var} no encontrada")

    def set_value(self, var, val):
        if var in self.outputs:
            self.outputs[var] = val
        elif var in self.marks:
            self.marks[var] = val
        else:
            # podría ser también en inputs pero no es común escribir en entradas
            print(f"Advertencia: {var} no es escribible, se ignora")

# --- Ejemplo de uso ---
plc = PlcMemory()
plc.mov(100, 'MW0')          # constante a marca MW0
plc.mov('IW0', 'MW2')        # entrada analógica IW0 a MW2 (IW0 vale 0 por defecto)
plc.inputs['IW0'] = 1234      # simulamos lectura de sensor
plc.mov('IW0', 'MW2')         # ahora MW2 = 1234

# Transferencia de bloque de datos (receta)
plc.data_blocks['DB1'] = [10, 20, 30, 40, 50]   # valores de receta
plc.blkmov('DB1', 0, 'DB1', 3, 2)               # copia 2 elementos desde índice 0 a índice 3
print("DB1 después de BLKMOV:", plc.data_blocks['DB1'])

Este código simula las operaciones MOV y BLKMOV usando un diccionario como memoria del PLC. Se puede probar en Thonny y adaptar para comunicarse con GPIO o con un PLC real mediante librerías como snap7.

Tendencias Actuales 2026

Los PLC modernos incorporan instrucciones de transferencia optimizadas para manejar tipos de datos estructurados (STRUCT, ARRAY) y para comunicación con periféricos IO-Link. La nueva edición de IEC 61131-3 (prevista 2026) estandariza el uso de punteros referenciados y transferencia asíncrona para no bloquear el ciclo de scan. Además, los entornos de desarrollo permiten arrastrar y soltar conexiones entre bloques de datos, generando automáticamente el código de transferencia.

Aplicaciones con Raspberry Pi

En Python sobre Raspberry Pi, las operaciones de transferencia se realizan con simples asignaciones. Por ejemplo, para leer un sensor analógico vía SPI y copiar su valor a una variable que controle un servo, basta con valor = leer_adc() y luego pwm.ChangeDutyCycle(valor). Sin embargo, para simular bloques de datos, podemos usar listas o diccionarios, como en el ejemplo anterior.

Aplicaciones con Arduino

En Arduino, las operaciones de transferencia son asignaciones directas: int valor = analogRead(A0); o mediante la función memcpy() para copiar bloques de memoria. En el contexto del Arduino PLC IDE, se pueden usar bloques MOVE gráficos.

💼 Caso Práctico Industrial

Contexto: En un sistema de dosificación, se tiene una receta almacenada en un bloque de datos DB_Receta (posición 0 a 9). El operador selecciona un producto y el PLC debe copiar los valores correspondientes (3 valores: tiempo, velocidad, temperatura) al área de trabajo DB_Trabajo (posiciones 0,1,2).

Pregunta: ¿Qué instrucción de transferencia es la más adecuada?

🍓 Raspberry Pi + Python

Contexto: Se tiene una lista `receta = [10, 20, 30]` y una lista `trabajo = [0, 0, 0]`. Se desea copiar los valores de receta a trabajo, pero asegurando que sean independientes (modificar trabajo no altere receta).

Pregunta: ¿Qué método de Python es el más adecuado para una copia profunda de los valores (no referencias)?

🐍 Desplazamiento de bits

Contexto: En un PLC, a veces se usan desplazamientos para empaquetar bits. En Python, se tiene un entero `dato = 0b1100` (12 decimal). Se quiere desplazar a la izquierda 2 posiciones y luego transferir a otra variable.

Pregunta: ¿Cuál es el resultado de `resultado = dato << 2`?

Tabla 1: Operaciones de programación comunes en IEC 61131-3
TipoEjemplos (IL/ST)Aplicación industrial
LógicasAND, OR, NOT, XOREnclavamientos, seguridad, combinación de sensores
AritméticasADD, SUB, MUL, DIVCálculo de velocidades, totalizadores, PID
ComparaciónGT, LT, EQ, GE, LE, NELímites de temperatura, presión, nivel
TemporizadoresTON, TOF, TPRetardos, temporización de procesos
ContadoresCTU, CTDConteo de piezas, ciclos de máquina
TransferenciaMOV, BLKMOV, ROL, SHLInicialización, recetas, manejo de buffers

📚 Recursos Técnicos y Normativos 2026

Normativas y Estándares

Recursos Raspberry Pi y Python