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;
}
解码步骤:
- 检测起始位(下降沿)
- 每半个bit产生TE_CNT个定时器中断
- 根据电平持续时间判断bit值
- 每两个TE解码一个bit
- 检测停止位(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 运维。
我们的市场覆盖:
服务网点已覆盖全国核心城市(含长三角、珠三角、成渝等),并以高品质的方案深耕家居生活、酒店民宿、企业办公、疗愈康养、餐馆会所等多个细分领域。
(如果您在开发或落地中遇到技术问题,欢迎通过官网或后台私信与我交流探讨)

