Introducción a los bloques de temporización, contaje y comparación

En la automatización industrial, el control de tiempos, el conteo de eventos y la comparación de magnitudes son esenciales para implementar procesos secuenciales, alarmas, supervisiones y lógica de seguridad. La norma IEC 61131-3 estandariza bloques funcionales (FB) para estas tareas, permitiendo su uso en cualquier PLC compatible (Siemens, Rockwell, CODESYS, etc.).

Estos bloques se representan gráficamente en los lenguajes FBD y Ladder, y también pueden invocarse en ST e IL. Su correcta parametrización y combinación permite construir desde sencillos retardos hasta complejos sistemas de control de producción.

Ejemplo industrial: en una línea de envasado, un sensor detecta el paso de botellas y alimenta un contador CTU. Cuando se alcanzan 24 botellas (PV=24), el bloque activa un temporizador TON que, tras 2 segundos, acciona un empujador para formar un lote. Un comparador verifica que el contador no supere un máximo por seguridad.

Bloque TON: Temporizador a la conexión

El TON (Timer ON Delay) genera un retardo a la activación. Su comportamiento es el siguiente:

  • Cuando la entrada IN pasa de FALSE a TRUE (flanco de subida), el temporizador comienza a incrementar el tiempo transcurrido ET (Elapsed Time) desde cero.
  • Mientras IN permanezca TRUE y ET sea menor que el tiempo prefijado PT (Preset Time), la salida Q permanece FALSE.
  • Cuando ET alcanza o supera PT, la salida Q se pone a TRUE y se mantiene mientras IN sea TRUE.
  • Si IN pasa a FALSE antes de alcanzar PT, el temporizador se reinicia (ET vuelve a 0).

