PLC 编程中定时器和计数器是最常用的功能模块。本教程通过实际案例讲解各种使用模式。

标准定时器模式

通电延时 (TON)

FUNCTION_BLOCK TON_Example
VAR_INPUT
    IN : BOOL;         // 启动输入
    PT : TIME;         // 预设时间
END_VAR
VAR_OUTPUT
    Q : BOOL;          // 输出
    ET : TIME;         // 已过时间
END_VAR
VAR
    running : BOOL := FALSE;
    start_time : TIME;
END_VAR

IF IN AND NOT running THEN
    // 检测到上升沿,启动定时
    start_time := CURRENT_TIME;
    running := TRUE;
    Q := FALSE;
END_IF

IF NOT IN THEN
    // 输入信号断开,复位
    running := FALSE;
    Q := FALSE;
    ET := T#0s;
END_IF

IF running THEN
    ET := CURRENT_TIME - start_time;
    IF ET >= PT THEN
        Q := TRUE;
        running := FALSE;  // 定时完成
    END_IF
END_IF
END_FUNCTION_BLOCK

应用场景:电机启动延时、设备预热计时、信号去抖。

FUNCTION_BLOCK BlinkTimer
VAR_INPUT
    enable : BOOL;
    t_on : TIME := T#500ms;   // 导通时间
    t_off : TIME := T#500ms;  // 断开时间
END_VAR
VAR_OUTPUT
    output : BOOL;
END_VAR
VAR
    state : BOOL := FALSE;
    timer : TIME;
    start_time : TIME;
    running : BOOL := FALSE;
END_VAR

IF enable AND NOT running THEN
    running := TRUE;
    state := TRUE;
    start_time := CURRENT_TIME;
END_IF

IF NOT enable THEN
    running := FALSE;
    state := FALSE;
    output := FALSE;
    RETURN;
END_IF

IF running THEN
    timer := CURRENT_TIME - start_time;
    IF state AND timer >= t_on THEN
        state := FALSE;
        start_time := CURRENT_TIME;
    ELSIF NOT state AND timer >= t_off THEN
        state := TRUE;
        start_time := CURRENT_TIME;
    END_IF
    output := state;
END_IF
END_FUNCTION_BLOCK

应用场景:指示灯控制、报警闪烁、PWM 信号生成。

计数器模式

通用加减计数器

FUNCTION_BLOCK UniversalCounter
VAR_INPUT
    count_up : BOOL;
    count_down : BOOL;
    reset : BOOL;
    preset : INT := 100;   // 预设值
    auto_reset : BOOL := FALSE;  // 到达预设值自动复位
END_VAR
VAR_OUTPUT
    current_value : INT := 0;
    done : BOOL;
END_VAR

IF reset THEN
    current_value := 0;
    done := FALSE;
    RETURN;
END_IF

// 上升沿计数
IF count_up AND NOT count_up_prev THEN
    current_value := current_value + 1;
END_IF

IF count_down AND NOT count_down_prev THEN
    IF current_value > 0 THEN
        current_value := current_value - 1;
    END_IF
END_IF

// 到达预设值
IF current_value >= preset THEN
    done := TRUE;
    IF auto_reset THEN
        current_value := 0;
        done := FALSE;
    END_IF
END_IF

// 保存前一个状态用于上升沿检测
count_up_prev := count_up;
count_down_prev := count_down;
END_FUNCTION_BLOCK

应用场景:工件计数、流量累计、转数检测。

频率测量器

FUNCTION_BLOCK FreqMeter
VAR_INPUT
    pulse : BOOL;          // 被测脉冲信号
    gate_time : TIME := T#1s;  // 闸门时间
END_VAR
VAR_OUTPUT
    frequency : REAL;      // 测量频率 (Hz)
    valid : BOOL;
END_VAR
VAR
    count : INT := 0;
    prev_pulse : BOOL := FALSE;
    gate_timer : TIME;
    gate_start : TIME;
    measuring : BOOL := FALSE;
    total_count : INT := 0;
END_VAR

// 启动测量
IF NOT measuring THEN
    gate_start := CURRENT_TIME;
    measuring := TRUE;
    count := 0;
    valid := FALSE;
END_IF

// 脉冲计数
IF pulse AND NOT prev_pulse THEN
    count := count + 1;
END_IF
prev_pulse := pulse;

// 闸门时间到,计算频率
gate_timer := CURRENT_TIME - gate_start;
IF gate_timer >= gate_time THEN
    total_count := count;
    // 频率 = 脉冲数 / 闸门时间(秒)
    frequency := INT_TO_REAL(total_count);
    valid := TRUE;
    measuring := FALSE;
END_IF
END_FUNCTION_BLOCK

应用场景:转速测量、流量计读数、脉冲频率检测。

综合应用:电机控制系统

PROGRAM MotorControl
VAR
    // 输入
    start_btn AT %I* : BOOL;
    stop_btn AT %I* : BOOL;
    speed_sensor AT %I* : BOOL;  // 转速脉冲
    
    // 输出
    motor_contact AT %Q* : BOOL;
    alarm_led AT %Q* : BOOL;
    speed_display AT %QW* : INT;
    
    // 功能块实例
    start_delay : TON_Example;
    blinker : BlinkTimer;
    speed_meter : FreqMeter;
    run_hours : UniversalCounter;
    
    // 状态
    motor_running : BOOL := FALSE;
    fault : BOOL := FALSE;
END_VAR

// 启动延时 2 秒
start_delay(IN := start_btn, PT := T#2s);

// 停止按钮或故障时停机
IF stop_btn OR fault THEN
    motor_running := FALSE;
END_IF

// 延时完成后启动
IF start_delay.Q AND NOT motor_running THEN
    motor_running := TRUE;
END_IF

// 控制接触器
motor_contact := motor_running;

// 转速监测 (闸门时间 1 秒)
speed_meter(pulse := speed_sensor, gate_time := T#1s);
IF speed_meter.valid THEN
    speed_display := REAL_TO_INT(speed_meter.frequency);
END_IF

// 超速报警 (假设 > 1500 RPM)
IF speed_display > 1500 THEN
    fault := TRUE;
END_IF

// 故障时报警灯闪烁
blinker(enable := fault, t_on := T#200ms, t_off := T#200ms);
alarm_led := blinker.output;

最佳实践

  1. 定时器分辨率:根据精度需求选择计时方式,毫秒级用 TIME 类型,微秒级用硬件定时器
  2. 计数器溢出:INT 上限 32767,长时间运行建议使用 DINT
  3. 脉冲捕获:确保采样频率至少是被测信号的 2 倍(奈奎斯特采样定理)
  4. 避免看门狗超时:不要在定时器中执行耗时操作(如大量 IO 读写)
  5. 使用功能块封装:将定时器/计数器逻辑封装为 FUNCTION_BLOCK,提高代码复用性