シリアルポートのRTS信号をめぐる混乱について。
僕「RTSって、受信可能なときに相手に送信許可を与える信号ですよね?」
上司「(゚Д゚)ハァ? 送信したいときに相手に許可を求める信号やろ」
僕「えー? それだとRTSを相手のCTSに接続するの、おかしくないです?」
上司「でも、本にもRTSは送信要求(Request To Send)って書いてあるやろ?」
( ´・ω) (´・ω・) (・ω・`) (ω・` )
そう、RTS信号の意味、世間には2通りの解釈があるのです。
(1)「送りたい」…送信バッファにデータが入ったら立て、送信完了したら下げる。
(2)「送っていいよ」…受信バッファに余裕あるなら立て、余裕ないなら下げる。
これに関する次のような説明を見つけました。
http://www15.atpages.jp/limz80msx/program/rs232c.html
つまり、本来のRS-232Cの規格では(1)が正しいのだけど、ハードウェアフロー制御のために(2)の使い方をするのがいつの間にか広まったというのです。
WindowsのWIN32APIは(1),(2)の両方をサポートしています。ただし(2)がデフォルトです。SetCommState関数に渡すDCB構造体のfRtsControlに、下記いずれかを指定できます。
(1) RTS_CONTROL_TOGGLE ...「送りたい」ときに上げる 。
(2) RTS_CONTROL_HANDSHAKE …「送っていいよ」のときに上げる。
(3) RTS_CONTROL_ENABLE …常に上げる。
(4) RTS_CONTROL_DISABLE …常に下げる。
Linuxは規格外の(2)はサポートしていますが、本来の規格どおりの(1)はサポートしてないように見受けられます。tcsetattr関数に渡すtermios構造体のc_cflagにCRTSCTSフラグを立てるとRTS/CTS ハードウェアフロー制御が有効になるのですが、これは(2)を意味するようです。The Linux Serial HOWTO の 16.4 を読むと、「本来のRTSの使い方とは違うので紛らわしいけど、むしろ現在では本来の使い方をする機器はあんまり無い」みたいなことが書かれています。
http://archive.linux.or.jp/JF/JFdocs/Serial-HOWTO.html (日本語訳)
たしかに、僕も今までハードウェアフロー制御といえばもっぱら(2)の方法をとってきました。しかしながら、RS-232Cを半二重であるRS-485に変換するときに、(1)の用法のRTSを利用してトランシーバーの方向を切り替えるという使い方が現在でもあるのです。
ためしにXP上のVC++2010で簡単なプログラムを組んで実験したところ、ちゃんと(1)のRTS_CONTROL_TOGGLEが機能しました。下図は、9600ボーで80バイト送信したときのRTSとTxDです。RTS立ち下がりタイミングもほぼ一定でした。
// 実験に使ったテキトーなプログラムのソース #include <stdio.h> #include <tchar.h> #include <windows.h> int main(void) { HANDLE comPort = CreateFile(_T("COM1"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); DCB dcb; GetCommState(comPort, &dcb); // 現在の設定 dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; dcb.fOutxCtsFlow = FALSE; dcb.fRtsControl = RTS_CONTROL_TOGGLE; // RTSは送信時に立てる SetCommState(comPort, &dcb); // 設定を変更 while(1){ // 80バイト送信 const char* sentData = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; DWORD lengthOfSent = 80; DWORD numberOfPut; WriteFile(comPort, sentData, lengthOfSent, &numberOfPut, NULL); // 1秒ウェイト Sleep(1000); } CloseHandle(comPort); system("pause"); return 0; }