Armadilloフォーラム

USBシリアルの受信バッファ数

suzukitsuneyuki

2018年1月30日 13時37分

いつもお世話になっております。
鈴木と申します。

Armadillo G3Lを使ったUSBシリアル通信プログラムを作成しております。
連続して115200bpsで10ms毎にデータを送ってくる相手に対して
下記のようにシリアルポートを設定しデータを受信しております。

 fd = open("/dev/ttyUSB0”, O_RDWR | O_NOCTTY); でオープンし、
 cfsetspeed(&tio, B115200); でボーレートの設定を行い、
tio.c_iflag = IGNBRK | IGNPAR;
tio.c_cflag = CLOCAL | CREAD | CS8;
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 0;
tcsetattr(fd, TCSANOW, &tio); でシリアルポートの設定を行い、
 select()で読み込みが可能にるまで待ってread()で受信する

この場合、連続して送られてくるデータを保持する
受信バッファのサイズを知りたいのですが、
(何バイトまでreadするタイミングが遅延しても、データが読めるか)
どなたかお教え頂けませんでしょうか。

よろしくお願いいたします。

コメント

溝渕です。

> この場合、連続して送られてくるデータを保持する
> 受信バッファのサイズを知りたいのですが、
> (何バイトまでreadするタイミングが遅延しても、データが読めるか)

TTYバッファの最大サイズですね。標準では最大サイズが64kByteに設定されています。

drivers/tty/tty_buffer.c:
#define TTYB_DEFAULT_MEM_LIMIT 65536

suzukitsuneyuki

2018年1月31日 18時07分

鈴木です。

お返事いただきありがとうございます。

標準で最大64kByteとのことですが、
現在の設定値を確認する方法があれば、お教え頂けませんでしょうか。
(ドライバ、カーネルをそのまま使っていたら64kByteになっているという事でしょうか)

現在、こちらのプログラムで、10ms間隔で33Byte連続で送られてくるデータを
10ms毎に読み込むようにしているのですが、たまに処理が間に合わず
1分間で100回データを読み取らないといけないところを、
6回くらい読み飛ばす場合があります。

この分が受信バッファにたまっていると思うのですが、
約18秒(33Bytex18秒x6個=約3.5kByte)で、
受信バッファがクリアされるような症状が発生しており、
(受信データにカウンタ情報がついており、それが一気に100数十進む)
64Byteとは合わないと思われる状況となっています。

そこで、詳細なバッファの動作を知りたいのですが、
何か良いアドバイスがあればお教え頂けませんでしょうか。

> 溝渕です。
>
> > この場合、連続して送られてくるデータを保持する
> > 受信バッファのサイズを知りたいのですが、
> > (何バイトまでreadするタイミングが遅延しても、データが読めるか)
>
> TTYバッファの最大サイズですね。標準では最大サイズが64kByteに設定されています。
>
> drivers/tty/tty_buffer.c:
> #define TTYB_DEFAULT_MEM_LIMIT 65536
>

> 標準で最大64kByteとのことですが、
> 現在の設定値を確認する方法があれば、お教え頂けませんでしょうか。
> (ドライバ、カーネルをそのまま使っていたら64kByteになっているという事でしょうか)

その通りです。

> 現在、こちらのプログラムで、10ms間隔で33Byte連続で送られてくるデータを
> 10ms毎に読み込むようにしているのですが、たまに処理が間に合わず
> 1分間で100回データを読み取らないといけないところを、
> 6回くらい読み飛ばす場合があります。

アプリ側でバッファを持つことによって、読み出しタイミングを気にしなくて
良くなると思います。

> この分が受信バッファにたまっていると思うのですが、
> 約18秒(33Bytex18秒x6個=約3.5kByte)で、
> 受信バッファがクリアされるような症状が発生しており、
> (受信データにカウンタ情報がついており、それが一気に100数十進む)
> 64Byteとは合わないと思われる状況となっています。

受信バッファから読み出していないデータがあるのが上記症状の原因でしょうか?

試しにアプリを「データが存在する限りread(2)し続ける」実装にした場合は
どうなりますか?

> そこで、詳細なバッファの動作を知りたいのですが、
> 何か良いアドバイスがあればお教え頂けませんでしょうか。

詳細な動作については、"drivers/tty/"以下のソースを読むのが確実かと思い
ます。

suzukitsuneyuki

2018年4月20日 18時44分

鈴木と申します。

以前、下記のUSBシリアル受信バッファのサイズを質問させていただいた者です。
バッファサイズは64KBとのことだったのですが、
(ソースコードのdrivers/tty/tty_buffer.cに
 #define TTYB_DEFAULT_MEM_LIMIT 65536の定義あり)
実際に動作させてみるともっと小さいように思われます。(4~5KB?)

当方のシステムは、Armadillo G3LのUSBに10ms毎にシリアルデータを転送してくる機器を接続し、
/dev/ttyUSB0でopenし、readでそのデータを取得しているのですが、
この場合の受信バッファのサイズも上記箇所の#defindeで定義されるのでしょうか。

以前、ご提案頂いた「データが存在する限りread(2)し続ける」実装で確認しても、
約1.5秒程度のsleepを入れると、データ落ちが発生してしまいます。

再度の確認で申し訳ありませんが、
ご回答いただけましたら幸いです。
よろしくお願いいたします。

> > 標準で最大64kByteとのことですが、
> > 現在の設定値を確認する方法があれば、お教え頂けませんでしょうか。
> > (ドライバ、カーネルをそのまま使っていたら64kByteになっているという事でしょうか)
>
> その通りです。
>
> > 現在、こちらのプログラムで、10ms間隔で33Byte連続で送られてくるデータを
> > 10ms毎に読み込むようにしているのですが、たまに処理が間に合わず
> > 1分間で100回データを読み取らないといけないところを、
> > 6回くらい読み飛ばす場合があります。
>
> アプリ側でバッファを持つことによって、読み出しタイミングを気にしなくて
> 良くなると思います。
>
> > この分が受信バッファにたまっていると思うのですが、
> > 約18秒(33Bytex18秒x6個=約3.5kByte)で、
> > 受信バッファがクリアされるような症状が発生しており、
> > (受信データにカウンタ情報がついており、それが一気に100数十進む)
> > 64Byteとは合わないと思われる状況となっています。
>
> 受信バッファから読み出していないデータがあるのが上記症状の原因でしょうか?
>
> 試しにアプリを「データが存在する限りread(2)し続ける」実装にした場合は
> どうなりますか?
>
> > そこで、詳細なバッファの動作を知りたいのですが、
> > 何か良いアドバイスがあればお教え頂けませんでしょうか。
>
> 詳細な動作については、"drivers/tty/"以下のソースを読むのが確実かと思い
> ます。
>

中村です。

気になったので調べてみました。

> 以前、下記のUSBシリアル受信バッファのサイズを質問させていただいた者です。
> バッファサイズは64KBとのことだったのですが、
> (ソースコードのdrivers/tty/tty_buffer.cに
>  #define TTYB_DEFAULT_MEM_LIMIT 65536の定義あり)
> 実際に動作させてみるともっと小さいように思われます。(4~5KB?)

4KB弱(3970バイトくらい?)でUSBシリアルのデータ転送が止まるようです。

USBシリアルはFTDIで試しました。
G3を使いましたがG3Lでも同じでしょう。

linux-3.14-x1-at21/drivers/usb/serialにあるgeneric.cに
次のデバッグ出力を追加して、syslog監視しました。
(diffで差分を多めに出して関数だけを切り出したものです)

void usb_serial_generic_read_bulk_callback(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
        unsigned char *data = urb->transfer_buffer;
        unsigned long flags;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
                if (urb == port->read_urbs[i])
                        break;
        }
        set_bit(i, &port->read_urbs_free);
 
        dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
                                                        urb->actual_length);
 
        if (urb->status) {
                dev_dbg(&port->dev, "%s - non-zero urb status: %d\n",
                        __func__, urb->status);
                return;
        }
 
+if (urb->actual_length > 2) {
+  char buf[1024];
+  int len = min_t(int, sizeof buf - 1, urb->actual_length - 2);
+  snprintf(buf, len + 1, "%s", data + 2);
+  printk(KERN_DEBUG "actual_length=%d [%s]\n", urb->actual_length, buf);
+}
        usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
        port->serial->type->process_read_urb(urb);
 
        /* Throttle the device if requested by tty */
        spin_lock_irqsave(&port->lock, flags);
        port->throttled = port->throttle_req;
        if (!port->throttled) {
                spin_unlock_irqrestore(&port->lock, flags);
                usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
        } else
                spin_unlock_irqrestore(&port->lock, flags);
 }

データが止まった時もバルクでのステータスのやり取り
(usb_serial_generic_read_bulk_callback()の呼び出し)はありますが、
データ(urb->transfer_bufferの3バイト目以降)は来なくなるみたいです。

何を見てUSBのデータ転送を止めているのかは調べ切れていません。

USBのデータ転送が止まったとき、送信側のバッファにも
ある程度の(送信側の実装依存)データ溜まりますので、
Armadilloでsleepを解除してread()を再開すると、
Armadiloのttyバッファに溜まっていた約4KBと、
送り側のバッファに溜まっていたものが順に取り出され、
送り側バッファに入れることができなかった(あふれた)
データが消えているように見えました。

今回のテストの送り側はarduinoのコンソールで、
次のようなプログラムです。

void loop()
{
  static unsigned int count = 0;
 
  delay(100);
 
  count++;
  printf("%8u\n", count * 10);
}

// このprintf()は"\n"でCR+LFを送信するように改造してます

--
なかむら

suzukitsuneyuki

2018年4月23日 11時18分

中村様

鈴木です。
ご回答いただきありがとうございます。

FTDIで4kB弱でUSBシリアルデータ転送が止まったとのことですが、弊社のシステムもFTDIを使用しており、
1回の受信サイズが3993バイト辺りを超えるところでデータ落ちが発生しており、
同様のことが起きているように思われます。

USBについて余り詳しくなく、インターネットで検索すると下記のサイトがありました。
http://www.hdl.co.jp/USB/FTDI/lt/index.html

この中で、パケット長の最大が4kBとの記載があり、今回の現象と何かあっているような気がします。

ただ、データ受信バッファは4kBしか使えないと仮定した場合、
10ms毎に33バイトを受信しようとすると、4096バイト / 33バイト x 10ms = 約1.24秒となり、
約1.24秒以上他のプロセスの処理が発生するとデータ落ちが発生することになってしまいます。

実験に単に時刻を取得して、ファイルに格納することを繰り返すだけのプログラムを作成しても、
稀に1秒~2秒くらい時刻が飛ぶことがあり、優先順位を上げて実行しても(nice値 ー20)
完全に発生しないようにすることはできておりません。

このような状況の中で、やはり受信バッファを64kB使えるようにしたいのですが、
何かアドバイスはございませんでしょうか。

よろしくお願いいたします。

> 中村です。
>
> 気になったので調べてみました。
>
> > 以前、下記のUSBシリアル受信バッファのサイズを質問させていただいた者です。
> > バッファサイズは64KBとのことだったのですが、
> > (ソースコードのdrivers/tty/tty_buffer.cに
> >  #define TTYB_DEFAULT_MEM_LIMIT 65536の定義あり)
> > 実際に動作させてみるともっと小さいように思われます。(4~5KB?)
>
> 4KB弱(3970バイトくらい?)でUSBシリアルのデータ転送が止まるようです。
>
> USBシリアルはFTDIで試しました。
> G3を使いましたがG3Lでも同じでしょう。
>
> linux-3.14-x1-at21/drivers/usb/serialにあるgeneric.cに
> 次のデバッグ出力を追加して、syslog監視しました。
> (diffで差分を多めに出して関数だけを切り出したものです)
>

> void usb_serial_generic_read_bulk_callback(struct urb *urb)
>  {
>         struct usb_serial_port *port = urb->context;
>         unsigned char *data = urb->transfer_buffer;
>         unsigned long flags;
>         int i;
> 
>         for (i = 0; i < ARRAY_SIZE(port->read_urbs); ++i) {
>                 if (urb == port->read_urbs[i])
>                         break;
>         }
>         set_bit(i, &port->read_urbs_free);
> 
>         dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
>                                                         urb->actual_length);
> 
>         if (urb->status) {
>                 dev_dbg(&port->dev, "%s - non-zero urb status: %d\n",
>                         __func__, urb->status);
>                 return;
>         }
> 
> +if (urb->actual_length > 2) {
> +  char buf[1024];
> +  int len = min_t(int, sizeof buf - 1, urb->actual_length - 2);
> +  snprintf(buf, len + 1, "%s", data + 2);
> +  printk(KERN_DEBUG "actual_length=%d [%s]\n", urb->actual_length, buf);
> +}
>         usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
>         port->serial->type->process_read_urb(urb);
> 
>         /* Throttle the device if requested by tty */
>         spin_lock_irqsave(&port->lock, flags);
>         port->throttled = port->throttle_req;
>         if (!port->throttled) {
>                 spin_unlock_irqrestore(&port->lock, flags);
>                 usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
>         } else
>                 spin_unlock_irqrestore(&port->lock, flags);
>  }
> 

>
> データが止まった時もバルクでのステータスのやり取り
> (usb_serial_generic_read_bulk_callback()の呼び出し)はありますが、
> データ(urb->transfer_bufferの3バイト目以降)は来なくなるみたいです。
>
> 何を見てUSBのデータ転送を止めているのかは調べ切れていません。
>
> USBのデータ転送が止まったとき、送信側のバッファにも
> ある程度の(送信側の実装依存)データ溜まりますので、
> Armadilloでsleepを解除してread()を再開すると、
> Armadiloのttyバッファに溜まっていた約4KBと、
> 送り側のバッファに溜まっていたものが順に取り出され、
> 送り側バッファに入れることができなかった(あふれた)
> データが消えているように見えました。
>
> 今回のテストの送り側はarduinoのコンソールで、
> 次のようなプログラムです。
>

> void loop()
> {
>   static unsigned int count = 0;
> 
>   delay(100);
> 
>   count++;
>   printf("%8u\n", count * 10);
> }
> 

> // このprintf()は"\n"でCR+LFを送信するように改造してます
>
> --
> なかむら
>
>
>
>

中村です。

> FTDIで4kB弱でUSBシリアルデータ転送が止まったとのことですが、弊社のシステムもFTDIを使用しており、
> 1回の受信サイズが3993バイト辺りを超えるところでデータ落ちが発生しており、
> 同様のことが起きているように思われます。

症状は同じようですね。

> USBについて余り詳しくなく、インターネットで検索すると下記のサイトがありました。
> http://www.hdl.co.jp/USB/FTDI/lt/index.html
>
> この中で、パケット長の最大が4kBとの記載があり、今回の現象と何かあっているような気がします。

この資料の4KBの話は、今回の問題とは関係ないと思います。

> 実験に単に時刻を取得して、ファイルに格納することを繰り返すだけのプログラムを作成しても、
> 稀に1秒~2秒くらい時刻が飛ぶことがあり、優先順位を上げて実行しても(nice値 ー20)
> 完全に発生しないようにすることはできておりません。
...
> 何かアドバイスはございませんでしょうか。

ファイルに書き出すとき少し待たされることがあります。
LinuxのディスクI/Oがどういうタイミングで発生するかを
解説した参考書を読んでみてください。
そのタイミングで1秒~2秒かかって、そのときの時刻が
とれないということはあると思います。

1つ前に投稿で「USBデータ取得専用スレッドを・・・」と
書きましたが、その時刻取得の実験プログラムでも時刻取得の
専用スレッドを使ってみてください。
時刻取得スレッドとディスク書き出しスレッドを分ければ、
この問題もでなくなるのではないかと思います。

--
なかむら

suzukitsuneyuki

2018年5月9日 11時07分

鈴木と申します。

ご回答いただきありがとうございました。

アドバイス頂いたように、USBシリアル受信を行うスレッドと、
ファイル書き込みのスレッドを分けたところ、
パケット落ちの不具合はなくなりました。

時刻も飛ぶようなことはなくなりました。
ありがとうございました。

ただ、今度はUSBシリアル受信中に、LTEでlibcurlライブラリを使ってサーバとftp通信を行うと、
また、1回のデータ通信の間(数秒間)、
USBシリアルの10msデータパケットが飛んでしまう症状が発生しております。

恐らくftp通信中のUSBシリアル受信ができていないと思われるのですが、
何か原因、対策でアドバイス頂けませんでしょうか。

libcurlによるftp通信はスレッド化しておりますので、
USBシリアル受信とは並行して動作するようにしております。

また、製品マニュアルのブロック図を見ても、
USBとLTEモジュールはiMXの別のUARTに振り分けてあるので
ハード的にも並行して動作できると思っております。

以上、よろしくお願いいたします。

> 中村です。
>
> > FTDIで4kB弱でUSBシリアルデータ転送が止まったとのことですが、弊社のシステムもFTDIを使用しており、
> > 1回の受信サイズが3993バイト辺りを超えるところでデータ落ちが発生しており、
> > 同様のことが起きているように思われます。
>
> 症状は同じようですね。
>
> > USBについて余り詳しくなく、インターネットで検索すると下記のサイトがありました。
> > http://www.hdl.co.jp/USB/FTDI/lt/index.html
> >
> > この中で、パケット長の最大が4kBとの記載があり、今回の現象と何かあっているような気がします。
>
> この資料の4KBの話は、今回の問題とは関係ないと思います。
>
> > 実験に単に時刻を取得して、ファイルに格納することを繰り返すだけのプログラムを作成しても、
> > 稀に1秒~2秒くらい時刻が飛ぶことがあり、優先順位を上げて実行しても(nice値 ー20)
> > 完全に発生しないようにすることはできておりません。
> ...
> > 何かアドバイスはございませんでしょうか。
>
> ファイルに書き出すとき少し待たされることがあります。
> LinuxのディスクI/Oがどういうタイミングで発生するかを
> 解説した参考書を読んでみてください。
> そのタイミングで1秒~2秒かかって、そのときの時刻が
> とれないということはあると思います。
>
> 1つ前に投稿で「USBデータ取得専用スレッドを・・・」と
> 書きましたが、その時刻取得の実験プログラムでも時刻取得の
> 専用スレッドを使ってみてください。
> 時刻取得スレッドとディスク書き出しスレッドを分ければ、
> この問題もでなくなるのではないかと思います。
>
> --
> なかむら
>

中村です。

> ただ、今度はUSBシリアル受信中に、LTEでlibcurlライブラリを使ってサーバとftp通信を行うと、
> また、1回のデータ通信の間(数秒間)、
> USBシリアルの10msデータパケットが飛んでしまう症状が発生しております。
>
> 恐らくftp通信中のUSBシリアル受信ができていないと思われるのですが、
> 何か原因、対策でアドバイス頂けませんでしょうか。

「データパケットが飛んでしまう」とは、具体的にはどのような状態ですか?
10msごとに連続するデータのうち1個だけが消えるとか、
まとまってごそっとなくなってしまうとか、
パラパラと飛び飛びでなくなるとか・・・

なんだかこれは厄介そうですね。

--
なかむら

suzukitsuneyuki

2018年5月9日 13時15分

> 「データパケットが飛んでしまう」とは、具体的にはどのような状態ですか?
> 10msごとに連続するデータのうち1個だけが消えるとか、
> まとまってごそっとなくなってしまうとか、
> パラパラと飛び飛びでなくなるとか・・・

接続している機器からは、10msデータが連続的に送られてくるのですが
そのデータにはシリアル番号のフィールドがあり、
データが1パケット送られるとそのフィールドが1つずつインクリメントされます。
そのシリアル番号が500個とか800個とか一気に飛んでしまいます。

ちなみに、接続機器にはフロー制御がないため、再送や停止ができません。

以上、よろしくお願いいたします。

中村です。

> 接続している機器からは、10msデータが連続的に送られてくるのですが
> そのデータにはシリアル番号のフィールドがあり、
> データが1パケット送られるとそのフィールドが1つずつインクリメントされます。
> そのシリアル番号が500個とか800個とか一気に飛んでしまいます。

5秒とか8秒とか、そのくらいの長い時間ということですよね。
ftpが
>>> また、1回のデータ通信の間(数秒間)、
とのことでしたので、その時間のUSBシリアル受信全部でしょうか・・・

>>> libcurlによるftp通信はスレッド化しておりますので、
このftp通信のスレッドと、
USBシリアルの受信スレッドと、
もし他にあれば他のスレッドと、
mutexロックなどで別スレッドを止めてしまうような処理がないか、
チェックしてみてください。

--
なかむら

中村です。

書き忘れがありました。対策です。

> 現在、こちらのプログラムで、10ms間隔で33Byte連続で送られてくるデータを
> 10ms毎に読み込むようにしているのですが、たまに処理が間に合わず
> 1分間で100回データを読み取らないといけないところを、
> 6回くらい読み飛ばす場合があります。

「1分間で100回データを読む」とありますが、
これは1秒間に100回ですね。

USBシリアルからひたすらread()するだけのスレッドを1つ作って、
自分のバッファに保持するようにれば、回避できるのではないかと思います。

溝渕さんが書いていた
> アプリ側でバッファを持つことによって、読み出しタイミングを気にしなくて
> 良くなると思います。

> 試しにアプリを「データが存在する限りread(2)し続ける」実装にした場合は
> どうなりますか?

は、そういうことではないかと思います。

--
なかむら