数字可寻址照明接口(DALI)总线驱动

[English]

DALI 组件提供了基于 ESP-IDF 的 DALI(IEC 62386)主站驱动。 该驱动使用 ESP 的 RMT 外设实现 前向帧 发送与 后向帧 解码,便于应用直接控制和查询 DALI 控制设备。

功能特性

  • 基于 RMT 的 DALI 物理层曼彻斯特编码收发实现。

  • 支持 短地址组地址、广播和特殊命令地址模式。

  • 支持 DAPC 直控、控制/配置命令和查询命令。

  • 内置 send-twice 机制,满足需要 100 ms 内双发的命令要求。

  • 支持 TX/RX 极性可配置,适配不同硬件接口反相链路。

  • 提供完整测试工程与示例工程。

支持目标

组件元数据当前支持以下 ESP32 系列芯片:

  • ESP32

  • ESP32-S2

  • ESP32-S3

  • ESP32-C3

  • ESP32-C6

  • ESP32-P4

  • ESP32-H2

快速开始

  1. 包含头文件:

    #include "dali.h"
    #include "dali_command.h"
    
  2. 初始化驱动:

    dali_master_handle_t dali;
    dali_master_config_t cfg = {
        .rx_gpio = GPIO_NUM_4,
        .tx_gpio = GPIO_NUM_5,
        .invert_tx = false,
        .invert_rx = false,
    };
    dali_master_rmt_config_t rmt_cfg = {
        .mem_block_symbols = 64,
    };
    ESP_ERROR_CHECK(dali_new_master_rmt(&cfg, &rmt_cfg, &dali));
    
  3. 发送命令(不期望后向帧):

    /* 驱动在每次事务后自动插入最小帧间隔 (> 22 Te),无需手动延时 */
    dali_master_transaction_config_t tx_cfg = {
        .addr_type = DALI_ADDR_SHORT,
        .addr = 0,
        .is_cmd = true,
        .command = DALI_CMD_RECALL_MAX_LEVEL,
        .send_twice = false,
        .tx_timeout_ms = DALI_TX_TIMEOUT_MS,
    };
    ESP_ERROR_CHECK(dali_master_do_transaction(dali, &tx_cfg, NULL));
    
  4. 发送查询(期望后向帧):

    int reply = DALI_RESULT_NO_REPLY;
    dali_master_transaction_config_t tx_cfg = {
        .addr_type = DALI_ADDR_SHORT,
        .addr = 0,
        .is_cmd = true,
        .command = DALI_CMD_QUERY_STATUS,
        .send_twice = false,
        .tx_timeout_ms = DALI_TX_TIMEOUT_MS,
    };
    ESP_ERROR_CHECK(dali_master_do_transaction(dali, &tx_cfg, &reply));
    if (DALI_RESULT_IS_VALID(reply)) {
        ESP_LOGI("dali", "QUERY_STATUS = 0x%02X", (unsigned)reply);
    }
    

配置项

DALI 驱动的全部配置均通过两个结构体在运行时完成,无需 Kconfig 选项:

dali_master_config_t cfg = {
    .rx_gpio = GPIO_NUM_4,
    .tx_gpio = GPIO_NUM_5,
    .invert_tx = false,      /* 默认不启用 TX 硬件链路奇数次反相 */
    .invert_rx = false,      /* 默认不启用 RX 硬件链路奇数次反相 */
};
dali_master_rmt_config_t rmt_cfg = {
    .mem_block_symbols = 0, /* 0 = 根据 SOC 能力自动检测 */
};
ESP_ERROR_CHECK(dali_new_master_rmt(&cfg, &rmt_cfg, &dali));

命令模型

统一通过 dali_master_do_transaction() 完成不同类型事务。 通过 dali_master_transaction_config_t 描述事务参数:

  • DAPC 直控:config.is_cmd = falseconfig.command 为亮度值。

  • 普通命令/查询:config.is_cmd = trueconfig.command 来自 dali_command.h

  • 需要双发的命令:设置 config.send_twice = true

  • 查询命令:传入 result != NULL,并用 DALI_RESULT_IS_VALID(*result) 判断是否收到有效回复。 dali_master_do_transaction() 每次调用后自动插入所需帧间隔 (> 22 Te), 无需在连续调用间手动延时。

