jevon

基于新唐M0的DMX512的实现

0
阅读(44388)

DMX 512协议是Digital Multiplex的缩写,是灯光行业数字化设备的通用信号控制协议,同时也是是一种国际协议;由美国剧场技术协会(United State Institute for Theatre Technology,Inc)于1986年8月提出的一个能在一对线上传送512路可控硅调光亮度信息的标准. DMX512 通信方式是采用了异步通信格式,每个调光点由11 位组成其中一个是起始位,8位调光数据,两个停止位.每一次传输能512个调光点.

特点:

1.采用EIA485标准硬件线路,波特率250Kbps,每个数据位4us,每帧传输的时间约22.7ms,半双工模式;

       2.每个调光点由1个起始位,8个调光数据位,两个停止位;

       3.需要传输1个88us的低电平数据间隙,作为一个数据包的起始帧头,接收方有间隙检测电路,需要找到起始帧头;无通信校验位;

4.数据帧头后面是一个空闲帧,通常是8us-1ms,紧接着还有一个字节数据,用来

表示设备代号或自定义用途,通常是0,该字节数据可以用于自动写码命令之用;

 

从DMX512(1990)的时序图看出,其在每一帧的必须有一个不得小于88us的break信号;接着才是8个数据位2个停止位的uart标准格式数据;所以用单片机的中断定时等方法来判断这个break显然有点麻烦;下面给出一些更为简易的方法:

 

Break的简易判断

方法1: uart/4859bit模式接收,判断第9bit 0还是1

在接收break信号的时候第9位必然为0(因为break为不低于88Us的低电平),而其它的数据的是2位的停止位,接收到的第9位就是其第一个停止位所以必然为1;

注意:

在接收break的时候,由于是88us的低电平,停止位不正确;但是单片机还是能正确检测到第9bit为0的;

不影响第九位的判断(cotex-M0的内核经过测试);

 51单片机,AVR都有9bit模式,arm内核可以通过强制奇偶校验位,利用奇偶校验错误来判断第9bit,

而cortex-M0内核,其拥有RS485的9bit模式(起始也是利用奇偶校验位来实现的)

 

以下先给出一个网友提供的基于AVR的代码:

// HardWare: Mega64@16MHz
// Tools:    GCC

#define  CHECK_DMX_SYNC             0
#define  CHECK_DMX_1ST_DATA         1
#define  CHECK_DMX_START_CHANNEL    2
#define  NORMAL_RECEIVE_DMX_DATA    3
#define  DMX_RECEIVE_OVER           4
#define  DMX_ERROR                  5

void DMX_Receive_Initial(void)
{
 dmx_receive_stage = CHECK_DMX_SYNC;
 dmx_sync_ok_f     = FALSE;
 dmx_fail_timer    = 0;

 UCSR1A = 0x00;
 UCSR1C = 0x86;
 UBRR1L = 0x03; 
 UBRR1H = 0x00; 
 UCSR1B = 0x94;
}


SIGNAL(SIG_UART1_RECV)
{
 unchar tmp_rb8;
 unchar tmp_receive_data;

//ReadSeriesRegister:
 tmp_rb8          = UCSR1B;
 tmp_receive_data = UDR1; 
 
//ReceiveDataProcess:
 tmp_rb8 &= 0x02;
 if(tmp_rb8 == 0) // 第9bit 为-0 ,表示是break
  {
   if(dmx_receive_stage == CHECK_DMX_SYNC)
    {
     dmx_receive_stage = CHECK_DMX_1ST_DATA;
    }
  } 
 else
  {
   if(dmx_receive_stage == CHECK_DMX_1ST_DATA)
    {
     if(tmp_receive_data == 0)  // 第一个数据,通常是0
      {
       dmx_receive_stage = NORMAL_RECEIVE_DMX_DATA;
       dmx_receive_point = 0;
       dmx_sync_ok_f     = TRUE;
       dmx_fail_timer    = 0;
      }
     else
      dmx_receive_stage = CHECK_DMX_SYNC;
     return;
    }
   if(dmx_receive_stage == NORMAL_RECEIVE_DMX_DATA)
    {
     dmx_receive_buf[dmx_receive_point] = tmp_receive_data;
     dmx_receive_point ++;
     if(dmx_receive_point > DMX_CHANNEL_NUMBER-1) dmx_receive_stage = CHECK_DMX_SYNC;
     return;
    }
   dmx_receive_stage = CHECK_DMX_SYNC;
  } 
}

