16 极物科技 | DALI协议栈 - 架构总览与模块设计


tags:

  • 极物
  • 极物智能
  • 极物科技
  • DALI协议栈
  • 嵌入式开发
  • IEC 62386
  • 智能照明
  • 协议栈架构
  • 物联网
  • MCU开发
    description: 本文深入解析极物科技自研DALI协议栈的整体架构设计,涵盖物理层编解码、帧传输状态机、命令处理引擎、EEPROM持久化、定时器与PWM控制等核心模块,展示在IEC 62386标准之上的嵌入式MCU工程实现能力。

极物科技 | DALI协议栈 - 架构总览与模块设计

前言

在智能控制领域,"协议壁垒"与"系统稳定性"始终是绕不开的难题。极物科技(Zeewo)以 "重构空间交互体验" 为使命,通过自研的 极物 OS,实现了 KNX、DALI、CAN、RS485 等多种总线协议在单一主机上的深度融合。

我们认为,一个真正优秀的智能系统,不仅要实现灯光、遮阳、温控、音乐、安防等全生态设备的无界融合 ,更要确保本地运行、断网可用 ,并具备WEB 远程调试OTA 固件升级的能力。基于此底层架构,我们推出了诸如带双路 DALI 的 KNX 主机、HomeKit 网桥等核心硬件,并将极物 OS 成功接入了 Apple HomeKit、小度、纯血鸿蒙及 HomeAssistant 等主流生态系统。

一句话概述:本文深入解析极物科技自研 DALI 协议栈的整体架构设计,展示从物理层信号编解码到上层命令处理的完整实现。

本文聚焦于 极物 DALI 协议栈的模块化架构设计,涵盖物理层位编解码、帧传输状态机、命令处理引擎、EEPROM 持久化、定时器与 PWM 控制、硬件抽象层等核心模块。通过深入剖析各模块的职责边界与数据交互,帮助读者理解一个符合 IEC 62386 标准的 DALI 从设备协议栈是如何在嵌入式 MCU 上高效运行的。


1. 协议栈整体架构

极物 DALI 协议栈采用分层模块化设计,自底向上分为六层:

复制代码
┌─────────────────────────────────────────────────────────┐
│                    应用层 (Application)                    │
│  - 命令处理引擎 (dali_cmd)                                │
│  - 调光/色温控制逻辑                                       │
│  - 场景/分组管理                                           │
├─────────────────────────────────────────────────────────┤
│                    帧处理层 (Frame)                        │
│  - 前向帧/后向帧状态机                                     │
│  - 地址解析与匹配                                          │
│  - 重复帧检测                                              │
├─────────────────────────────────────────────────────────┤
│                    编解码层 (Bit)                          │
│  - DALI 位编码/解码                                        │
│  - TE 时序检测                                             │
│  - 中断驱动收发                                            │
├─────────────────────────────────────────────────────────┤
│                    硬件抽象层 (HAL)                        │
│  - GPIO 控制 (RX/TX)                                      │
│  - PWM 输出 (亮度/色温)                                    │
│  - 拨码开关读取                                            │
├─────────────────────────────────────────────────────────┤
│                    持久化层 (EEPROM)                       │
│  - 配置数据保存/加载                                       │
│  - Bank0/Bank1 内存映射                                    │
│  - 更新标志管理                                            │
├─────────────────────────────────────────────────────────┤
│                    定时器层 (Timer)                        │
│  - 1ms 系统时钟                                           │
│  - DALI 位时序定时器 (TE=13)                               │
│  - PWM 输出控制                                            │
└─────────────────────────────────────────────────────────┘

1.1 模块文件结构

协议栈源码按功能模块组织,结构清晰:

复制代码
dali_slave/
├── dali_top.h                    # 顶层头文件,统一包含所有模块
├── dali_bit/                     # 物理层编解码
│   ├── dali_bit.h
│   └── dali_bit.c
├── dali_stack/                   # 协议栈核心
│   ├── dali_frame/               # 帧处理层
│   │   ├── dali_frame.h
│   │   └── dali_frame.c
│   ├── dali_command/             # 命令处理层
│   │   ├── dali_cmd.h
│   │   └── dali_cmd.c
│   └── dali_hal/                 # 硬件抽象层
│       ├── dali_hal.h
│       └── dali_hal.c
├── dali_eeprom/                  # EEPROM 持久化
│   ├── dali_eeprom.h
│   └── dali_eeprom.c
└── dali_timer/                   # 定时器管理
    ├── dali_tc.h
    └── dali_tc.c