时序说明

  • DALI 对帧间隔与后向帧响应窗口有严格约束。

  • 驱动在每次 dali_master_do_transaction() 调用后自动插入最小帧间隔 (> 22 Te), 满足 IEC 62386 时序要求。

  • dali_master_do_transaction() 为阻塞调用 — 请勿在 ISR 或对实时性要求极高的任务中调用。

示例与测试

示例覆盖内容包括:

  • DAPC 调光序列

  • 普通命令发送

  • 查询命令与回复解析

  • send-twice 配置命令流程

API 参考

Header File

Functions

esp_err_t dali_new_master_rmt(const dali_master_config_t *config, const dali_master_rmt_config_t *rmt_config, dali_master_handle_t *handle)

Create and initialize a new DALI driver instance backed by RMT.

Allocates a driver context, configures the RMT TX channel on config->tx_gpio and the RMT RX channel on config->rx_gpio, and returns an opaque handle. Multiple independent instances can be created on different GPIO pairs.

The naming convention follows the pattern dali_new_bus_<backend> so that future backends (e.g. dali_new_bus_uart) can coexist without naming conflicts.

参数
  • config[in] Pointer to bus configuration (GPIO numbers).

  • rmt_config[in] Pointer to RMT-specific configuration. Pass NULL to use built-in defaults (mem_block_symbols=64).

  • handle[out] Pointer to store the created handle on success.

返回

  • ESP_OK on success; *handle is valid

  • ESP_ERR_INVALID_ARG if config or handle is NULL, or GPIO numbers are invalid

  • ESP_ERR_NO_MEM if heap allocation fails

  • Other ESP_ERR codes propagated from RMT driver

esp_err_t dali_del_master(dali_master_handle_t handle)

De-initialize a DALI driver instance and release all resources.

Disables and deletes the RMT channels, encoder, and RX queue associated with handle, then frees the context memory. The handle is invalid after this call.

Safe to call even if dali_new_master_rmt only partially succeeded (e.g. during error recovery).

参数

handle[in] Handle returned by dali_new_master_rmt.

返回

ESP_OK always.

esp_err_t dali_master_do_transaction(dali_master_handle_t handle, const dali_master_transaction_config_t *config, int *result)

Execute a DALI transaction (forward frame, optional backward frame).

Transmits a 16-bit DALI forward frame composed of an address byte and a command/data byte, then optionally listens for an 8-bit backward frame. After the transaction completes the driver automatically inserts the minimum inter-frame gap required by IEC 62386 (> 22 Te ≈ 9.2 ms), so callers do not need to add an explicit delay between consecutive calls.

Address byte construction:

config->addr_type

First byte format

DALI_ADDR_SHORT

0AAAAAAS (A = 0–63)

DALI_ADDR_GROUP

100AAAAS (A = 0–15)

DALI_ADDR_BROADCAST

1111111S

DALI_ADDR_SPECIAL

config->addr passed through

The S (selector) bit is set to 1 when config->is_cmd is true (indirect command), or 0 when false (direct arc-power control, DAPC).

Send-twice commands: Certain configuration commands (e.g., RESET, STORE_ACTUAL_LEVEL) only take effect when sent twice within 100 ms. Set config->send_twice to true and the driver will automatically re-transmit the frame after a 40 ms delay.

Result semantics:

  • Pass NULL for result if no backward frame is expected. The driver will wait one backward-frame window before returning.

  • Pass a pointer for result to enable RX. On return:

    • *result >= 0: valid 8-bit backward-frame value (0x00–0xFF)

    • *result == DALI_RESULT_NO_REPLY: timeout — no reply received

备注

This function is blocking. It occupies the calling task for the duration of the TX transmission plus the backward-frame reception window (up to config->tx_timeout_ms + 50 ms). Do not call this function from an ISR or from a high-priority real-time task where blocking is not acceptable.

参数
  • handle[in] Handle returned by dali_new_master_rmt.

  • config[in] Pointer to transaction configuration.

  • result[out] Pointer to store the backward-frame result, or NULL if no reply is expected.

返回

  • ESP_OK on success (including DALI_RESULT_NO_REPLY case)

  • ESP_ERR_INVALID_ARG if handle or config is NULL, or address is out of range

  • Other ESP_ERR codes propagated from RMT driver

Structures

struct dali_master_config_t

Configuration structure for creating a DALI driver instance.

