evc.c
evc_control,evc.c:958)4.4 策略与功率计算4.5 充电时间段判定(evc_charge_time,evc.c:515)4.6 通讯丢失处理与"清空字段"技巧4.7 全局变量的协同5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/evc/evc.c所属模块:midware/evc(一代交流充电桩 SMILE-EVCT11 通信中间件) 代码行数:1146
1. 文件职责
本文件是 EMS 主控对一代交流充电桩(EVC,SMILE-EVCT11,单桩)的完整驱动实现,通过 USART3(RS485)以 Modbus-RTU 主站方式周期读写充电桩。
数据流上游:evc_gat_sys_data()(evc.c:145)从 g_t_logic_info、meter_read_*()、logic_read_*() 抓取电网/逆变器/电池快照;核心环节:evc_calculated_total_power()(evc.c:619)与 evc_calculate_single_station_power()(evc.c:790)按充电策略算出"剩余可分配给充电桩的功率",再由 send_cmd_evc()(evc.c:352)写成 Modbus 0x10 写多寄存器帧下发;下游:功率、电量、故障字通过 get_evc_*() 接口被 logic.c、net/ems.c、flash_data_store.c、fault_warn_log.c 消费。
驱动方:midware/interface/interface.c:351 的 Modbus 任务在 get_evc_enable_flag()==TRUE 且非升级状态下每 100ms 调用一次 evc_control()(evc.c:958),内部再自行分频出 1s 读、10s 控的节拍。
本文件与二代桩实现 evc_ii.c 是互斥的两套协议:同一时刻只启用其一(interface.c:347-357),二者共用 g_evc_send_buf/g_evc_recv_buf。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
g_evc_send_buf[EVC_BUF_LEN] | 全局数组 | 发送缓存 256B,一代/二代/升级三模块共用(evc.c:25) |
g_evc_recv_buf[EVC_BUF_LEN] | 全局数组 | 接收缓存 256B(evc.c:26) |
g_t_evc_setting_info | 全局变量(EVC_SETTING_TABLE_T) | 单桩设置表;本文件会就地改写其 u8_ev_phase、u16_max_charg_curr 字段(evc.c:811/824/834) |
g_t_evc_config_table | 全局变量(EVC_CONFIG_TABLE_T) | 整体配置表;本文件决定性地使用 u16_max_consume_cur(evc.c:630)与 u8_plot(evc.c:699) |
g_t_evc_run_info | 全局变量(EVC_RUN_INFO_T) | 运行数据表,本文件的读写核心 |
g_t_evc_debug_data | 全局变量(EVC_DEBUG_DATA_T) | 记录系统 RTC 与两个时间段的分钟刻度,供调试界面(evc.c:529-533) |
s_t_sys_data | static(EVC_SYS_DATA_T) | 系统侧输入快照,每个 10s 控制周期刷新一次(evc.c:33) |
s_t_evc_control_para | static(EVC_CONTROL_PARA_T) | 功率计算中间量:A/B/C 相与总剩余可充功率(evc.c:34) |
s_evc_rtc_datetime | static(struct RTC_DateTime) | 从 SYSTEM_DATETIME 拷入的 RTC 副本(evc.c:35) |
SYSTEM_DATETIME | extern(struct RTC_DateTime) | 系统全局时间,用于充电时间段的分钟刻度换算(evc.c:37) |
DEBUG_EVC | 宏(evc.h:12) | 调试打印总开关,为 0 时编译期剔除全部 printf |
EVC_BUF_LEN | 宏(evc.h:14) | 256,收发缓存长度上限 |
EVC_SLOW / EVC_NORMAL / EVC_QUICK / EVC_SPECIFIED_POWER | 枚举(evc.h:34) | switch(g_t_evc_config_table.u8_plot) 的四个分支(evc.c:701/721/761/773) |
EMS_START / CARD_START | 枚举(evc.h:20) | evc_state_u.BIT.startmode 的两种启动来源(evc.c:1070/1074) |
CHARGE_NORMAL / CONDITION_CHARGE / NO_CONDITION_CHARGE | 枚举(evc.h:50) | 条件充电延迟状态机取值(evc.c:906/915/921/933) |
EVC_NULL_MODEL / EVC_EMS_START_MODEL / EVC_CARD_START_MODEL / EVC_AVAILABLE_MODEL | 枚举(evc.h:42) | get_evc_chargemodel() 的四种输出 |
EVC_REMOTE_DENY 语义 | 常量(一代未宏化) | evc_set_u.BIT.remotcontrol:0=允许、1=禁止(evc.c:854/862) |
TIME_FORCE_CHARGE_MODE、FORCE_BAT_FEEDIN_MODE、TIME_PEAK_SHAVING_MODE、TIME_ALLOW_DISCHG_MODE、CMD_/SYS_/BAT_/USER_FORCE_CHARGE_MODE、TIME_FORBID_DISCHG_MODE | 枚举(logic.h) | logic_read_sequencecontrol_mode() 返回的调度模式,决定电池充放限值的裁剪(evc.c:651-683) |
UPS_RESERVE_SOC_MAX / UPS_RESERVE_SOC_MIN | 宏(logic.h) | UPS 预留 SOC 的使能门限判定(evc.c:653/667) |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
evc_master_send_cmd | static(evc.c:60) | 组 Modbus-RTU 帧(addr + fun + data + CRC16-Modbus 低字节在前)并经 uart3_send_data 发出 |
evc_master_recv_data | static(evc.c:82) | 500ms 窗口内轮询接收,校验尾部 CRC16,成功回填 *len,失败回填 0 |
evc_gat_sys_data | static(evc.c:145) | 抓取电网/电表/逆变器/电池数据到 s_t_sys_data,并刷新 RTC 副本 |
get_evc_id_info | static(evc.c:205) | 读 0x0100 起 38 个寄存器:SN、启动功率、相数、软/硬件版本、最大功率 |
get_evc_run_info | static(evc.c:261) | 读 0x9100 起 19 个寄存器:三相电压电流、总功率、电量、故障字、CP 电压、占空比、状态字 |
send_cmd_evc | static(evc.c:352) | 写 0x0100 起 9 个寄存器:总/分相设定功率 + 控制字(evc_set_u) |
set_evc_energy | static(evc.c:472) | 写 0x00A2 起 2 个寄存器为 0,清零充电桩电量 |
evc_charge_time | static(evc.c:515) | 把 RTC 时刻与两个时间段的时分换算成分钟刻度,判定 u8_time1_flag/u8_time2_flag |
evc_calculated_total_power | static(evc.c:619) | 按充电策略计算系统侧"剩余可充总功率",并裁剪电池充放限值 |
evc_calculate_single_station_power | static(evc.c:790) | 计算单桩总设定功率,内含启动功率预留与条件充电延时状态机 |
evc_control | 全局(evc.c:958) | 周期调度总入口:清电量、1s 读、10s 控制+下发、通讯丢失检测 |
get_evc_enable_flag | 全局(evc.c:1043) | 返回单桩使能;未使能时 memset 清空整张运行表 |
get_evc_chargemodel | 全局(evc.c:1060) | 依据 charge_en/startmode 折算上报云端的充电模式 |
get_evc_type | 全局(evc.c:1094) | 直接返回 FW_TYPE_EVC("SMILE-EVCT11") |
get_evc_power_total | 全局(evc.c:1106) | 返回 s32_evc_power_total |
get_evc_energy_total | 全局(evc.c:1118) | 返回 u32_evc_energy_total(可清零侧) |
clear_evc_energy | 全局(evc.c:1130) | 置 clear_energy_flag,由 evc_control 异步执行清零 |
set_evc_charge_flag | 全局(evc.c:1142) | 直接写入 u8_charge_flag(云端/本地充停指令) |
4. 核心逻辑解析
4.1 Modbus-RTU 帧格式与超时重试
三条命令都走同一个组帧函数 evc_master_send_cmd(u8 addr, u8 fun, u8 *buf, u8 len, u16 timeout)(evc.c:60):
+--------+--------+------------------+---------------+---------------+
| addr | fun | data[len] | CRC16 低字节 | CRC16 高字节 |
| 1B=0x01| 1B | len 字节 | | |
+--------+--------+------------------+---------------+---------------+
- CRC 由
GetCrcCode_Modbus()(common/crc.h)生成,低字节先发(evc.c:69-70)。 - 发送前先
uart3_clear_buffer()清空接收缓冲(evc.c:72),避免上一帧残包被当成本帧响应。 len由u8承载,data 域最大 255B;实际最大帧为send_cmd_evc的 5+18=23 字节 data + 4 字节头尾 = 27 字节。
响应接收 evc_master_recv_data(u8 *buf, u16 *len)(evc.c:82)的机制:
- 记录起始
OSTimeGet()(evc.c:90),超时窗口硬编码为 500 ms(evc.c:94),超时即*len = 0返回。 - 处理 tick 回绕:若
OSTimeGet() < l_u32_recv_tick,重置基准(evc.c:99-102)。 uart3_is_recv_async_data()为真时,取uart3_get_data_len(),上限截到EVC_BUF_LEN(evc.c:107-108),长度小于 2 直接continue。- 逐字节
uart3_read_data_async()拷入buf(evc.c:115-119)。 - 校验:
crc_temp = buf[len-1]<<8 | buf[len-2],与GetCrcCode_Modbus(buf, len-2)比对,相等则*len = l_u16_len返回,否则*len = 0并继续循环直到超时(evc.c:121-129)。
注意:本文件没有应用层重试计数,重试由调用方的时间节拍隐式完成——evc_control 每 1s 读一次、每 10s 控一次,通讯丢包等价于"下一拍再来"。唯一的显式重发是清电量循环(evc.c:968-973,最多 3 次)。
各命令的报文与响应判据:
| 命令 | 功能码 | 起始寄存器 | 寄存器数 | data 长度 | 超时 | 响应判据 |
|---|---|---|---|---|---|---|
get_evc_id_info | 0x03 | 0x0100 | 0x0026(38) | 4 | 500ms | buf[0]==1 && buf[1]==0x03 && buf[2]==0x26*2(76)(evc.c:218-221) |
get_evc_run_info | 0x03 | 0x9100 | 0x0013(19) | 4 | 500ms | buf[2]==0x13*2(38)(evc.c:273-276) |
send_cmd_evc | 0x10 | 0x0100 | 0x0009(9) | 5+18 | 500ms | 回显 addr/fun/起址高/起址低/寄存器数高/低 六字节(evc.c:448-454) |
set_evc_energy | 0x10 | 0x00A2 | 0x0002(2) | 5+4 | 500ms | 同上(evc.c:491-497) |
4.2 寄存器映射
身份信息(0x0100 起 38 寄存器,响应 76 字节,evc.c:223-231)
| 响应偏移 | 长度 | 含义 |
|---|---|---|
[3..34] | 32B | u8_evc_sn 桩序列号 |
[51..52] | 2B | u32_start_power_total 启动功率(单相,主机字节序) |
[53..54] | 2B | u8_evc_phase 相位数 |
[55..64] | 10B | u8_evc_fw_ver 软件版本 |
[65..74] | 10B | u8_evc_hw_ver 硬件版本 |
[75..78] | 4B | u32_max_power_total 最大功率(大端 32 位) |
型号 u8_evc_modle 不由桩上报,而是本地写死 FW_TYPE_EVC(evc.c:224-225)。随后按相数折算出 u32_start_power_single_phase 与 u32_max_power_single_phase(evc.c:233-234),并置 comm_flag=TRUE、刷新 u32_evc_comm_tick、清 Set_Setup_Config_Extend1_Status(FALSE)(表示扩展配置已读完)。
运行信息(0x9100 起 19 寄存器,响应 38 字节,evc.c:278-304)
| 响应偏移 | 长度 | 目标字段 | 说明 | |||
|---|---|---|---|---|---|---|
[3..4] | 2B | u16_evc_volt_l1 | A 相电压 | |||
[5..6] | 2B | s16_evc_curr_l1 | A 相电流(有符号) | |||
[7..10] | 4B | s32_evc_power_total | 总功率 | |||
[11..14] | 4B | u32_evc_energy_total_real | 桩侧原始累计电量 | |||
[15..16] | 2B | u16_evc_volt_l2 | B 相电压 | |||
[17..18] | 2B | s16_evc_curr_l2 | B 相电流 | |||
[19..20] | 2B | u16_evc_volt_l3 | C 相电压 | |||
[21..22] | 2B | s16_evc_curr_l3 | C 相电流 | |||
[23..26] | 4B | evc_fault_u.u32_evc_fault_all | 故障字,**按 `[25]<<24\ | [26]<<16\ | [23]<<8\ | [24]` 拼装** |
[31..32] | 2B | u16_evc_cp_volt | CP 电压 | |||
[33..34] | 2B | u16_evc_duty | CP 占空比 | |||
[35..36] | 2B | evc_state_u.u16_all | 状态字(startmode/chargingfull/chgsts/charge_en) | |||
[37..40] | 4B | u32_evc_energy_total | 可被清空的累计电量 |
控制下发(0x0100 起 9 寄存器,evc.c:358-428)
| data 下标 | 内容 |
|---|---|
[0..3] | 起址 0x0100 + 寄存器数 0x0009 |
[4] | 字节数 = 18 |
[5..8] | s32_evc_set_power_total(大端 4B) |
[9..12] / [13..16] / [17..20] | s32_evc_set_power_a/b/c(大端 4B) |
[21..22] | evc_set_u.u16_all(remotcontrol + phasecontrol) |
下发前先做两重裁剪(evc.c:364-405):负值全部截为 0;总值上限 = 单相最大功率 × 相数,分相值上限 = 单相最大功率。
4.3 主调度时序(evc_control,evc.c:958)
由 interface.c:351 每 100ms 调用一次(之后 OSTimeDlyHMSM(0,0,0,100)),内部三档节拍:
每 100ms 进入 evc_control
├─ clear_energy_flag == TRUE → 置 0,set_evc_energy() 最多重试 3 次
├─ (now - s_u32_recv_tick) >= 1000ms → 1s 档
│ ├─ comm_flag == FALSE → get_evc_id_info() (先读身份)
│ └─ comm_flag == TRUE → get_evc_run_info() (再读运行)
├─ (now - s_u32_send_tick) >= 10000ms → 10s 档(控制周期)
│ ├─ get_evc_run_info()
│ ├─ evc_gat_sys_data()
│ ├─ evc_charge_time()
│ ├─ evc_calculated_total_power()
│ ├─ evc_calculate_single_station_power()
│ ├─ send_cmd_evc()
│ └─ 刷新 evc_tags_u(server_control / rechargeable / full_tag)
└─ 通讯丢失判定
├─ (now - u32_evc_comm_tick) >= 10000ms → b_comm_failure = TRUE
│ 且首次进入时清空桩侧易失数据(见 4.6)
└─ else → b_comm_failure = FALSE
注意 10s 档里 get_evc_run_info() 被重复调用一次(evc.c:1001),使功率计算用的是最新数据而非 1s 档的旧值,代价是每个控制周期多一次 500ms 阻塞等待。
三个 static u32 计时基准 s_u32_recv_tick/s_u32_send_tick(evc.c:962-963)以及 g_t_evc_run_info.u32_evc_comm_tick 都做了 tick 回绕保护(evc.c:991/1011/754)。
4.4 策略与功率计算
(1)系统侧剩余可充功率 evc_calculated_total_power(evc.c:619)
单相剩余可充功率(evc.c:630-632):
P_phaseX = V_meterX * u16_max_consume_cur * 8 / 1000 + P_meterX
u16_max_consume_cur 单位 0.1A(默认 320 → 32A,system_config.c:785),P_meterX 为电表侧功率取负后的值(evc.c:153-155 用电表数据时取负号,即"负载为正")。三相均有效时取三相最小值 l_s32_min_power 作为策略 4 的输入;只有 A 相有效时降级为单相。
电池限值裁剪(evc.c:651-693):
TIME_FORCE_CHARGE_MODE且u8_bat_soc >= u8_charger_cut_soc→s16_bat_charge_max_power = 0;- 四种"放电截止"条件(强制馈网到截止 SOC、削峰到截止 SOC、UPS 预留到达、允放时段到截止 SOC)任一成立 →
s16_bat_discharge_max_power = 0; - 六种"强制充电/禁止放电"模式任一成立 → 放电限值同样置 0;
- 充/放限值均不超过逆变器额定功率
s16_inv_ac_max_power。
逆变器最大可放功率(evc.c:696):
P_inv_max = min(|P_pv| + P_bat_dischg_limit, P_inv_rated)
四套策略(switch (g_t_evc_config_table.u8_plot),evc.c:699):
| 策略 | 离网(grid_status == FALSE) | 并网 |
|---|---|---|
EVC_SLOW(1) | P = P_meter1_total(等于馈网功率) | P = P_meter1 - (92% * P_bat_charge_limit + P_bat),仅当 92%*P_bat_charge_limit + P_bat > 0(电池未到最大可充)时扣减,否则 P = P_meter1 |
EVC_NORMAL(2) | P = P_meter1 | P = P_meter1 + P_inv_max - P_inv;若为负且"逆变器已接近满放"(92%*P_inv_max > P_inv)且距上次上调 < 2 min,则钳为 0;否则刷新上调时刻 |
EVC_QUICK(3) | P = P_meter2(PV 电表) - get_evc_power_total() | P = P_meter2 + P_inv_max - get_evc_power_total() |
EVC_SPECIFIED_POWER(4) | — | P = l_s32_min_power * 3 |
| 其它 | P = 0 | P = 0 |
EVC_NORMAL 的 2 分钟"不下调"窗口由 static u32 s_u32_power_regulation_tick(evc.c:623)实现,语义是"逆变器已经最大出力时,给充电桩 2 分钟缓冲,不做激进降额"。
(2)单桩功率 evc_calculate_single_station_power(evc.c:790)
- 最大功率模式(策略 4)先把总额度按相数折算:
P = P_total * u8_ev_phase / 3(evc.c:802)。 - 相数自适应:
u8_ev_phase == 0时置 3(evc.c:809-812);只有 A 相电压 >1000(100.0V)时强制降为 1 相(evc.c:820-826);u16_max_charg_curr == 0时置默认 160(16A,evc.c:832-835)。 - 计算"实际启动功率"(
evc.c:839):
P_start = u32_start_power_single_phase * phasecontrol * V_meter_a / 2300
2300 即额定 230.0V(0.1V 单位),用于按实际电压修正启动门槛。
- 允许/禁止判定(
evc.c:847-863):仅当charge_en == TRUE且(本地充电指令非默认 或 刷卡启动 或 任一时间段命中)时把remotcontrol置 0(允许);若电表丢失则保持 0 功率输出;若u8_charge_flag == STOP_CHARGE则强制禁止。 - 正常充电时的目标功率(
evc.c:866-886):
P_a = V_meterA * I_l1 / 100 + P_avail_a (分相,同理 b/c)
P_set = max(P_actual, P_start) + P_avail_total (P_actual < 150 时不抬高)
充满时改为 P_set = P_start + 200
- 限流(
evc.c:889-897,仅策略 4):先取 14a/ENWG 动态限值get_enwg_evc_power_limit(),再按V * I_max * phasecontrol / 100限制,最后+= phasecontrol * 50补偿。 - 条件充电延迟状态机(
evc.c:900-936):
| 当前状态 | 条件 | 动作 |
|---|---|---|
| 任意 | P_set + 500 < P_start | 刷新 s_u32_charge_dealy_tick,状态 = NO_CONDITION_CHARGE |
CHARGE_NORMAL 或 (CONDITION_CHARGE 且耗时 < 3 min) | P_start - 500 <= P_set < P_start | P_set += 500,状态 = CONDITION_CHARGE |
CONDITION_CHARGE 且耗时 ≥ 3 min | 同上 | 刷新 tick,状态 = NO_CONDITION_CHARGE |
P_set >= P_start | — | 刷新 tick,状态 = CHARGE_NORMAL |
即:功率不足启动门槛时最多"勉强多给 500W"并给 3 分钟宽限,超时后判定为不满足充电条件。
4.5 充电时间段判定(evc_charge_time,evc.c:515)
把 RTC 与时间段起止都换算成"当日分钟数"(hour*60+minute),对每段时间输出三态:
u8_timeX_flag == 2:该段未使能或起止相同 → 视为无效;== 0:当前时刻落在有效段内;== 1:落在段外。
跨天段(start > end,如 22:00–06:00)采用反向判定:落在"段外区间"即认为在段内(evc.c:564-572,注释写"跨天在时间段内无效",与实现语义相反,属注释与代码不一致)。
4.6 通讯丢失处理与"清空字段"技巧
evc.c:1016-1033:若 u32_evc_comm_tick 超过 10s 未刷新:
- 首次进入时保存
u8_charge_flag与charge_en(这两项是 EMS 侧远程写入的,不能被桩掉线抹掉); - 用一条
memset把u8_evc_phase+1到u32_evc_comm_tick之前的整段运行数据清零(evc.c:1023)——利用结构体成员连续布局,一次性清掉电压/电流/功率/电量/故障/时间段标志; - 写回保存的两个字段;
- 置
evc_fault_u.bit.b_comm_failure = TRUE。
b_comm_failure 由 fault_warn_log.c:1129 按位读出,生成故障记录。
4.7 全局变量的协同
g_t_logic_info.t_logic_control_parameter.*:只读,本文件通过evc_gat_sys_data单向消费(evc.c:148-169)。g_t_evc_setting_info/g_t_evc_config_table:本文件就地改写多个字段(相数evc.c:811/824、最大电流evc.c:834),属于在 RAM 上"自学习/自纠正"配置,需注意这些改动只有靠g_t_config_save_flag链路才会落 Flash。g_t_evc_run_info:u8_charge_flag由set_evc_charge_flag()(net/server.c:1772)写入、evc_control读到后参与充停决策;clear_energy_flag由clear_evc_energy()(net/server.c:1222)写入;u32_evc_comm_tick由每次成功通讯刷新,是通讯在线的唯一时间源。g_t_evc_debug_data:写入 5 个 tick 字段(evc.c:529-533),只写不读(除调试界面)。
5. 对外接口
本文件导出的函数(供谁调用)
| 导出函数 | 调用方 |
|---|---|
evc_control | midware/interface/interface.c:351 |
get_evc_enable_flag | interface.c:331、data/resume_data/flash/flash_data_store.c:673、logic/logic.c:164、net/ems.c:540 |
get_evc_power_total | flash_data_store.c:673、logic.c:165、net/ems.c:540、本文件 evc_calculated_total_power |
get_evc_energy_total | flash_data_store.c:1037、midware/interface/modbus/modbus.c:933/944、net/ems.c:760 |
get_evc_chargemodel | modbus.c:956、net/ems.c:1010 |
get_evc_type | net/ems.c:3015/3096、net/server.c:3305/3709/3710 |
clear_evc_energy | net/server.c:1222 |
set_evc_charge_flag | net/server.c:1772 |
本文件导出的全局变量
g_evc_send_buf、g_evc_recv_buf(被 evc_ii.c/evc_upgrade.c 复用)、g_t_evc_setting_info、g_t_evc_config_table、g_t_evc_run_info、g_t_evc_debug_data(声明见 evc.h:350-358)。
本文件调用的外部函数/变量
| 来源模块 | 符号 |
|---|---|
hardware/usart3 | uart3_clear_buffer、uart3_send_data、uart3_is_recv_async_data、uart3_get_data_len、uart3_read_data_async |
common/crc | GetCrcCode_Modbus |
logic/logic.c | logic_read_sequencecontrol_mode、logic_read_charger_cut_soc、logic_read_ups_reserve_soc、logic_read_feedin_cut_soc、logic_read_allow_discharge_cut_soc、logic_read_peakshving_discharge_cut_soc、get_enwg_evc_power_limit、g_t_logic_info、各 *_MODE 枚举 |
midware/meter | meter_read_voltage、meter_read_actpower、GRID_METER、PV_METER、PHASE_A/B/C/T |
net/ems.c | Set_Setup_Config_Extend1_Status |
data/system_config | SYSTEM_DATETIME |
| uCOS-II | OSTimeGet |
| C 库 | memcpy、memset、printf、abs |
6. 依赖关系
- 依赖:
evc.h(evc.c:13)、evc_download.h(evc.c:14)、usart3.h(evc.c:15)、crc.h(evc.c:16)、system_aux.h(evc.c:17)、logic.h(evc.c:18)、meter.h(evc.c:19)、ems.h(evc.c:20)。 - 被依赖:
midware/interface/interface.c(调度驱动)、data/system_config/system_config.c(配置初始化/持久化)、data/fault_warn_log/fault_warn_log.c(故障字记录)、midware/interface/modbus/modbus.c(电量/模式映射到 Modbus 从站寄存器)、net/ems.c、net/server.c(云端上报与控制)、logic/logic.c、data/resume_data/flash/flash_data_store.c(断电续传)。
7. 备注与风险点
- 疑似逻辑缺陷(削峰截止 SOC 判据写错):
evc.c:665-666写作
(sys_logic == TIME_PEAK_SHAVING_MODE) && (u8_peakshaving_discharg_cut_soc <= u8_bat_feedin_cut_soc),
比较的是两个配置阈值而非"当前 SOC 与阈值"。对照二代同位置实现 evc_ii.c:957-958:
(u8_bat_soc <= u8_peakshaving_discharg_cut_soc)。一代此处很可能漏写了 u8_bat_soc,会导致削峰模式下放电限值清零条件几乎从不按预期触发(或被错误触发)。
- 除零风险:
evc.c:233-234直接u32_start_power_total / u8_evc_phase,当桩上报相数为 0 时触发整数除零(Cortex-M4 上SDIV得到 0 而非异常,但结果错误)。后续evc.c:809的"相数为 0 置 3"保护在这之后才执行,来不及补救。evc_calculate_single_station_power的phasecontrol若为 0,evc.c:894的乘法虽安全,但evc.c:839会把启动功率也算成 0。 - 魔法数字
*8/1000:evc.c:630-632的V * I * 8 / 1000。若 V 单位 0.1V、I 单位 0.1A,物理功率应为V*I/100,代码相当于打了 0.8 折(约 80% 降额)。同文件EVC_SLOW/EVC_NORMAL又统一用92/100,两处降额系数不一致,建议确认是否有意为之。 - 配置表被就地改写:
evc.c:811、evc.c:824、evc.c:834修改g_t_evc_setting_info(Flash 映射表),evc.c:822还会按电压情况强制把用户设置的 3 相改为 1 相且不会恢复。若该表在别处被回写 Flash,用户的相数设置会被 EMS 覆盖。 u8形参承载长度:evc_master_send_cmd的len为u8(evc.c:60),而调用点evc.c:445传l_u8_cmd[4]+5(整数提升后截断),当前最大值 23 安全;但若将来 data 域超过 255 字节会静默回绕。printf已屏蔽但存在真实笔误:evc.c:316的printf("%02X ", g_evc_recv_buf)漏了数组下标[i]。因DEBUG_EVC == 0编译期剔除,不影响发布固件,但一旦打开调试开关会打印错误内容。- 清空运行数据依赖结构体布局(
evc.c:1023):memset(&u8_evc_phase+1, 0, (u8*)&u32_evc_comm_tick - &u8_evc_phase - 1)依赖u8_evc_phase与u32_evc_comm_tick在EVC_RUN_INFO_T中按声明顺序连续排布。若有人调整evc.h的成员顺序,该 memset 会破坏无关字段且不会编译报错。 get_evc_chargemodel有隐式副作用:evc.c:1062内部调用get_evc_enable_flag(),后者在未使能时memset整张g_t_evc_run_info(evc.c:1047)。即"读一个上报值"会清空整张运行表,调用频次高时(net/ems.c:1010每帧上报)会造成数据抖动。- 10s 控制周期里重复读运行数据(
evc.c:1001)导致每个控制周期最多两次 500ms 阻塞等待,interface.c的 100ms 节拍被拉长;若充电桩无响应,单次evc_control最坏耗时约 1.0s,可能影响同任务里其它 485 设备(SUNSPEC、并机)的响应。 - 通讯丢失清空为破坏性操作:
evc.c:1023清零后u32_evc_energy_total归 0,get_evc_energy_total()会把 0 上报云端与断电续传存储(flash_data_store.c:1037),存在云端统计被"清零"的观感风险;energy_total_real未被清,可作为对照。 u8_timeX_flag的取值语义用裸数字 0/1/2 贯穿全文件(evc.c:546、evc.c:553-570、evc.c:849-852),无宏定义,可读性差且易与EVC_CHARGE_MODE_E混淆。- 注释与实现不一致:
evc.c:562、evc.c:594注释为"跨天在时间段内无效",但代码实际把跨天段的"段外区间"判定为段内(evc.c:564-572);evc.c:919注释写"五分钟到"而条件用的是3*60*1000(3 分钟)。