ここのところhastech氏と照明機器開発をしていたので一度ここでまとめておきます。
DMX512の仕様書さえ読めば乗っているが、
Ujjal's DMX512 Pages....The DMX512 Packet
を参考にしました。
信号生成
一番手軽な手段であるArduinoでやるなら、Break信号を生成する手段が少し強引となります。
手順
ブレークタイムを作る
マークアフターブレークを作る
<<シリアル通信スタート>>
スタートコード送信
各チャネルデータを送信
シリアルライブラリではブレークが実現できないので、その都度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(); | |
} |
試験的に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; | |
} |
/* | |
* 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_ */ |
あと通信線路に関しては、RS-485トランシーバ、レシーバを介せばいい感じです。