2016年3月30日水曜日

UnityとAVRのUSART シリアル通信とReadLineで問題

概要

Unityからシリアルポートの入出力を行います。入出力先はAVRマイコンです。AVRマイコンはUSART機能を使って通信を行います。

これまで

UnityのC#ではなく、通常のWindows .NetFrameworkでシリアル通信を実装してきました。(最新バージョンの.Netでも.Net2.0でも正しく動作しました。)

Unityでシリアル通信

Unity(mono)と.Netの違いはあるとはいえ、同じ実装でシリアル通信できるはずです。
ただし、使用するフレームワークを.Net2.0に指定しなければいけません。
ツールバのEditor→Project Settings→PlayerからApi Compability Levelを.Net2.0にします。
.Net2.0を選択

ReadLineで通信が途絶える問題

基本的には通常のC#の実装と同じです。しかし、次の問題に遭遇し、苦労しました。(おそらくUnityに付属するライブラリに問題があると思われます。)
  • SerialPort#ReadLine() で入力できない。
ReadLine()を実行する場所で止まってしまいます。また、タイムアウトするように設定したら、毎回タイムアウトしてしまいます。

1バイトずつ読みこむように変更

SerialPort#ReadLine()は文字通り、改行コードまでの1行を取得するメソッドです。
AVRマイコン側も
    uint16_t hallCount = 0;
・・・・
    printf("%d\n", hallCount);
とprintf()で改行コードを書き出しているため問題ないはずです。ところが、通信が止まってしまいます。
そこで、諦めて1バイトずつ入出力するようにしました。

  • AVR側は、16bit int型をビットシフトして1バイトずつ2回に分けて送信レジスタに登録する。
  • C#側はSeralPort#ReadByte()メソッドを使って2回に分けて取得する。
C#のコードは前回実装したコードと同じですが、次の変更を行いました。
var high = serialPort.ReadByte();
var low = serialPort.ReadByte();
var val = (high << 8) + low;
また、AVRのコードもこのように1バイトずつ書き込むように変更しました。

これで通信ができるようになりました。

まとめ

Unityでシリアル通信をするとき、SerialPort#ReadLine()では通信が止まってしまうため、ReadByte()で1バイトずつ通信することで問題を回避しました。