2. 核心数据结构

2.1 顶层数据视图 (dali_top.h)

协议栈通过 dali_top.h 统一管理各模块接口,定义了核心交互函数:

c 复制代码
/* DALI 地址与数据设置 */
void dali_set_addr_to_stack(uint8_t address);
void dali_set_data_to_stack(uint8_t data_1);

/* 接收标志与发送状态 */
void dali_set_received_flag_to_stack(bool flag);
void dali_set_sent_status_to_stack(uint8_t status);
uint8_t dali_get_sent_status_from_stack(void);
uint8_t dali_get_sent_data_from_stack(void);

/* EEPROM 操作接口 */
uint8_t *dali_get_update_flag_addr_from_stack(void);
uint8_t *dali_get_data_addr_from_stack(void);
uint8_t dali_get_data_size_from_stack(void);

/* 帧处理与状态机 */
void dali_frame_init(void);
void dali_process_frame_state(void);
void dali_frame_ms_tick(void);

/* 渐变处理 */
void dali_command_fade_ms_tick(void);

/* 设备状态处理 */
void dali_process_power_on(void);
void dali_process_interface_failure(void);
void dali_process_identify_device(void);

2.2 更新标志定义

c 复制代码
#define VAR_UPDATE   (1 << 0)   // 变量区更新
#define BANK0_UPDATE (1 << 1)   // Bank0 更新
#define BANK1_UPDATE (1 << 2)   // Bank1 更新

2.3 帧信息结构 (dali_frame.h)

c 复制代码
typedef struct {
    uint8_t rec_addr;           // 接收地址字节
    uint8_t rec_data;           // 接收数据字节
    uint8_t send_data;          // 发送数据字节
    volatile bool get_new_frame; // 新帧标志
    uint8_t frame_time;         // 帧计时
    uint8_t send_status;        // 发送状态
} dali_slave_service_info_t;

3. 模块职责详解

3.1 物理层编解码 (dali_bit)

核心职责

  • DALI 总线信号的电平检测与位解码
  • 基于中断的接收状态机
  • 基于定时器的发送编码
  • TE (Time Element) 时序容差处理

关键参数

c 复制代码
#define MIN_TE_CNT (TE_CNT - 4)    // TE 容差下限
#define MAX_TE_CNT (TE_CNT + 4)    // TE 容差上限
#define MIN_2TE_CNT (2 * MIN_TE_CNT)
#define MAX_2TE_CNT (2 * MAX_TE_CNT)
#define STOP_CNT (4 * TE_CNT)      // 停止位时长
#define TE_CNT 13                  // 半个 bit 的定时器计数

接收状态枚举

c 复制代码
#define BIT_START  0xFF    // 起始位
#define BIT_0      0x01    // 数据位 0
#define BIT_1      0x02    // 数据位 1
// ... BIT_2 ~ BIT_15
#define BIT_STOP1  0x11    // 停止位 1
#define BIT_STOP2  0x12    // 停止位 2

3.2 帧处理层 (dali_frame)

核心职责

  • 地址类型解析(单灯/分组/广播/特殊命令)
  • 前向帧/后向帧状态机管理
  • 重复帧检测与去重
  • 总线空闲检测

帧状态枚举

c 复制代码
enum {
    IDLE,                      // 空闲
    FORWARD_FRAME,             // 前向帧
    FORWARD_BACKWARD_DELAY,    // 前后向延迟
    FORWARD_IDLE_DELAY,        // 前向空闲延迟
    FORWARD_REPEAT_FRAME,      // 重复帧
    FORWARD_REPEAT_DELAY,      // 重复帧延迟
    BACKWARD_FRAME_ANSWER,     // 后向应答帧
    BACKWARD_IDLE_DELAY        // 后向空闲延迟
};

地址类型定义

c 复制代码
#define NOT_ME              0x00  // 非本设备
#define GROUP_ADDR          0x01  // 组地址
#define BROADCAST           0x02  // 广播
#define SHORT_ADDR          0x03  // 短地址
#define SPECIAL_CMD         0x04  // 特殊命令
#define BROADCAST_UNADDRESSED 0x05 // 未编址广播