在NUvoton M051上的实现

这里给出基于Nuvotn M051系列利用RS485 9bit 普通模式来实现的主要代码(基于CMSIS):

先初始化uart 和485 :

    /* Set UART Configuration */

    sParam.u32BaudRate      = DMX_512 _Baud;

    sParam.u8cDataBits      = DRVUART_DATABITS_8;

    sParam.u8cStopBits      = DRVUART_STOPBITS_1;

    sParam.u8cParity        = DRVUART_PARITY_ODD;

    sParam.u8cRxTriggerLevel= DRVUART_FIFO_1BYTES;

    sParam.u8TimeOut        = 0x7F;

    DrvUART_Open(UART_PORT1,&sParam);

//-----------------------------------------

    /* Set RS485 Configuration */

    sParam_RS485.u8cAddrEnable = ENABLE; // 使能第9bit地址数据

    sParam_RS485.u8cModeSelect = MODE_RS485_NMM|MODE_RS485_AUD ;//设置成普通模式和AUD

    sParam_RS485.u8cDelayTime  = 0;

    sParam_RS485.u8cRxDisable  = 0; 

    DrvUART_SetFnRS485(UART_PORT0,&sParam_RS485);  //请修改该库函数,确保RXDisble ,在RS_R485_NMM之前设置好(原厂的库函数有bug)

 

// 使能相关中断函数

DrvUART_EnableInt(UART_PORT0, DRVUART_RLSINT|DRVUART_RDAINT|DRVUART_TOUTINT,

                                 (PFN_DRVUART_CALLBACK*)RS485_HANDLE);

 

void RS485_HANDLE( void ) 

{

    volatile char addr;

    volatile char regRX;

    if(UART0->ISR.RLS_INT ==1)      /* RLS INT & RDA INT */

    { 

   

        if((UART0->FSR.RS485_ADD_DETF ==1) && (UART0->FUNSEL.FUN_SEL == FUN_RS485))  /* ADD_IF, RS485 mode */

        {          

            addr = UART0->DATA;

            UART0->FSR.RS485_ADD_DETF =1;                       /* clear ADD_IF flag */

            DMX_512_ADDR = 1;

            printf("bit 9 =1 \n");

        }

        printf("RLS_INT = %c \n",addr);

    }

    Else  if((UART0->ISR.RDA_INT == 1))/* Rx Ready */   /* Time-out INT */

    {

        regRX = UART0->DATA;

u8RecData[r_pointer++] = regRX;

        printf("bit 9 =0 \n");

        printf("RDA_INT = %c \n",regRX);

    }

 

    else if((UART0->ISR.TOUT_INT == 1))/* Rx Ready */   /* Time-out INT */

    {

        regRX = UART0->DATA;

 

        if(IsRS485ISR_TX_PORT)

            UART0->DATA= regRX;   

        else

            u8RecData[r_pointer++] = regRX;

            printf("TOUT_INT \n");

    }

 

    else if(UART0->ISR.BUF_ERR_INT == 1)                        /* Buff Error INT */

    {

        printf("\nBuffer Error...\n");

        while(1);  

    }

}

这里只是给出了判断第9bit为0还是为1的部分代码,大家可以仿造前面在AVR上实现的DMX512协议代码把其做修改即可;注意设置成RS485 9bit模式后,相关的奇偶校验将不能引发中断;

关于发送的话,可以用延时函数来实现;有的工程师也可以用9bit 模式发送第9bit 为1或0的方法来实现,然而这只是用于自己公司的系统中,并不是标准的DMX512, 不能通用;

 

 

 

 

 

 

方法2:用收线状态中断RLS_INTBIF

在cortex M0中可以用收线状态中断RLS_INT的BIF(钳制中断标志位:当接收数据时,数据线保持0的时间超过接收整个字节的时间时则置位)中断来实现BREAK的接收;

飘渺九哥已经写了个基于nuvoton M0516的例子,大家可以点击下面链接得到做参考;

http://bbs.21ic.com/icview-231705-1-1.html

 

方法3:用帧错误中断来判断

接收break的时候,因为break为不低于88us的低电平,必然会引起帧错误中断,所以可以以此来识别其是break信号;具体代码的实现这里不做说明;