24 极物科技 | DALI协议栈 - 定时器与中断系统


tags:

  • 极物
  • 极物智能
  • 极物科技
  • DALI协议栈
  • 定时器
  • 中断系统
  • 曼彻斯特编码
  • 嵌入式开发
  • IEC 62386
  • 智能照明
    description: 本文深入解析极物DALI协议栈定时器与中断系统实现,涵盖1ms系统时钟、DALI位定时器、PWM定时器、RX外部中断、曼彻斯特边沿解码等核心机制,展示从硬件中断到协议栈调度的完整实时处理流程。

极物科技 | DALI协议栈 - 定时器与中断系统

前言

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

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

一句话概述:本文深入解析极物 DALI 协议栈定时器与中断系统实现,展示从硬件中断到协议栈调度的完整实时处理机制。

本文聚焦于 DALI 协议栈的定时器与中断驱动架构,涵盖 1ms 系统时钟、DALI 位定时器(TE=13)、PWM 定时器(12位分辨率)、RX 外部中断、曼彻斯特边沿解码、发送时序控制等核心内容。通过源码级剖析,帮助读者理解 DALI 2400bps 半双工通信的底层实时处理机制。


1. 定时器与中断系统概述

DALI协议栈的实时性高度依赖定时器与中断系统。整个系统由三大核心定时器两类中断驱动:

定时器/中断 频率 用途
系统1ms定时器 1kHz 系统时钟、渐变控制、超时检测
DALI位定时器 31.2kHz DALI位编解码时序
PWM定时器 1.47kHz 亮度/色温PWM输出
RX外部中断 边沿触发 DALI总线边沿检测
定时器中断 周期性 位定时计数

二、定时器配置

2.1 定时器宏定义

c 复制代码
// dali_tc.h - 定时器配置宏
#define SYS_TICK_TC_TOP (CLK_CPU / DALI_PWM_CLOCK_HZ)
#define ONE_MS_TICK 1                    // 1ms tick
#define DALI_PWM_CLOCK_HZ 1470           // PWM时钟频率

#define DALI_BIT_TC_TOP (CLK_CPU / 2400 / TE_CNT)  // DALI位定时器
#define TE_CNT 13                        // 半个bit的定时器计数

#define DALI_PWM_TC_TOP 0x0FFF           // 12位PWM分辨率

2.2 TE时序参数

c 复制代码
// dali_bit.c - TE时序参数
#define MIN_TE_CNT (TE_CNT - 4)          // TE容差下限
#define MAX_TE_CNT (TE_CNT + 4)          // TE容差上限
#define MIN_2TE_CNT (2 * MIN_TE_CNT)     // 2TE下限
#define MAX_2TE_CNT (2 * MAX_TE_CNT)     // 2TE上限
#define STOP_CNT (4 * TE_CNT)            // 停止位时长

TE时序说明

  • DALI波特率:2400bps
  • 1bit时长:1/2400 ≈ 416μs
  • TE(半个bit):≈ 208μs
  • 定时器计数:TE_CNT = 13

三、定时器初始化

3.1 dali_tc_init

c 复制代码
// dali_tc.c - 定时器初始化
void dali_tc_init(void)
{
#if 0
    // 1ms系统时钟
    TIMER_Open(SYSTEM_TIMER, TIMER_PERIODIC_MODE, 1000);
    TIMER_EnableInt(SYSTEM_TIMER);
    NVIC_EnableIRQ(SYSTEM_TIMER_IRQ);
    TIMER_Start(SYSTEM_TIMER);

    // DALI解码发码时钟 每半个bit位时间产生13个中断
    TIMER_Open(DALI_TIMER, TIMER_PERIODIC_MODE, 31212);
    TIMER_EnableInt(DALI_TIMER);
    NVIC_EnableIRQ(DALI_TIMER_IRQ);
    TIMER_Start(DALI_TIMER);

    dali_pwm_channel_1_init();
    dali_pwm_channel_2_init();

    /* RTC from OSCULP32K is used for DALI random addressing seed */
    RTC.PER = 0xFFFF;
    /* Enable RTC */
    RTC.CTRLA = 0x01;
    DEBUG_PRINT("timer init sucess ! \n");
#endif
}