Contains bus-agnostic parameters shared across all backend implementations (RMT, UART, etc.). Backend-specific parameters are passed separately via the corresponding config struct (e.g. dali_master_rmt_config_t).

Public Members

gpio_num_t rx_gpio

GPIO number for the DALI bus receive line

gpio_num_t tx_gpio

GPIO number for the DALI bus transmit line

bool invert_tx

Invert TX signal polarity. Enable when the hardware path between the MCU TX GPIO and the DALI bus performs an odd number of signal inversions.

bool invert_rx

Invert RX input signal polarity. Enable when the hardware path between the DALI bus and the MCU RX GPIO performs an odd number of signal inversions.

struct dali_master_rmt_config_t

RMT-backend specific configuration for a DALI master driver instance.

Pass a pointer to this struct as the rmt_config argument of dali_new_master_rmt. Initialize with designated initializers to override the built-in defaults:

dali_master_rmt_config_t rmt_cfg = {
    .mem_block_symbols = 48,
};
dali_new_master_rmt(&cfg, &rmt_cfg, &handle);

Public Members

uint32_t mem_block_symbols

RMT memory block size in symbols for both TX and RX channels. Set to 0 to let the driver auto-detect the optimal value based on SOC_RMT_MEM_WORDS_PER_CHANNEL (48 on ESP32-C3/S3, 64 on ESP32/S2).

struct dali_master_transaction_config_t

Transaction configuration for dali_master_do_transaction().

Bundles all parameters that describe a single DALI transaction (forward frame addressing, command/data byte, and transmission options).

Public Members

dali_addr_type_t addr_type

Address type (dali_addr_type_t)

uint8_t addr

Device address (0–63 for short, 0–15 for group, ignored for broadcast, raw byte for special)

bool is_cmd

true → indirect command (S-bit = 1) false → direct DAPC value (S-bit = 0)

uint8_t command

Command code or arc-power value (second byte)

bool send_twice

true to repeat the frame within 100 ms

int tx_timeout_ms

Timeout in ms for the RMT TX completion wait

Macros

DALI_TX_TIMEOUT_MS

Default TX transmission timeout in milliseconds.

DALI_RESULT_NO_REPLY

Sentinel value returned in *result when no backward frame was received.

A genuine DALI backward frame carries an 8-bit value (0x00–0xFF). This negative sentinel is outside that range and is safe to use as a “no reply” indicator.

DALI_RESULT_IS_VALID(r)

Test whether a query result contains a valid backward-frame byte.

Usage:

int reply;
dali_master_do_transaction(handle, &config, &reply);
if (DALI_RESULT_IS_VALID(reply)) { ... }

Type Definitions

typedef struct dali_master_t *dali_master_handle_t

Opaque handle for a DALI driver instance.

Obtained from dali_new_master_rmt and passed to all subsequent API calls. Multiple independent instances can coexist on different GPIO pairs.

Enumerations

enum dali_addr_type_t

DALI address types.

Selects the addressing mode used when constructing the first byte of a forward frame.

Values:

enumerator DALI_ADDR_SHORT

Short address (0–63, format: 0AAAAAAS)

enumerator DALI_ADDR_GROUP

Group address (0–15, format: 100AAAAS)

enumerator DALI_ADDR_BROADCAST

Broadcast (format: 1111111S)

enumerator DALI_ADDR_SPECIAL

Special command byte passed through as-is

术语表

Te

DALI 半周期单位,标称值为 416.67 µs(IEC 62386 允许 ±10% 容差)。 所有 DALI 时序均以 Te 的整数倍表示。

前向帧(FF,Forward Frame)

由 DALI 主站发送给控制设备的 16 位帧,由 1 个起始位 + 16 个数据位 + 2 个停止位组成,共 38 Te。第一字节为地址字节,第二字节为命令或亮度值。

后向帧(BF,Backward Frame)

DALI 从设备响应查询命令时发送的 8 位回复帧,由 1 个起始位 + 8 个数据位 + 2 个停止位组成,共 22 Te。从设备须在前向帧结束后 7 Te~22 Te 内回复。

短地址(Short Address)

分配给单个 DALI 控制设备的唯一地址,范围 0–63。 在前向帧中编码为 ``0AAAAAAS``(A 为地址位,S 为选择位)。

组地址(Group Address)

最多 16 个控制设备共享的地址,范围 0–15。 编码为 100AAAAS,可同时控制多个灯具而无需逐一寻址。