LED电源的DALI协议开发实战:从协议帧解析到国产M0+实现

31 阅读8分钟

LED电源的DALI协议开发实战:从协议帧解析到国产M0+实现

导读: DALI(Digital Addressable Lighting Interface)是商业照明领域最主流的调光协议之一。本文从DALI协议规范入手,详细解析DALI 2.0的数据帧格式、地址机制、命令分类,并以CMS32L051为例讲解如何在国产Cortex-M0+平台上实现DALI从机功能。文章后半段结合实际开发经验,讲开发过程中的坑和调试技巧。


一、为什么DALI在LED电源里这么普遍

DALI和0-10V、PWM调光最大的区别是:数字寻址 + 可双向通信

调光方式特点
0-10V模拟信号,只能调亮度,单向,没地址
PWM模拟占空比,不能独立寻址
DALI数字总线,每个设备有独立地址,可查询状态、故障上报、分组控制

在商业照明项目(办公楼、酒店、工厂)中,DALI的优势:

  • 同一根总线可以挂64个(短地址)或128个(组地址)设备
  • 可以查询每个灯具的电流、电压、故障状态
  • 支持场景设置:一个场景命令让所有灯调到指定亮度
  • 布线简单:电源线和信号线可以共用同一根线缆

二、DALI协议基础:电气层

硬件接口:

参数
总线电压DC 12V-22V(通常16V)
总线电流每个从机消耗1mA-2mA
通信速率1200bps(半双工)
总线电容上限300nF(所有设备电容之和)

总线电平:

  • 空闲时:总线为高电平(16V经上拉电阻)
  • 发送"1":TX拉低总线,接收端检测到下降沿
  • 发送"0":TX拉高总线(释放,让上拉电阻拉高)

重要: DALI总线不是开漏输出,而是推挽结构,能主动拉高也能主动拉低。这和其他很多总线协议不同,配置时别搞错。


三、DALI数据帧结构(最核心的部分)

DALI的数据帧是11位,不是8位,这一点很多人搞错:

| START | D8 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | STOP |
|  bit  |    |    |    |    |    |    |    |    |    |      |
说明
START起始位,固定为高电平 + 下降沿
D8-D0数据位,D8是最高位
STOP停止位,固定为高电平(2个比特周期)

总bit时间 = 1个START + 9个数据位 + 2个STOP = 12个比特周期

一个bit的时间 = 416.7μs(1200bps)

前向帧(主机→从机):

| START | ADDR(8bit) | COMMAND(8bit) | STOP |
  • ADDR的高7位是地址位,最低位是广播/组播标记
  • COMMAND是8位命令码

后向帧(从机→主机):

| START | DATA(8bit) | STOP |

从机回复的帧只有8位数据,没有地址。


四、DALI地址机制(很多人踩坑的地方)

DALI地址分三类:

4.1 短地址(最常用)

  • 每个从机烧录一个 0-63 的短地址
  • 总线上不能有两个相同短地址
  • 广播地址:255(0xFF),发给所有设备
  • 命令格式:ADDR | COMMAND,ADDR[7:1] = 设备短地址,ADDR[0] = 0 表示单播

4.2 组地址

  • 设备可以属于 16个组(0-15) 之一
  • 发送命令时把ADDR[0]置1,ADDR[7:1]=组号,即为组播
  • 设备可以同时属于多个组

4.3 序列号地址

  • 每个DALI设备有全球唯一序列号(24位)
  • 可通过序列号直接寻址特定设备(即插即用场景)

五、DALI命令分类(DALI 2.0)

5.1 基础开关和调光命令

命令号命令名说明
0x00OFF关灯
0x05DAPC直接控制功率(设置亮度0-254)
0x10UP亮度+1
0x20DOWN亮度-1
0x94QUERY ACTUAL DIM LEVEL查询当前亮度值

DAPC(Direct Arc Power Control)是最核心的命令:

// 发送DAPC命令,设置亮度为200(0xC8)
uint8_t addr = device_short_addr;  // 0-63
uint8_t level = 0xC8;              // 0=off, 1-254=亮度, 255=最大亮度
uint8_t frame = (addr << 1) | 0x00; // 单播
DALI_Send_Frame(frame, 0x05);      // DAPC命令

5.2 配置命令(初始化时用一次)

命令号命令名说明
0xB8INITIALISE进入初始化模式
0xA5RANDOMIZE随机化地址(生产时用)
0xB7PROGRAM SHORT ADDRESS烧录短地址
0xA0SET MAX LEVEL设置最大亮度
0xA1SET MIN LEVEL设置最小亮度
0xA2SET SYSTEM FAILURE LEVEL系统故障时的亮度

5.3 查询命令(实现双向通信的关键)

命令号命令名说明
0x90QUERY STATUS查询设备状态
0x92QUERY LAMP FAILURE查询灯泡是否故障
0x94QUERY ACTUAL DIM LEVEL查询当前实际亮度
0x98QUERY PHYSICAL MIN LEVEL查询物理最小亮度

六、CMS32L051实现DALI从机

