stm32实现DMX512协议发送与接收(非标)

 最近把玩了一下485,期间也接触了dmx512通信协议,该协议主要用于各种舞台灯光的控制当中,进而实现各种光效以及色彩变化。根据标准的512协议,其物理连接与传统上的RS485是完全一致的,并没有什么差别,差别只是在协议上的不同,工业上应用的主要是modbus协议,而这里是用512通信协议。
       
      DMX512数据协议是美国舞台灯光协会(USITT)于1990年发布的一种灯光控制器与灯具设备进行数据传输的标准。它包括电气特性,数据协议,数据格式等方面的内容。
512协议规定使用的波特率是250Kbps,但是stm32可以支持shangMbps的波特率,所以说这不是什么大问题。
      
     该协议发送的数据帧一共11位,1位开始位,8位数据,2个停止位,无校验位。

 

根据波特率可以知道,位时间是4us,11位数据供需要44us的时间。当然对于标准的512协议是需要break和mark after break 帧的,break是一个92us的低电平,而mark after break是一个12us的高电平,如下图所示

 

 

 


    根据上面的图片(缺失了起始码,下图补上),512协议必须有break和mark,但是在我们通常的非标准收发中,检测break和mark相对比较困难,如果非要做,耗费的资源也比较多,比如定时器计时,中断等等。如果不是做标准控制器的,完全可以另辟蹊径。

      

根据512 协议,每一串数据的开始都要有一个起始码,也称复位码,其数据为0,但是从开始位数至第十位是0,用来声明数据传输开始,随后包含1-512个数据,也称调光数据,其是标准的数据帧,所以第十位是1,所以我们可以根据这个第十位来进行做文章。大家都知道,一般的单片机,像51,avr等都是支持8-9位数据发送的,所以我们就是用9位数据,1位停止位,无校验位,通过检测检测第十位,也就是所谓的RB8来进行数据的接收与传输,不需要发送break和mark。


1、发送端
     
      串口设为 9位数据,1停止位,无校验位,波特率250000

void USART1_Configuration(void)
{      
  USART_InitTypeDef USART_InitStructure;
 
  USART_InitStructure.USART_BaudRate = 250000;
  USART_InitStructure.USART_WordLength = USART_WordLength_9b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  /* Configure USART1 */
  USART_Init(USART1, &USART_InitStructure);  
  /* Enable USART1 Receive and Transmit interrupts */
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  //USART_ITConfig(USART1, USART_IT_TC, ENABLE);
  /* Enable the USART1 */
  USART_Cmd(USART1, ENABLE);
}


注意在初始化串口的时候别忘了485芯片设为发送状态

接下来主要就是数据包的发送,发送的时候注意起始码的数据第九位设为0,调光数据第九位设为1.

void DMX_SendPacket(void)
{
  pDMX_buf = 0;
  
  while (pDMX_buf <= 512) //1-512
  {
    /* send data packet to slaves*/
    if(USART1->SR & (1<<6))
    {
       /*发送起始码 00*/
       if (0 == pDMX_buf)   
       {
          USART1->DR = ((USART1->DR) & 0xfe00);   //第九位置0
       }
       else
       {
          USART1->DR = 0x0100 | DMX_buf[pDMX_buf];   //第九位置1
       }
       pDMX_buf++;
    }
  }
}

以上函数相比大家都可以看懂,接下来就是在main函数中进行循环数据的发送了,比如每200ms发送一次,由于发送快,偶尔的错误也不是很明显。


2,、接收端

      接收端得工作就是接收的信息进行解码(废话),关键是对RB8的处理,接收用到了中断接收,所以需要使能接收中断。

void USART1_Configuration(void)
{      
  USART_InitTypeDef USART_InitStructure;
 
  USART_InitStructure.USART_BaudRate = 250000;
  USART_InitStructure.USART_WordLength = USART_WordLength_9b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  /* Configure USART1 */
  USART_Init(USART1, &USART_InitStructure);  
  /* Enable USART1 Receive and Transmit interrupts */
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能接收中断
  //USART_ITConfig(USART1, USART_IT_TC, ENABLE);
  /* Enable the USART1 */
  USART_Cmd(USART1, ENABLE);
}


void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
#ifdef  VECT_TAB_RAM
  /* Set the Vector Table base location at 0x20000000 */
  NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else  /* VECT_TAB_FLASH  */
  /* Set the Vector Table base location at 0x08000000 */
  NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
    //设置优先级分组:先占优先级和从优先级 ,先占优先级0位,从优先级4位
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    
  /* Enable the USART1 Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void USART1_IRQHandler(void)
{
  uint16_t UDR; 
  static uint16_t RXB8; 
  static uint16_t pDMX_buf = 0; //数据指针
  static uint8_t fDMX_buf_right = 0;
        
  //接收数据
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//USART_FLAG_RXNE
  {
    //USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);
    UDR = USART_ReceiveData(USART1); 
    RXB8 = (UDR & 0x0100); 
    
    if (RXB8 == 0) //复位信号
    {
     
      if (!UDR)
      {
         fDMX_buf_right = 1;//接收数据正确
         pDMX_buf = 1; //直接接收第一个数据  不保存第0个数据。
      }
    }
    else //rb8 =1  pDMX_buf=1 调光数据
    {
      if (1 == fDMX_buf_right)
      {
        DMX_buf[pDMX_buf++] = (u8)UDR;
        //接收到512个数据
        if (pDMX_buf > 512)
        {
          fDMX_buf_right = 0;
          tim_update = SET; //更新调光数据
        }
      }
    }
    
  }

}
————————————————
版权声明:本文为CSDN博主「爱飞的小兵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cunlingwang/java/article/details/6712187

上一篇:Linux下升级python版本


下一篇:Linux记录-集群时间同步解决方案