EMS 知识库
首页 / knowledge/代码解析/midware/interface/drm

drm.c

源文件路径:midware/interface/drm/drm.c 所属模块:midware / interface / drm(DRM 需求响应模式与德国/奥地利电网限制) 代码行数:280

1. 文件职责

本文件实现 DRM(Demand Response Mode,需求响应模式)接口的状态采集与模式判定,是欧盟并网标准(英国 RRCR / G100、德国 VDE-AR-N 4105 §14a EnWG、奥地利 KNG 远程功率限制)在 EMS 侧的落地入口。它每 1 秒读一次硬件 DRM0~DRM4 五个信号(DRM0 走 ADC 模拟量、DRM1~4 走 GPIO 数字量),把结果打包成 4 位半字节状态 u16_drm_state,再翻译成 g_t_logic_info.u8_drm_mode(0~8、16)以及一组馈网/限功率使能与百分比字段。这些字段随后被 logic/logic.c(限功率与馈网计算)、midware/inverter/inverter.c(关机判定)、net/ems.c(云端上报)、midware/inverter/parallel*.c(并机同步)消费。本文件对外只有三个函数,由 midware/interface/interface.c 的 task_interface_parse 任务驱动:上电时调 drm_init(),之后每周期调 drm_mode_judge()(内部自带 1 s 节流)。文件本身没有任务、信号量或消息邮箱,也没有任何通信协议帧格式。

2. 关键数据结构与宏

名称类型说明
u16_drm_state全局变量(drm.c:21)DRM 信号打包态。位域布局:`(drm1!=0)<<12 \(drm2!=0)<<8 \(drm3!=0)<<4 \(drm4!=0),即 4 个半字节,取值只能是 0x0000/0x1000/0x0100/0x0010/0x0001 的 16 种组合。初始化 0x0000。**注意:全局导出但全工程无外部引用**,实际只在本文件内被 switch 使用,同时被镜像到 g_t_logic_info.u16_drm_state`
g_t_logic_info.u8_drm_mode外部全局(logic/logic.h:505)DRM 模式输出:0=DRM0 关断/CSIP 受限、1~4=DRM1~DRM4 半功率级、5~8=DRM1~DRM4 关断级、16=无效/无 DRM
g_t_logic_info.u16_drm_state外部全局(logic/logic.h:506)u16_drm_state 的镜像,供 net/ems.c:829、840 上报云端
g_t_logic_info.u8_rrcr_feedin_percent外部全局(logic.h:507)RRCR(英式需求响应)生效时的馈网限值百分比,取值来自配置 u8_feedin_percent1~4 或 u8_max_feedin_percent
g_t_logic_info.u8_eza_feedin_percent外部全局(logic.h:508)EZA(德国自用优化)馈网限值百分比
g_t_logic_info.u8_deu_feedin_enable外部全局(logic.h:509)RRCR/EZA 馈网限制总使能,被 net/ems.c:1125、1475 上报
g_t_logic_info.u8_14a_enwg_enable外部全局(logic.h:601)§14a EnWG 生效标志,被 logic/logic.c:195 用于 EV/电池功率额度限幅,net/ems.c:1124、1474 上报
g_t_logic_info.u8_wlv_percent / u8_wlv_enable外部全局(logic.h:510-511)奥地利 KNG WLV(远程降低功率)限值百分比与使能;logic/logic.c:1136 用 u8_wlv_percent 按比例压低 s32_inv_rated_output_power
RRCR_ENABLE / EZA_ENABLE / ALL_DISABLE枚举(logic/logic.h:114–119,RRCR_EZA_STATUS_E)t_config_data.u8_rrcr_en 的取值:0=ALL_DISABLE、1=RRCR_ENABLE、2=EZA_ENABLE
ENABLE / TRUE / FALSE宏(全局,includes.h 链)配置项与逻辑标志的通用取值
DRM 阈值 800 / 3000魔法数(drm.c:102、104)ADC 低通滤波值分档门限:<800 → 等级 0,<3000 → 等级 1,≥3000 → 等级 2
低通系数 0.5568 / 0.4432魔法数(drm.c:94–98)一阶 IIR 滤波系数,和为 1.0000
节流周期 1000魔法数(drm.c:77)单位 tick(OS_TICKS_PER_SEC = 1000),即 1 s

