AccessViolation Exception

仕事でもはんだづけ、家でもはんだづけ

DMX512信号送受信について

DMXというのは舞台照明機器等の通信に使われるプロトコル

ここのところhastech氏と照明機器開発をしていたので一度ここでまとめておきます。

DMX512の仕様書さえ読めば乗っているが、

Ujjal's DMX512 Pages....The DMX512 Packet

勉強部屋 4

を参考にしました。

信号生成


一番手軽な手段であるArduinoでやるなら、Break信号を生成する手段が少し強引となります。

手順

  1. ブレークタイムを作る

  2. マークアフターブレークを作る

<<シリアル通信スタート>>

  1. スタートコード送信

  2. 各チャネルデータを送信

シリアルライブラリではブレークが実現できないので、その都度Serial.beginをすることにします。

BaudRate:250000

StopBit:2bit

#define DMXCHANNELS 8
void setup()
{
}
uint8_t data[DMXCHANNELS] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,};
uint8_t tx = 1;
void loop()
{
pinMode(tx,OUTPUT);
digitalWrite(tx,0);//Break
delayMicroseconds(88);
digitalWrite(tx,1);//MAB
delayMicroseconds(8);
Serial.begin(250000,SERIAL_8N2);
Serial.write(0x0);//Start Code
for(uint16_t i = 0 ; i < DMXCHANNELS ; ++i){
Serial.write(data[i]);//Ch Data
}
Serial.end();
}
view raw DMXTEST.ino hosted with ❤ by GitHub

Arduino DMX Test

試験的に8chですが、適宜増やすことが可能です。

信号受信


生成した信号を見ればわかるとおり、フレーミングエラー検出がチャンネルリセットとして働けばいいので実装はとても楽です。が、Arduino上でUCSR0AのFE*1にアクセスする術が、ArduinoのSerialライブラリにはありません。

Arduino上にAVRのコードを書いても動くのですが、気持ち悪いので普通にAVRでやりました。

方法

  • フレーミングエラー検出されたらチャンネル数をリセット

  • 受信するたびにチャンネル数をインクリメント

  • 0番目はスタートコード

これだけなのでかなりあっさりできます。

/*
* dmx_receiver.c
*
* Created: 2014/07/15 19:43:52
* Author: kamiya
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include "dmx_receiver.h"
volatile uint8_t recv_data[DMX_CH];
volatile uint8_t data = 0;
volatile uint8_t is_fe = 0;
volatile uint16_t recv_counter = 0;
volatile uint8_t is_recv = 0;
//__ ______ ____
// |_____________| |[SC][ch1][ch2][ch3]...|
ISR(USART_RX_vect){
is_fe = (UCSR0A >> 4) & 0x1;
data = UDR0;
if(is_fe) recv_counter = 0x0;
else ++recv_counter;
if(1 < recv_counter && recv_counter < DMX_CH) recv_data[recv_counter - 2] = data;
is_recv = 0x1;
}
//////////////////////////////////////////////////////////////////////////
//dmx受信を初期化します
//sei()しないと受信を開始しないので注意してください
//内部でISR(USART_RX_vect)が定義されているので多重定義にも注意してください
void dmx_recv_init(){
//USART
UBRR0H = (MYUBRR >> 8) & 0xff;
UBRR0L = MYUBRR & 0xff;
UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
UCSR0C = (3<<UCSZ00) | (1 << 3);
data = 0x0;
is_fe = 0x0;
recv_counter = 0x0;
is_recv = 0x0;
dmx_recv_clear();
}
//////////////////////////////////////////////////////////////////////////
//受信データ配列を初期化します
void dmx_recv_clear(){
for(uint16_t i = 0 ; i < DMX_CH ; ++i){
recv_data[i] = 0x0;
}
}
//////////////////////////////////////////////////////////////////////////
//DMXを受信したかを返します。この関数を読み出し後受信フラグはリセットされます
uint8_t dmx_is_receive(){
uint8_t tmp = is_recv;
is_recv = 0x0;
return tmp;
}
view raw dmx_receiver.c hosted with ❤ by GitHub
/*
* dmx_receiver.h
*
* Created: 2014/07/15 19:43:41
* Author: kamiya
*/
#ifndef DMX_RECEIVER_H_
#define DMX_RECEIVER_H_
#define DMX_CH 128
#define BAUD 250000
#define MYUBRR (F_CPU/16/BAUD-1)
extern volatile uint8_t recv_data[DMX_CH];
void dmx_recv_init();
void dmx_recv_clear();
uint8_t dmx_is_receive();
#endif /* DMX_RECEIVER_H_ */
view raw dmx_receiver.h hosted with ❤ by GitHub

dmx receive for avr

あと通信線路に関しては、RS-485トランシーバ、レシーバを介せばいい感じです。

RS485/RS422トランシーバ LTC485CN8: 半導体 秋月電子通商 電子部品 ネット通販

*1:フレーミングエラー検出ビット