极物科技 | DALI协议栈 - 特殊命令与设备发现机制
前言
在智能控制领域,"协议壁垒"与"系统稳定性"始终是绕不开的难题。极物科技(Zeewo)以 "重构空间交互体验" 为使命,通过自研的 极物 OS,实现了 KNX、DALI、CAN、RS485 等多种总线协议在单一主机上的深度融合。
我们认为,一个真正优秀的智能系统,不仅要实现灯光、遮阳、温控、音乐、安防等全生态设备的无界融合 ,更要确保本地运行、断网可用 ,并具备WEB 远程调试 和 OTA 固件升级的能力。基于此底层架构,我们推出了诸如带双路 DALI 的 KNX 主机、HomeKit 网桥等核心硬件,并将极物 OS 成功接入了 Apple HomeKit、小度、纯血鸿蒙及 HomeAssistant 等主流生态系统。
一句话概述:本文深入解析极物 DALI 协议栈特殊命令与设备发现机制的完整实现。
本文聚焦于 DALI 特殊命令集(0xA1-0xC9)与设备发现流程,涵盖 INITIALISE 初始化窗口、RANDOMISE 随机地址生成、COMPARE 地址比较、WITHDRAW 退出竞争、PROGRAM_SHORT_ADDRESS 短地址编程等核心命令,展示 DALI 总线如何为多个从设备自动分配唯一短地址。