3. 函数清单

函数名作用域说明
drm_init全局(drm.c:33,声明 drm.h:21)上电初始化,把 u8_drm_mode 复位为 16(无效)
drm_get全局(drm.c:44,声明 drm.h:23)读 DRM1~DRM4 四个 GPIO 电平,打包成 u16_drm_state 并镜像到 g_t_logic_info.u16_drm_state
drm_mode_judge全局(drm.c:67,声明 drm.h:22)主判定:1 s 节流 → 采集 → DRM0 分档 → 决定 u8_drm_mode → 计算 RRCR/EZA/§14a/WLV 各使能与百分比

4. 核心逻辑解析

4.1 调度时序与节流(drm.c:77)

drm_mode_judge() 由 task_interface_parse 每周期调用,但函数开头用 static u32 s_u16_drm_tick 做 1 s 节流:

if (abs(OSTimeGet() - s_u16_drm_tick) < 1000) return;
drm_get();
s_u16_drm_tick = OSTimeGet();

OSTimeGet() 返回 32 位 tick 计数,OS_TICKS_PER_SEC = 1000(UCOSII/uCOS-CONFIG/os_cfg.h:51),所以 1000 tick = 1 s。abs() 包装是为了容忍 tick 回绕。注意变量名 s_u16_drm_tick 前缀是 u16 但类型是 u32,属命名遗留。

4.2 信号采集 drm_get()(drm.c:44)

l_u8_drm_falg[0] = get_drm1_flag();   // GPIOF PIN_10
l_u8_drm_falg[1] = get_drm2_flag();   // GPIOC PIN_1
l_u8_drm_falg[2] = get_drm3_flag();   // GPIOC PIN_4
l_u8_drm_falg[3] = get_drm4_flag();   // GPIOC PIN_5
u16_drm_state = (f0!=0)<<12 | (f1!=0)<<8 | (f2!=0)<<4 | (f3!=0);
g_t_logic_info.u16_drm_state = u16_drm_state;

四个 get_drmx_flag() 定义在 hardware/gpio/gpio.c:1403/1415/1427/1439,都是对 gpio_input_bit_get() 的单次读取,无去抖;去抖完全依赖下面的 ADC 通道低通滤波(但仅对 DRM0 有效)。局部变量名 l_u8_drm_falg 是 flag 的拼写错误。

4.3 DRM0 采集、滤波与分档(drm.c:84–107)

仅当逆变器安规类型属于以下集合时才做 DRM0 判定(drm.c:85):

inverter_read_safety_is_AS4777(...) 为真,或 inverter_read_safety() ∈ {PPDS, TOR_ERZEUGER, VDE4105_11_18, EN50549_NL, C10_C11, NC_RFG}。

(枚举定义见 midware/inverter/inverter.h:194–249。)

命中后:

  1. 只把 ADC 值放进索引 0,索引 1~4 写 0——注释明确"只用 DRM0"。由于配置集合里没有英国 G100/AS4777 之外的选项,DRM1~DRM4 的 GPIO 采集结果只影响 u16_drm_state 的 RRCR/§14a/WLV 分支,不参与 u8_drm_mode 的生成。
  2. 五个通道统一跑一阶 IIR:y = 0.5568*x + 0.4432*y_prev。因为 x[1..4] 恒为 0,s_u16_drm_lpf_buf[1..4] 会从 0 开始单调衰减到 0,恒 < 800 → l_u8_singnal_state[1..4] 恒为 0。
  3. 分档:< 800 → 0;< 3000 → 1;否则 → 2。

4.4 模式判定状态机 u8_drm_mode(drm.c:109–150)

signal[0] >= 2(DRM0 高电平 → 要求关断)
    → u8_drm_mode = 0
signal[0] < 2
    任一 signal[1..4] == 2(关断级)   → 5 / 6 / 7 / 8(按 DRM1/2/3/4 顺序,先命中先胜)
    否则任一 signal[1..4] == 1(半功率级)→ 1 / 2 / 3 / 4(同上)
    否则                              → 16(无效)

