ct.c
源文件路径:
midware/meter/ct.c所属模块:midware/meter(CT 采样) 代码行数:1494
1. 文件职责
本文件实现 EMS 主控板上的 CT(电流互感器)交流采样通道:通过 TIM1 触发 ADC2 + DMA1_CH0 以 100Hz×100 点的方式采集电网三相电压/电流与 PV 侧电流,再用 TIM2 + EXTI10_15 的外部中断捕获市电方波下降沿来测量电网频率。采样结果在 task_sampling 任务中做均值(中值)去偏、真有效值(RMS)计算、FIR 滑动平均滤波、分段线性校准,最终写入全局表 g_t_ct(t_grid_ct_info / t_pv_ct_info),并累加电量。它处在数据流的最前端:midware/meter/meter.c 的 ct_handle() 每隔 500ms 调用本文件导出的 ct_read_*() 接口把 CT 数据搬进 g_t_meter.t_grid_info / t_pv_info,logic 层与 modbus 从机再从这里取数,因此本文件是整个 EMS 功率/频率数据的源头。同时它还负责采样异常(fault bit 18)判定与两条调试用的电网波形曲线录制(本地 g_grid_sampling_curve_t 与上报服务器的 g_server_grid_sampling_curve_t)。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
DEBUG_SAMPLING | 宏 | 置 1 时统计 DMA 中断次数与任务循环次数(ct.h:20) |
FILTER_DEPTH_MID | 宏 | 中值(DC 偏置)滤波深度,200(ct.h:22) |
CALIBRATION | 宏 | 置 1 使能分段线性校准(ct.h:24) |
FILTER_DEPTH_VOLT / _FREQ / _GRID_CURR / _PV_CURR / _GRID_POWER / _PV_POWER | 宏 | 各路滑动平均窗口长度:5 / 10 / 10 / 10 / 10 / 10(ct.h:25-ct.h:30) |
CT_MAX_POWER | 宏 | 有功功率限幅上限 18000(ct.h:32) |
SAMPLING_CURVE_CHANNEL / _NUM / _BUF_LEN | 宏 | 本地波形曲线:2 通道 × 1000 点(ct.h:34-ct.h:36) |
SERVER_SAMPLING_CURVE_CHANNEL / _NUM / _BUF_LEN | 宏 | 上传服务器波形曲线:2 通道 × 5000 点(ct.h:38-ct.h:40) |
CT_SITE_E | 枚举 | CT 安装位置:GRID_CT=1、PV_CT、FAST_GRID_CT(高精度频率)、MAX_CT_SITE(ct.h:42) |
CT_PHASE_E | 枚举 | 相位选择:CT_PHASE_A/B/C/T(ct.h:50) |
GRID_SAMPLING_CURVE_T | 结构体 | 双缓冲波形录制结构:通道1/2 的使能标志、发送标志、缓冲区、计数(ct.h:59) |
GRID_SERVER_SAMPLING_CURVE_T | 结构体 | 上报服务器的波形结构:录制使能/发送标志/计数/RTC 时间戳/5000 点缓冲(ct.h:71) |
CT_DATA_T | 结构体 | 单路 CT 的实时数据:三相电压、三相电流、三相及总有功、无功、视在、功率因数、频率、快速频率、输入/输出电量(ct.h:80) |
CT_RUN_TABLE_T | 结构体 | 运行表:u8_clear_energy_flag + CT_DATA_T(ct.h:101) |
CT_CALIBRATION_TABLE_T | 结构体 | 单个校准点:s16_calibration_point(分界点)/s16_calibration_coef(系数,/10000)/s16_calibration_offset(偏移)(ct.h:107) |
CT_CONFIG_TABLE_T | 结构体 | 持久化校准表:A/B/C 相电压各 2 点、GRID/PV 三相功率各 5 点、35 字节保留、CRC(ct.h:114) |
CT_ENERGY_SAVE_TABLE_T | 结构体 | 持久化电量表:GRID/PV 各相及总输入/输出电量共 16 个 u32,60 字节保留,CRC(ct.h:134) |
CT_TABLE_T | 结构体 | 顶层聚合表:t_grid_ct_info、t_pv_ct_info、t_ct_config、t_ct_energy(ct.h:161) |
STATIC_ASSERT(...) | 宏 | 编译期断言 ASSERT_FLASH_SECTOR_SIZE % sizeof(...) == 0,保证结构体大小能整除 Flash 扇区(ct.h:132、ct.h:159) |
g_t_ct | 全局变量 | 顶层 CT 表实例(ct.c:39) |
g_grid_sampling_curve_t | 全局变量 | 绝对定位在 SDRAM_GRID_SAMPLING_CURVE_ADDR 的本地曲线缓冲(ct.c:41) |
g_server_grid_sampling_curve_t | 全局变量 | 绝对定位在 SDRAM_SEVER_GRID_SAMPLING_CURVE_ADDR 的服务器曲线缓冲(ct.c:42) |
s_u8_detect_first | static 全局变量 | 频率采样启动标记,同时用作「并/离网已稳定」标志(ct.c:33) |
s_u32_sampling_anomaly_tick | static 全局变量 | 采样异常计时基准,用于 fault bit 18 的 2 秒去抖(ct.c:37) |
3. 函数清单
| 函数名 | 作用域 | 说明 |
|---|---|---|
grid_active_power_filter | static(前向声明 ct.c:46,定义 ct.c:378) | GRID 侧三相有功功率的滑动平均 + 分段校准 + 死区归零 + 限幅,写回 t_grid_ct_info |
pv_active_power_filter | static(前向声明 ct.c:47,定义 ct.c:492) | PV 侧三相有功功率滤波,流程同上但死区为 5 且不限幅前归零 |
cal_grid_vol | static(前向声明 ct.c:48,定义 ct.c:80) | GRID 线电压滑动平均 + 两点分段校准,同时写入 GRID 与 PV 两张表的电压字 |
pv_active_curr_filter | static(前向声明 ct.c:49,定义 ct.c:279) | PV 侧三相电流滑动平均 + 4 段分段校准,写回 t_pv_ct_info 电流 |
grid_active_curr_filter | static(前向声明 ct.c:50,定义 ct.c:170) | GRID 侧三相电流滑动平均 + 4 段分段校准,并按有功符号决定电流正负 |
grid_ct_energy_statistics | static(前向声明 ct.c:51,定义 ct.c:598) | 用 OSTimeGet() 计时,按总有功累计 GRID 侧输入/输出电量(60 秒结算一次) |
pv_ct_energy_statistics | static(前向声明 ct.c:52,定义 ct.c:656) | 同上,累计 PV 侧输入/输出电量 |
ct_sampling_init | 全局(ct.c:61) | 初始化 ADC2、DMA1_CH0、TIM1(100Hz/100 点)、TIM2 与 EXTI10_15 |
grid_freq_sampling_handle | 全局(ct.c:719) | 由 TIM2 捕获中断/下降沿回调,测量周期并换算电网频率,首次下降沿时重启 ADC+DMA |
adc_regular_data_recv | 全局(ct.c:795) | ADC2 规则组 DMA 传输完成回调:标记缓冲区、切换下一个 DMA 缓冲、OSSemPost 唤醒采样任务 |
task_sampling | 全局(ct.c:844) | uCOS-II 采样任务主体:等待信号量、中值去偏、RMS/功率累加、调用各滤波器、异常与频率丢失检测、驱动 ct_handle() |
ct_read_voltage | 全局(ct.c:1194) | 按 site/phase 读线电压 |
ct_read_current | 全局(ct.c:1249) | 按 site/phase 读电流 |
ct_read_actpower | 全局(ct.c:1304) | 按 site/phase 读有功功率(支持 CT_PHASE_T) |
ct_read_reactpower | 全局(ct.c:1367) | 读无功功率,当前实现恒返回 0(桩函数) |
ct_read_factor | 全局(ct.c:1392) | 读功率因数,当前实现恒返回 0(桩函数) |
ct_read_frequent | 全局(ct.c:1417) | 读频率,FAST_GRID_CT 返回 0.001Hz 分辨率的快速频率 |
ct_read_einput | 全局(ct.c:1438) | 读 CT 累计输入电量(取 t_ct_energy 持久化值) |
ct_read_eoutput | 全局(ct.c:1457) | 读 CT 累计输出电量(取 t_ct_energy 持久化值) |
ct_energy_clear | 全局(ct.c:1476) | 清零指定 CT 的输入/输出电量并投递 CT_ENERGY_PAGE 保存作业 |
4. 核心逻辑解析
4.1 采样链路初始化与三重中断/任务协作
ct_sampling_init()(ct.c:61)一次性配置好整条链路:
adc2_init()+adc_dma1_channel0_init():ADC2 规则组走 DMA1 通道 0,共 4 个通道(ADC_CHANNEL=4,hardware/dma/dma.h:21)。timer1_init(100, 167):TIM1 以 100Hz、每周期 167 个计数值触发 ADC,即每 10ms 采一批。timer2_init()+exti10_15_init():TIM2 计时 + EXTI 外部中断捕获市电方波下降沿。
三者的配合方式是:
- TIM1 每 10ms 触发 ADC2 采样,DMA 按
ADC_DATA_LEN = ADC_CHANNEL * NUM_OF_SAMPLES = 4*250 = 1000个半字搬运到g_t_dma_data.t_dma_data[idx].s_s16_samples_value[250][4]。 - 搬运完成进 DMA 传输完成中断 →
adc_regular_data_recv()(ct.c:795)。 adc_regular_data_recv()记录本缓冲区有效点数s_u16_dma_data_len = (ADC_DATA_LEN - 剩余传输数)/ADC_CHANNEL,置s_u16_dma_data_flag = TRUE,然后在t_dma_data[DMA_BUFF_NUM=3]中找下一个flag == FALSE的空缓冲重新配置 DMA 地址与传输数量并启动;若三个缓冲都满则回绕到下标 0(ct.c:809-ct.c:832,i >= DMA_BUFF_NUM的判断被刻意放在循环最前以防数组越界)。最后OSSemPost(g_t_ct_sample_sem)唤醒采样任务。task_sampling()用OSSemPend(g_t_ct_sample_sem, 100, &err)等待(ct.c:883),遍历 3 个 DMA 缓冲,对每个flag == TRUE的缓冲做计算并把 flag 清为 FALSE,形成生产者/消费者环形缓冲。
信号量定义在 user/main.c:59(OS_EVENT *g_t_ct_sample_sem),在 user/main.c:114 由 OSSemCreate(0) 创建;采样任务由 user/main.c:277 的 OSTaskCreateExt(task_sampling, ...) 静态创建。
4.2 中值(DC 偏置)计算
task_sampling() 维护 9 个累加器 l_s32_add_value_channel[9] 与中值 l_s32_mid_value_channel[9],初值 2048(对应 ADC 12bit 中点)。
- 前
FILTER_DEPTH_MID(=200)个缓冲:只累加 4 个通道的采样值并l_u16_mid_count++。 - 达到 200 后采用滑窗刷新:
acc[i] -= acc[i]/200再累加本批 4 通道的Σ sample,中值 =acc[i] / (200 * l_u16_dma_data_len)(ct.c:918-ct.c:954)。
注意:并离网切换时会检测 l_u8_detect_first != s_u8_detect_first(ct.c:886),一旦变化就把累加器清零、中值复位为 2048,重新收敛。这就是文件注释里说的「并离网切换中值初始化」。
第 4 个通道(s_s16_samples_value[count][3])不是电流/电压,而是 ADC2_IN7 的板温采样,通过 set_adc2_in7_data() 上报:中值收敛后用中值,否则用首个样本(ct.c:1044-ct.c:1051)。
4.3 RMS 与功率计算(关键公式)
对每个有效点 count(ct.c:972-ct.c:1042):
l_s32_temp[0] = sample[0] - mid[0] // 电压瞬时值
l_s32_temp[1] = sample[1] - mid[1] // GRID 侧电流瞬时值
l_s32_temp[2] = sample[2] - mid[2] // PV 侧电流瞬时值
sum_grid_active_power_a += temp[0]*temp[1] // 电压×GRID电流
sum_pv_active_power_a += temp[0]*temp[2] // 电压×PV电流
temp[0] = temp[0]*992/4096 // 电压标度
temp[1] = temp[1]*233/4096 // GRID 电流标度
temp[2] = temp[2]*233/4096 // PV 电流标度
sum_grid_value_a += temp[0]*temp[0] // 电压平方和
sum_grid_curr_a += temp[1]*temp[1] // GRID 电流平方和
sum_pv_curr_a += temp[2]*temp[2] // PV 电流平方和
一个缓冲处理完后(ct.c:1055-ct.c:1069):
sum_grid_active_power_a = (sum_grid_active_power_a / 75) / len // 有功均值
sum_grid_value_a = sqrt(sum_grid_value_a * 10000 / len) // 电压有效值 ×100
sum_grid_curr_a = sqrt(sum_grid_curr_a * 10000 / len) // GRID 电流有效值 ×100
sum_pv_curr_a = sqrt(sum_pv_curr_a * 10000 / len) // PV 电流有效值 ×100
即无功真有效值算法:Xrms = sqrt( (10000/N) · Σ x_i² ),乘 10000 是为了在整数域保留两位小数。/75 是功率的标度转换(魔法数字),全文件仅此一处出现。
随后依次调用 cal_grid_vol()、pv_active_curr_filter()、grid_active_curr_filter()、grid_active_power_filter()、pv_active_power_filter() 完成滤波与落表;并在 ct.c:1091-ct.c:1092 把三相有功相加得到 s32_ct_actpower_t(总有功)。
4.4 滑动平均滤波与分段线性校准
四个 *_filter() 函数使用完全相同的一阶增删法滑动窗口(以 cal_grid_vol() 为例,ct.c:80):
- 窗口未满(
cnt < FILTER_DEPTH_*):filter += 新值,把新值存入hist[cnt],cnt++。 - 窗口已满:
filter -= hist[idx],filter += 新值,均值 =filter / DEPTH;hist[idx] = 新值,idx = (idx+1) % DEPTH。
好处是 O(1) 复杂度、无需每次重新求和,代价是 filter 累加器是 u32/s32,长期运行不会溢出(深度最大 200 × 值域)。
校准采用「分段线性」:把 CT_CALIBRATION_TABLE_T 数组当作多段折线,按输入值落在哪个 s16_calibration_point 区间选择 s16_calibration_coef / s16_calibration_offset:
- 电压/有功(2 点表或有功表的 4 段):
out = in * coef / 10000 + offset(ct.c:138-ct.c:146、ct.c:436-ct.c:452)。 - 电流(5 点表,实际只用前 4 段):分界点还要乘以
10/23(魔法数字,与采样电路放大倍数相关),且不加 offset,out = in * coef / 10000(ct.c:227-ct.c:243)。
GRID 侧有功校准由 calibration_flag(实参为 s_u8_detect_first)控制——只有在并网稳定后才做校准;PV 侧有功在并网模式下反而传 FALSE(因为此时功率是用 backup_volt * pv_current / 100 反算的,见 4.6)。
4.5 频率测量状态机
频率由 grid_freq_sampling_handle()(ct.c:719)在 EXTI10_15 中断中被调用,是一个两状态机:
状态 A:首次下降沿(s_u8_detect_first == FALSE,ct.c:751)
timer2_start_stop_control(FALSE)→timer2_set_counter(0)→timer2_start_stop_control(TRUE),从零起计。- 置
s_u8_detect_first = TRUE。 - 复位采样链路:关 ADC2、关 DMA 完成中断、关 DMA 通道、
memset(&g_t_dma_data, 0, ...)。 - 重配 DMA 到
t_dma_data[0]、传输数ADC_DATA_LEN、使能完成中断与通道、adc_enable(ADC2)。 - 该次
l_u32_ct_frequency_temp保持 0,因此本次会把频率写成 0。
状态 B:后续每个下降沿(ct.c:724)
- 读
timer2_get_counter();若<= 28000直接返回(去抖/防误触发,28000 是魔法数字)。 - 计数器清零,累加频率:
freq_001Hz = 2000000000 / counter - 1(TIM2 计数频率以 2MHz 反推,即f = 2e9 / ticks,单位 0.001Hz)。 - 若已采满
FILTER_DEPTH_FREQ(=10)次,用「先减均值再加新值」的方式维护滑动平均:sum = sum - sum/10 + new,freq = sum/10;否则只累加并cnt++。 - 异常闭锁:若
freq落在 (42000, 65000)(即 42.00~65.00Hz)区间,说明市电波形正常,关掉 ADC2 与 DMA 通道,停止高频采样;只有检测到不正常才重新由状态 A 打开。 - 最后写
u16_fast_ct_frequent = freq(0.001Hz),并按四舍五入(freq % 10 >= 5进位)写u16_ct_frequent(0.01Hz),GRID 与 PV 两张表同时写;s_u32_freq_sampling_tick = OSTimeGet()。
4.6 并/离网模式对采样与功率的影响
task_sampling() 中:
- 若
g_t_meter.t_config_data.u8_gridforming_en == TRUE(并机/并网构网模式,ct.c:1076),PV 侧有功不再来自 CT 的 V×I,而用逆变器输出电压乘 CT 电流反算:
pv_power_x = g_t_inv_info.t_run_info.u16_backup_volt_x * ct_current_x / 100
并强制 calibration_flag = FALSE(ct.c:1078-ct.c:1081)。
- 否则走正常的 CT 功率通路。
频率丢失检测(ct.c:1113):若距上次成功测频超过 100ms 且 s_u8_detect_first == TRUE,则把 s_u8_detect_first 置 FALSE,并把 GRID/PV 两张表的电压、电流、三相与总有功、无功、视在、功率因数、频率全部清零(ct.c:1119-ct.c:1147),避免在市电消失时把残值上报给上层。
采样异常检测(ct.c:1095):条件
(DMA有效点数 < 150 || 电压 < 300) 且 频率 >= 4200 (42.00Hz)
且 板型 != BOARD_EMS3_6_3_E 且 板型 != BOARD_EMS3_6_4_E
成立时,与 s_u32_sampling_anomaly_tick 比较,超过 2000ms 才置 ems_set_fault_bit(18, TRUE),否则先清;不成立时清故障并把计时基准刷新为当前 tick。这是一个带 2 秒确认的故障上报。
4.7 电量累计算法
grid_ct_energy_statistics()(ct.c:598)与 pv_ct_energy_statistics()(ct.c:656)结构一致,以 GRID 版本说明:
- 每次调用用
OSTimeGet()求本次调用与上次的时间差Δt(ms),并做了 tick 回绕处理(ct.c:609-ct.c:619)。 s_u32Time_sum += Δt累计到 1 分钟。- 若
s32_ct_actpower_t > 0,累加到s_u32_input_temp += |P|·Δt;否则累加到s_u32_output_temp += |P|·Δt(区分正/反向电量)。 - 当
s_u32Time_sum >= 1000*60时结算:
s_u32_input += s_u32_input_temp / (s_u32Time_sum * 60)
s_u32_input_temp = s_u32_input_temp % (s_u32Time_sum * 60)
电量 += s_u32_input / 10
s_u32_input %= 10
s_u32Time_sum = 0
即把 W·ms 换算成电量单位(综合 /60 与 /10 两步后,1 个 LSB≈10Wh),余数保留到下一分钟,避免整数除法丢精度。写回 u32_ct_energy_input/output。
4.8 波形曲线录制(两条独立通道)
在 RMS 累加循环内(ct.c:978-ct.c:1032)并行录制两条互斥的曲线:
- 本地曲线
g_grid_sampling_curve_t(u8_sampling_curve_en == TRUE时):双缓冲结构,u8_sampling_curve_buf_flag == FALSE时填curve1_buf,每点占 2 个 u16(电压、电流),计数满SAMPLING_CURVE_NUM(=1000)就置u8_sampling_curve1_send_flag;buf_flag == TRUE时填curve2_buf,满则置u8_sampling_curve2_send_flag。两个 flag 置位即切换缓冲,形成「边采边发」的双缓冲。 - 服务器曲线
g_server_grid_sampling_curve_t(u8_server_sampling_curve_en == TRUE时):单缓冲,满SERVER_SAMPLING_CURVE_NUM(=5000)时置u8_server_sampling_curve_send_flag = TRUE、计数清 0、关掉使能,并打上SYSTEM_DATETIME时间戳(ct.c:1019-ct.c:1025)。上层由net/ems.c的STATUS_SEND_GRID_CT_SAMPLING_DATE状态机负责发送。
两条曲线由外部(hmi / 服务器命令)设置 _en 标志后开始录制。
4.9 CT 数据回灌电表表(与 meter 模块的耦合)
采样任务末尾(ct.c:1154-ct.c:1164)调用 meter.c 的 ct_handle(u8 site, METER_RUN_TABLE_T *, u8 negate, u8 *hw_ver, u16 hw_ver_len):
- 当
meter_get_meter_or_ct(GRID_METER) == CT_SAMPLING时,把 CT 数据当作 GRID 电表数据写入g_t_meter.t_grid_info,同时带上取反标志与硬件版本号。 - 当 PV 侧不是电表采样(
meter_get_meter_or_ct(PV_METER) != METER_SAMPLING)且g_t_meter.t_pv_info.u8_detect_flag == TRUE时,把 CT 数据写入g_t_meter.t_pv_info。 - 两个调用都排除
BOARD_EMS3_6_3_E/BOARD_EMS3_6_4_E板型。 ct_handle()内部自带 500ms 节流(midware/meter/meter.c:4386),所以不必担心这里调用过于频繁。
4.10 对外读接口与桩函数
ct_read_*() 系列全部是纯查表函数,直接读 g_t_ct 并做 site/phase 分支;default 分支统一返回 0。只有 ct_read_frequent() 支持 FAST_GRID_CT(返回 0.001Hz 分辨率的 u16_fast_ct_frequent)。ct_read_reactpower()(ct.c:1367)与 ct_read_factor()(ct.c:1392)所有分支都赋 0,是未实现的桩函数——目前 CT 方案不计算无功与功率因数。
ct_energy_clear()(ct.c:1476)除清零电量外,还会去写 dsp_meter_calibration_data.u16_clear_cmd = 7 并置 u8_send_calibration_mode_flag,即顺带向 DSP 发送「林内电表数据清零」命令;然后 save_job_post(CT_ENERGY_PAGE)(CT_ENERGY_PAGE = 4081,data/system_config/system_config.h:33)投递持久化作业。
5. 对外接口
本文件导出(供外部调用)
| 名称 | 类型 | 调用方 |
|---|---|---|
ct_sampling_init() | 函数 | 同样由本文件 task_sampling() 调用(ct.c:878) |
grid_freq_sampling_handle() | 函数 | EXTI10_15 外部中断服务函数(hardware/exti/exti.c) |
adc_regular_data_recv() | 函数 | DMA1_CH0 传输完成中断(hardware/dma/dma.c) |
task_sampling(void *p_arg) | 任务函数 | user/main.c:277 的 OSTaskCreateExt |
ct_read_voltage/current/actpower/reactpower/factor/frequent/einput/eoutput | 函数 | midware/meter/meter.c 的 ct_handle()(meter.c:4399-meter.c:4443)、midware/interface/modbus/modbus.c:4312-modbus.c:4332、logic/dispatch.c:604/dispatch.c:626 |
ct_energy_clear(u8 site) | 函数 | data/data.c:362-data/data.c:363 |
g_t_ct | 全局变量 | data/system_config/system_config.c(读写与持久化)、midware/auxiliary/system_aux.c 等 |
g_grid_sampling_curve_t | 全局变量 | HMI / 服务器波形命令处理(hmi/、net/) |
g_server_grid_sampling_curve_t | 全局变量 | net/ems.c(STATUS_SEND_GRID_CT_SAMPLING_DATE) |
本文件调用的外部函数/变量
| 名称 | 来源模块 |
|---|---|
adc2_init / adc_enable / adc_disable / adc_dma_mode_enable / adc_dma_mode_disable / adc_flag_clear / adc_dma1_channel0_init / set_adc2_in7_data | hardware/adc/adc.h |
dma_channel_enable / dma_channel_disable / dma_memory_address_config / dma_transfer_number_config / dma_transfer_number_get / dma_interrupt_enable / dma_interrupt_disable / g_t_dma_data / DMA_CHXCTL_FTFIE / ADC_DATA_LEN / DMA_BUFF_NUM | hardware/dma/dma.h |
timer1_init / timer2_init / timer2_get_counter / timer2_set_counter / timer2_start_stop_control | hardware/timer/timer1.h、timer2.h |
exti10_15_init | hardware/exti/exti.h |
OSSemPend / OSSemPost / OSTimeGet / uCOS-II | UCOSII |
g_t_ct_sample_sem | user/main.h:59 |
g_t_meter(t_grid_info、t_pv_info、t_config_data.u8_grid_meter_negate、u8_pv_meter_negate、u8_grid_merter_hw_ver、u8_pv_merter_hw_ver、u8_gridforming_en)、meter_get_meter_or_ct、CT_SAMPLING、METER_SAMPLING、GRID_METER、PV_METER、ct_handle | midware/meter/meter.h、midware/meter/meter.c |
g_t_inv_info.t_run_info.u16_backup_volt_a/b/c | midware/inverter/inverter.h:1929 |
get_board_type / BOARD_EMS3_6_3_E / BOARD_EMS3_6_4_E | system/board_init/board_init.h:30 |
ems_set_fault_bit | data/data.h:128 |
save_job_post / CT_ENERGY_PAGE | data/system_config/system_config.h:834 / :33 |
SYSTEM_DATETIME | midware/auxiliary/system_aux.h:58 |
get_dma_data_debug_flag | midware/inverter/inverter.h:2251 |
dsp_meter_calibration_data | midware/inverter/alphaess/alphaess_comm_can.h:206 |
g_t_task_run_status | user/main.h(仅 DEBUG_SAMPLING==1 时使用) |
6. 依赖关系
依赖(本文件 include / 使用)
ct.h、dma.h、adc.h、timer1.h、timer2.h、math.h、main.h、system_aux.h、system_config.h、board_init.h、meter.h、inverter.h、exti.h、gpio.h、data.h、alphaess_comm_can.h(ct.c:13-ct.c:28)。
被依赖(哪些文件会用到它)
midware/meter/meter.c(ct_handle()读 CT 数据;meter.c亦处理 CT 采样与电表采样的互斥选择)data/data.c(ct_energy_clear)data/system_config/system_config.c(读写并持久化g_t_ct.t_ct_energy、t_ct_config)midware/interface/modbus/modbus.c(Modbus 从机寄存器映射到ct_read_*)logic/dispatch.c(用ct_read_frequent做频率越限判断)net/ems.c(服务器波形曲线上报)user/main.c/user/main.h(创建采样任务与信号量)hmi/(菜单界面显示 CT 数据、下发曲线录制命令)
7. 备注与风险点
- B/C 相功率恒为 0:在
task_sampling()的累加循环里只累加了l_s32_sum_grid_active_power_a(ct.c:1034)与l_s32_sum_pv_active_power_a(ct.c:1035),_b/_c三个变量虽被声明和清零(ct.c:965-ct.c:970)但从未累加,随后却被除以 75 和点数(ct.c:1056-ct.c:1060)传给滤波器。这与 DMA 只采 4 个通道(V、GRID_I、PV_I、温度)、硬件仅单相 CT 的现状相符,但对三相 CT 硬件来说这是明确的遗漏。 l_s32_mid_value_channel[4..8]是死数据:初始化、复位(ct.c:873-ct.c:876、ct.c:896-ct.c:899)都只覆盖下标 0-3,其余 5 个从未被使用,属于可清理的冗余(原调试打印ct.c:1175-ct.c:1178曾用到,现已注释)。abs()作用于无符号量:cal_grid_vol()中abs(l_u16_grid_v_temp[i]) < l_pt_cal[i][0].s16_calibration_point(ct.c:138)以及cv.c的功率死区判断(ct.c:459)中abs()的实参来自可能已溢出为负值的u16,函数式宏abs对无符号取绝对值无意义,建议改为有符号中间变量。- 校准系数写入 u16/溢出风险:
l_u16_grid_v_temp[i] = (... )/10000 + offset(ct.c:140、ct.c:144)右侧可为负,回写无符号变量会回绕,随后才在ct.c:154-ct.c:156用< 0判断(该判断永远为假)。 - 大量魔法数字:
/75(功率标度,ct.c:1055)、*992/4096、*233/4096(电压/电流标度,ct.c:1036-ct.c:1038)、*10000(RMS 扩位,ct.c:1061)、28000(TIM2 计数器下限,ct.c:728)、2000000000(频率换算常数,ct.c:736、ct.c:741)、*10/23(电流校准分界点标度,ct.c:227)、300(电压异常阈值)、150/238(DMA 点数有效窗口,ct.c:910)、42000/65000(频率有效区间)、4200(频率异常阈值,ct.c:1095)、2000(异常确认窗口)、100(频率丢失窗口)。这些常量没有命名宏,改动风险高。 FILTER_DEPTH_FREQ收敛前的频率是 0:l_u32_ct_frequency_temp只在s_u8_freq_sampling_cnt >= 10时才被赋值(ct.c:733-ct.c:743),前 10 个周期它一直是 0,却被无条件写到u16_ct_frequent/u16_fast_ct_frequent(ct.c:773-ct.c:785)。上层若在此期间读频率会拿到 0。- 首次下降沿分支会重配 DMA 但不清
e_dma_buff_idx:ct.c:764只 memset 了g_t_dma_data(会顺带清零e_dma_buff_idx),但在adc_regular_data_recv()正在中断中操作 DMA 时重配同样的寄存器,存在并发风险(中断优先级关系中未见屏蔽)。 - 电量统计依赖任务周期:
grid_ct_energy_statistics()用两次调用之间的OSTimeGet()差值估算 Δt,若采样任务被长时间阻塞(例如ct_handle()内部的电表通讯),Δt 会被合并成一个大值一次结算,短时功率尖峰可能被平均掉。 ct_read_einput/eoutput读的是持久化副本t_ct_energy,而累加写入的是运行表t_ct_data.u32_ct_energy_input。两者由data/system_config/system_config.c:1633-system_config.c:1636(上电加载)与system_config.c:3044-system_config.c:3047(保存)同步;若保存作业延迟或失败,ct_read_einput()的读数会与实时累加值不一致(存在最长一个保存周期的滞后)。DMA_BUFF_NUM循环上界与越界防护:for (size_t i = 0; i <= DMA_BUFF_NUM; i++)(ct.c:809)用了<=而非<,靠循环体内i >= DMA_BUFF_NUM提前break兜底;写法脆弱,若后人调整循环体顺序就会越界。DEBUG_SAMPLING == 1时才有中断计数(ct.c:797-ct.c:799、ct.c:1181-ct.c:1183);ct.c:1166-ct.c:1179是整块被注释的调试打印代码,属于遗留。