3. FTU软件总体架构设计

FTU(馈线终端单元)的软件不是“把功能堆进一个main循环”就能稳定运行的。它要同时面对毫秒级的保护逻辑、秒级的遥测刷新、分钟级的故障录波上传,还要在电磁干扰强、供电波动大的柱上环境里长期无人值守。因此,软件总体架构设计的核心,是先把“实时性、可靠性、可维护性”三个约束落到分层与任务划分上,再谈具体代码怎么写。

一、嵌入式实时操作系统选型

FTU的软件通常运行在ARM Cortex-M或Cortex-A系列处理器上,选型时先看三个硬指标:中断响应延迟、任务切换时间、以及是否支持优先级抢占。以典型的馈线保护场景为例,过流保护从采样到出口跳闸要求控制在几毫秒内,如果RTOS的任务切换抖动超过100μs,保护定值再准也会失配。因此,选型的第一条原则是:保护相关逻辑必须能运行在最高优先级任务或中断上下文中,且切换时间可预测。

常见候选包括FreeRTOS、RT-Thread、μC/OS-II/III以及VxWorks。对于中小型FTU,FreeRTOS和RT-Thread更常见:前者内核精简、移植成熟,后者自带设备框架和FinSH控制台,便于调试。若FTU需要同时处理IEC 61850 MMS、GOOSE和大量录波数据,且对内存保护有要求,则可考虑带MMU的Cortex-A平台配合Linux+RT补丁,或直接选用VxWorks。选型时建议按以下维度打分:内核尺寸、抢占延迟、优先级数量、是否支持静态内存分配、是否有成熟BSP、以及授权成本。示例评分表如下(数值为教学假设,非实测):

候选RTOS典型抢占延迟内核ROM优先级数适用场景
FreeRTOS约10~30μs6~10KB32中小型FTU,成本敏感
RT-Thread约10~40μs10~20KB256需设备框架与调试终端
μC/OS-III约10~20μs10~15KB256认证要求较高的场合
VxWorks约5~15μs较大256高端FTU,IEC 61850全栈

选型确定后,必须明确一条纪律:所有任务栈、消息队列、信号量在编译期静态分配,禁止在运行中动态malloc。柱上FTU长期运行,内存碎片是隐性故障源。若必须动态申请,应使用RTOS自带的内存池并限定上限。

二、任务划分与优先级设计

任务划分不是按“功能模块”切,而是按实时性等级和数据依赖关系切。FTU中典型任务可归为四类:

  • 采样与保护任务:最高优先级,周期1ms或由ADC中断触发,完成交流采样、有效值计算、过流/零序判断,必要时直接驱动出口继电器。
  • 通信与协议任务:中高优先级,处理IEC 60870-5-101/104、Modbus或IEC 61850报文,周期10~100ms,不能阻塞保护任务。
  • 人机与维护任务:中低优先级,处理本地按键、液晶刷新、参数读写,允许被抢占。
  • 后台与录波任务:最低优先级,负责故障录波存储、历史数据上传、文件系统维护,在空闲时运行。

优先级分配遵循“速率单调”思想:周期越短、截止时间越紧,优先级越高。例如采样任务优先级设为最高(如configMAX_PRIORITIES-1),通信任务次之,录波任务最低。任务间通过消息队列传递采样结果和故障事件,避免共享全局变量。下面给出一个典型任务参数示例(教学假设):

任务名          优先级   周期/触发      栈大小   通信方式
SamplingTask    30      1ms定时器      512字    ADC中断→队列
ProtectionTask  29      由采样队列触发  768字    队列接收
CommTask        20      20ms轮询       1024字   队列+信号量
HmiTask         10      100ms          512字    共享参数区+互斥量
RecordTask      5       事件触发       2048字   文件系统+队列