地址掩码定义

c 复制代码
#define ADDR_Y              0x80  // Y 位 (组/广播标识)
#define ADDR_NON_SHORT      0x60  // 非短地址掩码
#define ADDR_SHORT_A        0x7E  // 短地址掩码
#define ADDR_GROUP_A        0x1E  // 组地址掩码
#define ADDR_S              0x01  // S 位 (特殊命令标识)
#define ADDR_BROADCAST      0xFE  // 广播地址
#define ADDR_BROADCAST_UNADDRESSED 0xFC  // 未编址广播

3.3 命令处理层 (dali_cmd)

核心职责

  • 标准 DALI 命令解析与执行(0~255)
  • 特殊命令处理(0xA1~0xC9)
  • DTR0/DTR1/DTR2 数据寄存器管理
  • 调光渐变引擎
  • 色温 PWM 控制
  • 状态查询应答

命令分类

命令范围 类型 说明
0~10 基础控制 OFF/UP/DOWN/STEP/RECALL 等
16~31 场景调用 GO TO SCENE 0~15
32~48 配置命令 RESET/STORE/SET 等
64~127 场景/分组配置 SET SCENE/ADD GROUP 等
128~129 地址/内存 SET SHORT ADDRESS/ENABLE WRITE
144~197 查询命令 QUERY STATUS/LEVEL 等
224~255 扩展应用 色温/调光曲线/故障查询
0xA1~0xC9 特殊命令 INITIALISE/RANDOMISE/COMPARE 等

3.4 EEPROM 持久化层 (dali_eeprom)

核心职责

  • 从 EEPROM 加载 DALI 配置数据
  • 将变更数据写回 EEPROM
  • 数据完整性校验
  • 更新标志管理

初始化校验标志

c 复制代码
#define EEROM_DATA_INIT_SUCESS_DATA_1 0X55
#define EEROM_DATA_INIT_SUCESS_DATA_2 0XAA

3.5 定时器层 (dali_timer)

核心职责

  • 1ms 系统时钟节拍
  • DALI 位时序定时器 (TE=13)
  • PWM 输出控制(亮度/色温)

关键参数

c 复制代码
#define SYS_TICK_TC_TOP    (CLK_CPU / DALI_PWM_CLOCK_HZ)
#define DALI_PWM_CLOCK_HZ  1470
#define DALI_BIT_TC_TOP    (CLK_CPU / 2400 / TE_CNT)  // 2400bps
#define TE_CNT             13                          // 半 bit 计数
#define DALI_PWM_TC_TOP    0x0FFF                      // 12-bit PWM

3.6 硬件抽象层 (dali_hal)

核心职责

  • GPIO 引脚配置(RX 高阻输入 / TX 推挽输出)
  • 前向/后向帧收发切换
  • PWM 输出更新(亮度/色温)
  • 拨码开关地址读取
  • 设备识别闪烁

拨码开关地址读取

c 复制代码
#define SHOTADDRESS_VAULE_BIT1 (P02 ? 0 : 1)
#define SHOTADDRESS_VAULE_BIT2 (P43 ? 0 : 1)
#define SHOTADDRESS_VAULE_BIT3 (P03 ? 0 : 1)
#define SHOTADDRESS_VAULE_BIT4 (P04 ? 0 : 1)
#define SHOTADDRESS_VAULE_BIT5 (P05 ? 0 : 1)
#define SHOTADDRESS_VAULE_BIT6 (P06 ? 0 : 1)

#define SHOTADDRESS_VALUE (SHOTADDRESS_VAULE_BIT1 |            \
                           _crol_(SHOTADDRESS_VAULE_BIT2, 1) | \
                           _crol_(SHOTADDRESS_VAULE_BIT3, 2) | \
                           _crol_(SHOTADDRESS_VAULE_BIT4, 3) | \
                           _crol_(SHOTADDRESS_VAULE_BIT5, 4) | \
                           _crol_(SHOTADDRESS_VAULE_BIT6, 5))

4. 数据流全景

4.1 接收数据流

复制代码
DALI 总线信号
    ↓
外部中断 (INT0)
    ↓
dali_rx_IRQHandler()     ← 边沿检测,启动解码
    ↓
dali_decode_IRQHandler() ← TE 定时器驱动,逐位解码
    ↓