3.2 PWM通道初始化

c 复制代码
// dali_tc.c - PWM通道2初始化(色温)
void dali_pwm_channel_2_init()
{
    int channel_num = 2;

    /* PWM0 channel 2 frequency prescaler to 1 */
    PWM_SET_PRESCALER(PWM0, channel_num, 0);

    /* PWM0 channel 2 frequency period to 4095 (12bits) */
    PWM_SET_CNR(PWM0, channel_num, DALI_PWM_TC_TOP);

    /* PWM0 channel 2 frequency comparator to 0, default output 0 */
    PWM_SET_CMR(PWM0, channel_num, 0);

    /* PWM0 channel 2 is edge-aligned and down counter type */
    PWM_SET_ALIGNED_TYPE(PWM0, BIT2, PWM_EDGE_ALIGNED);

    /* ZeroLevel: Low, CmpDownLevel: High */
    PWM_SET_OUTPUT_LEVEL(PWM0, PWM_CH_2_MASK, 
                         PWM_OUTPUT_LOW, PWM_OUTPUT_NOTHING, 
                         PWM_OUTPUT_NOTHING, PWM_OUTPUT_HIGH);

    /* Enable output of PWM0 channel 2 */
    PWM_EnableOutput(PWM0, PWM_CH_2_MASK);

    /* Start PWM counter */
    PWM_Start(PWM0, PWM_CH_2_MASK);
}

// dali_tc.c - PWM通道1初始化(亮度)
void dali_pwm_channel_1_init()
{
    int channel_num = 1;

    PWM_SET_PRESCALER(PWM0, channel_num, 0);
    PWM_SET_CNR(PWM0, channel_num, DALI_PWM_TC_TOP);
    PWM_SET_CMR(PWM0, channel_num, 0);

    PWM0->CTL1 = (PWM0->CTL1 & (~PWM_CTL1_CNTTYPE0_Msk)) | PWM_DOWN_COUNTER;

    PWM_SET_OUTPUT_LEVEL(PWM0, PWM_CH_1_MASK, 
                         PWM_OUTPUT_LOW, PWM_OUTPUT_NOTHING, 
                         PWM_OUTPUT_NOTHING, PWM_OUTPUT_HIGH);

    PWM0->POEN |= PWM_POEN_POEN1_Msk;
    PWM0->CNTEN |= PWM_CNTEN_CNTEN0_Msk;
}

四、中断系统

4.1 中断类型

DALI协议栈涉及两类中断:

中断类型 触发条件 处理函数
RX外部中断 DALI总线边沿变化 dali_rx_IRQHandler()
定时器中断 定时器溢出 dali_decode_IRQHandler()

4.2 dali_rx_IRQHandler - 边沿检测中断

