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. 注意事项
- TE 容差:协议栈允许 TE 值 ±4 的容差,适应不同晶振频率
- 串口独占:DALI 总线必须保证只有一个程序在使用,严禁多程序同时使用
- EEPROM 写入:仅在数据变更时写入,避免频繁擦写
- 渐变精度:使用 32 位累加器保证渐变平滑,避免步进跳跃
- 重复帧窗口:115ms 内收到两帧相同数据才执行配置命令
- 设备发现超时: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 运维。
我们的市场覆盖:
服务网点已覆盖全国核心城市(含长三角、珠三角、成渝等),并以高品质的方案深耕家居生活、酒店民宿、企业办公、疗愈康养、餐馆会所等多个细分领域。
(如果您在开发或落地中遇到技术问题,欢迎通过官网或后台私信与我交流探讨)

