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/485的9bit模式接收,判断第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_INT的BIF
在cortex M0中 可以用收线状态中断RLS_INT的BIF(钳制中断标志位:当接收数据时,数据线保持0的时间超过接收整个字节的时间时则置位)中断来实现BREAK的接收;
飘渺九哥已经写了个基于nuvoton M0516的例子,大家可以点击下面链接得到做参考;
http://bbs.21ic.com/icview-231705-1-1.html
方法3:用帧错误中断来判断
接收break的时候,因为break为不低于88us的低电平,必然会引起帧错误中断,所以可以以此来识别其是break信号;具体代码的实现这里不做说明;
用户428812 2012-11-3 10:53
用户1605399 2012-5-30 11:08