dali_set_addr_to_stack() ← 地址字节写入协议栈
dali_set_data_to_stack() ← 数据字节写入协议栈
dali_set_received_flag() ← 设置接收完成标志
    ↓
dali_frame_check_addr()  ← 地址类型匹配
    ↓
dali_cmd_process_xxx()   ← 命令解析与执行
    ↓
PWM 输出 / EEPROM 写入 / 状态更新

4.2 发送数据流(后向帧)

复制代码
命令处理需要应答
    ↓
dali_frame_process_data() 返回 FORWARD_BACKWARD_DELAY
    ↓
dali_hal_disable_forward_enable_backward()  ← 切换到发送模式
    ↓
dali_set_sent_status(SENDING_START)
    ↓
dali_decode_IRQHandler() 编码分支
    ↓
逐位输出 DALI 后向帧 (8 bit)
    ↓
dali_set_sent_status(SENDING_STOP)
    ↓
dali_hal_enable_forward_disable_backward()  ← 切换回接收模式

4.3 调光渐变流

复制代码
DAPC 命令 (直接弧光功率控制)
    ↓
dali_cmd_direct_arc_power_control()
    ↓
计算 fade_step = flash_inv_fadetime_val[fade_time] × level_change
    ↓
设置 fade_direction (UP/DOWN/DIRECT)
    ↓
dali_command_fade_ms_tick()  ← 每 1ms 调用
    ↓
dali.long_level += fade_step  ← 32 位渐变累加
    ↓
dali_cmd_set_pwm_output()     ← 更新 PWM
    ↓
dali_hal_update_pwm_output()  ← 硬件输出

5. 内存映射

5.1 变量区 (struct var)

协议栈维护完整的 DALI 设备状态变量:

c 复制代码
struct var {
    uint8_t target_level;           // 目标亮度
    uint8_t tc_target_level;        // 目标色温
    uint8_t data_check_1;           // 数据校验
    
    uint8_t power_on_level;         // 上电亮度
    uint8_t tc_powon_level;         // 上电色温
    uint8_t system_failure_level;   // 故障亮度
    uint8_t tc_fail_level;          // 故障色温
    uint8_t minimum_level;          // 最小亮度
    uint8_t maximum_level;          // 最大亮度
    uint8_t physical_minimum_level; // 物理最小亮度
    uint8_t fade_time;              // 渐变时间 (0~15)
    uint8_t fade_rate;              // 渐变速度 (0~15)
    uint8_t extended_fade_time_base;      // 扩展渐变时间基数
    uint8_t extended_fade_time_multiplier;// 扩展渐变时间倍率
    uint8_t short_address;          // 短地址 (0~63)
    uint8_t operating_mode;         // 操作模式
    uint8_t group_0_7;              // 分组 0~7 归属
    uint8_t group_8_15;             // 分组 8~15 归属
    uint8_t scene[16];              // 16 个场景亮度
    uint8_t tc_scene[16];           // 16 个场景色温
    uint8_t version_number;         // 版本号
    uint8_t random_address[3];      // 随机地址 (24 bit)
    // ... 更多字段
};

5.2 Memory Bank0

c 复制代码
#define BANK0_LAST_MEMORY_LOCATION 0x1A

// Bank0 内存映射
dali_ram.memory.bank0[0]  = BANK0_LAST_MEMORY_LOCATION  // 最后存储位置
dali_ram.memory.bank0[2]  = NUMBER_OF_LAST_BANK         // Bank 数量
dali_ram.memory.bank0[3~8] = GTIN                       // GTIN 码
dali_ram.memory.bank0[9~10] = FIRMWARE_VER              // 固件版本
dali_ram.memory.bank0[11~18] = ID_NUMBER                // 设备 ID
dali_ram.memory.bank0[19~20] = HARDWARE_VER             // 硬件版本
dali_ram.memory.bank0[21~23] = NUM_OF_10X_VER           // 协议版本
dali_ram.memory.bank0[24] = CONTROL_DEVICE_NUM          // 控制设备数
dali_ram.memory.bank0[25] = CONTROL_GEAR_NUM            // 控制灯具数
dali_ram.memory.bank0[26] = CONTROL_GEAR_INDEX          // 灯具索引

5.3 Memory Bank1

