女子力発電所(仮題)

ブログの中身も人生もぶれる日々

ArduinoとPCのシリアル通信でデジタル出力を制御する

ArduinoのD2~D9の8つを使って、デジタル出力をします。


出力パターンが決められていたり、即時性がそれほど求められない場合はもっと違う手法が有るのですが、出力に数ミリ秒の分解能が必要だったりすることがある場合に使えるのではないかと思います。


ソースは汚いですが、まずArduinoのスケッチから。MsTimer2をインストールしてください。

#include <MsTimer2.h>

volatile byte outdata = B00000000;
volatile unsigned int count = 0;
volatile boolean dataoutput = false; //デジタル出力中
volatile int ptr = 0; //PCからの電文解析:0は0x40待ち,nはnByte目読み中
volatile boolean nowstop = true; //最初の電文待ちかどうか
volatile int TXRXPacketDelta = 0; //電文バッファーがどれだけあるか
volatile int bufcont = 0; //電文バッファーのコントロール用パラメーター

void samplingOUT(){
	byte buf1,buf2;

	if(dataoutput){
		count --;
		buf1 = (outdata & B00111111)<<2; //PORTD;
		buf2 = (outdata & B11000000)>>6; //PORTB
	}else{
		buf1 = B00000000;
		buf2 = B00000000;
	}

	PORTD = (PORTD & B00000011) | (buf1 & B11111100);
	PORTB = (PORTB & B11111100) | (buf2 & B00000011);
  
	if(count == 0){
		dataoutput = false;
	}

}

void setup(){

	for(int i=2;i<=9;i++){
		pinMode(i,OUTPUT);
		digitalWrite(i,LOW);
	}

	Serial.begin(19200);
//Arduinoのシリアルはすぐには初期化できないみたいなので…
	delay(5000);

//分解能は10ミリ秒(10ミリ秒以下のパルスは出せない)
	MsTimer2::set(10,samplingOUT);
 	MsTimer2::start();
}

void loop(){

	byte sdata;

	int incoming = Serial.available();

	if(dataoutput == false && incoming >0){
		sdata = Serial.read();
		if(denbun_parse(sdata) == -1){
			bufcont = 1;
		}
	}

	switch(bufcont){
		case 1:
		case 3:
			if(TXRXPacketDelta >=10){
				bufcont = 2;
			}else{
				Serial.write((byte)incoming);
				TXRXPacketDelta ++;
			}
			break;

		case 2:
			if(TXRXPacketDelta <=5){
				bufcont = 3;
			}
			break;

		default:
			break;
	}

}
  

int denbun_parse(byte sd){
	int ret=0;

	switch(ptr){
		case 0:
			if(sd == 0x40){
				ptr = 1;
			}
			return 0;

		case 1:
			count = ((unsigned int)(sd))*256;
			ptr = 2;
			return 0;

		case 2:
			count = count + (unsigned int)sd;
			ptr = 3;
			return 0;

		case 3:
			outdata = sd;
			ptr = 4;
			return 0;

		case 4:
			if(sd == 0x80 && count != 0){
				if(nowstop == true){
					nowstop = false;
					ret = -1;
				}else{
					dataoutput = true;
					ret = 1;
					TXRXPacketDelta --;
				}
		   }
			ptr = 0;
			return ret;
	}
}

PC側からArduinoのシリアルポートへは、以下のような電文のシーケンスが必要になります。


(1)PC側が準備できたら 0x40 0x00 0x00 0x00 0x80の5バイトをシリアルに送信する(実は頭の0x40と0x80以外は何でもいい)。


(2)PC側はシリアルポートから1バイトの電文を受けるたびに、今後出力したい順番通りに以下の電文を出す。


0x40(固定) (2バイトで時間を表す) (ポートの出力パターンを1バイトで) 0x80(固定)


 時間はビッグエンディアンで、実際に出したい状態の時間の10ミリ秒で割った数です。
 また、ポートのパターンは下位ビットがD2→最上位ビットがD9になります。


 (例1) 100ミリ秒間、全ポート出力 → 0x40 0x00 0x0A 0xFF 0x80
(例2) 1分間(60000ミリ秒=6000×10ミリ秒)、D2,D4,D6,D8ポートのみON → 0x40 0x17 0x70 0x55 0x80


 ただし、10分以上連続したパルス(要するに時間のところが0xFF 0xFF … 655350ミリ秒)を越す場合は電文を分ける必要があります。


テストしたPC側のPythonのソースです。


Windows7環境下でserialが必要になります。("COM5"は環境に応じて変更してください)


ArduinoのD2出力にLEDを接続すると、1秒間ごとにON/OFFを繰り返し、D3は2秒毎、D4は4秒ごと…となります。

import serial
from time import sleep
from struct import pack,unpack

s = serial.Serial("COM5",baudrate=19200,parity='E',timeout=0)
count = 0;

sleep(5)
s.flushInput()
s.flushOutput()
sleep(5)
s.write(pack('5B',0x40,0x00,0x00,0x00,0x80))

while True:
	r = s.inWaiting()
	if r != 0:
		p = s.read()
		m = count % 256
		head = pack('5B',0x40,0x00,0x64,m,0x80)
		s.write(head)
		print "Pulse Output Pattun %d Rest Buffer(Arduino) %d Incoming Buffer(Arduino to PC) %d" % (m,ord(p),r)
		count = count + 1