3. 交通信号灯状态机

交通信号灯状态机 学习卡片

任务说明

构建一个交通信号灯FSM,包含红、黄、绿三种常规状态及闪烁模式(黄灯闪烁,用于夜间或故障),支持定时自动切换,并支持外部触发的紧急模式(红灯常亮,禁止通行)。

状态定义与迁移

  • 常规循环:红(5s) → 绿(4s) → 黄(2s) → 红,循环往复。
  • 闪烁模式:黄灯以1s间隔闪烁(亮0.5s/灭0.5s),不参与常规循环。
  • 紧急模式:红灯常亮,忽略定时迁移,直到紧急信号解除。
  • 模式切换:emergency信号置位时进入紧急模式;flash信号置位时进入闪烁模式;两个信号均无效时回到常规循环(从红灯开始)。

核心代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef enum { RED, GREEN, YELLOW, FLASH, EMERGENCY } State;
typedef enum { TIMER_EXPIRED, EMERGENCY_ON, EMERGENCY_OFF, FLASH_ON, FLASH_OFF } Event;

typedef struct {
    State state;
    int timer;          // 剩余秒数
    int flash_toggle;   // 闪烁亮/灭标志
} FSM;

void init(FSM *fsm) {
    fsm->state = RED;
    fsm->timer = 5;
    fsm->flash_toggle = 0;
}

void handle_event(FSM *fsm, Event ev) {
    switch (fsm->state) {
        case RED:
            if (ev == TIMER_EXPIRED) { fsm->state = GREEN; fsm->timer = 4; }
            break;
        case GREEN:
            if (ev == TIMER_EXPIRED) { fsm->state = YELLOW; fsm->timer = 2; }
            break;
        case YELLOW:
            if (ev == TIMER_EXPIRED) { fsm->state = RED; fsm->timer = 5; }
            break;
        case FLASH:
            if (ev == FLASH_OFF) { fsm->state = RED; fsm->timer = 5; }
            break;
        case EMERGENCY:
            if (ev == EMERGENCY_OFF) { fsm->state = RED; fsm->timer = 5; }
            break;
        default: break;
    }
    // 模式切换优先级:紧急 > 闪烁 > 常规
    if (ev == EMERGENCY_ON) { fsm->state = EMERGENCY; fsm->timer = -1; }
    else if (ev == FLASH_ON && fsm->state != EMERGENCY) {
        fsm->state = FLASH; fsm->timer = 1;
    }
}

void tick(FSM *fsm) {
    if (fsm->state == EMERGENCY) return;  // 紧急模式不计时
    if (fsm->state == FLASH) {
        fsm->flash_toggle = !fsm->flash_toggle;
        handle_event(fsm, fsm->flash_toggle ? FLASH_ON : FLASH_OFF);
        return;
    }
    fsm->timer--;
    if (fsm->timer <= 0) handle_event(fsm, TIMER_EXPIRED);
}

void print_state(FSM *fsm) {
    const char *name[] = {"RED", "GREEN", "YELLOW", "FLASH", "EMERGENCY"};
    if (fsm->state == FLASH)
        printf("%s (%s)\n", name[fsm->state], fsm->flash_toggle ? "ON" : "OFF");
    else
        printf("%s (timer=%d)\n", name[fsm->state], fsm->timer);
}

int main() {
    FSM fsm;
    init(&fsm);
    printf("初始状态: ");
    print_state(&fsm);

    // 模拟10秒常规运行
    for (int i = 0; i < 10; i++) {
        sleep(1);
        tick(&fsm);
        printf("t=%d: ", i+1);
        print_state(&fsm);
    }

    // 触发紧急模式
    printf("\n-- 紧急模式 --\n");
    handle_event(&fsm, EMERGENCY_ON);
    print_state(&fsm);
    sleep(2);
    tick(&fsm);  // 紧急模式下tick不改变状态
    print_state(&fsm);

    // 解除紧急,进入闪烁
    printf("\n-- 闪烁模式 --\n");
    handle_event(&fsm, EMERGENCY_OFF);
    handle_event(&fsm, FLASH_ON);
    for (int i = 0; i < 4; i++) {
        sleep(1);
        tick(&fsm);
        print_state(&fsm);
    }

    // 恢复正常
    printf("\n-- 恢复正常 --\n");
    handle_event(&fsm, FLASH_OFF);
    print_state(&fsm);
    return 0;
}

编译与运行

gcc -o traffic_light traffic_light.c
./traffic_light

验证方法

  • 常规循环:观察前10秒输出,状态应按 RED→GREEN→YELLOW→RED 顺序迁移,且各状态持续秒数与定义一致(红5s、绿4s、黄2s)。
  • 紧急模式:触发后状态显示EMERGENCY,即使调用tick(),状态仍保持EMERGENCY不变。
  • 闪烁模式:解除紧急并开启闪烁后,状态在FLASH(ON)与FLASH(OFF)之间交替,每秒切换一次。
  • 恢复正常:关闭闪烁后回到RED状态,定时器重置为5,重新进入常规循环。

预期输出(节选)

初始状态: RED (timer=5)
t=1: RED (timer=4)
t=2: RED (timer=3)
t=3: RED (timer=2)
t=4: RED (timer=1)
t=5: GREEN (timer=4)
t=6: GREEN (timer=3)
t=7: GREEN (timer=2)
t=8: GREEN (timer=1)
t=9: YELLOW (timer=2)
t=10: YELLOW (timer=1)
-- 紧急模式 --
EMERGENCY (timer=-1)
EMERGENCY (timer=-1)
-- 闪烁模式 --
FLASH (ON)
FLASH (OFF)
FLASH (ON)
FLASH (OFF)
-- 恢复正常 --
RED (timer=5)

关键说明

  • 紧急模式优先级最高,tick()中直接返回,不进行任何状态迁移。
  • 闪烁模式通过flash_toggle翻转产生亮/灭效果,每次tick()触发一次翻转。
  • 模式解除后统一回到红灯状态,保证安全起始条件。