6.1 硬件连接

CMS32L051              DALI总线
  PA6 (UART1 TX)  ←─────────────────────→  总线(TX/RX)
  PA7 (UART1 RX)  ←─────────────────────→

注意: DALI总线电压16V,RX引脚需串联10KΩ限流电阻,建议加TVS二极管做ESD保护。

6.2 UART配置(DALI固定1200bps,奇校验,9位数据)

// CMS32L051 UART1 配置DALI从机
UART1_InitTypeDef uart_cfg;
uart_cfg.BaudRate    = 1200;                // DALI固定1200bps
uart_cfg.DataBits    = UART_DATA_BITS_9;   // 8数据位 + 1校验位
uart_cfg.Parity      = UART_PARITY_ODD;    // DALI奇校验
uart_cfg.StopBits    = UART_STOP_BITS_2;   // 2个停止位

UART1_Init(&uart_cfg);
UART1_ITConfig(UART1_IT_RXIEN, ENABLE);    // 使能接收中断

注意: DALI是9位数据模式,不是标准UART的8位!这是最容易配置错的地方。

6.3 帧解析实现

uint16_t dali_frame = 0;   // 接收缓冲
uint8_t  rx_cnt = 0;

void UART1_RX_IRQHandler(void)
{
    uint8_t dat = UART1->DR;   // 9位模式读取

    dali_frame = (dali_frame << 9) | (dat & 0x1FF);
    rx_cnt++;

    if (rx_cnt == 2) {        // 收到ADDR+CMD两个字节
        uint8_t addr = (dali_frame >> 9) & 0x7F;  // 高7位地址
        uint8_t cmd  = dali_frame & 0xFF;          // 低8位命令
        DALI_ProcessCommand(addr, cmd);
        dali_frame = 0;
        rx_cnt = 0;
    }
}

6.4 地址存储

// DALI短地址存在Flash InfoPage,每次上电读取
#define DALI_SHORT_ADDR_ADDR  (0x0801F800)

uint8_t DALI_GetShortAddress(void)
{
    return *(uint8_t *)(DALI_SHORT_ADDR_ADDR);
}

void DALI_SetShortAddress(uint8_t addr)
{
    FLASH_Unlock();
    FLASH_ProgramByte(DALI_SHORT_ADDR_ADDR, addr);
    FLASH_Lock();
}

七、实战中踩过的DALI开发坑

坑1:总线上设备多了就丢帧

现象: 接30个DALI电源没问题,接50个就开始丢帧。

根因: 总线电容超标。每个从机输入电容约2nF,50个就是100nF,波形上升沿变缓导致接收端采样错误。

解决:

  • 在总线中段加DALI信号放大器/隔离器
  • 减少总线长度
  • 选低输入电容的DALI模块

坑2:广播命令有效,单独命令无效

现象: 广播调光命令有效,单独发命令给某个灯没反应。

根因: 从机短地址没有正确烧录。广播命令不校验地址所以有效,单播需要地址匹配才响应。

排查: 发送"广播 + QUERY SHORT ADDRESS",没响应说明从机没地址,需要重新 INITIALISE + PROGRAM SHORT ADDRESS。


坑3:某些品牌DALI主控时序不符合标准

现象: 接某品牌面板没问题,接另一家主控就丢帧。

根因: 便宜DALI主控的STOP位宽度不够,或两个连续帧间隔时间太短。

解决: 从机留容差窗口,STOP位小于250μs的帧直接丢弃。


坑4:QUERY命令返回0xFF不知道啥意思

返回值含义
0xFF设备不支持此查询(老版本DALI)
bit5=1灯泡故障(Lamp failure)
bit2=1开路(Open circuit)
bit1=1短路(Load short-circuit)
bit0=1电源故障(Power failure)
uint8_t status = DALI_SendAndReceive(QUERY_STATUS, 0x00);
if (status == 0xFF) {
    // 老版本DALI,不支持状态查询
} else {
    if (status & 0x04) printf("开路故障");
    if (status & 0x02) printf("短路故障");
    if (status & 0x20) printf("灯珠故障");
}

八、实测数据

在24W DALI LED电源上实测:

测试项结果
亮度设置范围0.1%-100%(最小0.1%)
亮度设置精度±1%(全电压范围)
命令响应时间小于5ms
最大总线设备数64个短地址(实测64个稳定)

九、DALI 2.0的新功能(值得关注的趋势)

DALI 2.0相比初代增加了:

  • 事件驱动:灯故障了主动上报主控,不需要主控轮询
  • 颜色控制:支持RGBW灯具的颜色调节
  • 能耗报告:可查询每个灯具的累计能耗
  • 固件升级:可以远程升级DALI设备固件

如果你的DALI电源要出海,通过DALI 2.0认证是很多欧美项目的入场券。


如果你正在做类似的项目,欢迎在评论区交流。关注我,下一篇继续深入讲DALI 2.0的新特性与认证要点。


相关技术标签: DALI协议 LED驱动 CMS32L051 嵌入式 数字电源 智能照明 Cortex-M0+