c 复制代码
// dali_bit.c - RX边沿检测中断处理
void dali_rx_IRQHandler(void)
{
    static uint8_t bit_index;
    uint8_t dali_bit_rx;

    pin_level = DALI_RX_PORT_PIN_VALUE;
    
    if (status_receive == 0) {
        if (pin_level == LOW) {
            /* DALI bus falling edge indicates start bit */
            bit_index = 0;
            status_receive = BIT_START;
            dali_rec_addr = 0;
            dali_rec_data = 0;
        }
    }
    else if (status_receive == BIT_START) {
        /* DALI pin must be high after the second INT0 edge */
        if ((level_time >= MIN_TE_CNT) && (level_time <= MAX_TE_CNT)) {
            /* Get the start bit and get ready for 16 bit data. */
            status_receive = BIT_0;
            bit_index += 1;
        }
        else {
            /* Start bit error */
            status_receive = 0;
        }
    }
    else if (status_receive < BIT_STOP1) {
        if (level_time >= MIN_2TE_CNT) {
            if (bit_index & 0x01) {
                /* Long level (2xTe) is detected */
                bit_index += 2;
            }
            else {
                // Error as it is front half of the bit
                status_receive = 0;
            }
        }
        else if ((level_time >= MIN_TE_CNT) && (level_time <= MAX_TE_CNT)) {
            /* Short level (1xTe) is detected */
            bit_index += 1;
        }
        else {
            status_receive = 0;
        }

        if (bit_index >= 34) {
            /* If the last Te is low (dali bit 0), a rising edge is
             * detected before stop bit */
            status_receive = BIT_STOP1;
        }

        /* Decode dali bit at every second Te bit */
        if (bit_index & 0x01) {
            /* Shift out the lowest bit to get dali bit */
            dali_bit_rx = bit_index >> 1;
            if (dali_bit_rx <= BIT_7) {
                /* Get the address byte */
                dali_rec_addr <<= 1;
                if (pin_level) {
                    dali_rec_addr |= 0x01;
                }
            }
            else {
                /* Get the data byte */
                dali_rec_data <<= 1;
                if (pin_level) {
                    dali_rec_data |= 0x01;
                    if (dali_bit_rx == BIT_15) {
                        /* If the last Te is high (dali bit 1), a Te
                         * high period is added before stop bit */
                        status_receive = BIT_STOP2;
                    }
                }
            }
        }
    }
    else {
        status_receive = 0;
    }
    level_time = 0;
}

4.3 dali_decode_IRQHandler - 定时器中断

c 复制代码
// dali_bit.c - 定时器中断处理(解码/编码)
void dali_decode_IRQHandler(void)
{
    static uint8_t dali_bit_index;
    static uint8_t bit_length;
    uint8_t dali_bit_tx;
    uint8_t bit_length_temp;

    send_status = dali_get_sent_status_from_stack();

    /* Dali decoding */
    if (status_receive) {
        level_time++;
        if (status_receive == BIT_STOP2) {
            if (pin_level == HIGH) {
                if (level_time > (STOP_CNT + TE_CNT)) {
                    /* Stop bit */
                    status_receive = 0;
                    /* Receive complete */
                    dali_set_addr_to_stack(dali_rec_addr);
                    dali_set_data_to_stack(dali_rec_data);
                    dali_set_received_flag_to_stack(true);
                }
            }
            else {
                /* Stop bit error if dali pin low */
                status_receive = 0;
            }
        }
        else if (status_receive == BIT_STOP1) {
            if (pin_level == HIGH) {
                if (level_time > STOP_CNT) {
                    /* Stop bit */
                    status_receive = 0;
                    /* Receive complete */
                    dali_set_addr_to_stack(dali_rec_addr);
                    dali_set_data_to_stack(dali_rec_data);
                    dali_set_received_flag_to_stack(true);
                }
            }
            else {
                /* Stop bit error if dali pin low */
                status_receive = 0;
            }
        }
        else {
            if (level_time >= MAX_2TE_CNT) {
                /* Error */
                status_receive = 0;
            }
        }
    }
    else if (send_status) {
        /* Dali encoding */
        dali_send_data = dali_get_sent_data_from_stack();

        /* Send dali backward frame */
        dali_bit_tx = dali_bit_index;
        bit_length_temp = bit_length;
        bit_length_temp++;
        if (send_status != BIT_STOP1) {
            /* Backward data byte */
            if (dali_bit_tx == 0) {
                /* Start bit */
                if (bit_length_temp == 1) {
                    DALI_TX_LOW;
                }
                else if (bit_length_temp == (TE_CNT + 1)) {
                    DALI_TX_HIGHT;
                }
            }
            else if (dali_send_data & (1 << (BIT_7 - dali_bit_tx))) {
                /* DALI bit value 1*/
                if (bit_length_temp == 1) {
                    DALI_TX_LOW;
                }
                else if (bit_length_temp == (TE_CNT + 1)) {
                    DALI_TX_HIGHT;
                }
            }
            else {
                /* DALI bit value 0 */
                if (bit_length_temp == 1) {
                    DALI_TX_HIGHT;
                }
                else if (bit_length_temp == (TE_CNT + 1)) {
                    DALI_TX_LOW;
                }
            }

            if (bit_length_temp == (2 * TE_CNT)) {
                dali_bit_tx++;
                if (dali_bit_tx > BIT_7) {
                    send_status = BIT_STOP1;
                    dali_bit_tx = 0;
                }
                bit_length_temp = 0;
            }
        }
        else {
            /* Stop bit */
            if (bit_length_temp < (4 * TE_CNT)) {
                DALI_TX_HIGHT;
            }
            else if (bit_length_temp >= (4.5 * TE_CNT)) {
                /* Send complete */
                send_status = SENDING_STOP;
                bit_length_temp = 0;
            }
        }
        dali_set_sent_status_to_stack(send_status);
        bit_length = bit_length_temp;
        dali_bit_index = dali_bit_tx;
    }
}