c 复制代码
#define BANK1_LAST_MEMORY_LOCATION 0x10
#define BANK1_LOCK_BYTE 0xFF        // 锁定字节

// Bank1 包含 OEM 信息、GTIN、设备 ID 等
// 可通过 WRITE_MEMORY_LOCATION 命令写入(需先解锁)

6. 关键设计亮点

6.1 中断驱动的位解码

DALI 协议基于曼彻斯特编码,每个 bit 由两个 TE (Time Element) 组成:

  • Bit 0:先低后高 (Low-High)
  • Bit 1:先高后低 (High-Low)

协议栈通过外部中断捕获边沿变化,结合 TE 定时器精确判断 bit 值:

c 复制代码
void dali_rx_IRQHandler(void) {
    // 边沿检测
    pin_level = DALI_RX_PORT_PIN_VALUE;
    
    if (status_receive == 0) {
        if (pin_level == LOW) {
            // 下降沿 = 起始位
            status_receive = BIT_START;
        }
    }
    else if (status_receive == BIT_START) {
        if (level_time >= MIN_TE_CNT && level_time <= MAX_TE_CNT) {
            status_receive = BIT_0;  // 起始位正确,准备接收 16 bit 数据
        }
    }
    // ... 逐位解码
}

6.2 32 位高精度渐变

协议栈使用 32 位联合体实现高精度渐变:

c 复制代码
union daliunion {
    struct {
        uint8_t level;   // 实际 DALI 亮度值 (0~254)
        uint8_t byte2;
        uint8_t byte1;
        uint8_t byte0;   // 分数部分
    } s;
    uint32_t long_level; // 32 位整体
} dali;

渐变步进通过查表获得:

c 复制代码
// 渐变时间倒数表 (移位 25 bit)
uint32_t const flash_inv_fadetime_val[] = {
    0x0000, 0xB964, 0x8312, 0x5CB2, 0x4189, 
    0x2E59, 0x20C5, 0x172B, 0x1062, 0x0B96,
    0x0831, 0x05CB, 0x0419, 0x02E5, 0x020C, 0x0173
};

// 计算步进
fade_step = flash_inv_fadetime_val[fade_time];
fade_step = fade_step * level_change;
fade_step = fade_step >> 1;  // 右移 1 bit

6.3 重复帧检测机制

DALI 标准要求配置命令需要连续发送两帧相同数据才生效:

c 复制代码
case FORWARD_REPEAT_FRAME:
    if (service_info.get_new_frame == true) {
        addr_type = dali_frame_check_addr(&service_info);
        if (data_backup == service_info.rec_data) {
            // 两帧相同,执行命令
            dali_frame_process_data(&service_info, addr_type, true);
        } else {
            // 两帧不同,执行新的命令
            dali_frame_process_data(&service_info, addr_type, false);
        }
    }

6.4 设备发现随机地址机制

DALI 设备通过 24 bit 随机地址参与寻址竞争:

c 复制代码
case RANDOMISE:
    // 生成随机地址
    dali_ram.variable.random_address.high = rand();
    dali_ram.variable.random_address.mid = rand();
    dali_ram.variable.random_address.low = rand();
    break;

case COMPARE:
    // 比较随机地址与搜索地址
    if (temp_random_addr <= temp_search_addr) {
        ret = ANSWER_YES;  // 应答
    }
    break;

case WITHDRAW:
    // 匹配的设备退出竞争
    if (temp_random_addr == temp_search_addr) {
        withdraw = 1;
    }
    break;

7. 模块交互时序

7.1 主循环调用顺序

c 复制代码
while (1) {
    dali_process_frame_state();      // 帧状态机
    dali_command_fade_ms_tick();     // 亮度渐变
    dali_command_fade_ms_tick_tc();  // 色温渐变
    dali_process_power_on();         // 上电处理
    dali_process_interface_failure();// 总线故障处理
    dali_process_identify_device();  // 设备识别
    dali_eeprom_update_tick();       // EEPROM 更新
}

7.2 定时器中断

c 复制代码
// 1ms 系统时钟
void system_timer_1ms(void) {
    ms_tick_flag = true;
    dali_frame_ms_tick();    // 帧计时
    // 渐变计时
    // 保活计时
}

// DALI 位时序定时器 (每 TE 触发)
void dali_decode_IRQHandler(void) {
    // 接收解码
    // 发送编码
}

