EMS 知识库
首页 / knowledge/代码解析/midware/inverter/parallel_comm

parallel_sci.c

源文件路径:midware/inverter/parallel_comm/parallel_sci.c 所属模块:midware/inverter/parallel_comm(并机通信 / SCI 串口通道) 代码行数:1557

1. 文件职责

本文件实现 EMS 并机系统在串口 Modbus-RTU 通道上的主从机组网、功率指令下发与遥测回读,是并机两条物理通道中的 SCI 分支(物理层可选 USART3 或 UART4,由 inverter_get_parallel_mode() 运行期决定,见 parallel_sci.c:196、parallel_sci.c:676)。它同时包含主机角色(parallel_sci_comm_host_machine_handle,parallel_sci.c:610)与从机角色(parallel_sci_comm_from_machine_handle,parallel_sci.c:1531)两套逻辑,由 midware/inverter/parallel.c:1103 / parallel.c:1107 按 g_t_inv_info.t_config_data.u8_parallel_mode 二选一调用。

数据流上,它是"主机发功率指令 → 从机执行;从机上报遥测 → 主机聚合"的完整闭环:主机侧把 g_t_host_machine_running_info[i] 的 31 个控制寄存器用功能码 0x10 逐台写入,再用功能码 0x03 读回 52 个数据寄存器写回 g_t_host_machine_running_info[i];从机侧被动响应主机的读/写请求,并把主机写下来的控制量落到 g_t_from_machine_running_info。文件由 uCOS-II 任务 task_parallel_sci(user/main.c:299 创建,midware/inverter/inverter.c:1296-1307,100 ms 周期)驱动,不使用信号量或消息邮箱。

2. 关键数据结构与宏

