May 11, 2009

シリアルポートをstd::iostreamと同等に扱う

マイコンを扱っているとシリアルポートで通信を扱う機会がよくあるのですが、標準入出力やファイルと同等にシリアルポートを扱いたいという要望があります。例えば何かの入力を拾ってきて、それを加工し出力するC++プログラムがあったとします。このC++プログラム、当初はファイルに記録されたログをfstreamで読み込み、後から処理させることを想定していました。しかしとある事情で、シリアルポートからリアルタイムで情報を取得し、それを加工して出力する必要がでてきました。さてどうすれば変更を最小限に留められるでしょうか、というのが今回のお題です。

まずですが、プラットフォームはWindowsを想定することにします。なぜなら*NIXなら/dev/ttyS0等のスペシャルデバイスによってシリアルポートはファイルとかなり同等に扱えますので、あえて茨の道を進みます(笑)。ちなみにWindowsにインストールしたCygwinなら/dev/ttyS0等が使えるので問題なし、と思った方へ、僕も試しましたが動作がいまいちでした。

Windowsでシリアルポートを扱う際の作法としては、WindowsAPIにあるCreateFileでCOMn(nは番号)と名前のついた特別なデバイスをオープンし、ハンドルを取得、その後ReadFileWriteFileで読み書きという寸法です。これらのAPIをうまく隠蔽し、C++標準のIO環境であるstd::iostreamにうまくマップできれば本体の処理プログラムの変更は最小限で済みそうです。

このような時にはstd::iostreamのうち、n文字の読み書きといった低レベルの入出力を担当しているstd::streambufを継承したカスタムバッファを作成するのが王道のようです(Codianの『カスタムバッファ』の説明がわかりやすいと思います)。

方法がわかってしまえば、あとは調べて実装するのみです。C++の仕様がまとめられたcplusplus.comのstreambufの解説ページ『C++ iostream 実装資料2』等を参考にしました。

結果としてできたのがcomstream.hで、WindowsAPIによるシリアルポートの同期読み書きをラップしたカスタムstd::streambufのComportStreambuf、ならびにそれに処理を委譲したカスタムstd::iostreamのComportStreamです。std::iostreamライクなエコーバックするだけのテストプログラムcomstream_test.cppも書いてみましたが、gcc3.3.4とVC9(VS2008 Express Edition)で動作確認しました。

正常動作しているもののひとつ疑問なのが、std::streambufの定義をオーバーライドしているuflow()underflow()です。読み込み用内部バッファのポインタ操作で両者の違いを出す必要があるようですが、今回は内部バッファがないのでこの定義をどうすればよいのか、いまいちつかめていません。M$提供のヘッダをみる限りではuflow()が内部でunderflow()を呼び出しているようなので、underflow()だけ定義があればいいのかもしれません。

今後の拡張の展望としては、読み戻しのサポート(現在、pbackfail()は常に失敗するので読み戻しできません)や非同期化(いわゆるノンブロッキングIO)がありますが、両者とも僕としてはマイナーな用途なので実装する機会が訪れるかは不明です。

MacOSXをサポート(多分*NIX系全てOK)し、クロスプラットフォームになりました。上記リンクは最新版です。

23:51 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク | トラックバック
このエントリーのトラックバックURL: https://fenrir.naruoka.org/mt/mt-tb.cgi/700
コメント

はじめまして、いつも楽しく拝見しております。
シリアルポートですが、boostのasioはどうでしょうか?

Posted by: ゆう : January 16, 2010 01:25 AM

>ゆうさん
こんにちははじめましてfenrirです。
boost::asioというのがあるんですね。教えていただいてありがとうございます。さらっと調べてみたところネットワークストリームでの適用例があったので参考にしてみたいと思います。

Posted by: fenrir : January 19, 2010 11:01 PM
コメントする









名前、アドレスを登録しますか?
(次回以降コメント入力が楽になります)
  • 匿名でのコメントは受け付けておりません。
  • 名前(ハンドル名可)とメールアドレスは必ず入力してください。
  • メールアドレスを表示されたくないときはURLも必ず記入してください。
  • コメント欄でHTMLタグは使用できません。
  • コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
  • コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。