8. 注意事项

  1. TE 容差:协议栈允许 TE 值 ±4 的容差,适应不同晶振频率
  2. 串口独占:DALI 总线必须保证只有一个程序在使用,严禁多程序同时使用
  3. EEPROM 写入:仅在数据变更时写入,避免频繁擦写
  4. 渐变精度:使用 32 位累加器保证渐变平滑,避免步进跳跃
  5. 重复帧窗口:115ms 内收到两帧相同数据才执行配置命令
  6. 设备发现超时:INITIALISE 命令启动 15 分钟寻址窗口,TERMINATE 可提前终止

9. 相关文档

  • 《极物科技 | DALI协议栈 - 物理层位编解码详解》
  • 《极物科技 | DALI协议栈 - 帧传输状态机与地址解析》
  • 《极物科技 | DALI协议栈 - 标准命令集解析》
  • 《极物科技 | DALI协议栈 - 特殊命令与设备发现机制》
  • 《极物科技 | DALI协议栈 - DT8 双色温扩展实现》
  • 《极物科技 | DALI协议栈 - 调光曲线与 PWM 映射》
  • 《极物科技 | DALI协议栈 - 渐变引擎深度解析》
  • 《极物科技 | DALI调光灯 - 快速接入指南》
  • 《极物科技 | DALI调光灯 - UDP 通讯协议规范》

关于极物科技(ZEEWO)

极物科技(Zeewo)致力于为用户提供智能控制系统及硬件产品。我们以总线系统为技术底座,以**"稳定、可靠、快速响应"**为产品底线,是国内少有的拥有 KNX、DALI 全套软硬件自主研发能力的厂商之一。

我们的核心能力:

  • 系统架构:自研极物 OS,支持多协议无界融合(KNX/DALI/CAN/RS485/IP)。
  • 核心硬件:带双路 DALI 的 KNX 主机、超薄全金属定制面板、各类智选传感器及网关。
  • 生态互联:深度融入 Apple HomeKit、Matter、小度、HomeAssistant 及纯血鸿蒙生态。
  • 调试交付:独家支持 ETS 导出 XML 直接导入进行 APP 免编程调试,支持远程 WEB 运维。

我们的市场覆盖:

服务网点已覆盖全国核心城市(含长三角、珠三角、成渝等),并以高品质的方案深耕家居生活、酒店民宿、企业办公、疗愈康养、餐馆会所等多个细分领域。

(如果您在开发或落地中遇到技术问题,欢迎通过官网或后台私信与我交流探讨)

相关推荐
anew___1 天前
从零搭建智能家居:智能插座+智能灯泡+温湿度联动,你需要做哪些准备?
智能家居
阿钱真强道1 天前
11 极物科技 | DALI调光灯 - 功能接口与调试指南
智能照明·极物·极物智能·极物科技·dali调光灯·调光控制·调试指南
乐橙开放平台2 天前
养老 SaaS 笔记:setMessageCallback 事件桥接 + msgType 映射 P0/P1,先 ACK 再分流值班流
笔记·物联网·音视频·智能家居
乐橙开放平台2 天前
老旧社区双通道笔记:周界 hoveringAlarm / aiPerArea + 电梯 aiNonVehDetect,一套 callback 做 24h 防护
笔记·物联网·音视频·智能家居
阿钱真强道2 天前
12 极物科技 | DALI调光灯 - 设备发现与地址分配机制
智能照明·极物·极物智能·极物科技·dali调光灯·设备发现·地址分配
阿钱真强道3 天前
09 极物科技 | DALI调光灯 - IEC 62386 底层协议解析
智能照明·总线协议·极物·极物智能·极物科技·dali调光灯·iec 62386
明天会出太阳4 天前
GB/T 50034‑2024 照明工程落地:UGR、RG0、Ra 参数核查与不同空间设计要点
智能家居·武汉·knx·照明设计·无主灯
阿钱真强道4 天前
07 极物科技 | DALI调光灯 - 快速接入指南
智能照明·极物·极物智能·极物科技·dali调光灯·iec 62386·调光控制
阿钱真强道4 天前
08 极物科技 | DALI调光灯 - UDP 通讯协议规范
通讯协议·智能照明·极物·极物智能·极物科技·dali调光灯·iec 62386