由于 4.3 已使 signal[1..4] 恒为 0,在当前集合的安规下实际只有两条出口:signal[0] >= 2 → 0,否则 16。

CSIP 覆写(drm.c:141–145):若 sys_limited_by_csip_judge() == TRUE(南澳 CSIP-AUS 调度受限,定义于 logic/logic.c:3499)且(u8_opmodenergize != FALSE 或 u8_opmodconnect != FALSE,即逆变器已带电或已并网),无条件把 u8_drm_mode 覆写为 0。

不满足安规集合时(drm.c:147–150):整段跳过,直接 u8_drm_mode = 16。

消费侧:midware/inverter/inverter.c:846 用 u8_drm_mode == 0(本机与并机从机)作为"必须关机"的判据之一;midware/inverter/parallel.c:187、872、887 把它同步进 g_t_host_machine_running_info[0].u8_drm_mode,再经 parallel_can.c:226/838、parallel_sci.c:411/844 在并机总线(CAN/SCI)上传递;net/ems.c:829/840 上报云端。

4.5 德国 RRCR / EZA 馈网限值(drm.c:152–211)

两个分支互斥且都以 inverter_read_safety() == VDE4105_11_18 为前提。

RRCR 分支(u8_rrcr_en == RRCR_ENABLE,drm.c:152):置 u8_deu_feedin_enable = TRUE,然后按 u16_drm_state 映射到 u8_rrcr_feedin_percent:

u16_drm_state百分比来源
0x1000 0x1100 0x1011 0x1110 0x1101 0x0101u8_feedin_percent1
0x0100 0x0110u8_feedin_percent2
0x0010 0x0011u8_feedin_percent3
0x0001 0x1001 0x0111u8_feedin_percent4
其余(含 0x0000、0x1111 等)u8_max_feedin_percent,并置 u8_deu_feedin_enable = FALSE

EZA 分支(u8_rrcr_en == EZA_ENABLE,drm.c:180):同样置总使能,映射到 u8_eza_feedin_percent:

u16_drm_state百分比
0x0000 0x1000固定 100
0x0100 0x1101u8_feedin_percent1
0x0010 0x1110u8_feedin_percent2
0x0001 0x0111 0x0101 0x1011u8_feedin_percent3
其余(含 0x1111、0x1100、0x0110、0x0011、0x1001)u8_max_feedin_percent,且 u8_deu_feedin_enable = FALSE

两个分支都不成立(u8_rrcr_en == ALL_DISABLE 或安规不是 VDE4105_11_18)→ 仅清 u8_deu_feedin_enable = FALSE(drm.c:210),不清 u8_rrcr_feedin_percent / u8_eza_feedin_percent(保留上次值,但下游已由使能位屏蔽)。

4.6 §14a EnWG 使能(drm.c:213–246)

u8_14a_enwg_en 同样是三值枚举,但语义与 RRCR/EZA 表不同:

u8_14a_enwg_enable 被 logic/logic.c:195 用于 EV/电池功率额度限幅(与 u8_14a_enwg_active 配合)。

4.7 奥地利 KNG WLV 远程功率限制(drm.c:248–277)

条件 t_config_data.u8_kng_wlv_config_en == TRUE(注释"奥地利远程功率限制使能"):

u16_drm_stateu8_wlv_enableu8_wlv_percent
0x0000FALSE100
0x0100TRUE30
0x1000TRUE0
其余TRUE0

不使能时只清 u8_wlv_enable = FALSE,不动 u8_wlv_percent(drm.c:276)。下游在 logic/logic.c:1136 用 u8_wlv_percent 按比例压低逆变器额定输出功率:s32_inv_rated_output_power = parallel_read_system_rated_output_power() * u8_wlv_percent / 100。

5. 对外接口

本文件导出

名称类型使用方
drm_init函数(drm.h:21)midware/interface/interface.c:215
drm_mode_judge函数(drm.h:22)midware/interface/interface.c:219
drm_get函数(drm.h:23)仅本文件 drm_mode_judge() 内部调用;头文件对外声明但全工程无外部调用者
u16_drm_state全局变量(drm.c:21)无外部引用(未在 drm.h 中 extern 声明),属事实上的文件私有变量

本文件调用的外部函数 / 变量