Parámetros:

  • IN (BOOL): entrada de activación.
  • PT (TIME): tiempo de retardo (ej. T#5s, T#100ms).
  • Q (BOOL): salida temporizada.
  • ET (TIME): tiempo transcurrido (solo lectura).

Aplicación típica: retardo para evitar rebotes en un pulsador, o tiempo de seguridad antes de arrancar una máquina.

Ejemplo en Structured Text (ST):

TON_inst(IN := Start, PT := T#3s);
IF TON_inst.Q THEN
    Motor := TRUE;
END_IF

Bloque TOF: Temporizador a la desconexión

El TOF (Timer OFF Delay) actúa de manera inversa al TON. Al activarse la entrada IN, la salida Q se pone inmediatamente a TRUE. Cuando IN pasa a FALSE, comienza la temporización: ET se incrementa desde 0 hasta PT, y mientras tanto Q permanece TRUE. Al alcanzar PT, Q pasa a FALSE.

Si IN vuelve a TRUE durante la temporización, el temporizador se detiene y Q se mantiene TRUE (reinicio).

Aplicación típica: mantener una cinta en movimiento unos segundos después de que el último objeto haya pasado, para asegurar que sale del sistema.

Ejemplo en ST:

TOF_inst(IN := Sensor, PT := T#2s);
Ventilador := TOF_inst.Q;

Bloque CTU: Contador ascendente

El CTU incrementa su valor interno cada vez que detecta un flanco ascendente (de FALSE a TRUE) en la entrada CU (Count Up). Cuando el valor actual CV alcanza o supera el valor prefijado PV, la salida Q se pone a TRUE. La entrada R (Reset) pone CV a 0 y Q a FALSE.

Parámetros:

  • CU (BOOL): entrada de conteo (flanco ascendente).
  • R (BOOL): reset prioritario (pone CV a 0).
  • PV (INT / DINT): valor de consigna.
  • CV (INT / DINT): valor actual.
  • Q (BOOL): salida activa cuando CV >= PV.

Aplicación típica: conteo de piezas, ciclos de máquina, lotes de producción.

Ejemplo en ST:

CTU_inst(CU := Sensor_pieza, R := Reset, PV := 100);
Lote_completo := CTU_inst.Q;

Bloque CTD: Contador descendente

El CTD decrementa CV desde PV hasta 0 con cada flanco ascendente en CD. Cuando CV llega a 0, la salida Q se activa. La entrada LD (Load) carga el valor de PV en CV (inicialización).

Parámetros:

  • CD (BOOL): entrada de decremento.
  • LD (BOOL): carga el valor preset (pone CV = PV).
  • PV (INT): valor inicial.
  • CV (INT): valor actual.
  • Q (BOOL): activa cuando CV = 0.

Aplicación típica: control de stock (cantidad restante de material), temporizaciones descendentes, juegos de luces.

Ejemplo en ST:

CTD_inst(CD := Consumo, LD := Cargar_lote, PV := 500);
Alarma_stock := CTD_inst.Q;   (* cuando llega a 0 *)

Bloque CMP: Comparador de valores

El CMP (Comparador) evalúa dos operandos según la condición seleccionada (igual, mayor que, menor que, etc.) y produce una salida booleana. Aunque no es un bloque estándar único (puede implementarse con operadores en ST o con funciones de comparación), en FBD se representa como un bloque con dos entradas y una salida, donde se selecciona el tipo de comparación.

Las operaciones más comunes:

  • EQ (equal) – igualdad
  • GT (greater than) – mayor que
  • LT (less than) – menor que
  • GE (greater or equal) – mayor o igual
  • LE (less or equal) – menor o igual
  • NE (not equal) – diferente

Aplicación típica: comparar la temperatura actual con un umbral, activar una alarma si se supera. Comparar el contador de piezas con el lote deseado.

Ejemplo en ST:

Alarma := (Temperatura > 100.0);
IF (Piezas_contadas >= Lote_objetivo) THEN
    Lote_completo := TRUE;
END_IF

En FBD, se suele usar el bloque CMP con dos entradas y la salida correspondiente.

Editor FBD en CODESYS con bloques TON (PT=t#2s), CTU (PV=4) y GT (comparador mayor que >). Las conexiones muestran que la salida Q del TON alimenta el CU del contador, y la salida CV del contador se compara con PV mediante el bloque GT para generar el reset.
Figura 7: Ejemplo de red FBD en CODESYS con temporizador TON, contador CTU y comparador GT interconectados. Fuente: PLC Blog – Codesys Counter Instructions / Licencia: © plcblog.in — uso educativo (verificar permiso del autor)
🐍 Simulación de bloques TON, CTU y CMP en Python (orientado a Raspberry Pi)
# plc_blocks.py - Clases para simular TON, CTU y CMP en Python
# Puede ejecutarse en Thonny y adaptarse a GPIO de Raspberry Pi

import time

class TON:
    """Temporizador a la conexión (IEC 61131-3)"""
    def __init__(self, PT):
        self.PT = PT          # tiempo preset en segundos
        self.IN_prev = False
        self.start_time = None
        self.Q = False
        self.ET = 0.0

    def evaluate(self, IN):
        if IN and not self.IN_prev:          # flanco ascendente
            self.start_time = time.time()
            self.ET = 0.0
        if IN:
            if self.start_time is not None:
                self.ET = time.time() - self.start_time
                self.Q = (self.ET >= self.PT)
        else:
            self.Q = False
            self.ET = 0.0
            self.start_time = None
        self.IN_prev = IN
        return self.Q, self.ET

class CTU:
    """Contador ascendente"""
    def __init__(self, PV):
        self.PV = PV
        self.CV = 0
        self.Q = False
        self.last_CU = False

    def evaluate(self, CU, R):
        if R:
            self.CV = 0
            self.Q = False
        elif CU and not self.last_CU:        # flanco ascendente
            self.CV += 1
            self.Q = (self.CV >= self.PV)
        self.last_CU = CU
        return self.Q, self.CV

class CMP_GE:
    """Comparador: mayor o igual (>=)"""
    def __init__(self):
        pass
    def evaluate(self, IN1, IN2):
        return (IN1 >= IN2)

# --- Ejemplo de uso combinado ---
if __name__ == "__main__":
    import time

    # Instanciar bloques
    ton = TON(PT=2.0)          # temporizador de 2 segundos
    ctu = CTU(PV=5)            # contar hasta 5
    cmp = CMP_GE()

    # Simular entradas
    sensor = [False, True, True, True, False, True, True, True, True, True]
    reset = False

    print("Tiempo| Sensor | TON.Q | CTU.CV | CTU.Q | CMP(CTU.CV>=3)")
    for i, val in enumerate(sensor):
        # Evaluar TON
        ton_q, ton_et = ton.evaluate(val)
        # Evaluar CTU (con el sensor como pulso)
        ctu_q, ctu_cv = ctu.evaluate(val, reset)
        # Comparar si CV >= 3
        cmp_res = cmp.evaluate(ctu_cv, 3)

        print(f"  {i}   |   {int(val)}    |   {int(ton_q)}   |    {ctu_cv}     |   {int(ctu_q)}   |       {int(cmp_res)}")
        time.sleep(1)  # simula ciclo de scan

    # Resultado: Al llegar a 5, CTU.Q se activa; cuando CV>=3, CMP activo.

Este código implementa versiones simplificadas de TON, CTU y un comparador. Se puede probar en Thonny y después utilizar con GPIO para contar pulsos reales o temporizar eventos en Raspberry Pi.

Tendencias Actuales 2026

Los bloques de temporización y contaje evolucionan hacia versiones con soporte para tiempos de alta resolución (microsegundos) y conteo de eventos en frecuencias elevadas (hasta MHz) gracias a la integración de hardware específico (coprocesadores). Además, la norma IEC 61131-3 edición 4 incorpora bloques para temporizadores multipropósito (TP) y contadores bidireccionales con reset selectivo. En el ámbito de la Industria 4.0, estos bloques pueden ser monitorizados y parametrizados remotamente vía OPC UA.

Aplicaciones con Raspberry Pi

Con Python y librerías como RPi.GPIO, podemos implementar los bloques anteriores para controlar procesos reales. Por ejemplo, leer un sensor infrarrojo (contar piezas), activar un relé después de un tiempo (TON) y comparar el conteo con un valor para encender un LED. El código del recuadro Python es directamente adaptable.

Aplicaciones con Arduino

En el entorno Arduino, los temporizadores se implementan con millis() (no bloqueantes) y los contadores con variables. El Arduino PLC IDE proporciona estos bloques como instrucciones gráficas, facilitando la programación según IEC 61131-3.

💼 Caso Práctico Industrial

Contexto: En una prensa hidráulica, se requiere que el ciclo de prensado solo comience si el operador ha mantenido pulsado el botón de marcha (X0) durante al menos 3 segundos (para evitar arranques involuntarios). Además, la prensa debe realizar un ciclo completo y luego esperar 2 segundos antes de permitir un nuevo ciclo. Se utiliza un bloque TON para el retardo de arranque y un TOF para el retardo entre ciclos.

Pregunta: ¿Qué combinación de bloques y señales es la correcta?

🍓 Raspberry Pi + Python

Contexto: En una Raspberry Pi, se ha implementado una clase CTU similar a la del ejemplo. Se conecta un sensor de barrera al GPIO17 y se cuenta el número de veces que se interrumpe. El contador tiene PV=10. Al alcanzar 10, se activa un LED en GPIO27.

Pregunta: Si el sensor se interrumpe 12 veces (flanco ascendente) y luego se activa la entrada de reset (R) durante un ciclo, ¿cuál será el valor de CV después del reset?

🐍 Lógica de comparación

Contexto: Se tiene un comparador que debe activar una salida cuando la temperatura actual (real) sea mayor o igual a 100.5 grados. En Python, se escribe:

if temp >= 100.5:
    alarma = True
else:
    alarma = False

Pregunta: ¿Qué operador de comparación se está usando implícitamente?

Tabla 1: Comparativa de bloques de temporización y contaje
BloqueFunciónEntradasSalidasAplicación típica
TONRetardo a la conexiónIN, PTQ, ETArranque temporizado, filtro de rebotes
TOFRetardo a la desconexiónIN, PTQ, ETApagado progresivo, tiempo de espera
CTUContador ascendenteCU, R, PVQ, CVConteo de piezas, ciclos
CTDContador descendenteCD, LD, PVQ, CVControl de stock, tiempo restante
CMPComparadorIN1, IN2, tipoQSupervisión de límites, umbrales

📚 Recursos Técnicos y Normativos 2026

Normativas y Estándares

Recursos Raspberry Pi y Python