命名规范
一致的命名规范提高代码可读性和可维护性。
标识符命名
| 元素 | 规范 | 示例 |
|---|---|---|
| 程序 (PROGRAM) | PascalCase | MotorControl, TempManager |
| 功能块 (FUNCTION_BLOCK) | PascalCase | PIDController, TimerDelay |
| 函数 (FUNCTION) | PascalCase | CalculateSpeed, ConvertToReal |
| 变量 | snake_case | motor_speed, output_value |
| 常量 | UPPER_SNAKE | MAX_SPEED, TIMEOUT_MS |
| 输入参数 | 名词性前缀 | enable, setpoint, mode |
| 输出参数 | 状态性结束 | done, error, value |
| 类型别名 | T_PascalCase | T_SensorData, T_AxisConfig |
IO 变量前缀
// 物理 IO 映射变量使用有意义的名称
sensor_1 AT %I* : BOOL; // 数字量输入
motor_out AT %Q* : BOOL; // 数字量输出
temp_value AT %IW* : INT; // 模拟量输入
speed_ref AT %QW* : INT; // 模拟量输出
代码结构
程序组织单元 (POU) 命名
功能_对象_修饰符
示例:
- Check_Temperature
- Control_Motor_Speed
- Monitor_Battery_Level
功能块设计原则
- 单一职责:每个功能块只完成一个逻辑功能
- 显式接口:所有输入/输出在 VAR_INPUT/VAR_OUTPUT 中声明
- 状态封装:内部状态使用 VAR 声明,不对外暴露
- 初始化安全:所有变量有明确的初始值
// 好的设计
FUNCTION_BLOCK ValveControl
VAR_INPUT
open_command : BOOL;
close_command : BOOL;
feedback_open : BOOL;
feedback_closed : BOOL;
END_VAR
VAR_OUTPUT
control_signal : BOOL;
state : INT := 0;
error : BOOL := FALSE;
END_VAR
VAR
timeout : INT := 0;
prev_open : BOOL := FALSE;
END_VAR
程序模块化
MainProgram/
├── Main.st # 主程序(调度入口)
├── MotorControl.st # 电机控制
├── TempControl.st # 温度控制
├── SafetyLogic.st # 安全逻辑
├── Communication.st # 通信模块
└── Diagnostics.st # 诊断模块
代码风格
缩进与格式
PROGRAM WellFormatted
VAR
input_value : INT := 0;
output_value : INT := 0;
counter : INT := 0;
END_VAR
// IF 语句使用 4 空格缩进
IF input_value > 100 THEN
output_value := 100;
counter := counter + 1;
ELSE
output_value := input_value;
END_IF
// 嵌套结构
FOR i := 0 TO 10 BY 1 DO
IF array[i] > threshold THEN
result := result + array[i];
END_IF
END_FOR
注释规范
// 1. 文件头部注释(每个 POU)
// ==========================================
// 功能: 电机速度 PID 控制器
// 作者: KVPAC Team
// 版本: 1.0
// 更新: 2026-06-04
// ==========================================
// 2. 行内注释
counter := counter + 1; // 每周期计数递增
// 3. 段注释(逻辑块说明)
// === 启动逻辑 ===
// 顺序: 自检 → 预充电 → 启动
错误处理
防御性编程
// 1. 检查输入范围
IF input < 0 OR input > 10000 THEN
error := TRUE;
error_code := ERR_INVALID_INPUT;
RETURN; // 提前返回,避免无效操作
END_IF
// 2. 除数检查
IF divisor <> 0 THEN
result := dividend / divisor;
ELSE
result := 0;
error := TRUE;
END_IF
// 3. 数组边界检查
IF index >= 0 AND index < ARRAY_SIZE THEN
value := data_array[index];
ELSE
value := 0;
error := TRUE;
END_IF
状态机错误恢复
CASE state OF
IDLE:
IF start THEN state := INIT; END_IF
INIT:
IF init_done THEN
state := RUNNING;
ELSIF init_failed THEN
state := ERROR;
END_IF
RUNNING:
IF fault_detected THEN
state := ERROR;
error_time := CURRENT_TIME;
END_IF
ERROR:
// 自动恢复
IF (CURRENT_TIME - error_time) > T#5s THEN
state := IDLE;
error := FALSE;
END_IF
END_CASE
性能优化
避免的写法
// ❌ 避免:不必要的中断执行流
WAIT 100;
// 使用定时器代替 WAIT
// ❌ 避免:大循环中的 IO 操作
WHILE index < 1000 DO
// IO 操作很慢,不要在循环中频繁执行
data := read_io(index);
index := index + 1;
END_WHILE
// ❌ 避免:重复计算
result1 := (a + b) * c / d;
result2 := (a + b) * e / f;
// 改为:
temp := a + b;
result1 := temp * c / d;
result2 := temp * e / f;
// ❌ 避免:频繁的 IF 判断
IF mode = 1 THEN mode1_logic(); END_IF
// 使用 CASE 更高效
CASE mode OF
1: mode1_logic();
END_CASE
推荐的写法
// ✅ 使用功能块封装可复用逻辑
// ✅ 避免 Main 程序过于庞大
// ✅ 使用 CONSTANT 而不是硬编码数字
// ✅ 优先使用 DINT 而非 INT(避免溢出)
// ✅ 循环中使用 FOR(固定次数)而非 WHILE(可能死循环)
版本控制
推荐的 .gitignore
build/
*.bin
*.out
logs/
node_modules/
.DS_Store
提交信息规范
feat: 添加 Modbus TCP 通信功能
fix: 修复定时器溢出导致的看门狗复位
refactor: 重构 IO 映射配置结构
docs: 更新 ST 语言语法参考文档
test: 添加 Modbus 通信测试用例
调试技巧
- 使用断言:在关键逻辑后添加调试输出变量
- 状态追踪:将所有状态机当前状态输出到调试变量
- 性能分析:测量各任务的执行周期时间
- 日志分级:ERROR > WARNING > INFO > DEBUG
// 调试输出示例
debug_current_state := state;
debug_cycle_time := cycle_end - cycle_start;
debug_memory_usage := GET_MEMORY_USAGE();
安全检查清单
- 所有 IO 变量有默认值
- 除法运算有除零保护
- 数组访问有边界检查
- 状态机有默认分支 (ELSE)
- 看门狗配置合理
- 错误状态可恢复
- 紧急停止功能正常
- 异常情况有日志输出