本文件不定义结构体/枚举,全部复用 parallel_sci.h、parallel.h 与 hardware/*。

名称类型说明
PARALLEL_HOST_MACHINE_ADDRESS宏 = 0x01(parallel_sci.h:15)主机地址常量(本文件实际未使用,收发都靠 g_t_..._networking_info 中的运行期地址)
PARALLEL_FROM_MACHINE_ADDRESS宏 = 0x02(parallel_sci.h:16)从机地址常量(同样未被本文件使用)
PARALLEL_HOST_MACHINE_READ_CODE宏 = 0x03Modbus 功能码"读保持寄存器"
PARALLEL_HOST_MACHINE_WRITE_MULTI_CODE宏 = 0x10Modbus 功能码"写多个保持寄存器"
PARALLEL_QUERY_DATA_REGISTER_ADDRESS宏 = 0x00常规数据寄存器起始地址(从机 → 主机的遥测区)
PARALLEL_QUERY_DATA_NUM宏 = 52常规数据寄存器数量(寄存器 0x00~0x33)
PARALLEL_CONTROL_DATA_REGISTER_ADDRESS宏 = 0x50控制数据寄存器起始地址(主机 → 从机的指令区)
PARALLEL_CONTROL_DATA_NUM宏 = 31控制数据寄存器数量(寄存器 0x50~0x6E)
PARALLEL_NETWORKING_CODE宏 = 0x41广播组网开始指令
PARALLEL_NET_ASSIGN_CODE宏 = 0x42地址分配指令
PARALLEL_NET_COMPLETE_CODE宏 = 0x43组网完成应答
PARALLEL_BROADCAST_ADDRESS宏 = 0x00广播地址
E_MODBUS_Recv_Parallel_State枚举(parallel_sci.h:30-44)从机解析普通 Modbus 帧的接收状态机状态:PARALLEL_ADDR_M(0) → PARALLEL_FUNC_M(1) → PARALLEL_REGISTER_BITS_8_15(2) → PARALLEL_REGISTER_BITS_0_7(3) → PARALLEL_REGISTER_NUM_8_15(4) → PARALLEL_REGISTER_NUM_0_7(5) → PARALLEL_LEN_8_15(6) / PARALLEL_LEN_0_7(7) → PARALLEL_DATA_RECVD(8) → PARALLEL_CRC_Bits_8_15(9) → PARALLEL_CRC_Bits_0_7(10) → PARALLEL_EXCEPTION_E(11)
E_MODBUS_Recv_Parallel_Networking_State枚举(parallel_sci.h:46-58)从机解析组网帧的接收状态机状态:PARALLEL_START_M → PARALLEL_BROADCAST_ADDR_M → PARALLEL_NET_FUNC_M / PARALLEL_UNIQUE_DEVICE_ID_0 → PARALLEL_UNIQUE_DEVICE_ID_1 → PARALLEL_ASSIGN_ADDR / PARALLEL_NET_EXCEPTION_E
REG_PARALLEL_QUERY_MODBUS_E枚举(parallel_sci.h:60-152)并机寄存器地址总表。前半段 0x00~0x33(52 个)为遥测寄存器;后半段 REG_SET_AEQUENCE_CONTROL_E = 0x50 起至 REG_SET_BMS_SHUTDOWN(31 个)为控制寄存器
PARALLEL_HOST_MACHINE_NETWORKING_PARAMETER_T结构体(parallel.h:82-90)主机侧每台从机的组网参数,[0] 为本机
PARALLEL_HOST_MACHINE_RUNNING_INFO_T结构体(parallel.h:101-217)主机侧"指令 + 遥测"大结构,遥测解包目标
PARALLEL_FROM_MACHINE_NETWORKING_PARAMETER_T结构体(parallel.h:92-98)从机组网状态
PARALLEL_FROM_MACHINE_RUNNING_INFO_T结构体(parallel.h:220-260)从机侧接收主机指令的结构(27 个功率/模式字段 + 升级标志)
PARALLEL_MAX_NUM宏 = 16(parallel.h:15)并机最大台数
PARALLEL_MODE_E / PARALLEL_COM_TYPE_E枚举(parallel.h:25-39)HOST_MACHINE/FORM_MACHINE;PARA_COM_TYPE_UART4/PARA_COM_TYPE_USART3/PARA_COM_TYPE_CAN/PARA_COM_TYPE_NONE
BAT_WORK_FLAG_U / UPGRADE_FLAG_U联合体(parallel.h:48-70)电池工作标志位域 / 升级标志位域

2.1 控件寄存器编址(REG_SET_*,0x50 起)

地址枚举名目标字段(g_t_from_machine_running_info)
0x50REG_SET_AEQUENCE_CONTROL_Eu8_sequencecontrol_mode
0x51REG_SET_THREE_UNBALANCE_MODE_Eu8_three_unbalance_mode(只取 bit0)
0x52REG_SET_DC_TOTAL_BAT_POWER_Es16_dc_total_set_bat_power
0x53REG_SET_DC_TOTAL_INV_POWER_Es16_dc_total_set_inv_power
0x54REG_SET_DC_TOTAL_GRID_POWER_Es16_dc_total_set_grid_power
0x55REG_SET_DC_THREE_PHASE_BAT_POWER_Es16_dc_three_phase_set_bat_power
0x56~0x58REG_SET_DC_THREE_PHASE_INV_L1/L2/L3_POWER_Es16_dc_three_phase_set_inv_a/b/c_power
0x59REG_SET_AC_TOTAL_INV_POWER_Es16_ac_total_set_inv_power
0x5AREG_SET_AC_TOTAL_BAT_CHARGE_LIMIT_POWER_Es16_ac_total_set_bat_charge_limit_power
0x5BREG_SET_AC_TOTAL_BAT_DISCHARGE_LIMIT_POWER_Es16_ac_total_set_bat_dischg_limit_power
0x5CREG_SET_AC_TOTAL_BAT_FORCED_CHARGE_ENABLE_Es16_ac_total_set_bat_forced_charge_en
0x5D~0x5FREG_SET_AC_THREE_PHASE_INV_L1/L2/L3_POWER_Es16_ac_three_phase_set_inv_a/b/c_power
0x60REG_SET_AC_THREE_PHASE_BAT_CHARGE_LIMIT_POWER_Es16_ac_three_phase_set_bat_charge_limit_power
0x61REG_SET_AC_THREE_PHASE_BAT_DISCHARGE_LIMIT_POWER_Es16_ac_three_phase_set_bat_dischg_limit_power
0x62REG_SET_AC_THREE_PHASE_BAT_FORCED_CHARGE_ENABLE_Es16_ac_three_phase_set_bat_forced_charge_en
0x63REG_SET_MPPT_PERCENTAGE_Eu16_mppt_percentage
0x64REG_SET_SAFETY_Eu8_safety
0x65REG_SET_FASESUB_Eu8_safesub
0x66REG_SET_UPGRADE_FLAG_Ehost_machine_upgrade_flag_u.u8_upgrade_flag
0x67REG_BB_MODELu8_bb_model
0x68REG_GC_CONTROL_ENu8_gc_control_en
0x69REG_FREQ_DISPATCH_ENu8_freq_dispatch_en
0x6AREG_NN_SHORT_DETECT_ENu8_nn_short_detect_state
0x6BREG_DRM_MODEu8_drm_mode
0x6CREG_BACKUP_ENu8_inv_backup_en
0x6DREG_SET_INV_ON_OFFu8_set_inv_on_off
0x6EREG_SET_BMS_SHUTDOWNu8_set_bms_shutdown

(0x6E - 0x50 + 1 = 31,与 PARALLEL_CONTROL_DATA_NUM 一致。)

2.2 文件级静态变量

名称类型说明
s_u8_parallel_modbus_recv_buffstatic u8[256](parallel_sci.c:24)并机收帧缓冲。主机侧与从机侧共用;从机解析状态机以它同时充当"原始帧缓存 + 已解析字段暂存区"
s_u8_parallel_modbus_send_buffstatic u8[256](parallel_sci.c:25)并机发帧缓冲。所有组帧函数(含主机 71 字节写帧、从机 109 字节读应答)都直接写它
s_u8_sci_timeout_cntstatic u8(parallel_sci.c:27)连续超时计数器。主机读失败累计(parallel_sci.c:534)、从机帧接收超时累计(parallel_sci.c:1245、1438)、成功时清零(parallel_sci.c:476、1379);> 5 时重初始化串口

3. 函数清单

函数名作用域说明
handle_join_requeststatic(定义 parallel_sci.c:70)主机侧处理从机入网应答:三级策略分配地址(复用已登记 ID→指定地址→空闲地址),组 0x42 分配帧下发,收到 0x43 后登记组网信息
host_send_networking_data_to_salvestatic(parallel_sci.c:174)主机广播组网帧 [0x00][0x41][0x00][CRC_L][CRC_H](5 字节)
parallel_write_datastatic(parallel_sci.c:194)按当前通道(USART3/UART4)清缓冲并发送,送 uart3_send_data / uart4_send_data,超时 100 ms
host_parallel_recv_datastatic(parallel_sci.c:214)主机侧阻塞式读一帧:把串口异步缓冲全部取入 buf,用 GetCrcCode_Modbus 校验尾部 2 字节 CRC(Modbus 低字节在前)
host_read_query_data_from_salvestatic(parallel_sci.c:290)组 Modbus 0x03 读帧(8 字节)并发送
host_set_query_data_to_salvestatic(parallel_sci.c:315)组 Modbus 0x10 写帧(addr+func+reg_hi/lo+num_hi/lo+bytecount+data+CRC)并发送;带 register_num<=256 && len_buff==register_num*2 的入参自检
parallel_comm_host_send_control_data_to_salvestatic(parallel_sci.c:348)遍历从机,把 g_t_host_machine_running_info[0](全局模式类)与 [i](每台功率类)拼成 62 字节控制数据,用 0x10 写入各从机 0x50 起的 31 个寄存器
parallel_comm_host_read_query_data_from_salvestatic(parallel_sci.c:459)遍历从机,用 0x03 读 52 个寄存器,字节数校验通过后把 104 字节逐字段解包进 g_t_host_machine_running_info[i];失败则累计 s_u8_sci_timeout_cnt,>5 时重初始化串口
parallel_sci_comm_host_machine_handle全局(parallel_sci.c:610)主机角色 SCI 通信主入口:限速 ≥200 ms、每 10 s 广播组网、每次轮询做一轮"读全部 + 写全部"、最后 15 s 心跳失联处理
parallel_modbus_send_datastatic(parallel_sci.c:674)从机侧发送封装:按通道清缓冲后调 uart3_send_data / uart4_send_data(超时 100 ms)
parallel_modbus_data_is_recv_asyncstatic(parallel_sci.c:693)从机侧"是否有整帧待读"判断:USART3 用 uart3_is_recv_async_data2(),否则 uart4_is_recv_async_data()
parallel_modbus_get_data_bytestatic(parallel_sci.c:711)从机侧从串口异步缓冲取 1 字节
Parallel_Set_Para_to_Reg_Addrstatic(parallel_sci.c:729)寄存器地址 → 从机字段的写映射:仅当 device_addr == 本机地址 时生效,31 个 case 把 value 写入 g_t_from_machine_running_info 对应字段
Parallel_get_para_from_reg_addrstatic(parallel_sci.c:870)寄存器地址 → 主机字段的读映射:52 个 case 把 g_t_host_machine_running_info[0] 的遥测字段(含 32 位字段的高/低 16 位拆分)打成 16 位寄存器值
parallel_handle_modbus_responsestatic(parallel_sci.c:1119)从机应答 0x03 读请求:组 [addr][0x03][104][52×2 字节数据][CRC] 共 109 字节并发送
parallel_handle_modbus_write_responsestatic(parallel_sci.c:1152)从机处理 0x10 写请求的数据域:把 buf 中 31 个大端 16 位值按 0x50 起的地址逐个交给 Parallel_Set_Para_to_Reg_Addr
parallel_handle_networking_responsestatic(parallel_sci.c:1173)从机应答广播组网:[0x00][0x41][uid0][uid1][mac0][mac1][mac2][CRC] 共 9 字节
parallel_handle_assign_responsestatic(parallel_sci.c:1199)从机应答地址分配:[本机地址][0x43][uid0][uid1][CRC] 共 6 字节
parallel_modbus_comm_handlestatic(parallel_sci.c:1223)从机侧普通 Modbus 从站接收状态机(地址匹配 → 功能码 → 寄存器地址 → 寄存器数 → 长度 → 数据 → CRC → 应答),300 ms 超时退出
parallel_modbus_net_comm_handlestatic(parallel_sci.c:1421)从机侧组网帧接收状态机(广播地址 → 0x41/0x42 分支 → uid 匹配 → 采纳地址并应答),300 ms 超时退出
parallel_sci_comm_from_machine_handle全局(parallel_sci.c:1531)从机角色 SCI 通信主入口:先按 10~99 ms 随机延时错峰,再按"未分配地址走组网状态机 / 已分配地址走 Modbus 状态机"分流,最后 15 s 心跳失联处理

4. 核心逻辑解析

4.1 通道选择:USART3 与 UART4 的对称切换

全部底层 I/O 都收敛到 4 个 if (inverter_get_parallel_mode() == PARA_COM_TYPE_USART3) 分支:

功能USART3 分支其他(UART4)分支位置
主机发送uart3_clear_buffer() + uart3_send_datauart4_clear_buffer() + uart4_send_dataparallel_sci.c:194-206
主机接收uart3_is_recv_async_data() + uart3_read_data_async()uart4_is_recv_async_data2() + uart4_read_data_async()parallel_sci.c:222-281
从机发送uart3_clear_buffer() + uart3_send_datauart4_clear_buffer() + uart4_send_dataparallel_sci.c:674-686
从机接收判据uart3_is_recv_async_data2()uart4_is_recv_async_data()parallel_sci.c:693-703

注意主机侧与从机侧使用的"接收就绪"函数不同:主机 USART3 用 uart3_is_recv_async_data、UART4 用 uart4_is_recv_async_data2;从机 USART3 用 uart3_is_recv_async_data2、UART4 用 uart4_is_recv_async_data。四个组合互不相同,说明 USART3/UART4 各自维护了两套接收上下文(大小写与 2 后缀),切换通道时必须成套更换,任何一处漏改都会导致收发错配。

4.2 Modbus RTU 帧格式与 CRC 约定

统一采用标准 Modbus RTU:[从站地址][功能码][数据域...][CRC_L][CRC_H],CRC 低字节在前,由 GetCrcCode_Modbus()(common/crc.c:144)计算。

帧字节布局长度
主机读请求[addr][0x03][reg_hi][reg_lo][num_hi][num_lo][CRC_L][CRC_H]8
主机写请求(控制)[addr][0x10][0x00][0x50][0x00][0x1F][62][62 字节数据][CRC_L][CRC_H]71
从机读应答[addr][0x03][104][104 字节数据][CRC_L][CRC_H]109
主机广播组网[0x00][0x41][0x00][CRC_L][CRC_H]5
从机组网应答[0x00][0x41][uid0][uid1][mac0][mac1][mac2][CRC_L][CRC_H]9
主机地址分配[0x00][0x42][uid0][uid1][addr][mac0][mac1][mac2][CRC_L][CRC_H]10
从机分配应答[addr][0x43][uid0][uid1][CRC_L][CRC_H]6

寄存器映射由 Parallel_get_para_from_reg_addr(读,parallel_sci.c:870-1111)与 Parallel_Set_Para_to_Reg_Addr(写,parallel_sci.c:729-862)两张大 switch 承载,是主机侧 Modbus 字节偏移与结构体字段之间唯一的对应表。

32 位字段按"高 16 位寄存器 + 低 16 位寄存器"拆分,例如 u32_bat_capacity 对应 REG_BAT_CAPACITY_H_E(0x04) 取 >>16、REG_BAT_CAPACITY_L_E(0x05) 取 &0xffff(parallel_sci.c:895-901)。因此主机侧解包时对应反拼:buff[11]<<24 | buff[12]<<16 | buff[13]<<8 | buff[14](parallel_sci.c:482-483)。

4.3 主机侧时序(parallel_sci.c:610-662)

if (abs(OSTimeGet() - s_u32_send_tick_200ms) < 200) return;   // 限速:最小 200ms 一轮
s_u32_send_tick_200ms = OSTimeGet();

if (abs(s_u32_parallel_send_tick_4000ms - OSTimeGet()) >= 10000) {   // 每 10s 广播组网
    s_u32_parallel_send_tick_4000ms = OSTimeGet();
    host_send_networking_data_to_salve();                 // 发 [0x00][0x41][0x00][CRC]
    memset(recv_buff, 0, 256);
    host_parallel_recv_data(recv_buff, &l_u16_recv_num);  // 阻塞收应答
    if (recv_buff[1] == PARALLEL_NETWORKING_CODE)         // 0x41
        handle_join_request(&recv_buff[2], 0);            // 从 recv_buff[2] 起是 uid0,uid1,mac0..2
}

parallel_comm_host_read_query_data_from_salve();          // 读全部从机
parallel_comm_host_send_control_data_to_salve();          // 写全部从机

for (i = 1; i < 16; i++)                                  // 15s 心跳
    if (addr_en && abs(u32_rcv_tick - OSTimeGet()) >= 15000) { ... }

实际节拍:任务周期 100 ms(inverter.c:1305)+ 本函数 200 ms 门限 → 每 200 ms 完成一轮"读 15 台 + 写 15 台"。由于 parallel_comm_host_read_query_data_from_salve 内部对每台从机都做一次发送 + 一次 host_parallel_recv_data,在从机数量多、或某台从机不在线时,一轮的最坏耗时 = 15 × 100 ms(串口发送超时)= 1.5 s,会远超 200 ms;这是纯阻塞式实现,会拖慢整个 task_parallel_sci。

地址分配算法(handle_join_request,parallel_sci.c:83-124)与 CAN 版本完全同构,同样用 switch(0) fallthrough:

case 0: 遍历 i=1..15,memcmp(unique_id, &g_t_host_machine_networking_info[i], 5) == 0 → 复用地址 i
        (ps: 结构体前 5 字节 = u8_unique_device_id[2] + u8_mac[3],pack(1) 保证连续)
case 1: send_id != 0 且地址未占用 → 用 send_id
case 2: 遍历找第一个 u8_parallel_addr_en == FALSE 的空闲地址

但本文件的 send_id 恒为 0(调用点 parallel_sci.c:639 硬编码 0),所以 case 1 实际不可达,地址分配退化为"查重复 ID → 找空闲地址"两级。分配失败(返回 0)静默 return。

分配帧下发后紧接 host_parallel_recv_data 收 0x43 应答(parallel_sci.c:141),只有 recv_buff[1] == PARALLEL_NET_COMPLETE_CODE(0x43)才落库组网信息(parallel_sci.c:143-166)。这是"两段式握手":0x42 分配 → 0x43 确认。

心跳超时(parallel_sci.c:648-661)与 CAN 版不同:除了置 u8_parallel_lost_flag = TRUE、清遥测,还额外 清 u8_parallel_addr_en = FALSE 和 u8_parallel_addr = 0。清地址后该台从机在下一轮不再被读/写,也不会再被单播,必须靠 10 s 一次的广播组网重新登记。

4.4 从机侧普通 Modbus 接收状态机(parallel_modbus_comm_handle,parallel_sci.c:1223-1412)

这是个 1 字节 1 步的状态机,state_recv 初值 PARALLEL_ADDR_M,300 ms 无进展即超时返回。

PARALLEL_ADDR_M(0)          : byte == 本机地址 ? → 清 recv_buff、存 byte、state=FUNC_M : 保持
PARALLEL_FUNC_M(1)          : byte == 0x03 或 0x10 ? 存 byte、state=REGISTER_BITS_8_15 : 回 ADDR_M
PARALLEL_REGISTER_BITS_8_15 : 存 byte,register_temp = byte<<8,state=REGISTER_BITS_0_7
PARALLEL_REGISTER_BITS_0_7  : 存 byte,register_temp |= byte
                              recv_buff[0] == 本机地址 ? state=REGISTER_NUM_8_15 : state=EXCEPTION_E
PARALLEL_REGISTER_NUM_8_15  : register_num_temp = byte<<8,存 byte,state=REGISTER_NUM_0_7
PARALLEL_REGISTER_NUM_0_7   : register_num_temp += byte
                              recv_buff[1] == 0x03 ? state=CRC_Bits_0_7(读请求无数据域,直接进 CRC)
                              recv_buff[1] == 0x10 ? state=LEN_0_7(写请求先收字节计数)
                              else state=ADDR_M
PARALLEL_LEN_0_7            : byte == register_num_temp*2 ? data_len_temp=byte、存 byte、state=DATA_RECVD
                              else state=EXCEPTION_E
PARALLEL_DATA_RECVD         : 存 byte;data_len_temp + 7 <= idx ? state=CRC_Bits_0_7
PARALLEL_CRC_Bits_0_7       : crc_16 = byte(不存缓冲),state=CRC_Bits_8_15
PARALLEL_CRC_Bits_8_15      : crc_16 |= byte<<8;crc_tmp = GetCrcCode_Modbus(recv_buff, idx)
                              crc_16 == crc_tmp ?
                                  buff[1] == 0x03 → parallel_handle_modbus_response()          // 回 109 字节
                                  buff[1] == 0x10 → parallel_handle_modbus_write_response(&buff[7], buff[0])
                                                    并置 lost=FALSE、comm_success=TRUE、addr_en=TRUE、刷新 rcv_tick
                                  return
                              else state=EXCEPTION_E

关键点:

4.5 从机组网接收状态机(parallel_modbus_net_comm_handle,parallel_sci.c:1421-1522)

PARALLEL_START_M        : byte == 0x00(广播地址)? 清缓冲、存 byte、state=BROADCAST_ADDR_M
PARALLEL_BROADCAST_ADDR_M: byte == 0x41(NETWORKING)  → 存 byte、state=NET_FUNC_M
                           byte == 0x42(NET_ASSIGN)  → 存 byte、state=UNIQUE_DEVICE_ID_0
PARALLEL_NET_FUNC_M     : 本机地址 == 0 ? 调 parallel_handle_networking_response()(回 9 字节)并 return
                                          : state=NET_EXCEPTION_E(已有地址,忽略)
PARALLEL_UNIQUE_DEVICE_ID_0: 存 byte(uid0),state=UNIQUE_DEVICE_ID_1
PARALLEL_UNIQUE_DEVICE_ID_1: 存 byte(uid1),state=ASSIGN_ADDR
PARALLEL_ASSIGN_ADDR    : recv_buff[2]==本机 uid0 && recv_buff[3]==本机 uid1 ?
                              存 byte(recv_buff[4] = 分配到的地址)
                              u8_parallel_addr = recv_buff[4]
                              parallel_handle_assign_response()(回 6 字节)
                              return
                          : state=NET_EXCEPTION_E

完整入网时序:主机 10 s 广播 0x41 → 未入网从机回 0x41 + uid + mac(9 字节)→ 主机 handle_join_request 分配地址后下发 0x42 + uid + addr + mac(10 字节)→ 从机比对 uid 匹配后采纳地址并回 0x43 + uid(6 字节)→ 主机收到 0x43 落库。此后从机走 parallel_modbus_comm_handle 分支。

同样 300 ms 超时保护,超时累计 > 5 时重初始化 UART4(parallel_sci.c:1442,注意此处硬编码 uart4_init,没有按通道判断,见风险点)。

4.6 从机主入口的随机错峰(parallel_sci.c:1531-1543)

if (g_t_from_machine_networking_info.u8_parallel_addr == 0) {
    u16 delay = 10 + (rand() % 90);
    OSTimeDlyHMSM(0, 0, 0, delay);
    parallel_modbus_net_comm_handle();
} else {
    u16 delay = 10 + (rand() % 90);
    OSTimeDlyHMSM(0, 0, 0, delay);
    parallel_modbus_comm_handle();
}

用途是避免多台从机在同一 tick 同时应答广播帧造成 485 总线碰撞。rand() 在工程内没有任何 srand() 调用,因此上电后序列固定(同一固件的多台设备会得到相同的伪随机序列),错峰效果只在"上电时刻不同"这一维度上成立。

4.7 心跳与失联

方向判据动作位置
主机判定某从机失联u8_parallel_addr_en && (now - u32_rcv_tick) >= 15000清 addr_en、清 addr、置 lost、清该台 g_t_host_machine_running_info[i]parallel_sci.c:648-661
从机判定主机失联(now - u32_rcv_tick) >= 15000置 lost、清 addr_en、清 addr、清 g_t_from_machine_running_infoparallel_sci.c:1546-1555

u32_rcv_tick 的刷新点:主机侧仅在读应答字段校验通过时(parallel_sci.c:475)与 handle_join_request 落库时(parallel_sci.c:154);从机侧仅在收到合法 0x10 写帧时(parallel_sci.c:1391)。从机对 0x03 读请求不做 rcv_tick 刷新(parallel_sci.c:1381-1384 只调 parallel_handle_modbus_response()),这是合理设计——"主机还在下发控制"才算存活。

失联后 s_u8_sci_timeout_cnt 与串口重初始化形成第二层自愈:连续 5 次以上收发失败会 uart3_init(115200) / uart4_init(115200)(parallel_sci.c:536-546、1246-1257)。

4.8 与 uCOS-II 的配合

5. 对外接口

5.1 本文件导出

符号类型调用方
parallel_sci_comm_host_machine_handle全局函数midware/inverter/parallel.c:1103(parallel_sci_comm_handle() 内)
parallel_sci_comm_from_machine_handle全局函数midware/inverter/parallel.c:1107(同上)

其余 19 个函数均为 static。文件级 3 个缓冲/计数器变量也均为 static,不对外暴露。

5.2 本文件引用的外部符号

符号来源
uart3_init / uart3_clear_buffer / uart3_send_data / uart3_read_data_async / uart3_get_data_len / uart3_is_recv_async_data / uart3_is_recv_async_data2hardware/usart3/usart3.c(usart3.h)
uart4_init / uart4_clear_buffer / uart4_send_data / uart4_read_data_async / uart4_get_data_len / uart4_is_recv_async_data / uart4_is_recv_async_data2hardware/usart4/usart4.c(usart4.h)
GetCrcCode_Modbuscommon/crc.c:144(crc.h)
GetCrcCode_8_MAXIMcommon/crc.c:126(仅在 parallel_sci.c:78-82 的注释代码中引用)
inverter_get_parallel_modemidware/inverter/inverter.c:2957(inverter.h:1971)
OSTimeGet / OSTimeDlyHMSMuCOS-II
memcmp / memset / memcpy / rand标准 C 库
printf / SERIAL_DEBUG_LOG_OUT#if (MODBUS_RTU_DEBUG == 1) / #if (DEBUG==1)&(DEBUG_MODBUS==1) 块内,默认为关
g_t_host_machine_networking_info[]、g_t_host_machine_running_info[]midware/inverter/parallel.c:36-37
g_t_from_machine_networking_info、g_t_from_machine_running_infomidware/inverter/parallel.c:39-40
g_t_parallel_total_machine_networkmidware/inverter/parallel.c:41(parallel.h:264)
PARALLEL_* 宏与全部结构体/枚举midware/inverter/parallel_sci.h、midware/inverter/parallel.h

6. 依赖关系

6.1 依赖(include)

parallel_sci.h  → 寄存器枚举 REG_PARALLEL_QUERY_MODBUS_E、两个接收状态机枚举、Modbus/组网功能码宏
parallel.h      → 结构体、PARALLEL_MAX_NUM、extern 全局变量
usart3.h        → USART3 通道 I/O
usart4.h        → UART4 通道 I/O
crc.h           → GetCrcCode_Modbus
includes.h      → OSTime* / mem* / rand 等聚合头
inverter.h      → inverter_get_parallel_mode()

6.2 被依赖

文件关系
midware/inverter/parallel.c:1103,1107唯一调用方,按 u8_parallel_mode 分发
midware/inverter/inverter.c:1296-1307(task_parallel_sci)间接驱动(100 ms 周期调用 parallel_sci_comm_handle)
user/main.c:299创建 task_parallel_sci 任务
midware/inverter/parallel.c 的 parallel_host_machine_logic_handle / parallel_from_machine_logic_handle数据消费者,读取本文件写入的 g_t_host_machine_running_info[i](主机聚合)与 g_t_from_machine_running_info(从机指令)

7. 备注与风险点

  1. Parallel_Set_Para_to_Reg_Addr 中 REG_SET_INV_ON_OFF 缺少 break(parallel_sci.c:851-856):写寄存器 0x6D 时,u8_set_inv_on_off 赋值后会直落 REG_SET_BMS_SHUTDOWN 分支,把同一个 value 也写进 u8_set_bms_shutdown。即"逆变器开关机指令"会同时触发 BMS 关机指令,是实打实的逻辑缺陷。
  1. Parallel_get_para_from_reg_addr 的地址参数判断语义错误(parallel_sci.c:874):函数形参名为 device_addr(从站地址),却与 PARALLEL_QUERY_DATA_REGISTER_ADDRESS(寄存器地址 0x00)比较。两个调用点(parallel_sci.c:1133)都传字面量 0,所以条件恒真、功能恰好正确,但这是"巧合式正确"——任何按名字理解而传入真实从站地址的调用都会让整张映射表静默失效(返回全 0)。
  1. parallel_handle_modbus_response 的应答是给"任意"读请求的固定内容:无论主机读 0x00 起的 52 个寄存器还是其它地址,从机都回 [0x03][104][52×2 字节];register_temp/register_num_temp 在收到读请求时被解析但从未被使用(parallel_sci.c:1321、1302 的赋值在 parallel_sci.c:1381 之后无消费者)。主机侧也只按固定位置解包(parallel_sci.c:478-530)。即协议只支持一种读写布局,寄存器地址字段形同装饰。
  1. 从机组网状态机完全不校验 CRC(parallel_modbus_net_comm_handle,parallel_sci.c:1421-1522):帧尾的两个 CRC 字节被当作普通字节读入(PARALLEL_UNIQUE_DEVICE_ID_1 之后即进入 PARALLEL_ASSIGN_ADDR 处理 recv_buff[4]),从未调用 GetCrcCode_Modbus。总线上任何形如 00 42 xx xx addr 的干扰字节流都可能让从机错误采纳一个不存在的地址。
  1. PARALLEL_NET_EXCEPTION_E / PARALLEL_EXCEPTION_E 在两个 switch 中都没有 case(parallel_sci.c:1454-1520、1268-1410):一旦进入异常态,状态机不会自复位,只能靠 300 ms 超时退出。异常态期间所有字节被丢弃,等于丢了最多 300 ms 的数据,属于可接受的降级,但状态枚举里给了"异常态"却没有恢复路径,设计不完整。
  1. 组网超时重初始化硬编码 UART4(parallel_sci.c:1442):uart4_init(115200) 没有像其余 4 处那样按 inverter_get_parallel_mode() 分支。若通道配成 USART3,组网失败时会把另一路串口的波特率改成 115200,而 USART3 侧的问题不被修复。相比 parallel_sci.c:1249-1256 与 538-545 的正确写法,这里明显漏改。
  1. handle_join_request 的 CRC8 校验被整段注释掉(parallel_sci.c:78-82):从机上报的组网应答只靠 memcmp(unique_id, ..., 5) 做身份确认,无任何校验。对比 CAN 版本 parallel_can.c:92-96 有 CRC8/MAXIM 保护。
  1. host_parallel_recv_data 中的 s_u32_loss_cnt 是死变量(parallel_sci.c:221):声明为 static u32 并 ++,但全文件无任何读取点,也不上报。
  1. u8_parallel_addr != 0 || u8_parallel_addr_en != FALSE 用的是 || 而非 &&(parallel_sci.c:354-355、465-466、354):只要地址非 0 就进入读写流程,即使 u8_parallel_addr_en == FALSE。失联清地址后 addr 也置 0,所以当前恰好成立;但一旦有任何路径只清 addr_en 不清 addr(CAN 侧就是这种写法,parallel_can.c:493-497),同一条件在 SCI 侧会变成"对已标记失联的机器持续读写",白白消耗 100 ms 的串口超时。
  1. 一轮轮询是全阻塞串行(parallel_sci.c:463-600):15 台从机 × (发送 ≤100 ms + 接收 ≤100 ms) 最坏可占用约 3 s,超出 task_parallel_sci 的 100 ms 周期与主函数 200 ms 限速,会显著拉长并机刷新周期。parallel_comm_host_send_control_data_to_salve 同样是 15 次阻塞写,且不读应答(0x10 的正常响应被下一轮 host_parallel_recv_data 当作垃圾丢弃或误判)。
  1. 魔法数字 5 / 6 用于串口重初始化门限(parallel_sci.c:535、1246、1439):注释缺失,含义是"连续 6 次超时"。文档化后应提为宏。
  1. Parallel_Set_Para_to_Reg_Addr 无条件截断语义的多处隐式转换:u8_sequencecontrol_mode = (value)、u8_safety = (value)(parallel_sci.c:736、816)直接把 16 位寄存器值截到 8 位;u8_three_unbalance_mode = ((value & 0x01) == TRUE)(parallel_sci.c:740)只保留 bit0。主机侧按 16 位大端发送这些 8 位量时高字节恒为 0(参见 parallel_sci.c:357-418 大量 >>8 & 0xff 对 u8 字段的冗余写法),读写路径的位宽语义不一致。
  1. abs() 作用于 u32 tick 差值(parallel_sci.c:617、631、651、1546):运行超过约 24.8 天后差值转 int 溢出,与 parallel_can.c 同源的系统性问题。
  1. rand() 无 srand()(parallel_sci.c:1535、1540):仅出现在 rand() 的 global 位置(工程内 grep srand 无结果),上电后错峰序列确定,多台同固件设备的初始延时分布可能高度相关。