STM32 DMX512 标准协议实现方案

一、DMX512 协议详解

1.1 协议规范

参数 规格
物理层 RS485 差分信号
波特率 250 kbps
数据位 8 bit
停止位 2 bit
校验位
帧结构 Break + MAB + Start Code + Data Slots
通道数 最大 512 通道
刷新率 典型 44 Hz(23ms/帧)

1.2 时序要求

Break: ≥ 88 μs (低电平)
Mark After Break (MAB): ≥ 8 μs (高电平)
Start Code: 1字节 (通常为0x00)
Data Slot: 1字节/通道 (0-255)
Inter-slot: ≥ 0 μs

二、硬件设计

2.1 硬件连接

STM32F103C8T6 + MAX485/SP3485
┌─────────────┐     ┌─────────────┐
│    STM32    │     │   RS485     │
│             │     │ Transceiver │
│  PA9  TX ───┼────►│ DI          │
│  PA10 RX ◄──┼─────│ RO          │
│  PA8  DE ───┼────►│ DE          │
│  PA7  RE ───┼────►│ RE          │
│             │     │             │
│  GND ───────┼─────│ GND         │
└─────────────┘     └─────────────┘
        │                   │
        └────── 120Ω ───────┘
             终端电阻

2.2 推荐芯片

  • 收发器: MAX485、SP3485、SN75176
  • 隔离: ADUM1201(可选,用于电气隔离)
  • 保护: TVS二极管(防浪涌)

三、完整源码实现

3.1 DMX512 头文件 (dmx512.h)

/**
  * @file dmx512.h
  * @brief DMX512 标准协议实现头文件
  */
#ifndef __DMX512_H
#define __DMX512_H

#include "stm32f10x.h"
#include <string.h>
#include <stdlib.h>

/* DMX512 配置参数 */
#define DMX512_CHANNELS_MAX    512     // 最大通道数
#define DMX512_BREAK_US        176     // Break时间(μs) - 标准要求≥88μs
#define DMX512_MAB_US          12      // Mark After Break时间(μs)
#define DMX512_SLOT_TIMEOUT_MS 100     // 槽超时时间(ms)
#define DMX512_FRAME_TIMEOUT_MS 200    // 帧超时时间(ms)

/* DMX512 状态定义 */
typedef enum {
    DMX512_STATE_IDLE = 0,       // 空闲状态
    DMX512_STATE_BREAK,          // 检测Break
    DMX512_STATE_MAB,            // Mark After Break
    DMX512_STATE_START_CODE,     // 起始码
    DMX512_STATE_DATA,           // 数据接收
    DMX512_STATE_COMPLETE,       // 接收完成
    DMX512_STATE_ERROR           // 错误状态
} DMX512_State;

/* DMX512 错误类型 */
typedef enum {
    DMX512_ERR_NONE = 0,         // 无错误
    DMX512_ERR_BREAK_TIMEOUT,    // Break超时
    DMX512_ERR_MAB_TIMEOUT,      // MAB超时
    DMX512_ERR_FRAME_TIMEOUT,    // 帧超时
    DMX512_ERR_BUFFER_OVERFLOW,  // 缓冲区溢出
    DMX512_ERR_PARITY,           // 奇偶校验错误
    DMX512_ERR_FRAMING           // 帧错误
} DMX512_Error;

/* DMX512 数据结构 */
typedef struct {
    uint8_t channels[DMX512_CHANNELS_MAX];  // 通道数据
    uint16_t channel_count;                  // 实际通道数
    uint8_t start_code;                      // 起始码
    uint32_t frame_counter;                  // 帧计数器
    uint32_t error_counter;                  // 错误计数器
    DMX512_State state;                      // 当前状态
    DMX512_Error error;                      // 错误类型
    uint8_t is_valid;                        // 数据有效性
    uint32_t last_frame_time;                // 最后一帧时间
} DMX512_Data;

/* DMX512 句柄 */
typedef struct {
    USART_TypeDef* usart;                    // USART外设
    GPIO_TypeDef* de_port;                   // DE控制端口
    uint16_t de_pin;                         // DE引脚
    GPIO_TypeDef* re_port;                   // RE控制端口
    uint16_t re_pin;                         // RE引脚
    DMA_Channel_TypeDef* dma_channel;        // DMA通道
    uint8_t* rx_buffer;                      // 接收缓冲区
    uint16_t buffer_size;                    // 缓冲区大小
    DMX512_Data data;                        // DMX数据
    void (*frame_callback)(DMX512_Data*);    // 帧接收完成回调
    void (*error_callback)(DMX512_Error);    // 错误回调
} DMX512_Handle;