五、位状态机

5.1 接收状态枚举

c 复制代码
// dali_bit.c - 接收状态定义
#define BIT_START 0xFF    // 起始位
#define BIT_0 0x01        // 第0位
#define BIT_1 0x02        // 第1位
#define BIT_2 0x03        // 第2位
#define BIT_3 0x04        // 第3位
#define BIT_4 0x05        // 第4位
#define BIT_5 0x06        // 第5位
#define BIT_6 0x07        // 第6位
#define BIT_7 0x08        // 第7位
#define BIT_8 0x09        // 第8位
#define BIT_9 0x0A        // 第9位
#define BIT_10 0x0B       // 第10位
#define BIT_11 0x0C       // 第11位
#define BIT_12 0x0D       // 第12位
#define BIT_13 0x0E       // 第13位
#define BIT_14 0x0F       // 第14位
#define BIT_15 0x10       // 第15位
#define BIT_STOP1 0x11    // 停止位1
#define BIT_STOP2 0x12    // 停止位2

5.2 接收状态转换

复制代码
IDLE (0)
  ↓ 下降沿
BIT_START
  ↓ TE检测成功
BIT_0 ~ BIT_15 (逐位解码)
  ↓ 最后一位
BIT_STOP1 或 BIT_STOP2
  ↓ 停止位检测成功
IDLE (接收完成)

六、曼彻斯特解码原理

6.1 曼彻斯特编码规则

DALI采用曼彻斯特编码,每个bit由两个TE组成:

DALI bit 前半TE 后半TE 边沿
0 下降沿
1 上升沿

6.2 解码逻辑

c 复制代码
// 检测TE长度
if (level_time >= MIN_2TE_CNT) {
    if (bit_index & 0x01) {
        /* Long level (2xTe) is detected */
        bit_index += 2;
    }
    else {
        // Error as it is front half of the bit
        status_receive = 0;
    }
}
else if ((level_time >= MIN_TE_CNT) && (level_time <= MAX_TE_CNT)) {
    /* Short level (1xTe) is detected */
    bit_index += 1;
}

解码步骤

  1. 检测起始位(下降沿)
  2. 每半个bit产生TE_CNT个定时器中断
  3. 根据电平持续时间判断bit值
  4. 每两个TE解码一个bit
  5. 检测停止位(4TE时长)

七、发送时序控制

7.1 向后帧发送