来源名称
hardware/gpio/gpio.h:183-187get_drm1_flag()~get_drm4_flag()、get_drm0_data()(内部转 get_adc2_in7_data(),见 hardware/gpio/gpio.c:1451)
midware/inverter/inverter.hinverter_read_safety()(2084)、inverter_read_safety_is_AS4777()(2258)
logic/logic.hsys_limited_by_csip_judge()(707)、g_t_logic_info 及其 t_config_data(u8_rrcr_en、u8_feedin_percent1~4、u8_max_feedin_percent、u8_14a_enwg_en、u8_kng_wlv_config_en)
uCOS-IIOSTimeGet()
标准库abs()

6. 依赖关系

依赖

drm.h、gpio.h、logic.h、inverter.h(drm.c:13–16)。其中 inverter.h 的引用是必要的(安规枚举与读取函数),drm.h 仅提供 includes.h 与自身原型。

被依赖

7. 备注与风险点

  1. DRM1~DRM4 的输入在本文件内被实质丢弃:drm.c:89–92 把 l_u16_drm_buf[1..4] 硬编码为 0,导致 l_u8_singnal_state[1..4] 恒为 0,因此 u8_drm_mode 的 1~8 分支(drm.c:117–133)在当前安规集合下永不可达。这些分支与 u8_drm_mode = 5/6/7/8、1/2/3/4 的语义仍被 inverter.c 和云端沿用,属历史遗留代码,若后续启用 DRM1~4 数字量判定需一并复核。
  2. drm_get() 无去抖:四个 GPIO 单次读取直接进 u16_drm_state,而 u16_drm_state 直接决定 RRCR/EZA/§14a/WLV 的百分比映射。虽然该函数 1 s 才被调一次,但期间任何抖动会立即改变馈网限值。
  3. u16_drm_state 是导出符号但无 extern 声明:其他文件若要用只能自己 extern,容易与 g_t_logic_info.u16_drm_state 混用。建议统一走 g_t_logic_info。
  4. u8_rrcr_feedin_percent / u8_eza_feedin_percent / u8_wlv_percent 在失能时不被复位(drm.c:210、276),残留上次值。当前靠 u8_deu_feedin_enable、u8_wlv_enable 屏蔽,但如果下游有代码只读百分比不看使能位,就会用到过期数据(net/ems.c:1119、1121 就是只读百分比)。
  5. abs(OSTimeGet() - s_u16_drm_tick) < 1000 是带符号回绕技巧(drm.c:77):OSTimeGet() 返回 INT32U,相减结果被当作有符号解释,在 tick 差距小于 2^31 时成立;写法可读性差但功能正确。
  6. 魔法数密集:ADC 门限 800/3000(drm.c:102、104)、IIR 系数 0.5568/0.4432(drm.c:94)、WLV 固定 30(drm.c:260)、EZA 的 100(drm.c:186)、节流 1000(drm.c:77)均无宏定义或注释说明来源。
  7. 0.5568 是浮点运算:s_u16_drm_lpf_buf[0] = 0.5568 * l_u16_drm_buf[0] + s_u16_drm_lpf_buf[0] * 0.4432 走软件 double 浮点,GD32F407 无硬件双精度 FPU,在 1 s 周期的任务里开销可接受,但若被移植到更小周期需评估。
  8. l_u8_singnal_state 拼写错误(signal → singnal,drm.c:69),l_u8_drm_falg 亦为 flag 误拼(drm.c:46)。不影响功能,但会误导检索。
  9. u8_drm_mode 的隐含依赖:drm.c:109–139 的整套判定只在"安规集合命中"分支内;而 drm.c:152 之后的 RRCR/EZA/§14a/WLV 用同一个 u16_drm_state 但不受安规集合限制(只受各自配置位限制)。因此当安规为 VDE4105_11_18 之外的其它安规、而 u8_rrcr_en 又被误设为 RRCR_ENABLE 时,RRCR 分支不会执行(有安规判断),但 §14a 与 WLV 分支没有安规判断,会照常依据 u16_drm_state 动作。
  10. 无通信协议与重试机制:DRM 是本地硬线接口,本文件不涉及帧格式、校验或超时重试。这一节在本文档中按"不适用"处理。