1,背景介绍
从事灯光行业的研发,总会碰到DMX,RDM,art-net,scan等等协议,其中使用最多的协议还是DMX协议.在该协议中有美标与中标之分,不过大家硬件上都使用的是RS485,波特率统一为250k/bps,8位数据位,2位停止位.
美标协议的具体内容,这里直接截图上传,方便直观的做记录与参考:(这里参考STM32官方给的UM1004
文档)
可以看出,DMX信号特殊在开始位的Mark信号与Mark after break信号,这里要强调一下,STM/GD32是自带Break信号产生单元的,不过可惜DMX没有使用正常的Break信号.
再来记录中标的DMX信号,也是上图:(标准参考WH/T 32-2008)
综合对比,发现中标与美标基本是一致的,最新的标准可以参考这个网站:https://tsp.esta.org/tsp/documents/published_docs.php在这里搜索DMX,可以发现,2019年出了新的标准,并添加了RDMnet规范,也就是加入了网络DMX/RDM的最新标准.
言归正传,DMX协议的规范,特殊在开始的帧头部分,在实际使用中,发送端的难点是发送这个非标的Break信号,而接收端也是处理这个Break信号.按照正常的串口发送流程,应该是1位起始位,8位数据位,2位停止位,整个一帧数据发送完需要44us的时间.而Break信号标准至少需要88us的时间,也就意味着发送端口需要发送至少两帧的低电平时间,STM32与GD32的Break控制单元是无法满足这个时间(题外话LPC的单片机可以实现).那么这里就需要对STM32的引脚做特殊处理.
如果DMX是512个通道的话,协议的后面是正常的513个字节数据(这里加上起始位的1位数据),当然按照协议可以延伸到1025,2049个字节数据,将DMX的地址扩展到1024,2048个通道.每个字节之间的间隔时间,协议规定不超过1s.
2,实现思路
在使用STM32/GD32实现该协议的方法上,总结网上与实际经验,整体上有一下几种方式:
1.一种是串口配置为9位数据位,1位停止位,发送端在发送break信号时配置为普通IO模式,发送数据时最高位进行或0x100运算.此办法优点是方便接收与发送同时处理,在接收时只需要检测最高位是否为1,并在接收到一个数据时判断接收的数据是否为0,以此来确认接收到Break信号.缺点就是无法使用单片机的DMA功能,发送与接收将消耗单片机大约25ms左右的时间.只能使用循环发送与接收的方式,等待数据发送完成与接收完成.
2.一种是串口配置为8位数据位,2位停止位,发送端在发送break信号时配置为普通IO模式,正常发送时配置为串口模式..此办法优点是解放了单片机的发送等待时间,只需要在DMA发送完成中断里检测发送完成即可.缺点是在接收时,不方便处理Break信号的识别,一种可行的识别方式是打开单片机的帧错误中断,在检测到该中断时开启DMA接收功能.但是如果是正常的DMX512信号的话接收的数据长度将会是514个字节,需要人为剔除第一个错误数据,第二个0数据起始码.
3.一种是串口配置为上述1.2任意模式,在接收端再引入一个单片机的定时器捕获引脚,发送时数据正常发送.接收时,利用单片机的定时器的捕获功能,捕获这个特殊的Break信号,捕获到正常的低电平时间在88us以上,以及Mark after break的8us以上,则任务开始正常接收.(注:STM32官方给的参考方式用了两个定时器捕获通道).
4.一种是串口配置为上述1.2任意模式,在接收端再引入一个引脚,接单片机外部中断,中断设置为上升沿与下降沿触发,再外加一个1us的定时器,计算上一个下降沿到下一个上降沿的间隔时间,如果操过88us以上,再坚持到下一个下降沿时间间隔在8us以上,认为检测到Break与Mark after break信号,后面数据串口接收进行识别即可.
5.一种是单纯的发送DMX信号,利用定时器->dma->gpio,定时器4us计算一次,直接使用DMA控制GPIO口翻转,实现模拟的串口,这种方式不考虑接收,可最多模拟出16路DMX串口输出.
这里贴出以上思路的几个参考链接:
1.https://en.wikipedia.org/wiki/DMX512
http://www.openedv.com/thread-44810-1-1.html
https://www.amobbs.com/forum.php?mod=viewthread&tid=4991795
https://www.amobbs.com/thread-4810567-1-1.html
http://www.stmcu.org.cn/module/forum/forum.php?mod=viewthread&ordertype=1&tid=615237
2.http://www.stm32cube.com/article/52
(下面这个使用空闲中断检测DMX接收信号,个人认为不够严谨,不过配置可以参考)
https://bbs.21ic.com/forum.php?mod=viewthread&tid=450252&extra=&page=1&mobile=2
http://archive.ednchina.com/group.ednchina.com/GROUP_MES_14350_306_58521.HTM
3.https://github.com/xhpohanka/dmx_dimmer/tree/master
第4种与第5种目前考虑工作原因,这里就不做记录了
3,总结
个人认为,使用STM32/GD32发送与检测DMX信号,做的最稳定的方式,还是在发送端使用DMA发送,解放CPU等待发送完成时间,这25ms的时间CPU可以做很多时间了在接收端,多引出一个引脚,将该引脚的尽可能配置为最高优先级,外加一个定时器,计算采集时间,同时该定时器可以用作1us计时单位,用作发送break信号与mark after break的延时基准,同时在检测到标准的开始时间后,再启动DMA接收,这样就解放了串口接收完成中断,省去了CPU频繁进入接收完成中断.这样做的目的还有一个好处,在检测端适配市面上各个厂家不同的发送端装置时,可以进行大范围适配不同发送端break时间.
另外,其实使用LPC系列的单片机,就不用这么费劲考虑break信号,LPC系列单片机自带Break信号产生单元,可以很好的控制break信号的产生与检测.