c 复制代码
// 发送DALI bit 1
if (dali_send_data & (1 << (BIT_7 - dali_bit_tx))) {
    if (bit_length_temp == 1) {
        DALI_TX_LOW;      // 前半TE低
    }
    else if (bit_length_temp == (TE_CNT + 1)) {
        DALI_TX_HIGHT;    // 后半TE高
    }
}
// 发送DALI bit 0
else {
    if (bit_length_temp == 1) {
        DALI_TX_HIGHT;    // 前半TE高
    }
    else if (bit_length_temp == (TE_CNT + 1)) {
        DALI_TX_LOW;      // 后半TE低
    }
}

7.2 停止位发送

c 复制代码
/* Stop bit */
if (bit_length_temp < (4 * TE_CNT)) {
    DALI_TX_HIGHT;
}
else if (bit_length_temp >= (4.5 * TE_CNT)) {
    /* Send complete */
    send_status = SENDING_STOP;
    bit_length_temp = 0;
}

八、系统1ms定时器

8.1 system_timer_1ms

c 复制代码
// dali_tc.c - 1ms系统定时器
void system_timer_1ms(void)
{
    // 系统时钟tick处理
}

8.2 1ms定时器用途

功能 说明
帧超时检测 dali_frame_ms_tick()
亮度渐变 dali_command_fade_ms_tick()
色温渐变 dali_command_fade_ms_tick_tc()
上电处理 dali_process_power_on()
故障处理 dali_process_interface_failure()
识别处理 dali_process_identify_device()
EEPROM更新 dali_eeprom_update_tick()

九、关键变量

9.1 接收相关变量

c 复制代码
// dali_bit.c - 接收相关变量
volatile bool pin_level;              // DALI输入引脚电平
volatile uint8_t level_time = 0;      // 电平保持时间
volatile uint8_t status_receive = 0;  // DALI位接收状态
volatile uint8_t dali_rec_addr = 0;   // 接收地址字节
volatile uint8_t dali_rec_data = 0;   // 接收数据字节

9.2 发送相关变量

c 复制代码
// dali_bit.c - 发送相关变量
volatile uint8_t send_status = 0;     // DALI位发送状态
volatile uint8_t dali_send_data = 0;  // 发送数据字节

十、GPIO配置

10.1 DALI引脚初始化

c 复制代码
// dali_bit.c - DALI引脚初始化
void dali_bit_init(void)
{
    // TX P27 - 推挽输出
    P2M1 = 0x00 | P2M1;
    P2M0 = 0x80 | P2M0;  // P27 dali tx

    // RX P32 - 高阻输入
    P3M1 = 0x04;  // P32 高阻输入
    P3M0 = 0x00;  // 设置为准双向口

    DALI_TX_HIGHT;
}

10.2 HAL层引脚控制

c 复制代码
// dali_hal.c - HAL层引脚控制
void dali_hal_enable_forward_disable_backward(void)
{
    STC_GPIO_ENABLE;  // 使能接收,禁用发送
}

void dali_hal_disable_forward_enable_backward(void)
{
    STC_GPIO_DISABLE;  // 禁用接收,使能发送
}

bool dali_hal_get_dali_input_level(void)
{
    return DALI_RX_PORT_PIN_VALUE;
}

十一、中断处理流程

11.1 接收完整流程

复制代码
DALI总线下降沿
  ↓
dali_rx_IRQHandler() - 检测起始位
  ↓
status_receive = BIT_START
  ↓
定时器中断 - dali_decode_IRQHandler()
  ↓
level_time++ 计数
  ↓
检测TE长度 (MIN_TE_CNT ~ MAX_TE_CNT)
  ↓
bit_index递增
  ↓
每两个TE解码一个bit
  ↓
dali_rec_addr / dali_rec_data 移位存储
  ↓
检测停止位 (STOP_CNT)
  ↓
dali_set_addr_to_stack(dali_rec_addr)
dali_set_data_to_stack(dali_rec_data)
dali_set_received_flag_to_stack(true)
  ↓
帧处理状态机处理

11.2 发送完整流程

复制代码
帧处理状态机决定发送向后帧
  ↓