/* 函数声明 */
void DMX512_Init(DMX512_Handle* handle);
void DMX512_Start(DMX512_Handle* handle);
void DMX512_Stop(DMX512_Handle* handle);
void DMX512_SetChannel(DMX512_Handle* handle, uint16_t channel, uint8_t value);
uint8_t DMX512_GetChannel(DMX512_Handle* handle, uint16_t channel);
void DMX512_SetFrameCallback(DMX512_Handle* handle, void (*callback)(DMX512_Data*));
void DMX512_SetErrorCallback(DMX512_Handle* handle, void (*callback)(DMX512_Error));
void DMX512_Process(DMX512_Handle* handle);
void DMX512_IRQHandler(DMX512_Handle* handle);

#endif /* __DMX512_H */

3.2 DMX512 源文件 (dmx512.c)

/**
  * @file dmx512.c
  * @brief DMX512 标准协议实现
  */
#include "dmx512.h"
#include "timer.h"

/* 静态变量 */
static uint8_t rx_buffer[DMX512_CHANNELS_MAX + 1];  // 接收缓冲区(+1为起始码)
static uint16_t rx_index = 0;
static uint32_t break_start_time = 0;
static uint32_t frame_start_time = 0;

/* 初始化DMX512 */
void DMX512_Init(DMX512_Handle* handle)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    /* 1. 使能时钟 */
    if (handle->usart == USART1) {
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    } else if (handle->usart == USART2) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    } else if (handle->usart == USART3) {
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    }
    
    /* 2. 配置GPIO */
    // DE/RE控制引脚
    GPIO_InitStructure.GPIO_Pin = handle->de_pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(handle->de_port, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = handle->re_pin;
    GPIO_Init(handle->re_port, &GPIO_InitStructure);
    
    // 默认设置为接收模式
    GPIO_ResetBits(handle->de_port, handle->de_pin);  // DE=0
    GPIO_ResetBits(handle->re_port, handle->re_pin);  // RE=0
    
    // USART引脚
    if (handle->usart == USART1) {
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;  // TX
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
        
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    
    /* 3. 配置USART */
    USART_InitStructure.USART_BaudRate = 250000;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_2;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx;
    USART_Init(handle->usart, &USART_InitStructure);
    
    /* 4. 配置DMA */
    if (handle->usart == USART1) {
        handle->dma_channel = DMA1_Channel5;
    } else if (handle->usart == USART2) {
        handle->dma_channel = DMA1_Channel6;
    } else if (handle->usart == USART3) {
        handle->dma_channel = DMA1_Channel3;
    }
    
    DMA_DeInit(handle->dma_channel);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&handle->usart->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = sizeof(rx_buffer);
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(handle->dma_channel, &DMA_InitStructure);
    
    /* 5. 配置中断 */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    /* 6. 使能USART接收中断和空闲中断 */
    USART_ITConfig(handle->usart, USART_IT_RXNE, ENABLE);
    USART_ITConfig(handle->usart, USART_IT_IDLE, ENABLE);
    USART_ITConfig(handle->usart, USART_IT_LBD, ENABLE);  // Line Break Detection
    
    /* 7. 初始化数据结构 */
    memset(&handle->data, 0, sizeof(DMX512_Data));
    handle->data.channel_count = DMX512_CHANNELS_MAX;
    handle->data.is_valid = 0;
    handle->rx_buffer = rx_buffer;
    handle->buffer_size = sizeof(rx_buffer);
    
    /* 8. 使能DMA和USART */
    DMA_Cmd(handle->dma_channel, ENABLE);
    USART_DMACmd(handle->usart, USART_DMAReq_Rx, ENABLE);
    USART_Cmd(handle->usart, ENABLE);
}

/* 启动DMX512接收 */
void DMX512_Start(DMX512_Handle* handle)
{
    handle->data.state = DMX512_STATE_IDLE;
    handle->data.is_valid = 0;
    rx_index = 0;
    
    // 设置为接收模式
    GPIO_ResetBits(handle->de_port, handle->de_pin);  // DE=0
    GPIO_ResetBits(handle->re_port, handle->re_pin);  // RE=0
}

/* 停止DMX512接收 */
void DMX512_Stop(DMX512_Handle* handle)
{
    USART_Cmd(handle->usart, DISABLE);
    DMA_Cmd(handle->dma_channel, DISABLE);
    handle->data.state = DMX512_STATE_IDLE;
}

/* 设置通道值(发送模式) */
void DMX512_SetChannel(DMX512_Handle* handle, uint16_t channel, uint8_t value)
{
    if (channel < DMX512_CHANNELS_MAX) {
        handle->data.channels[channel] = value;
    }
}

/* 获取通道值 */
uint8_t DMX512_GetChannel(DMX512_Handle* handle, uint16_t channel)
{
    if (channel < DMX512_CHANNELS_MAX && handle->data.is_valid) {
        return handle->data.channels[channel];
    }
    return 0;
}

/* 设置帧回调函数 */
void DMX512_SetFrameCallback(DMX512_Handle* handle, void (*callback)(DMX512_Data*))
{
    handle->frame_callback = callback;
}

/* 设置错误回调函数 */
void DMX512_SetErrorCallback(DMX512_Handle* handle, void (*callback)(DMX512_Error))
{
    handle->error_callback = callback;
}

/* 处理DMX512数据 */
void DMX512_Process(DMX512_Handle* handle)
{
    uint32_t current_time = Timer_GetMs();
    
    /* 检查帧超时 */
    if (handle->data.state != DMX512_STATE_IDLE && 
        (current_time - handle->data.last_frame_time) > DMX512_FRAME_TIMEOUT_MS) {
        handle->data.error = DMX512_ERR_FRAME_TIMEOUT;
        handle->data.state = DMX512_STATE_ERROR;
        handle->data.is_valid = 0;
        
        if (handle->error_callback) {
            handle->error_callback(DMX512_ERR_FRAME_TIMEOUT);
        }
    }
    
    /* 处理接收到的数据 */
    if (handle->data.state == DMX512_STATE_COMPLETE && handle->data.is_valid) {
        // 复制数据到通道缓冲区
        if (handle->data.start_code == 0x00) {  // 标准起始码
            memcpy(handle->data.channels, &rx_buffer[1], 
                   MIN(handle->data.channel_count, DMX512_CHANNELS_MAX));
            handle->data.frame_counter++;
            
            if (handle->frame_callback) {
                handle->frame_callback(&handle->data);
            }
        }
        
        handle->data.state = DMX512_STATE_IDLE;
        handle->data.is_valid = 0;
    }
}

/* USART中断处理函数 */
void DMX512_IRQHandler(DMX512_Handle* handle)
{
    uint32_t current_time = Timer_GetUs();
    
    if (USART_GetITStatus(handle->usart, USART_IT_LBD) != RESET) {
        /* 检测到Break */
        USART_ClearITPendingBit(handle->usart, USART_IT_LBD);
        handle->data.state = DMX512_STATE_BREAK;
        break_start_time = current_time;
        rx_index = 0;
    }
    
    if (USART_GetITStatus(handle->usart, USART_IT_IDLE) != RESET) {
        /* 检测到空闲帧 */
        USART_ReceiveData(handle->usart);  // 清除空闲中断标志
        
        if (handle->data.state == DMX512_STATE_DATA) {
            handle->data.state = DMX512_STATE_COMPLETE;
            handle->data.is_valid = 1;
            handle->data.last_frame_time = Timer_GetMs();
        }
    }
    
    if (USART_GetITStatus(handle->usart, USART_IT_RXNE) != RESET) {
        uint8_t data = USART_ReceiveData(handle->usart);
        
        switch (handle->data.state) {
            case DMX512_STATE_BREAK:
                /* 检查MAB时间 */
                if ((current_time - break_start_time) >= DMX512_MAB_US) {
                    handle->data.state = DMX512_STATE_START_CODE;
                    handle->data.start_code = data;
                    rx_index = 0;
                }
                break;
                
            case DMX512_STATE_START_CODE:
                if (rx_index < DMX512_CHANNELS_MAX) {
                    rx_buffer[rx_index++] = data;
                    handle->data.state = DMX512_STATE_DATA;
                }
                break;
                
            case DMX512_STATE_DATA:
                if (rx_index < DMX512_CHANNELS_MAX) {
                    rx_buffer[rx_index++] = data;
                } else {
                    handle->data.error = DMX512_ERR_BUFFER_OVERFLOW;
                    handle->data.state = DMX512_STATE_ERROR;
                    if (handle->error_callback) {
                        handle->error_callback(DMX512_ERR_BUFFER_OVERFLOW);
                    }
                }
                break;
                
            default:
                break;
        }
    }
    
    if (USART_GetITStatus(handle->usart, USART_IT_PE) != RESET) {
        USART_ClearITPendingBit(handle->usart, USART_IT_PE);
        handle->data.error = DMX512_ERR_PARITY;
        if (handle->error_callback) {
            handle->error_callback(DMX512_ERR_PARITY);
        }
    }
    
    if (USART_GetITStatus(handle->usart, USART_IT_FE) != RESET) {
        USART_ClearITPendingBit(handle->usart, USART_IT_FE);
        handle->data.error = DMX512_ERR_FRAMING;
        if (handle->error_callback) {
            handle->error_callback(DMX512_ERR_FRAMING);
        }
    }
}

3.3 DMX512 发送器实现 (dmx512_tx.c)

/**
  * @file dmx512_tx.c
  * @brief DMX512 发送器实现
  */
#include "dmx512.h"

/* 发送器状态 */
typedef enum {
    TX_STATE_IDLE = 0,
    TX_STATE_BREAK,
    TX_STATE_MAB,
    TX_STATE_START_CODE,
    TX_STATE_DATA,
    TX_STATE_COMPLETE
} TX_State;

/* 发送器句柄 */
typedef struct {
    DMX512_Handle* dmx_handle;
    TX_State state;
    uint16_t current_channel;
    uint32_t last_transmit_time;
    uint8_t is_transmitting;
} DMX512_TxHandle;

/* 初始化发送器 */
void DMX512_Tx_Init(DMX512_TxHandle* tx_handle, DMX512_Handle* dmx_handle)
{
    tx_handle->dmx_handle = dmx_handle;
    tx_handle->state = TX_STATE_IDLE;
    tx_handle->current_channel = 0;
    tx_handle->last_transmit_time = 0;
    tx_handle->is_transmitting = 0;
    
    // 配置为发送模式
    GPIO_SetBits(dmx_handle->de_port, dmx_handle->de_pin);  // DE=1
    GPIO_SetBits(dmx_handle->re_port, dmx_handle->re_pin);  // RE=1
}

/* 发送DMX512帧 */
void DMX512_Tx_SendFrame(DMX512_TxHandle* tx_handle)
{
    uint32_t current_time = Timer_GetMs();
    
    // 限制发送频率(最大44Hz)
    if ((current_time - tx_handle->last_transmit_time) < 23) {
        return;
    }
    
    tx_handle->is_transmitting = 1;
    tx_handle->state = TX_STATE_BREAK;
    tx_handle->current_channel = 0;
    
    // 发送Break(低电平)
    USART_SendBreak(tx_handle->dmx_handle->usart);
    
    // 等待Break时间
    Timer_DelayUs(DMX512_BREAK_US);
    
    // 发送MAB(高电平)
    tx_handle->state = TX_STATE_MAB;
    USART_SendData(tx_handle->dmx_handle->usart, 0x00);  // 发送任意数据以产生高电平
    Timer_DelayUs(DMX512_MAB_US);
    
    // 发送起始码
    tx_handle->state = TX_STATE_START_CODE;
    USART_SendData(tx_handle->dmx_handle->usart, 0x00);  // 标准起始码
    while (USART_GetFlagStatus(tx_handle->dmx_handle->usart, USART_FLAG_TXE) == RESET);
    
    // 发送数据通道
    tx_handle->state = TX_STATE_DATA;
    for (tx_handle->current_channel = 0; 
         tx_handle->current_channel < tx_handle->dmx_handle->data.channel_count; 
         tx_handle->current_channel++) {
        
        uint8_t data = tx_handle->dmx_handle->data.channels[tx_handle->current_channel];
        USART_SendData(tx_handle->dmx_handle->usart, data);
        while (USART_GetFlagStatus(tx_handle->dmx_handle->usart, USART_FLAG_TXE) == RESET);
    }
    
    tx_handle->state = TX_STATE_COMPLETE;
    tx_handle->last_transmit_time = current_time;
    tx_handle->is_transmitting = 0;
}

/* 定时发送任务 */
void DMX512_Tx_Task(DMX512_TxHandle* tx_handle)
{
    if (!tx_handle->is_transmitting) {
        DMX512_Tx_SendFrame(tx_handle);
    }
}

3.4 定时器辅助模块 (timer.c)

/**
  * @file timer.c
  * @brief 定时器辅助函数
  */
#include "timer.h"

static volatile uint32_t sys_tick = 0;

/* 初始化系统滴答定时器 */
void Timer_Init(void)
{
    if (SysTick_Config(SystemCoreClock / 1000)) {
        while (1);  // 初始化失败
    }
}

/* 获取毫秒计数 */
uint32_t Timer_GetMs(void)
{
    return sys_tick;
}

/* 获取微秒计数 */
uint32_t Timer_GetUs(void)
{
    return sys_tick * 1000 + (SysTick->LOAD - SysTick->VAL);
}

/* 延时毫秒 */
void Timer_DelayMs(uint32_t ms)
{
    uint32_t start = sys_tick;
    while ((sys_tick - start) < ms);
}

/* 延时微秒 */
void Timer_DelayUs(uint32_t us)
{
    uint32_t start = Timer_GetUs();
    while ((Timer_GetUs() - start) < us);
}

/* 系统滴答中断处理函数 */
void SysTick_Handler(void)
{
    sys_tick++;
}

3.5 主程序示例 (main.c)

/**
  * @file main.c
  * @brief DMX512 接收器主程序
  */
#include "stm32f10x.h"
#include "dmx512.h"
#include "timer.h"
#include "led.h"

/* 全局变量 */
DMX512_Handle dmx_handle;
DMX512_TxHandle dmx_tx_handle;

/* DMX512帧回调函数 */
void DMX512_FrameCallback(DMX512_Data* data)
{
    // 处理接收到的DMX数据
    uint8_t dimmer = DMX512_GetChannel(&dmx_handle, 0);  // 通道1:调光
    uint8_t red = DMX512_GetChannel(&dmx_handle, 1);     // 通道2:红色
    uint8_t green = DMX512_GetChannel(&dmx_handle, 2);   // 通道3:绿色
    uint8_t blue = DMX512_GetChannel(&dmx_handle, 3);    // 通道4:蓝色
    
    // 更新LED输出
    LED_SetColor(red, green, blue);
    LED_SetBrightness(dimmer);
    
    // 打印调试信息
    printf("DMX Frame #%lu: Dim=%d, R=%d, G=%d, B=%d\r\n", 
           data->frame_counter, dimmer, red, green, blue);
}

/* DMX512错误回调函数 */
void DMX512_ErrorCallback(DMX512_Error error)
{
    printf("DMX512 Error: %d\r\n", error);
    
    switch (error) {
        case DMX512_ERR_BREAK_TIMEOUT:
            LED_Blink(100, 100);  // 快速闪烁
            break;
        case DMX512_ERR_FRAME_TIMEOUT:
            LED_Blink(500, 500);  // 慢速闪烁
            break;
        default:
            LED_On();  // 常亮
            break;
    }
}

int main(void)
{
    /* 系统初始化 */
    SystemInit();
    Timer_Init();
    LED_Init();
    
    /* 初始化DMX512接收器 */
    dmx_handle.usart = USART1;
    dmx_handle.de_port = GPIOA;
    dmx_handle.de_pin = GPIO_Pin_8;
    dmx_handle.re_port = GPIOA;
    dmx_handle.re_pin = GPIO_Pin_7;
    dmx_handle.frame_callback = DMX512_FrameCallback;
    dmx_handle.error_callback = DMX512_ErrorCallback;
    
    DMX512_Init(&dmx_handle);
    DMX512_Start(&dmx_handle);
    
    /* 初始化DMX512发送器(可选) */
    DMX512_Tx_Init(&dmx_tx_handle, &dmx_handle);
    
    /* 设置默认通道值 */
    DMX512_SetChannel(&dmx_handle, 0, 255);  // 亮度100%
    DMX512_SetChannel(&dmx_handle, 1, 255);  // 红色
    DMX512_SetChannel(&dmx_handle, 2, 0);    // 绿色
    DMX512_SetChannel(&dmx_handle, 3, 0);     // 蓝色
    
    printf("DMX512 Receiver Started...\r\n");
    
    /* 主循环 */
    while (1) {
        /* 处理DMX512数据 */
        DMX512_Process(&dmx_handle);
        
        /* 定时发送DMX数据(发送器模式) */
        // DMX512_Tx_Task(&dmx_tx_handle);
        
        /* 其他任务 */
        LED_Task();
        
        /* 延时 */
        Timer_DelayMs(1);
    }
}

/* USART1中断处理函数 */
void USART1_IRQHandler(void)
{
    DMX512_IRQHandler(&dmx_handle);
}

四、Keil工程配置

4.1 工程设置

Target:
  - Device: STM32F103C8T6
  - Clock: 8MHz HSE → 72MHz PLL
  
C/C++:
  - Define: USE_STDPERIPH_DRIVER
  - Include Paths: .\;..\Libraries\CMSIS\Include;..\Libraries\STM32F10x_StdPeriph_Driver\inc
  
Debug:
  - Use Simulator: No
  - Use J-LINK/J-TRACE: Yes

4.2 链接脚本

/* STM32F103C8T6 Flash: 64KB, RAM: 20KB */
MEMORY
{
  RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 20K
  FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 64K
}

参考代码 SMT32 DMX512标准协议程序 www.youwenfan.com/contentcnu/56364.html

五、测试与调试

5.1 测试方法

  1. 硬件连接

    DMX控制器 → XLR-5接口 → MAX485 → STM32
    
  2. 测试工具

    • DMX512控制器(如Enttec Open DMX USB)
    • 示波器(观察Break/MAB时序)
    • USB转RS485适配器
  3. 测试步骤

    # 1. 发送标准DMX信号
    # 2. 观察LED指示灯
    # 3. 使用串口打印接收数据
    # 4. 验证通道值是否正确
    

5.2 常见问题排查

问题 可能原因 解决方案
无法接收数据 波特率不匹配 确认250kbps,8N2
数据错误 终端电阻缺失 在末端加120Ω电阻
Break检测失败 中断配置错误 检查USART_IT_LBD使能
通道值跳变 电磁干扰 增加TVS保护,使用屏蔽线
帧丢失 缓冲区溢出 优化中断处理,减少延迟

六、性能优化建议

6.1 软件优化

  1. 使用DMA接收:减少CPU中断负载
  2. 双缓冲机制:避免数据覆盖
  3. 环形缓冲区:平滑数据处理
  4. 优先级调整:确保DMX中断优先级最高

6.2 硬件优化

  1. 信号隔离:使用ADUM1201隔离RS485
  2. 浪涌保护:添加TVS二极管(SMBJ6.5CA)
  3. 滤波电路:在A/B线间添加100pF电容
  4. 阻抗匹配:确保120Ω终端电阻

七、扩展功能

7.1 RDM(远程设备管理)支持

/* RDM协议扩展 */
typedef struct {
    uint8_t rdm_start_code;  // RDM起始码 0xCC
    uint8_t sub_start_code;  // 子起始码 0x01
    uint8_t message_length;  // 消息长度
    uint8_t destination_uid[6]; // 目标UID
    uint8_t source_uid[6];    // 源UID
    uint8_t transaction_num;   // 事务号
    uint8_t message_count;     // 消息计数
    uint16_t sub_device;       // 子设备
    uint16_t command_class;    // 命令类
    uint16_t parameter_id;     // 参数ID
    uint16_t parameter_data_length; // 参数数据长度
    uint8_t parameter_data[231]; // 参数数据
} RDM_Message;

7.2 Art-Net 转 DMX

/* Art-Net协议支持 */
typedef struct {
    uint8_t id[8];           // "Art-Net\0"
    uint16_t opcode;         // Opcode
    uint16_t protocol_version; // 协议版本
    uint8_t sequence;       // 序列号
    uint8_t physical;        // 物理端口
    uint16_t universe;      // Universe编号
    uint16_t length;         // 数据长度
    uint8_t data[512];      // DMX数据
} ArtNet_DMX;

八、安全注意事项

重要提示

  1. 电气隔离:舞台设备必须电气隔离,防止触电
  2. 接地保护:确保所有设备共地
  3. 防雷保护:室外安装需加装防雷器
  4. 定期维护:每季度检查连接器是否松动
  5. 备用方案:重要场合准备备用控制器
posted @ 2026-05-06 09:44  yes_go  阅读(151)  评论(0)    收藏  举报