USBSerial
Maple(Arduino STM32)ボードとコンピュータ間のシリアル通信に使用されます。
概要
/icons/hr.icon
Maple(Arduino STM32)のSTM32マイクロプロセッサには、3つのシリアルポートに加えて、専用のUSBペリフェラルが含まれています。 このUSBペリフェラルは、シリアルポートをエミュレートしてUSBホストからは通常のシリアル通信として利用できます(CDC プロトコルのCDC ACM クラス利用)。 エミュレートされたシリアル通信はシリアルポートと比較しては比較的早いです。通常のシリアル速度(kbps)でデータを転送するのが最適です。また独立した専用の512バイトバッファがあり、シリアルポートよりも信頼性の高い通信を行うことができます。
エミュレートされたシリアルポートへのライブラリアクセスは、USBSerialのインスタンスであるSerialオブジェクトを介して提供されます。GPIOピンのシリアルポートは、Serial1、Serial2、Serial3オブジェクトとして使用できます。
注意
1. スケッチの書き込みが実シリアルポート経由の場合、Serialオブジェクトは、HardwareSerialのインスタンスに置き換わることに注意してください。Serialオブジェクトがどちらのクラスのインスタンスであるかは、SERIAL_USBが定義されているかで判断できます。
2. USBSerialの機能には、書き込みのための50ミリ秒のタイムアウトが含まれており、USBホストが「本当に」接続されているか、列挙されて初期化されたかどうかを検出しようとしません。つまり、コードでUSBSerialのwrite()関数またはprint()関数のいずれかを呼び出していて、コンピュータ上でUSBSerialを監視していない場合、プログラムが監視されている場合よりもはるかに遅く実行されるか、 完全に切り離されています(バッテリから流出)。DTRとRTSの回線ステータスを使用してポートステータスを解読することで、この動作を回避できます(これらの制御ラインの動作はプラットフォームによって異なり、デフォルトでは解釈しません)。
3. ボード起動直後またはリセット直後のホストとのシリアル通信が確立していない状態で、にUSBSerialを使ってデータを送信する場合、データが正しく送信出来ない場合があります。状況によってはそれ以降のデータ送信が一切できない状態になります。
code:note01.ino
void setup() {
Serial.begin(115200);
Serial.println("hello,World");
}
void loop() {
}
関連項目
CDC (ACM): Communication Device Class (Abstract Control Model)
調査用資料
ライブラリリファレンス
/icons/hr.icon
SerialオブジェクトはUSBSerialクラスのインスタンスです。このセクションではUSBSerialクラスのメンバ関数について説明しています。 つまり、Serial.functionName(arguments ...)を記述することで、これらの関数のいずれかを使用することができます。 たとえば、 "hello、world!"というメッセージを表示するには、Serial.println("hello、world!")と書くことができます。
class USBSerialクラス
エミュレートされたUSB上のシリアル通信クラス。 SerialUSBはあらかじめ定義された(シングルトン)インスタンスです。
通信の開始
■ void begin(void)
■ void begin(unsigned long ignoreBaud)
■ void begin(unsigned long ignoreBaud, uint8_t ignore)
エミュレートされたシリアル通信用にUSB周辺機器を設定します。
デフォルトでは、ボード起動時にこの設定が行われています(実シリアルポートとようなsetup()でのbegin()呼び出しを行う必要がない)。
実シリアルポート(HardwareSerialの利用)のbegin(初期化)では通信速度の指定が必要ですが、エミュレートされたシリアル通信では不要となります。ただし、Arduinoおよび実シリアルポートとの互換性のため、通信速度の指定、通信条件の指定をしてもエラーとはなりません。
この関数の呼び出しは、SerialUSB.end()を使用してペリフェラルを無効にした場合にのみ必要です。
引数
ignoreBaud :通信速度(単位はbps) 指定しても値は無視されます。
ignore :通信条件(データ長、パリティ、ストップビットの組み合わせ)。指定しても値は無視されます。
戻り値
なし
通信の終了
■ void end()
USBポートのシリアル通信を無効にします。
この関数を使用すると、Maple(Arduino STM32)とUSBホスト間のすべてのUSB通信が終了することに注意してください。
特に、ホスト側からボードをリセットしたり、ブートローダモードを使用した新しいプログラムをアップロードすることができなくなることを意味します。 スケッチにて明示的にend()を実行した後は、Arduino IDEからのリセットを行うことができません。
新たにスケッチの書き込みを行う場合は、書き込み直後にボード上のリセットボタンを押して下さい。書き込みを行うことができます。
引数
なし
戻り値
なし
通信状態のチェック
■ operator bool()
シリアル通信が確立しているかを判定します。
if (Serial)の形式で利用する時に、Serialオブジェクトをブール型にキャストする際に呼びだれます。
(注意)Arduino STM32パッケージの安定版 R20170323では、常にtrueを返します。これは意図しない動作となる可能性があります。 この場合はisConnected()`を利用して下さい。
戻り値
true :シリアル通信が確立している状態 ※Arduino STM32では常にtrueを返します。
false :シリアル通信が未確立の状態
利用例
code:sample1.ino
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB
}
}
void loop() {
//proceed normally
}
関連項目
■ uint8 isConnected()
シリアル通信が確立しているかを判定します。
処理としては、operator bool()を呼び出してその結果を返しています。
code:code
uint8 isConnected() { return (bool) *this; };
本関数はArduinoとの互換性がありません。Arduino STM32ではif (Serial)の利用を推奨しています。
(注意)Arduino STM32パッケージの安定版 R20170323では、if (Serial)は常にtrueを返します。これは意図しない動作となる可能性があります。 この場合はisConnected()を利用して下さい。
戻り値
true :シリアル通信が確立している状態
false :シリアル通信が未確立の状態
■ uint8 getRTS()
ホストからのRTS(Request To Send:送信要求)の状態を取得します。
(注意)本関数はArduinoとの互換性がありません。
戻り値
true :RTSがセット(HIGH)されている
false :RTSがリセット(LOW)されている
■ uint8 getDTR()
ホストからのDTR(Data Terminal Ready:データ端末レディ)の状態を取得します。
(注意)本関数はArduinoとの互換性がありません。
戻り値
true :DTRがセット(HIGH)されている
false :DTRがリセット(LOW)されている
■ uint8 pending()
ホストへのデータ送信バッファの状態を確認し、データ送信可能であるかを返します。
(※仕様については、ソースをみて確認。要検証)
戻り値
true :送信可能
false :送信不可
■ unsigned int available()
読み込み可能(受信可能)なバイト数を返します。
戻り値
読み込み可能バイト数
■ int availableForWrite(void) 原文 書き込み操作をブロックすることなく、シリアルバッファに書き込み可能なバイト数(文字数)を取得します。
戻り値
書き込み可能なバイト数
(注意):この関数はヘッダーファイルusb_serial.hには宣言されていますが、実体が実装されていません。
スケッチをコンパイルした場合、 warning: undefined reference to 'USBSerial::availableForWrite()'のワーニングが表示され、availableForWrite()の実行時にフリーズします。
シリアルバッファから取り出さずに次に受信する1バイト(文字)を返します。 つまり、peek()を連続して呼び出すと、次のread()の呼び出しと同じ文字が返されます。 peek()はStreamクラスから継承します。
戻り値
読み込み可能なデータの最初の1バイトを返します。
バッファにデータがない場合、-1の場合を返します。
■ void flush(void)
受信バッファに着信したデータを破棄します。
(注意)この動作は、Arduinoの仕様(送信バッファ内データの送信完了待ち) とは異なります。
関連項目
低レベルデータ送信
■ size_t write(unsigned char ch)
USB接続で1文字送信します。ノンブロキングにて書き込みが行われます。
(補足:Arduino STM32では、ノンブロキング書き込み対応となった。オリジナルのMapleではブロッキングされていた)
内部的処理としては、 size_t write(void* buf, unsigned int size)を呼びでして1バイト送信を行っています。
これは低レベル関数です。print()関数やprintln()関数群は、文字列を出力する場合や数値を書式化して出力する場合などに便利です。
戻り値
送信したデータバイト数を返します。
■ size_t write(const char* str)
指定されたヌル終了文字列(\0が終端)をUSB接続経由で送信します。
内部的処理としては、 size_t write(void* buf, unsigned int size)を呼びでして1バイト送信を行っています。
戻り値
送信したデータバイト数を返します。
■ size_t write(void* buf, unsigned int size)
bufの最初のサイズバイトをUSB接続に書き込みます。 各バイトは個々の文字として送信されます。
戻り値
送信したデータバイト数を返します。
データ受信
■ size_t readBytes(char *buf, const size_t& len)
戻り値
受信した1バイトデータを返します。
■ unsigned char read()
利用可能な次の未読文字を返します。 使用可能な文字がない場合(使用可能な状態でこれをチェックすることができます)、データを受信するまでブロックされます。
戻り値
受信した1バイトデータを返します。
高レベルデータ送信
■ print(unsigned char b)
指定されたバイトデータbをUSB接続経由で出力します。
■ print(char c)
指定された文字cをUSB接続で印刷します。 通常、ASCIIテキストとして解釈されます。
■ print(const char* str)
指定したヌル終了文字列をUSB接続経由で出力します。
■ print(int n)
引数の数字を10進形式でUSB接続に出力します。 負の値には接頭辞 ' - 'が付きます。
■ print(unsigned int n)
引数の数字を10進形式でUSB接続に出力します。
■ print(long n)
引数の数字を10進形式でUSB接続に出力します。 負の値には接頭辞 ' - 'が付きます。
■ print(unsigned long n)
引数の数字を10進形式でUSB接続に出力します。
■ print(long n, int base)
baseを基底(2〜16の間でもよい)として、USB接続上にnの数字を表示します。 基底の2はバイナリ、8は8進数、10は10進数、16は16進数に対応します。 負の値には接頭辞 ' - 'が付きます。
■ print(double n)
小数点以下2桁まで正確なnを出力します。
■ println(char c)
print(c)の後に "\ r \ n"が続きます。
■ println(const char* c)
print(c)の後に "\ r \ n"が続きます。
■ println(unsigned char b)
print(b)の後に "\ r \ n"が続きます。
■ println(int n)
print(n)の後に "\ r \ n"が続きます。
■ println(unsigned int n)
print(n)の後に "\ r \ n"が続きます。
■ println(long n)
■ println(unsigned long n)
print(n)の後に "\ r \ n"が続きます。
■ println(long n, int base)
print(n、b)の後に "\ r \ n"が続きます。
■ println(double n)
print(n)の後に "\ r \ n"が続きます。
■ println()
"\ r \ n"を出力します。
利用例
/icons/hr.icon
安全な出力:この機能は円滑に実行され、ブロックされません。 LEDは、監視されているか、バッテリから実行されているか、または接続されているが監視されていなくても、ほぼ同じ速度で点滅する必要があります。 ご使用のプラットフォームおよびデバイス構成に合わせて、DTR / RTSロジックを試す必要があります。
code:sample2.ino
void setup() {
/* Set up the LED to blink */
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// LED will stay off if we are disconnected, and will blink
// quickly if USB is unplugged (battery power, etc.).
if(Serial.isConnected()) {
digitalWrite(LED_PIN, 1);
}
delay(100);
// If this logic fails to detect if bytes are going to be read
// by the USB host, then the println() take a long time,
// causing a very slow LED blink. If the characters are
// printed and read, the blink will only slow a small amount
// when "really" connected, and will be fast fast when the
// virtual port is only configured.
if(Serial.isConnected() && (Serial.getDTR() || Serial.getRTS())) {
for(int i = 0; i < 10; i++) {
Serial.println(123456, BIN);
}
}
digitalWrite(LED_PIN, 0);
delay(100);
}
補足事項
起動直後のホスト間との通信確立について
USBSerialを利用する場合、ボード起動直後においてホストとの通信確立を確認してから通信を行うことを推奨します。
STM32F103系のボード(例えばBlue Pillボード)では、電源ONまたはリセット後にスケッチプログラムが実行してからホストとの通信確立には約260ミリ秒かかります。この間にホストとの通信を行った場合、その通信データは破棄されます。ボード側が送信準備すら出来ていない状態で送信を行った場合は、永久に通信が確立出来ない状況になります。
次の例の"hello,Word"は恐らくホスト側では受信出来ないでしょう。
code:sample3.ino
void setup() {
Serial.begin(115200);
Serial.println("hello,World");
}
void loop() {
}
一つの改善策として、delay(300)を入れて時間待ちを行うことです。
この場合、ホスト側に受信処理を行うプログラムが起動していない場合、データは破棄されますが、それ以降の通信において永久に通信が確立出来ない状況には陥りません。
code:sample3_1.ino
void setup() {
Serial.begin(115200);
delay(300);
Serial.println("hello,World");
}
void loop() {
}
別の方法としては、Serial.isConnected()か!Serialの値をチェックし、ホスト間の通信が確立するまで時間待ちを行うことです。この方法では、ホスト側に確実にデータが渡されます。ただし、ホスト側との接続が出来るまでの間、それ以降の処理に進めることが出来ません。
Serial.isConnected()をつかう場合(Arduinoとは非互換であることに注意)
code:sample3_2.ino
void setup() {
Serial.begin(115200);
while(!Serial.isConnected()) delay(100);
Serial.println("hello,World");
}
void loop() {
}
!Serialを使う場合
(注意) Arduino STM32 安定版R20170323ではSerialは常にtrueを返すため、正常に動作しません。
code:sample3_3.ino
void setup() {
Serial.begin(115200);
while(!Serial) delay(100);
Serial.println("hello,World");
}
void loop() {
}
関連項目
このドキュメントはleafLabs, LLC.が執筆し、たま吉が翻訳・一部加筆修正したものです。