amazon

17.4.11

シリアルポート送信とその注意


CまたはC++でtermiosを使ってシリアル送信を試みた。
クラスを用いてオブジェクトを作ってみた。
始めうまく行かなかった原因が分かったのでメモ。

マシンはmacだが、シリアル接続に使っているのはELECOM製UC-SGT。これでUSB to シリアルケーブルしている。
UC-SGTには、macのドライバが付属していないが、別途使えるようにする方法があるので、mac UC-SGTなどとぐぐってみるとよい。

シリアル送受信の接続確認は
windowsだとTeratermなどが簡単だが、
macならターミナルでscreenかcuを使うとよい。

screenは林檎生活100さんのサイトを参照した。

接続する際のportのパスは、
/dev/cu.usbserial
または
/dev/tty.usbserial
僕の場合はどちらで接続できた。

ここから本題。
稚拙だがC++で接続するプログラムを書いてみた。

termiosの設定については、
FreeBSD 日本語マニュアル検索 (jman/japropos/jwhatis)

termios(4) Mac OS X Manual Page
を参照。

//serialsend.h
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>

#define BAUDRATE B9600
#define PORT "/dev/cu.usbserial"

class SerialPort{
private:
int fd;
struct termios oldtio,newtio;
public:
SerialPort();
int open(const char*);
int send(const char*, int);
void close(void);
};

//serialsend.cpp
/*シリアルポートに信号を送るプログラム*/
#include "serialsend.h"

SerialPort::SerialPort(){ //コンストラクタ
memset(&newtio, 0, sizeof(newtio));//ポート設定の初期化

cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);

newtio.c_iflag |= IGNPAR;

newtio.c_oflag |= OPOST;
newtio.c_oflag &= ~ONLCR;

newtio.c_cflag |= CS8;
newtio.c_cflag |= CSIZE;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag |= CREAD;
newtio.c_cflag &= ~PARENB;
newtio.c_cflag &= ~PARODD;
newtio.c_cflag |= HUPCL;
newtio.c_cflag |= CLOCAL;

newtio.c_lflag |= ICANON;

newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;
}

int SerialPort::open(const char* port){
//モデムデバイスを開く
fd = ::open(port, O_RDWR | O_NOCTTY | O_NDELAY);

//モデムデバイスが開けたかチェック
if(fd<0){
std::cerr<<"Error! "<<port<<" can not be opend."<<std::endl;
return -1;
}else{
fcntl(fd, F_SETFL, 0);
std::cout<<"Serial port fd="<<port<<" was opened."<<std::endl;
}

tcgetattr(fd,&oldtio); // 現在のポート設定を待避
tcflush(fd, TCIOFLUSH); //受信したが読み込んでいないデータ、および書き込んだが送信していないデータの両方を消去
tcsetattr(fd,TCSANOW,&newtio);
send("Port initiated. ",16); //ポートの初期化に一度何かを送っておくと調子がよさそう
return fd;
}

int SerialPort::send(const char* buf, int size){
int rt = write(fd, buf, size);

if(rt == size) {
std::cout<<buf[0]<<"... was sent."<<std::endl;
}else if(rt < 0){
std::cerr<<"Error! "<<buf[0]<<"... was not sent."<<std::endl;
}else{
std::cerr<<"Error! "<<buf[0]<<"... was partially sent."<<std::endl;
}

usleep(20000); //送信完了までの待ち時間

return rt;
}

void SerialPort::close(void){
tcsetattr(fd,TCSANOW,&oldtio);
::close(fd);
}

//main.cpp
#include "serial_send.h"

int main(void){
SerialPort a;
a.open(PORT);
a.send("something",9);
a.close();
return 0;
}

正しく送信するには、赤字の行が必須のようである。
試しにその行をコメントアウトすると、送信できないか、送信はできるが文字化けした。
usleep(20000)は20 msであるが、10 msだとほどんどの場合文字化けが起こり送信が正しく行われなかった。

シリアルポートではもっと細かい時間制御はできないのであろうか。
理論上1µsくらいまでいけそうだと思うのだが。
とにかく、上のプログラムでデータ送信を行う場合は処理に20 ms以上かかることを考慮に入れて設計をしなければならない。

0 件のコメント:

コメントを投稿