1. 特殊命令总览
1.1 命令列表
| 命令地址 | 命令名称 | 功能说明 |
|---|---|---|
| 0xA1 | TERMINATE | 终止初始化窗口 |
| 0xA3 | DATA_TRANSFER_REGISTER | 设置 DTR0 |
| 0xA5 | INITIALISE | 启动初始化窗口(15分钟) |
| 0xA7 | RANDOMISE | 生成随机地址 |
| 0xA9 | COMPARE | 比较随机地址 |
| 0xAB | WITHDRAW | 匹配设备退出竞争 |
| 0xAD | PING | 忽略(主设备存在指示) |
| 0xB1 | SEARCHADDRH | 设置搜索地址高字节 |
| 0xB3 | SEARCHADDRM | 设置搜索地址中字节 |
| 0xB5 | SEARCHADDRL | 设置搜索地址低字节 |
| 0xB7 | PROGRAM_SHORT_ADDRESS | 编程短地址 |
| 0xB9 | VERIFY_SHORT_ADDRESS | 验证短地址 |
| 0xBB | QUERY_SHORT_ADDRESS | 查询短地址 |
| 0xC1 | ENABLE_DEVICE_TYPE | 启用设备类型 |
| 0xC3 | DATA_TRANSFER_REGISTER_1 | 设置 DTR1 |
| 0xC5 | DATA_TRANSFER_REGISTER_2 | 设置 DTR2 |
| 0xC7 | WRITE_MEMORY_LOCATION | 写入内存位置(应答) |
| 0xC9 | WRITE_MEMORY_LOCATION_NO_REPLY | 写入内存位置(不应答) |
1.2 特殊命令特征
- 使用地址字节传输:S 位为 0,命令值在地址字节中
- 地址范围:0xA1 ~ 0xC9
- 需要重复帧:部分命令需要两帧相同数据才生效
- 依赖初始化窗口:部分命令仅在 15 分钟窗口内有效
2. 特殊命令处理入口
c
uint16_t dali_cmd_process_special(dali_slave_service_info_t *slave_info, uint8_t repeat_flag) {
uint16_t ret = ANSWER_NO;
uint8_t temp_short_addr;
uint8_t temp = 0;
uint8_t enable_device_cmd = 0;
uint32_t temp_random_addr, temp_search_addr;
bool reply_flag = false;
// 部分命令仅在初始化窗口内有效
if ((slave_info->rec_addr >= RANDOMISE) && (slave_info->rec_addr <= QUERY_SHORT_ADDRESS)) {
if (w15min_waiting_time == 0) {
return ret; // 窗口已关闭,不响应
}
}
// 特殊命令处理
switch (slave_info->rec_addr) {
// ... 各命令处理
}
return ret;
}
3. 初始化窗口管理
3.1 INITIALISE 命令 (0xA5)
功能:启动 15 分钟初始化窗口。
c
case INITIALISE:
if (repeat_flag == false) {
return RECEIVE_FIRST; // 需要重复帧
}
if (slave_info->rec_data == 0) {
temp = 1; // 广播初始化,所有设备响应
} else if (slave_info->rec_data == ADDR_MASK) {
// 仅未编址设备响应
if (dali_ram.variable.short_address == ADDR_MASK) {
temp = 1;
}
} else if ((slave_info->rec_data & 0x81) == 0x01) {
// 指定短地址的设备响应
if (temp_short_addr == dali_ram.variable.short_address) {
temp = 1;
}
}
if (temp) {
w15min_waiting_time = W15MIN; // 启动 15 分钟窗口
} else {
w15min_waiting_time = 0;
}
break;
三种初始化模式:
| rec_data 值 | 模式 | 响应设备 |
|---|---|---|
| 0 | 广播初始化 | 所有设备 |
| 0xFF (ADDR_MASK) | 未编址初始化 | 仅未编址设备 |
| 0AAA AAA1 | 指定地址初始化 | 仅指定地址设备 |
3.2 TERMINATE 命令 (0xA1)
功能:立即终止初始化窗口。
c
case TERMINATE:
w15min_waiting_time = 0; // 关闭窗口
break;
3.3 窗口超时
c
#define W15MIN ((uint32_t)15 * (uint32_t)60 * (uint32_t)1000 * ONE_MS_TICK)
// 主循环中递减
if (w15min_waiting_time > 0) {
w15min_waiting_time -= tick_ms;
}
4. 设备发现流程
4.1 发现流程概览
1. TERMINATE → 终止之前的初始化
2. INITIALISE (0) → 启动 15 分钟窗口(所有设备)
3. RANDOMISE → 所有设备生成随机地址
4. 循环二分查找:
a. SEARCHADDRH/M/L → 设置搜索地址
b. COMPARE → 随机地址 ≤ 搜索地址的设备应答
c. 主设备调整搜索地址(二分法)
d. WITHDRAW → 匹配的设备退出竞争
e. PROGRAM_SHORT_ADDRESS → 分配短地址
5. 重复直到所有设备编址完成
4.2 RANDOMISE 命令 (0xA7)
功能:生成 24 bit 随机地址。
c
case RANDOMISE:
if (repeat_flag == false) {
return RECEIVE_FIRST;
}
// 使用定时器值作为随机种子
srand(dali_hal_get_seed0_value());
dali_ram.variable.random_address.high = rand();
dali_ram.variable.random_address.mid = rand();
srand(dali_hal_get_seed1_value());
dali_ram.variable.random_address.low = rand();
update_flag |= VAR_UPDATE;
break;
随机地址用途:每个设备生成唯一的 24 bit 随机数,用于寻址竞争。
4.3 SEARCHADDR 命令 (0xB1/0xB3/0xB5)
功能:设置搜索地址(主设备二分查找用)。
c
case SEARCHADDRH:
search_address.high = slave_info->rec_data;
break;
case SEARCHADDRM:
search_address.mid = slave_info->rec_data;
break;
case SEARCHADDRL:
search_address.low = slave_info->rec_data;
break;
4.4 COMPARE 命令 (0xA9)
功能:比较随机地址与搜索地址。
c
case COMPARE:
if (withdraw == 0) { // 未退出的设备才比较
if (temp_random_addr <= temp_search_addr) {
ret = ANSWER_YES; // 应答
}
}
break;
二分查找原理:
搜索地址 = 0x800000 (中间值)
→ 随机地址 ≤ 0x800000 的设备应答
→ 主设备检测到应答,缩小范围到 0x000000~0x800000
→ 继续二分,直到找到最小随机地址的设备
4.5 WITHDRAW 命令 (0xAB)
功能:匹配的设备退出竞争。
c
case WITHDRAW:
if (temp_random_addr == temp_search_addr) {
withdraw = 1; // 设置退出标志
}
break;
退出状态管理:
c
if (withdraw == 1) {
if (w15min_waiting_time == 0) { // 超时或终止
withdraw = 0; // 重置退出标志
}
}
4.6 PROGRAM_SHORT_ADDRESS 命令 (0xB7)
功能:为匹配的设备分配短地址。
c
case PROGRAM_SHORT_ADDRESS:
if (slave_info->rec_data == ADDR_MASK) {
// 删除短地址
dali_ram.variable.short_address = ADDR_MASK;
update_flag |= VAR_UPDATE;
status_information |= MISSING_SHORT_ADDR;
} else if (temp_random_addr == temp_search_addr) {
// 匹配的设备
if ((slave_info->rec_data & 0x01) == 0x01) {
// 格式正确 (0AAA AAA1)
dali_ram.variable.short_address = temp_short_addr;
update_flag |= VAR_UPDATE;
status_information &= ~MISSING_SHORT_ADDR;
}
}
break;
4.7 VERIFY_SHORT_ADDRESS 命令 (0xB9)
功能:验证短地址是否正确。
c
case VERIFY_SHORT_ADDRESS:
if ((slave_info->rec_data & 0x81) == 0x01) {
if (dali_ram.variable.short_address == temp_short_addr) {
ret = ANSWER_YES; // 验证通过
}
}
break;
4.8 QUERY_SHORT_ADDRESS 命令 (0xBB)
功能:查询已分配的短地址。
c
case QUERY_SHORT_ADDRESS:
if (temp_random_addr == temp_search_addr) {
ret = (dali_ram.variable.short_address << 1) | 0x01;
}
break;
返回格式 :0AAA AAA1 (短地址左移 1 位,最低位置 1)
5. 设备类型启用
5.1 ENABLE_DEVICE_TYPE 命令 (0xC1)
功能:启用后续扩展命令处理。
c
case ENABLE_DEVICE_TYPE:
if (slave_info->rec_data == dali_ram.variable.led_type ||
slave_info->rec_data == dali_ram.variable.version_number) {
device_enabled = true;
}
enable_device_cmd = 1;
break;
重要性 :扩展应用命令 (224-255) 仅在 device_enabled = true 时才处理。
5.2 设备类型定义
c
#define DEVICE_TYPE 255 // LED 模块
#define LIGHT_SOURCE LED_MODULES // 6
6. DTR 寄存器设置
6.1 DATA_TRANSFER_REGISTER (0xA3)
c
case DATA_TRANSFER_REGISTER:
dtr0 = slave_info->rec_data;
break;
6.2 DATA_TRANSFER_REGISTER_1 (0xC3)
c
case DATA_TRANSFER_REGISTER_1:
dtr1 = slave_info->rec_data;
break;
6.3 DATA_TRANSFER_REGISTER_2 (0xC5)
c
case DATA_TRANSFER_REGISTER_2:
dtr2 = slave_info->rec_data;
break;
7. 内存写入命令
7.1 WRITE_MEMORY_LOCATION (0xC7)
c
case WRITE_MEMORY_LOCATION:
reply_flag = true; // 需要应答
case WRITE_MEMORY_LOCATION_NO_REPLY:
if (write_memory_enabled == false) {
break; // 未启用写入
}
temp = 0;
if (dtr1 == BANK1) {
if (dtr0 < 0x02) {
temp = 1; // 只读内存
} else if (dtr0 == 0x02) {
temp = 2; // 读/写内存(锁定字节)
} else if ((dtr0 > 0x02) && (dtr0 <= 0x10)) {
if (dali_ram.memory.bank1[2] == NOT_LOCK) {
temp = 2; // 可锁定内存
}
}
if (temp > 0) {
if (temp == 2) {
dali_ram.memory.bank1[dtr0] = slave_info->rec_data;
if (reply_flag) {
ret = slave_info->rec_data; // 应答写入的值
}
update_flag |= BANK1_UPDATE;
}
}
if (dtr0 < 0xFF) {
dtr0++; // 自动递增
}
}
break;
7.2 写入权限控制
c
// 必须先启用 WRITE_MEMORY
case ENABLE_WRITE_MEMORY:
write_memory_enabled = true;
break;
// 非 DTR/写入命令后自动禁用
if (write_memory_enabled == true) {
if ((slave_info->rec_addr != DATA_TRANSFER_REGISTER) &&
(slave_info->rec_addr != DATA_TRANSFER_REGISTER_1) &&
(slave_info->rec_addr != DATA_TRANSFER_REGISTER_2) &&
(slave_info->rec_addr != WRITE_MEMORY_LOCATION) &&
(slave_info->rec_addr != WRITE_MEMORY_LOCATION_NO_REPLY)) {
write_memory_enabled = false;
}
}
7.3 Bank1 锁定机制
c
#define NOT_LOCK 0x55
#define BANK1_LOCK_BYTE 0xFF
// 锁定字节在 bank1[2]
// 0x55 = 未锁定,可写入
// 0xFF = 已锁定,只读
8. 设备识别机制
8.1 IDENTIFY_DEVICE 命令 (37)
c
case IDENTIFY_DEVICE:
identify_flag = true;
w10s_identify_time = W10S; // 10 秒识别
if (status_information & FADE_RUNNING) {
fade_direction = FADE_DIRECT; // 停止渐变
}
break;
8.2 识别闪烁
c
void dali_hal_identify_device(void) {
if (identify_delay-- == 0) {
identify_delay = 10;
} else {
return;
}
// PWM 值在 0~0xFFF 之间循环
if (identify_count == 0XFFF) {
identify_count_flag = 0;
} else if (identify_count == 0) {
identify_count_flag = 1;
}
if (identify_count_flag) {
identify_count++;
} else {
identify_count--;
}
dali_hal_update_pwm_output(identify_count);
dali_hal_update_pwm_output_tc(identify_count);
}
效果:灯具亮度在 0~最大之间循环变化,持续 10 秒,便于现场识别设备。
8.3 识别中断条件
c
if (identify_flag == true) {
if ((slave_info->rec_data != RECALL_MIN_LEVEL) &&
(slave_info->rec_data != RECALL_MAX_LEVEL) &&
(slave_info->rec_data != IDENTIFY_DEVICE)) {
identify_flag = false; // 其他命令中断识别
w10s_identify_time = 0;
}
}
9. 完整设备发现示例
9.1 单设备发现
主设备发送:
1. TERMINATE
2. INITIALISE (0) → 设备启动 15 分钟窗口
3. RANDOMISE → 设备生成随机地址 0x123456
4. SEARCHADDRH (0x80)
SEARCHADDRM (0x00)
SEARCHADDRL (0x00) → 搜索地址 = 0x800000
5. COMPARE → 0x123456 ≤ 0x800000 → 应答 YES
6. 主设备缩小范围...
最终搜索地址 = 0x123456
7. WITHDRAW → 设备匹配,设置 withdraw=1
8. PROGRAM_SHORT_ADDRESS (0x0B) → 分配短地址 5
9. VERIFY_SHORT_ADDRESS (0x0B) → 验证通过,应答 YES
9.2 多设备发现
主设备发送:
1. TERMINATE
2. INITIALISE (0) → 所有设备启动窗口
3. RANDOMISE → 设备 A: 0x123456, 设备 B: 0x789ABC
第一轮:
4. SEARCHADDRH/M/L (0x400000)
5. COMPARE → A (0x123456 ≤ 0x400000) 应答
B (0x789ABC > 0x400000) 不应答
6. 主设备检测到应答,继续二分查找 A
7. ... 找到 A 的随机地址
8. WITHDRAW → A 退出竞争
9. PROGRAM_SHORT_ADDRESS → A 获得短地址 0
第二轮:
10. SEARCHADDRH/M/L (0x400000)
11. COMPARE → B (0x789ABC > 0x400000) 不应答
12. 主设备调整搜索地址到 0x789ABC
13. ... 找到 B 的随机地址
14. WITHDRAW → B 退出竞争
15. PROGRAM_SHORT_ADDRESS → B 获得短地址 1
10. 注意事项
- 初始化窗口:15 分钟窗口超时后,设备发现命令无效
- 重复帧要求:INITIALISE、RANDOMISE 需要两帧相同数据
- 随机地址唯一性:24 bit 随机地址冲突概率极低
- 二分查找效率:最多 24 次比较即可定位一个设备
- 短地址格式 :DTR0 中为
0AAA AAA1格式,需右移 1 位 - device_enabled:扩展命令前必须先 ENABLE_DEVICE_TYPE
- 内存写入:必须先 ENABLE_WRITE_MEMORY,且 Bank1 需解锁
- 识别中断:除 RECALL MAX/MIN 和 IDENTIFY 外的命令会中断识别
11. 相关文档
- 《极物科技 | DALI协议栈 - 架构总览与模块设计》
- 《极物科技 | DALI协议栈 - 物理层位编解码详解》
- 《极物科技 | DALI协议栈 - 帧传输状态机与地址解析》
- 《极物科技 | DALI协议栈 - 标准命令集解析》
- 《极物科技 | DALI协议栈 - 配置命令详解》
- 《极物科技 | DALI协议栈 - 查询命令全解》
- 《极物科技 | DALI协议栈 - DT8双色温扩展与PWM映射》
- 《极物科技 | DALI调光灯 - 快速接入指南》
- 《极物科技 | DALI调光灯 - 设备发现与地址分配机制》
关于极物科技(ZEEWO)
极物科技(Zeewo)致力于为用户提供智能控制系统及硬件产品。我们以总线系统为技术底座,以**"稳定、可靠、快速响应"**为产品底线,是国内少有的拥有 KNX、DALI 全套软硬件自主研发能力的厂商之一。
我们的核心能力:
- 系统架构:自研极物 OS,支持多协议无界融合(KNX/DALI/CAN/RS485/IP)。
- 核心硬件:带双路 DALI 的 KNX 主机、超薄全金属定制面板、各类智选传感器及网关。
- 生态互联:深度融入 Apple HomeKit、Matter、小度、HomeAssistant 及纯血鸿蒙生态。
- 调试交付:独家支持 ETS 导出 XML 直接导入进行 APP 免编程调试,支持远程 WEB 运维。
我们的市场覆盖:
服务网点已覆盖全国核心城市(含长三角、珠三角、成渝等),并以高品质的方案深耕家居生活、酒店民宿、企业办公、疗愈康养、餐馆会所等多个细分领域。
(如果您在开发或落地中遇到技术问题,欢迎通过官网或后台私信与我交流探讨)