dali_hal_disable_forward_enable_backward()
  ↓
service_info.send_status = SENDING_START
  ↓
dali_decode_IRQHandler() - 定时器中断编码
  ↓
逐位发送 (DALI bit 0/1)
  ↓
发送停止位 (4TE)
  ↓
send_status = SENDING_STOP
  ↓
dali_hal_enable_forward_disable_backward()
  ↓
恢复接收状态

十二、关键设计亮点

12.1 中断驱动架构

复制代码
外部中断 (边沿检测)
  ↓
启动接收状态机
  ↓
定时器中断 (TE计数)
  ↓
逐位解码/编码
  ↓
帧处理状态机
  ↓
命令处理

12.2 TE容差机制

c 复制代码
#define MIN_TE_CNT (TE_CNT - 4)  // 9
#define MAX_TE_CNT (TE_CNT + 4)  // 17

允许±4个计数器的误差,提高抗干扰能力。

12.3 双通道PWM

c 复制代码
#define DALI_BRIGHTNESS_PWM_CHANNEL 1  // 亮度PWM
#define DALI_CCT_PWM_CHANNEL 2         // 色温PWM

独立控制亮度和色温,实现双色温调光。

12.4 12位PWM分辨率

c 复制代码
#define DALI_PWM_TC_TOP 0x0FFF  // 4095

提供4096级精细控制,确保平滑调光。


13. 总结

本文详细解析了DALI协议栈中定时器与中断系统的实现,涵盖:

  • 三大定时器:1ms系统时钟、DALI位定时器、PWM定时器
  • 两类中断:RX外部中断、定时器中断
  • 曼彻斯特解码:TE时序检测、边沿解码
  • 位状态机:18个接收状态、完整的状态转换
  • 发送时序控制:向后帧编码、停止位生成
  • GPIO配置:推挽输出、高阻输入
  • 中断驱动架构:高效的实时处理机制

通过本文的解析,读者可以深入理解DALI协议栈底层时序控制的完整实现,为智能照明系统的开发提供技术参考。


关于极物科技(ZEEWO)

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

我们的核心能力:

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

我们的市场覆盖:

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

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

相关推荐
阿钱真强道6 小时前
23 极物科技 | DALI协议栈 - EEPROM持久化机制
eeprom·极物·极物智能·极物科技·iec 62386·dali协议栈·智慧照明
阿钱真强道1 天前
20 极物科技 | DALI协议栈 - 配置命令详解
智能照明·极物·极物智能·极物科技·iec 62386·dali协议栈·场景配置
jufeng13071 天前
STM32F103 定时器输入捕获实战:测频测周、编码器测速、红外解码全流程拆解
stm32·输入捕获·定时器·编码器
阿钱真强道1 天前
22 极物科技 | DALI协议栈 - 特殊命令与设备发现机制
极物·极物智能·极物科技·iec 62386·设备发现·dali协议栈·智慧照明
阿钱真强道2 天前
19 极物科技 | DALI协议栈 - 标准命令集解析
极物·极物智能·极物科技·iec 62386·调光控制·dali协议栈·标准命令
阿钱真强道2 天前
17 极物科技 | DALI协议栈 - 物理层位编解码详解
曼彻斯特编码·极物·极物智能·极物科技·iec 62386·dali协议栈
阿钱真强道4 天前
16 极物科技 | DALI协议栈 - 架构总览与模块设计
智能家居·照明·极物·极物智能·极物科技·iec 62386·dali协议栈
阿钱真强道5 天前
11 极物科技 | DALI调光灯 - 功能接口与调试指南
智能照明·极物·极物智能·极物科技·dali调光灯·调光控制·调试指南
阿钱真强道5 天前
12 极物科技 | DALI调光灯 - 设备发现与地址分配机制
智能照明·极物·极物智能·极物科技·dali调光灯·设备发现·地址分配