3、主控软件架构:IEC 61131-3 标准、任务调度模型、主循环与中断处理、状态机设计模式
好,咱们进入第三章。这一章聊的是主控软件的骨架——架构。
你想想看,风机主控系统说白了就是一个实时控制系统。它得在几十毫秒内完成风速采样、扭矩计算、变桨指令下发、并网状态判断……这一大串活儿。如果软件架构搭得不好,代码写得再漂亮也没用。
我个人习惯,在开始写任何一行代码之前,先把架构图在脑子里过三遍。这一章我就把这几年的经验掰开揉碎,跟你聊聊IEC 61131-3标准、任务调度、主循环与中断,还有状态机。
3.1 IEC 61131-3 标准:不只是编程语言
很多人一听到IEC 61131-3,第一反应就是“哦,PLC编程语言标准”。对,但不全对。它定义了五种编程语言:梯形图(LD)、功能块图(FBD)、顺序功能图(SFC)、结构化文本(ST)、指令表(IL)。但在风机主控领域,我们最常用的是ST和FBD。
为什么?因为风机控制逻辑复杂,用梯形图画一个PID调节器,你会疯掉的。ST语言类似Pascal,写算法、做数学运算非常顺手。FBD则适合做信号链路的可视化,比如把风速、转速、功率一路串起来。
我在项目中遇到过一件事:某次现场调试,同事用梯形图写了一个变桨控制逻辑,结果因为扫描周期太长,导致变桨响应滞后,风机超速差点触发安全链。后来改成ST+FBD混合编程,把关键路径放到中断里,问题才解决。
核心要点:IEC 61131-3 不只是语言规范,它定义了变量作用域(全局/局部)、数据类型(包括自定义结构体)、程序组织单元(POU:程序、功能块、函数)。这些才是架构设计的基石。
3.2 任务调度模型:谁先跑,谁后跑
风机主控系统里,任务是有优先级的。你不能让“记录日志”这种活儿抢了“紧急停机”的CPU时间。对吧?
常见的调度模型有两种:
- 周期调度:每个任务固定时间片,比如10ms跑一次主控制环,100ms跑一次通信任务。
- 优先级抢占调度:高优先级任务来了,低优先级任务先靠边站。
我建议在风机主控里用混合模型。说白了,就是周期调度为主,关键中断用优先级抢占。
| 任务名称 | 周期 | 优先级 | 说明 |
|---|---|---|---|
| 主控制环 | 10ms | 高 | 扭矩、变桨、偏航核心算法 |
| 状态机扫描 | 20ms | 中 | 运行/停机/故障状态切换 |
| 通信任务 | 100ms | 低 | SCADA、PLC间数据交换 |
| 日志记录 | 500ms | 低 | 非实时,可被抢占 |
嗯,这里要注意:任务周期不是越短越好。我曾经见过一个项目,把主控制环设成1ms,结果CPU负载飙到90%,连看门狗都来不及喂。后来改成10ms,一切正常。你得算算你的CPU跑一个完整控制环需要多少时间,留出余量。
3.3 主循环与中断处理:两条腿走路
主循环是“大管家”,它负责把各个任务串起来。但中断是“特种兵”,专门处理紧急事件。
典型的主循环结构长这样:
// 伪代码:风机主控主循环
while (true) {
读取输入信号(风速、转速、温度等);
执行状态机(判断当前状态);
执行主控制算法(扭矩计算、变桨控制);
输出控制指令(变桨角度、转矩给定);
执行通信任务(发送数据到SCADA);
喂看门狗;
等待下一个周期开始;
}
但光有主循环不够。比如电网突然掉电,你等主循环扫描到那一刻再处理?黄花菜都凉了。这时候就需要中断。
中断处理的原则:快进快出。中断服务程序里只做最紧急的事,比如置一个标志位、记录一个时间戳。真正的处理逻辑放到主循环里去做。
避坑指南:我曾经在中断里直接调用了变桨控制的函数,结果因为中断嵌套,导致变量被重入,整个控制逻辑乱掉了。从那以后,我定了一条铁律:中断里只设标志,不跑逻辑。
3.4 状态机设计模式:风机的“大脑”
风机主控本质上是一个状态机。它不可能一直发电,它得经历:待机、启动、并网、运行、停机、故障……这些状态。
状态机设计得好不好,直接决定了系统的健壮性。我见过最糟糕的代码,是用一堆if-else嵌套来模拟状态切换,结果加了几个状态之后,代码变成一团乱麻。
正确的做法:用二维表驱动或者状态模式。
下面这张图是我常用的风机主控状态机结构:
这张图里,每个椭圆代表一个状态,箭头代表状态切换条件。你看,从“待机”到“启动”只需要风速达标。但从“运行”到“停机”可能是电网故障,也可能是人为指令。
个人经验:状态机里一定要有一个“故障”状态,并且所有状态都能跳转到故障状态。我见过一个设计,只有“运行”状态才能进故障,结果在“启动”过程中出了异常,系统直接死机。嗯,这种低级错误,犯一次就够了。
3.5 代码示例:一个简单的状态机实现
下面用ST语言写一个简单的风机状态机骨架。实际项目里会比这个复杂得多,但核心逻辑是一样的。
// 状态枚举
TYPE E_State :
(
ST_IDLE,
ST_STARTING,
ST_GRID_CONNECTED,
ST_RUNNING,
ST_STOPPING,
ST_FAULT
);
END_TYPE
// 状态机功能块
FUNCTION_BLOCK FB_StateMachine
VAR_INPUT
WindSpeedOk : BOOL;
SpeedSynced : BOOL;
GridConnected : BOOL;
StopCommand : BOOL;
FaultTrigger : BOOL;
ResetCommand : BOOL;
END_VAR
VAR_OUTPUT
CurrentState : E_State;
END_VAR
VAR
State : E_State := ST_IDLE;
END_VAR
// 状态切换逻辑
CASE State OF
ST_IDLE:
IF WindSpeedOk THEN
State := ST_STARTING;
END_IF
ST_STARTING:
IF FaultTrigger THEN
State := ST_FAULT;
ELSIF SpeedSynced THEN
State := ST_GRID_CONNECTED;
END_IF
ST_GRID_CONNECTED:
IF FaultTrigger THEN
State := ST_FAULT;
ELSIF GridConnected THEN
State := ST_RUNNING;
END_IF
ST_RUNNING:
IF FaultTrigger THEN
State := ST_FAULT;
ELSIF StopCommand THEN
State := ST_STOPPING;
END_IF
ST_STOPPING:
IF FaultTrigger THEN
State := ST_FAULT;
ELSIF NOT WindSpeedOk THEN
State := ST_IDLE;
END_IF
ST_FAULT:
IF ResetCommand THEN
State := ST_IDLE;
END_IF
END_CASE
CurrentState := State;
END_FUNCTION_BLOCK
这段代码你看懂了吗?每个状态只处理自己该做的事。故障状态是“收容站”,所有异常都往这里丢。复位之后回到待机,重新开始。
3.6 总结一下
这一章我们聊了:
- IEC 61131-3 标准不只是语言,更是架构规范
- 任务调度要分优先级,关键任务用中断
- 主循环是骨架,中断是应急通道
- 状态机是风机的“大脑”,用表驱动或状态模式来设计
我个人觉得,软件架构这东西,没有绝对的对错。但有一条:简单、清晰、可维护,永远是第一位的。你想想看,一个风机要跑20年,中间不知道要换多少波工程师来维护。如果你的架构让人一看就头大,那这代码迟早要重写。
一句话记住本章:主控软件架构 = 合理的任务调度 + 干净的中断处理 + 清晰的状态机。这三样做好了,剩下的就是填业务逻辑了。
公众号:蓝海资料掘金营,微信deep3321