afci_comm.c
afci_self_check_handle,:663)4.4 日志分帧拉取与成组(afci_fault_log_data_handle,:827)4.5 上传握手:与网络任务的协作(跨任务共享 g_t_afci_log_tab)4.6 与 uCOS-II 的配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/afci/afci_comm.c所属模块:midware / afci(电弧故障断路器 AFCI 通信与日志) 代码行数:951
1. 文件职责
本文件是 EMS 与 AFCI 板(电弧故障检测模块)之间的常规通信层,负责 5 条业务:读 AFCI 软版本号并按机型重排为可读字符串、AFCI 通道自检(工厂模式进入/退出 + 通道使能与结果回读)、告警清除与故障日志的逐帧拉取、AFCI 软重启、以及清除 AFCI 故障记录。
它处在「逆变器通信任务 → USART2 总线 → AFCI 板」这一环:所有帧都经 alphaess_write_data() / alphaess_afci_recv_data() 走 USART2(与阿尔法逆变器主控共用同一条串口),被 midware/inverter/alphaess/alphaess_comm_usart.c 的 alphaess_afci_comm_function()(:5045)周期驱动。
拉取到的 AFCI 故障日志以 ASCII HEX 字符串形式滚动缓存到 SDRAM 定址全局量 g_t_afci_log_tab,再由 net/ems.c 的 fill_send_afci_log() 打包成 CMD_SEND_AFCI_LOG_DATA(0x62) JSON 上报云端,云端回复后由 net/server.c:7084 回调 set_afci_log_data_upload_send_status() 收尾 —— 因此本文件同时是 AFCI 日志上传链路的生产者。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
ALPHAESS_AFCI_STATE_E | 枚举 | afci_comm.h:59。自检状态机三态:AFCI_START_STATE(0)、AFCI_SELF_CHECK_STATE(1)、AFCI_FAULT_STATE(2) |
AFCI_LOG_UPDATA_STATE_E | 枚举 | afci_comm.h:66。日志上传状态:AFCI_DATA_NONE(0x00)、AFCI_DATA_UPDATA(0x01)、AFCI_UPDATA_COMPLETE(0x02);后两者直接作为 JSON UploadStatus 字段上报 |
AFCI_QRY_RET_E | 枚举 | afci_comm.h:73。日志帧查询返回值:AFCI_QRY_OK、AFCI_QRY_FAIL、AFCI_QRY_END(本帧无有效数据 = 拉完)、AFCI_QRY_BUF_FULL |
AFCI_LOG_DATA_TABLE | 结构体 | afci_comm.h:80。AFCI 日志上传的全局唯一状态块,见下方字段说明 |
g_t_afci_log_tab | 全局变量 | afci_comm.c:27。AFCI_LOG_DATA_TABLE 的唯一定义,用 __attribute__((at(SDRAM_AFCI_FAULT_LOG_ADDR))) 定址到 SDRAM(sdram.h:56,保留 2 KB);被 extern 于 afci_comm.h:95 |
AFCI_SLAVR_ADDRESS | 宏 | afci_comm.h:22,值 0x01。AFCI 常规 Modbus 从站地址。注意与逆变器从站地址 ALPHAESS_SLAVR_ADDRESS(0x03) 不同,靠首字节区分报文归属 |
SELF_CHECK_CHANNEL | 宏 | afci_comm.h:23,值 0x3F。4 路 AFCI 检测通道全使能位图(bit0~bit5) |
AFCI_ADDR_REG_WARNING_MESSAGE / AFCI_NUM_WARNING_MESSAGE | 宏 | afci_comm.h:25-26,寄存器 0x0002,1 个寄存器。读出的 16 位值即“无告警”判别码 |
AFCI_ADDR_REG_QUERY_SOFT_VERSION2 / AFCI_NUM_QUERY_SOFT_VERSION2 | 宏 | afci_comm.h:28-29,寄存器 0x0010,11 个寄存器(22 字节)软版本区 |
AFCI_ADDR_REG_SELF_CHECK_FUNCTION_ENABLE / AFCI_NUM_SELF_CHECK_FUNCTION_ENABLE | 宏 | afci_comm.h:31-32,寄存器 0x0024,1 个寄存器。写 = 使能自检通道,读 = 回读自检状态 |
AFCI_ADDR_REG_SET_SOFT_REBOOT / ALPHAESS_NUM_SET_SOFT_REBOOT | 宏 | afci_comm.h:34-35,寄存器 0x0025,写 1 触发软重启(注意第二个宏沿用了 ALPHAESS_ 前缀,命名不一致) |
AFCI_ADDR_REG_SEND_FAC_MODE_CMD / AFCI_NUM_SEND_FAC_MODE_CMD | 宏 | afci_comm.h:37-38,寄存器 0x0026,1 = 进入工厂模式,0 = 退出 |
AFCI_ADDR_REG_SEND_CLEAR_INFO_CMD / AFCI_NUM_SEND_CLEAR_INFO_CMD | 宏 | afci_comm.h:40-41,寄存器 0x00E0,写 0 清除信息 |
AFCI_FUNC_REG_SEND_CLEAR_DATA_CMD / AFCI_NUM_SEND_CLEAR_DTA_CMD | 宏 | afci_comm.h:43-44,功能码 0xE0(拼写 DTA 为笔误),用于 8 字节裸帧清故障记录 |
AFCI_ADDR_REG_GET_LOG_DATA_CMD / AFCI_ADDR_REG_REPEAT_LOG_DATA_CMD | 宏 | afci_comm.h:46-50,功能码 0xE1(取新日志)/ 0xE2(重发当前帧),只占 1 字节 |
AFCI_GET_MAX_LOG_DATA_LEN | 宏 | afci_comm.h:52,值 64。单帧最多拉取 64 字节日志数据 |
AFCI_LOG_MAX_FRAME_NUM / AFCI_LOG_TOTAL_MAX_LEN | 宏 | afci_comm.h:54-55。17 帧 × 64 字节 = 1088 字节,即 afci_log_buff 的容量(注意存的是 HEX 文本,实际承载 544 字节原始数据) |
AFCI_LOG_GROUP_SIZE | 宏 | afci_comm.h:57,值 8。每凑满 8 帧日志就打成一个上传包 |
DEBUG_ALPHAESS_AFCI | 宏 | afci_comm.h:16,值 0。本文件与 alphaess_comm_usart.c:403 的调试打印总开关,当前关闭 |
AFCI_LOG_DATA_TABLE 字段 | — | u32_afci_log_send_len(已拼接长度,单位字节=2×字符数)、u32_afci_log_send_net_len(发往云端长度)、u16_afci_log_seq(当前帧序号,实际未被本文件使用)、u8_afci_log_status、u8_afci_log_send_flag(待发标志)、u8_afci_log_rev_ok_flag(云端已应答标志)、afci_log_pack_flag[32](包标识/时间戳)、u32_afci_log_send_offset(发送次数)、afci_log_buff[1088] |
本文件不定义任何结构体/枚举/宏,全部来自 afci_comm.h;唯一的文件级变量是 s_u8_afci_recv_buf。
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
send_query_data_to_afci | static | :51。组 0x03 读保持寄存器请求帧(8 字节)并发送 |
ems_send_ctrl_data_to_afci | 全局 | :77。组 0x06 写单寄存器 / 0x10 写多寄存器帧并发送,带长度合法性校验 |
alphaess_afci_recv_data | 全局 | :119。从 USART2 同步收一帧 AFCI 应答,校验从站地址与 Modbus CRC,成功回填长度 |
afci_log_init | 全局 | :200。清零 g_t_afci_log_tab 并复位 u8_ems_set_inv_afci_log_flag |
get_version_sort_map | static | :213。按逆变器机型类别返回“版本字节重排表”及其长度 |
parse_afci_version_data | static | :242。校验版本帧头、按重排表抽取版本字符、置 u8_support_afci_flag |
query_afci_software_version | 全局 | :292。查询 AFCI 软版本(唯一上电/升级后识别 AFCI 是否存在的入口) |
inverter_read_ver_afci | 全局 | :339。返回版本字符串指针,先强制末字节为 0 保证可当 C 串用 |
send_afci_clear_info | 全局 | :352。向寄存器 0x00E0 写 0,清除 AFCI 信息(全工程无调用点) |
set_afci_fac_mode | 全局 | :402。向寄存器 0x0026 写 fac_mode,1 进工厂模式 / 0 退出 |
send_afci_clear_warning_message | 全局 | :442。向寄存器 0x0002 写 0 清除告警报文 |
send_afci_self_check_enable | static | :482。向寄存器 0x0024 写 SELF_CHECK_CHANNEL(0x3F) 使能全部自检通道,成功后打自检计时戳 |
query_afci_self_check_status | static | :523。读寄存器 0x0024,取应答第 5 字节低 4 位,为 0 判定自检“已完成” |
query_afci_warning_message | static | :568。读寄存器 0x0002,与机型对应基准码(S 相 0x2100 / T 相 0x2700)比对判定“无告警” |
set_afci_soft_reboot | 全局 | :623。向寄存器 0x0025 写 1 软重启 AFCI |
afci_self_check_handle | 全局 | :663。AFCI 自检状态机(3 态),成功清 bit27、失败置 bit27 并要求停机 |
send_clear_afci_log_data | 全局(未在头文件声明) | :747。发 8 字节裸帧 [01][E0][00][00][00][00][CRC] 清除 AFCI 故障记录 |
query_afci_log_data | static | :773。按功能码 + 帧序号拉一帧日志,HEX 化后追加进 afci_log_buff |
afci_fault_log_data_handle | 全局 | :827。日志分帧拉取主状态推进器,凑 8 帧成组、连续 3 次失败收尾 |
set_afci_log_data_upload_send_status | 全局 | :926。云端应答后的回调:解锁发送、累加 u32_afci_log_send_offset,整包传完则清零并清 AFCI 报错 |
get_afci_soft_version | 全局 | :947。返回 g_t_inv_info.t_afci_info.u8_afci_soft_version 指针(全工程无调用点) |
共 21 个函数:14 个全局 + 7 个 static。无函数指针、无回调、无递归。
4. 核心逻辑解析
4.1 通信协议:两种帧格式共用一条 USART2
本文件所有报文都通过 alphaess_write_data()(alphaess_comm_usart.c:262)下发、alphaess_afci_recv_data() 收取,物理层是 USART2(hardware/usart2/usart2.c)。接收侧由 USART2_IRQHandler 的 IDLE 空闲中断 OSSemPost(g_t_uart2_rcv_sem) 触发(usart2.c:94),alphaess_afci_recv_data 里的 usart2_is_recv_async_data() 实际就是 OSSemPend(..., 500) ——500 ms 就是本模块的应答超时。
帧格式 A:标准 Modbus RTU(功能码 0x03 / 0x06 / 0x10)
读请求 (send_query_data_to_afci, 8B)
[0x01][0x03][addr_H][addr_L][num_H][num_L][CRC_L][CRC_H]
↑ 起始寄存器 ↑ 寄存器个数
写单寄存器 0x06 (ems_send_ctrl_data_to_afci, 8B)
[0x01][0x06][addr_H][addr_L][val_H][val_L][CRC_L][CRC_H]
写多寄存器 0x10 (ems_send_ctrl_data_to_afci, 7+n B)
[0x01][0x10][addr_H][addr_L][num_H][num_L][byte_cnt][data...][CRC_L][CRC_H]
读应答
[0x01][0x03][byte_cnt][data...][CRC_L][CRC_H]
校验:CRC16-Modbus,低字节在前;GetCrcCode_Modbus()(common/crc.c)
ems_send_ctrl_data_to_afci()(:87)在组帧前做三重长度校验:register_num <= ALPHAESS_MAX_REGISTER_NUM(100)、len_buff <= 200、且 len_buff == register_num * 2;只有全部满足才填寄存器个数与数据域。0x06 分支用 memcpy(l_u8_tx_buf + 4, tx_buff, 2)(:100)直接覆盖刚写好的“寄存器个数”字段,这是 Modbus 0x06 “地址+值”语义的正确写法,但也意味着 0x06 分支里 l_u8_tx_buf[4][5] 的写入是多余的。
alphaess_afci_recv_data()(:119)的校验链:usart2_is_recv_async_data() 拿信号量 → 轮询 usart2_get_data_len() ≥ 2(最多等 500 ms)→ 逐字节搬进调用者缓冲 → 判 buf[0] == AFCI_SLAVR_ADDRESS(0x01) → 用末两字节与 GetCrcCode_Modbus(buf, len-2) 比对。任一步失败 *len = 0。它不做长度上限检查,只按 l_u16_len 全量拷贝。
帧格式 B:AFCI 日志命令帧(功能码 0xE0 / 0xE1 / 0xE2)
取日志请求 (query_afci_log_data → send_query_data_to_afci)
[0x01][0xE1或0xE2][seq_H][seq_L][0x00][0x40][CRC_L][CRC_H]
↑ 帧序号占用了“起始寄存器地址”字段 ↑ 64
日志应答
[0x01][func][b2][b3][b4][b5][data...][CRC_L][CRC_H]
└── 4 字节长度/状态,全 0 表示本帧无数据(取完)
有效数据下标 = 6 .. (recv_len-3),即长度 = recv_len - 8
清故障记录裸帧 (send_clear_afci_log_data, 8B)
[0x01][0xE0][0x00][0x00][0x00][0x00][CRC_L][CRC_H]
注意 0x00E0 在本文件有两种身份:send_afci_clear_info() 把它当寄存器地址(配 0x06 功能码),send_clear_afci_log_data() 把它当功能码(裸帧)。
超时与重试:本文件不做自动重发,重试由上层状态机完成 —— 自检失败靠 s_u8_err_cnt(阈值 3,:720),日志靠 l_u8_err_cnt(阈值 3,:884)+ 功能码 0xE2 重发当前帧(:849),升级靠 afci_upgrade.c 的 s_u8_rev_err_cnt。
4.2 版本号解析:机型相关的字节重排
AFCI 板返回的 22 字节版本区不是顺序的版本字符,不同机型(贝瓦单相 S5 / 贝瓦三相 T10)的排布不同,因此用重排表映射(get_version_sort_map,:213):
机型(get_inverter_type_category()) | 重排表 | 长度 |
|---|---|---|
INVERTER_TYPE_BYTE_WATT_S(贝瓦单相) | s_map_s5 = {4,3,6,5,8,7,9,10,12,14} | 10 |
INVERTER_TYPE_BYTE_WATT_T(贝瓦三相) | s_map_t10 = {4,3,6,5,8,7,9,10,11} | 9 |
INVERTER_TYPE_NON_BYTE_WATT / default | NULL | 0 |
parse_afci_version_data()(:242)的执行顺序:
- 长度守卫
min_required_len = 3 + map_len; - 帧头三连判:从站地址
0x01、功能码0x03、字节数必须精确等于AFCI_NUM_QUERY_SOFT_VERSION2 * 2 = 22; u8_afci_soft_version[0] = 'V',随后按map[i]取字节逐个填入[i+1],每个下标再做一次map[i] >= recv_len越界守卫(:264);- 以
[map_len + 1] = '\0'收尾; - AFCI 存在性判定:
version[1] != '\0' && version[1] != 0xFF→u8_support_afci_flag = TRUE,否则 FALSE。
第 5 步是本模块的“探针”:整机是否具备 AFCI,完全由这一次读版本的成败决定。query_afci_software_version()(:292)无论成功失败都会把 u8_query_afci_device_info_flag 清为 FALSE(:314 / :325),因此该查询每次请求只尝试一次;成功时额外刷新 u32_ems_inv_comm_tick(逆变器通信心跳)。
4.3 自检状态机(afci_self_check_handle,:663)
┌──────────────── AFCI_START_STATE ────────────────┐
│ 若 !s_u8_afci_self_check_sent: │
│ set_afci_fac_mode(1) 进工厂模式 │
│ send_afci_clear_warning_message() │
│ send_afci_self_check_enable() 写 0x3F 并打时间戳 │
│ → sent=TRUE, state=SELF_CHECK │
└──────────────────────┬───────────────────────────┘
▼
AFCI_SELF_CHECK_STATE
条件:(OSTimeGet() - u32_ems_afci_self_check_tick) > 8000
├─ query_afci_self_check_status() == TRUE
│ └─ query_afci_warning_message() == TRUE
│ → ems_set_fault_bit(27, FALSE) // 清 AFCI 自检故障
│ u8_afci_self_check_flag = FALSE
│ u8_afci_self_check_result = TRUE
│ u8_afci_heart_flag = TRUE
│ send_afci_clear_warning_message()
│ set_afci_fac_mode(0) // 退出工厂模式
│ set_info_status(AFCI_SELFCHECK_STATUS)
│ → AFCI_START_STATE
│ └─ 否则 err_cnt++, → AFCI_FAULT_STATE
└─ 否则 err_cnt++, → AFCI_FAULT_STATE
AFCI_FAULT_STATE
sent = FALSE;send_afci_clear_warning_message();重打自检时间戳
若 err_cnt > 3:u8_afci_self_check_flag = FALSE
u8_afci_self_check_result = FALSE
u8_afci_self_result_shut_down = TRUE // 要求停机
err_cnt = 0
set_info_status(AFCI_SELFCHECK_STATUS)
ems_set_fault_bit(27, TRUE) // 置 AFCI 自检故障
否则 → AFCI_START_STATE(重来一轮)
关键点:
u32_ems_afci_self_check_tick的唯一写点在send_afci_self_check_enable()成功分支(:498),因此 8 s 等待窗口从“通道使能应答成功”开始计时。AFCI_FAULT_STATE每次进入都会把s_u8_afci_self_check_sent清 FALSE,于是下一轮AFCI_START_STATE会重新走一遍「进工厂模式 → 清告警 → 使能通道」。err_cnt累加到 4 才真正停机判故障。ems_set_fault_bit(27, ...)(data/data.c:338)操作的是g_t_ems_info.u_ems_fault.u32_all的 bit27,即EMS_FAULT_AFCI_SELF_CHECK_FAULT(data/data.h:59),上报故障码E0085(data/fault_warn_log/fault_warn_log.c:546)。set_info_status(AFCI_SELFCHECK_STATUS)(net/ems.c:2722)只是g_stEMSInfo.u32InfoType |= 0x100000(net/ems.h:269),用来触发net/ems.c:4949-4955的“立即上报信息”分支,使自检结果尽快上云。- 本函数被调用的前置条件在
alphaess_comm_usart.c:4996的afci_self_check_condition():u8_afci_self_check_flag && u8_quary_afci_enable && u8_afci_fault_enable && u8_afci_enable && AFCI_RUN_MODE_ALLOW(u8_run_mode)(模式 ∈ {cNormalMode,cUPS,cByPassMode},alphaess_comm_usart.h:178)。函数内部没有对u8_afci_self_check_flag的读取,该标志只用于外层门控,说明设计上自检是“一次性任务”。 - 自检成功后
u8_afci_heart_flag = TRUE,随后alphaess_comm_usart.c:5207-5210会在每次 1 s 运行数据查询后调set_afci_fac_mode(0),即持续把 AFCI 摁在非工厂模式。
4.4 日志分帧拉取与成组(afci_fault_log_data_handle,:827)
函数用 3 个 static 变量保存跨调用进度:l_u16_frame_seq(初值 1,注释明确“从第 0 包查不上来”)、l_u16_logic_cnt、l_u8_err_cnt。
单帧逻辑 query_afci_log_data(func, seq, &valid_len)(:773):
- 发
[0x01][func][seq][0x00][0x40]请求; - 应答校验四连:长度 > 0、
buf[0]==0x01、buf[1]==func、buf[2..5]不全为 0; log_end = recv_len - 3(去掉 2 字节 CRC 与 1 字节索引修正),valid_len = log_end - 6 + 1;valid_len == 0→AFCI_QRY_END;- 缓冲上界守卫:
u32_afci_log_send_len + valid_len*2 >= sizeof(afci_log_buff)→AFCI_QRY_BUF_FULL; - 逐字节
sprintf(&buff[send_len + i*2], "%02X", raw[6 + i])追加(:804),send_len += valid_len*2。
主函数流程:
if (u8_afci_log_send_flag) return; // 上一包还没被网络任务发走,防重入
ret = query(0xE1 取新日志, seq)
if OK → seq++, logic_cnt++, err_cnt = 0
else ret = query(0xE2 重发当前帧, seq) // 0xE1 失败立即换 0xE2
if OK → seq++, logic_cnt++, err_cnt = 0
else → err_cnt++
若 (logic_cnt >= 8) 或 (ret == AFCI_QRY_BUF_FULL): // 8 帧成组 或 缓冲将满
buff[send_len] = '\0'
u32_afci_log_send_net_len = u32_afci_log_send_len
u8_afci_log_send_flag = TRUE // 交给网络任务
u8_afci_log_rev_ok_flag = TRUE
u32_afci_log_send_len = 0 // 本组结束,缓冲复位复用
u8_afci_log_status = AFCI_DATA_UPDATA(1)
logic_cnt = 0
若 err_cnt >= 3:
if logic_cnt > 0 : // 尾部残包,作为最后一组发出
buff[send_len] = '\0';net_len = send_len;send_len = 0
u8_afci_log_status = AFCI_DATA_UPDATA;logic_cnt = 0
else : // 已无新帧 → 本次传输结束
u8_afci_log_status = AFCI_UPDATA_COMPLETE(2)
l_u16_frame_seq = 1;err_cnt = 0
u8_ems_set_inv_afci_log_flag = FALSE
send_len = 0;net_len = 0
memset(afci_log_buff, 0, 1088)
send_flag = TRUE;rev_ok_flag = TRUE
设计要点:
- “连续 3 次失败”被当作“AFCI 已经没有更多日志”的判据,而非通信故障 —— 末尾无数据时 AFCI 回的帧
buf[2..5]全 0,走AFCI_QRY_FAIL分支累加err_cnt。因此单帧丢失与日志取完在协议层被混同,靠重试次数区分。 l_u16_logic_cnt只在logic_cnt > 0时才走“结束”分支,保证不会在组中丢失残包。afci_log_buff是按组复用的滚动缓冲(每组发出后send_len归零),只有u8_afci_log_status == AFCI_UPDATA_COMPLETE时才真正清空整个缓冲。整轮下来u32_afci_log_send_len清零,但u32_afci_log_send_offset(发送次数)继续累加。AFCI_QRY_END与AFCI_QRY_FAIL在本函数中不做区分,都落到err_cnt++。
4.5 上传握手:与网络任务的协作(跨任务共享 g_t_afci_log_tab)
[逆变器任务] [网络任务]
afci_fault_log_data_handle()
send_flag = TRUE ─────────────────────────► net/ems.c:4966 判定条件成立
(send_flag && rev_ok_flag &&
!is_goto_get_update_file_status() &&
check_force_upgradable_flag() &&
Get_Resume_Status_Current()==NONE)
→ STATUS_SEND_AFCI_LOG_DATA
net/ems.c:6207 fill_send_afci_log()
打包 JSON:UploadDataTime / SN /
PackFlag / UploadStatus(=u8_afci_log_status) /
FileOffset(=u32_afci_log_send_offset) /
FileLen(=u32_afci_log_send_net_len) /
FileData(=afci_log_buff)
net/ems.c:6211 rev_ok_flag = FALSE
↑ 云端回复 CMD_SEND_AFCI_LOG_DATA(0x62)
└────────────────────────────────────────── net/server.c:7084
set_afci_log_data_upload_send_status()
send_flag = FALSE → 允许拉下一组
rev_ok_flag = TRUE
send_offset++
若 status == COMPLETE:
memset(整个 g_t_afci_log_tab)
send_clear_afci_log_data() // 清 AFCI 记录
send_afci_clear_warning_message()
fill_send_afci_log() 只在 u32_afci_log_send_offset == 0 时刷新 afci_log_pack_flag 时间戳(net/ems.c:2677-2688),所以 PackFlag 是“本次日志传输开始时刻”,而不是每包时间。
4.6 与 uCOS-II 的配合
本文件不创建任务、不创建信号量/邮箱,只消费 g_t_uart2_rcv_sem(由 USART2 IDLE 中断 post):
alphaess_afci_recv_data()→usart2_is_recv_async_data()→OSSemPend(g_t_uart2_rcv_sem, 500)(usart2.c:213),这是本文件唯一的阻塞点,每次收发最长阻塞 500 ms;自检轮内最多 3 次收发(清告警、使能、读状态、读告警),最坏叠加约 2 s。alphaess_write_data()内部先usart2_clear_buffer()(usart2.c:149)——它会OSSemSet(g_t_uart2_rcv_sem, 0)清掉信号量并复位读写指针,因此每次发送都会丢弃尚未被读走的旧数据。- 调用方是逆变器通信任务:
midware/inverter/inverter.c:1116alphaess_inverter_comm_usart()→alphaess_comm_usart.c:5339 alphaess_afci_comm_function()→ 本文件各函数。同一任务内串行执行,所以 AFCI 帧与逆变器帧在总线上不会交错,但会互相挤占(见风险点 1)。
5. 对外接口
本文件导出(供谁调用)
| 导出名 | 调用方 |
|---|---|
query_afci_software_version | alphaess_comm_usart.c:5076(升级完成后 50 s)、:5411(u8_query_afci_device_info_flag 为 TRUE 时,标志在 :1205 置位) |
afci_self_check_handle | alphaess_comm_usart.c:5060(alphaess_afci_comm_function(),受 :4996 afci_self_check_condition() 门控) |
afci_fault_log_data_handle | alphaess_comm_usart.c:5071(u8_ems_set_inv_afci_log_flag 为 TRUE 时;该标志在 :5513 由逆变器 AFCI 故障位置位) |
set_afci_fac_mode | 本文件 :675/:698;alphaess_comm_usart.c:5209(心跳置位后持续退出工厂模式) |
send_afci_clear_warning_message | 本文件 :677/:696/:718/:935;alphaess_comm_usart.c:5065(u8_afci_clear_warning_info_flag) |
set_afci_log_data_upload_send_status | net/server.c:7084(收到 CMD_SEND_AFCI_LOG_DATA(0x62) 应答) |
inverter_read_ver_afci | net/ems.c:2270(信息上报 JSON 的 AfciVersion 字段) |
afci_log_init | alphaess_comm_usart.c:5412 |
ems_send_ctrl_data_to_afci | afci_upgrade.c:55 |
alphaess_afci_recv_data | afci_upgrade.c:56 |
set_afci_soft_reboot | afci_upgrade.c:432(升级首轮失败时软重启 AFCI) |
g_t_afci_log_tab | net/ems.c:2672-2713(fill_send_afci_log)、:4966-4973(发送条件)、:6211(清 rev_ok_flag);net/server.c:7084 |
send_afci_clear_info | 无调用点(仅 afci_comm.h:107 声明) |
get_afci_soft_version | 无调用点(仅 afci_comm.h:105 声明) |
本文件调用的外部函数/变量
| 名称 | 来源 |
|---|---|
g_t_inv_info(t_afci_info / t_config_data / t_run_info) | midware/inverter/inverter.h:1914(AFCI_TABLE_T 定义于 :1887) |
g_t_alphaess_comm_info | alphaess_comm_usart.h:715(ALPHAESS_TABLE_T) |
alphaess_write_data / ALPHAESS_DATA_RCV_TIMEOUT(500) / ALPHAESS_FUNC_READ_HOLDING_REGISTER(0x03) / ALPHAESS_FUNC_WRITE_SIGNAL_HOLDING_REGISTER(0x06) / ALPHAESS_FUNC_WRITE_MULTIPLE_HOLDING_REGISTER(0x10) / ALPHAESS_MAX_REGISTER_NUM(100) / ALPHAESS_INV_RECV_BUFFER_SIZE(256) | midware/inverter/alphaess/alphaess_comm_usart.{c,h} |
usart2_is_recv_async_data / usart2_get_data_len / usart2_read_data_async | hardware/usart2/usart2.h |
GetCrcCode_Modbus | common/crc.h |
OSTimeGet / OSTimeDlyHMSM | uCOS-II |
ems_set_fault_bit | data/data.c:338(声明 data.h:128) |
set_info_status / AFCI_SELFCHECK_STATUS | net/ems.c:2722 / net/ems.h:269 |
get_inverter_type_category / INVERTER_TYPE_BYTE_WATT_* | midware/inverter/inverter.c:8003 / inverter.h:403 |
SDRAM_AFCI_FAULT_LOG_ADDR | hardware/sdram/sdram.h:56 |
memset / memcpy / sprintf / abs | C 标准库 |
6. 依赖关系
依赖(本文件 include):inverter.h、alphaess_comm_usart.h、afci_comm.h、ems.h、usart2.h、data.h、sdram.h、crc.h(:13-20)。其中 ems.h 只为 set_info_status / AFCI_SELFCHECK_STATUS,sdram.h 只为 SDRAM 定址宏。
被依赖:
midware/inverter/alphaess/alphaess_comm_usart.c:30(主要驱动方)midware/afci/afci_upgrade.c:15net/ems.c:56(fill_send_afci_log等)net/server.c:55(set_afci_log_data_upload_send_status回调)net/afci_download.c:20
7. 备注与风险点
- USART2 与逆变器主控共享总线,AFCI 帧会阻塞逆变器通信:
alphaess_write_data()/alphaess_recv_data()与 AFCI 用的是同一个g_t_uart2_rcv_sem和同一 RX 缓冲。alphaess_write_data()每次都调usart2_clear_buffer()把信号量与缓冲清零,如果 AFCI 帧与逆变器帧在同一轮内靠近发出,先发的应答会被后发的清缓冲动作丢掉。当前靠同一任务串行 + 每帧 500 ms 超时兜底,没有优先级或仲裁机制。 g_t_afci_log_tab是跨任务无保护共享:逆变器任务在afci_fault_log_data_handle()里持续写afci_log_buff/send_len/send_flag,网络任务在set_afci_log_data_upload_send_status()里对整个结构体memset(:933)。二者只靠u8_afci_log_send_flag做粗粒度握手,memset与逆变器侧正在进行的sprintf追加之间没有临界区,存在数据撕裂与长度错乱的可能。alphaess_afci_recv_data()不做缓冲上界检查::143-150按usart2_get_data_len()的长度全量写入调用者缓冲。本文件内调用者给的是 256 字节(ALPHAESS_INV_RECV_BUFFER_SIZE),但afci_upgrade.c:28给的是 11 字节(见该文件风险点)。该函数是公共导出,安全性取决于调用者自觉。alphaess_afci_recv_data()只校验从站地址不校验功能码,send_afci_clear_info/set_afci_fac_mode/send_afci_clear_warning_message等写操作在*len > 0时把应答当成成功(只判buf[0]、buf[1],不校验寄存器地址与长度),因此只要 AFCI 回一个同功能码的帧就算成功,无法发现“写错寄存器”的应答异常。query_afci_self_check_status()只取低 4 位::546(s_u8_afci_recv_buf[4] & 0x0f),且l_u16_afci_check_status声明为u16却用u16 = u8 & 0x0f,高字节recv_buf[3]被完全忽略。若 AFCI 把状态放在寄存器高字节,本判定恒为 0(恒判“完成”)。query_afci_warning_message()对非贝瓦机型会误判::574-581,只有 S/T 两类机型才赋0x2100/0x2700,INVERTER_TYPE_NON_BYTE_WATT时l_u16_afci_sub保持 0,于是“寄存器读回 0”会被判为告警已清(TRUE)。虽然调用链上门控了机型,但函数自身无守卫。ems_send_ctrl_data_to_afci()长度校验失败时会发 0 字节帧::87的if不成立时l_u8_len保持 0,:109仍调用alphaess_write_data(l_u8_tx_buf, 0, ...)—— 实际效果是只清空接收缓冲和信号量,调用者随后等 500 ms 超时。属于静默失败。send_query_data_to_afci()用“起始寄存器地址”字段承载日志帧序号(:79+send_query_data_to_afci(log_func_code, l_u16_frame_seq, 64)):语义上是复用,功能码 0xE1/0xE2 并非标准 Modbus,读代码时容易误判为非法寄存器地址。0x00E0双重语义:既是寄存器地址(AFCI_ADDR_REG_SEND_CLEAR_INFO_CMD,配 0x06)也是功能码(AFCI_FUNC_REG_SEND_CLEAR_DATA_CMD,裸帧)。两个常量值与命名高度相似,是易错点。send_clear_afci_log_data()是全局函数但未在afci_comm.h声明:外部无法正常调用(-Wmissing-prototypes会告警),当前只有本文件:934使用。同理get_afci_soft_version()与send_afci_clear_info()已声明却全工程无调用点,属死代码。u16_afci_log_seq字段从未被本文件读写:AFCI_LOG_DATA_TABLE里的u16_afci_log_seq(afci_comm.h:84)注释为“当前帧序号”,但实际帧序号是afci_fault_log_data_handle的static l_u16_frame_seq,结构体字段成为遗留。l_u16_frame_seq只在“整轮结束”分支复位为 1(:901):若传输在中途被u8_ems_set_inv_afci_log_flag清零打断(如故障位恢复),static帧序号会停在中途值,下一轮会从中间帧开始拉,而非从头。abs()用于u32差值::135abs(OSTimeGet() - l_u32_recv_tick) > 500,参数为无符号,abs接收int。OSTimeGet()约 49.7 天回绕一次,回绕瞬间该判断可能失效(与工程内其余同类写法一致,非本文件独有)。sprintf逐字节写 HEX 的效率与安全::804每字节一次sprintf,且%02X输出到send_len + i*2。上界守卫生效时安全,但 3 帧/秒的调用频率下 64 次sprintf明显低效,可换成查表写入。- 注释与实现不符:
:617-621的函数头注释写的是set_afci_fac_mode(u8 fac_mode),但实际函数是set_afci_soft_reboot();:766-771注释名写afci_fault_log_data_handle,实际是query_afci_log_data()。均为复制粘贴遗留。 ALPHAESS_NUM_SET_SOFT_REBOOT命名前缀不一致:软重启是 AFCI 的寄存器,却用ALPHAESS_前缀(afci_comm.h:35),与同文件其余AFCI_*常量风格不符。