注意:保护任务与采样任务如果合并为一个任务,可减少一次队列拷贝,但会拉长单次执行时间。工程上常把采样中断只做“取数+打时间戳”,把计算和保护判断放在高优先级任务中,兼顾实时性与可维护性。

三、驱动层/协议层/应用层分层原则

分层的目的不是“好看”,而是让换硬件、换规约、改逻辑时互不牵连。FTU软件通常分为四层:硬件驱动层、操作系统适配层、协议与业务层、应用层。

驱动层直接操作寄存器或调用厂商HAL,向上提供统一接口,例如adc_read_samples()、do_set_output()、uart_send()。这一层不允许出现规约解析或业务判断。驱动层的关键是“可替换”:换ADC芯片时只改驱动,不动上层。

协议层负责报文组帧、解析、超时重传和链路管理。IEC 60870-5-104的APCI/ASDU解析、Modbus功能码处理都放在这里。协议层通过回调或消息队列把“遥测值”“遥控命令”交给应用层,不直接操作继电器。

应用层实现FTU的业务逻辑:遥测越限判断、故障检测、遥控出口、定值管理、录波触发。应用层只调用协议层和驱动层提供的接口,不关心报文具体字节。

层间调用方向应单向向下,数据向上通过队列或回调。禁止应用层直接写寄存器,也禁止驱动层调用应用层函数。这样在调试时,可以用模拟驱动替换真实硬件,在PC上跑应用层逻辑。

FTU软件分层架构与任务映射 应用层 故障检测 / 遥测越限 / 遥控出口 / 定值管理 / 录波触发 ProtectionTask / HmiTask 协议层 IEC 60870-5-104 / Modbus / IEC 61850 / 报文组帧与解析 CommTask OS适配层 任务调度 / 队列 / 信号量 / 定时器 / 静态内存池 FreeRTOS / RT-Thread 硬件驱动层 ADC采样 / 继电器出口 / UART / 以太网 / RTC / Flash SamplingTask / RecordTask 调用/下发 数据/事件上报 分层原则:上层只依赖下层接口,禁止跨层直接访问寄存器;数据向上用队列/回调,命令向下用函数调用。

四、代码组织与工程结构

代码目录应直接反映分层,而不是按“人”或“日期”分。推荐结构如下:

ftu_project/
├── app/                # 应用层:保护、遥测、遥控、录波
│   ├── protection.c
│   ├── telemetry.c
│   └── record.c
├── protocol/           # 协议层:104、Modbus、61850
│   ├── iec104.c
│   └── modbus_slave.c
├── osal/               # OS适配层:任务、队列、定时器封装
│   ├── osal_task.c
│   └── osal_queue.c
├── drivers/            # 驱动层:ADC、DO、UART、ETH
│   ├── adc_drv.c
│   └── do_drv.c
├── config/             # 定值、点表、硬件配置
│   ├── points_table.h
│   └── protection_cfg.h
└── main.c              # 仅负责初始化与启动任务

每个模块提供.h接口和.c实现,接口中不暴露其他层的内部结构体。例如应用层只看到protocol_send_telemetry(),看不到104报文的APCI字段。这样在更换通信规约时,应用层代码几乎不动。

实践建议:在main.c中只做三件事——硬件初始化、OS初始化、创建任务。所有业务逻辑放在任务函数里,避免在启动阶段执行耗时操作导致看门狗复位。
常见误区:把保护判断写在通信任务里,或让录波任务直接操作继电器。前者会导致保护动作被报文处理阻塞,后者会造成出口误动。保护出口必须由最高优先级任务或中断直接控制,且出口前要做“启动+动作”双重确认。

最后,架构设计要留出可观测性:每个任务应有运行计数、栈使用峰值、队列溢出计数,通过调试串口或后台报文可读取。这些数据在柱上FTU出现偶发死机或通信中断时,是定位问题的第一手依据。架构不是一次画完就固定,而是在调试中逐步收敛——先保证保护实时性,再优化通信吞吐,最后完善录波与维护接口。