Next Step社製の 「Sony Spresense 用 RS-422/485 AddOnボード」 をVisual Studio CodeベースのSPRESENSE開発環境「SPRESENSE SDK」から利用してみました。RS-422/RS-485は産業向けネットワークとして人気の高いネットワークで、差動信号を用いた伝送により長距離を、ノイズの影響を少なく通信できる特徴を備えています。RS-485は半二重通信、RS-422は全二重通信です。
RS-422/485に対応したPLC自体は珍しくなく、Raspberry Pi由来のものもありますが、 SPRESENSEをRS-422/485に対応させることにより、長寿命・低消費電力・ファンレス・コンパクトなPLCを実現することができます。
今回購入したデバイス
関連するコミット
配線方法
「Sony Spresense 用 RS-422/485 AddOnボード」ボード背面のハンダ付けが必要です。
- MASTER側とSLAVE側で以下のジャンパ設定をします
- JP1:2-3をショート:I2S端子を制御に利用
- JP2:2-3をショート:I2S端子を制御に利用
- JP3:終端抵抗 = 有効(クローズ)
- JP4:RS-422(オープン):全二重通信
- JP5:RS-422(オープン):全二重通信
また、RS-422を利用するので、
MASTERのAとSLAVEのY、MASTERのBとSLAVEのZ、
MASTERのYとSLAVEのA、MASTERのZとSLAVEのBをケーブルで接続してください。
ビルド方法
SDKコンフィグを開き「Examples」にある「rs422」を選択してください。RS-422/RS-485の受信・送信制御にI2SのピンあるいはEMMCのピンを利用します。同居するアプリケーションにご注意ください。
プログラムの構造
NextStep社製のボードではRS-422/RS-485がSPRESENSE Main Board上のUART2と接続されています。SPRESENSE SDKでは、NuttXというRTOS(リアルタイムOS)が、ハードウェアを制御してくれますが、本環境ではこれらのポートは「/dev/ttyS2」というファイルで表現されます。このファイルをopenしてUART通信を行い、RS-422/RS-485の信号を生成します。
UART通信ポートの初期化
ttyS2はUART通信用に以下のソースコードで初期化します。
struct termios tio;
/* Open and Setup RS485_TTY_NAME */
comm_tty = open(RS485_TTY_NAME, O_RDWR | O_NOCTTY);
ret = errno;
if (ret != 0)
{
printf("could not open %s\n", RS485_TTY_NAME);
return -1;
}
tcgetattr(comm_tty, &tio);
tio.c_cflag |= CREAD; /* Enable receive */
tio.c_cflag |= CLOCAL; /* Local line, no modem control */
tio.c_cflag &= ~CSIZE; /* Clean the bits */
tio.c_cflag |= CS8; /* Data bit 8bit */
tio.c_cflag &= ~CSTOPB; /* Stop bit 1bit */
tio.c_cflag &= ~PARENB; /* Paritiy none */
cfsetspeed(&tio, RS485_TTY_SPEED);
tio.c_iflag = 0;
tio.c_oflag = 0;
tio.c_lflag = 0;
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
tcflush(comm_tty, TCIOFLUSH);
tcsetattr(comm_tty, TCSANOW, &tio);
fcntl(comm_tty, F_SETFL, O_NONBLOCK);
データの受信
UARTはいつデータが来るかわからないため、ファイルディスクリプタに対してpoll関数で待ちます。また、すべてのデータが一度に届くとは限らないため、要求長データが揃うまでループしてデータを繋げていきます。
int ret;
struct pollfd fds[1];
int nfds = 1;
int done = 0;
fds[0].fd = comm_tty;
fds[0].events = POLLIN | POLLRDNORM;
while (remain > 0)
{
ret = poll(fds, nfds, 500);
while ((ret = poll(fds, nfds, 0)) > 0)
{
if (ret > 0)
{
if ((fds[0].revents & (POLLIN | POLLRDNORM)) != 0)
{
ret = read(comm_tty, pbuf + offset, remain);
offset += ret;
remain -= ret;
done += ret;
}
}
}
}
データの送信
送信は簡単です。write関数で書き込み、すべてのデータが送出完了するまでループします。
int done = 0;
while (remain > 0)
{
ret = write(comm_tty, pbuf + offset, remain);
offset += ret;
remain -= ret;
done += ret;
}
以上のコードを組み合わせて、プログラムを作りました。正確には、追加でコマンドパーサーなどを実装しており、MASTER側で生成するメッセージの先頭4文字(ヌル文字含む)を変えることにより、SLAVE側の挙動を切り替えられるようにしました。
プログラムの利用方法
Slave側を先に起動し、NuttShell(RTOS:NuttXのシェル)上で「rs422 -s」を実行、スレーブを起動します。次にMaster側に移動し、同じくNuttShell上で「rs422 -m」を実行、マスターを開始すると、マスターからコマンドがスレーブへ送出され、下記のシーケンスの後、マスターが終了します。スレーブは、初期状態に戻ります。
-
ini
- マスターから"ini"コマンドを送出
- スレーブ側でバッファを確保、初期化
- 完了をACK応答
-
dat
- マスターから"dat"コマンドを送出
- スレーブ側のバッファをマスターへ送出
- マスター側でバッファ内容をチェック
-
fin
- マスターから"fin"コマンドを送出
- スレーブ側でバッファを解放
- 完了をACK応答
おわりに
ノイズに強い有線接続の伝送線が準備できました。
色々なアプリケーションに利用できそうですね。