USB-パラレル変換ケーブルの制御
昔のPCにはプリンターポートがあり,プリンターを使う以外にも,データ転送など様々な用途に転用することができた. 私も,WindowsでIOポートを叩いたり,Linuxでparportを使って制御することによって,GPIBをエミュレートして装置を制御したりしたことがある. しかし,最近のPCにはUSBポートぐらいしか無い. USB-パラレル変換ケーブルは,プリンターポートの代わりに使えるので,原理的にはそのケーブルによって,同様に装置の制御ができるはずである. しかし,USB-パラレル変換ケーブルを制御する方法が分からなかった. USBについて多少は詳しくなったので,変換ケーブルの制御の仕方が分かるのでは無いかと思い,いろいろと試してみた.
家にあった変換ケーブルをUSBポートに挿すと,/dev/usb/lp0ができた. lsusb -vで調べてみると,interfaceが一つあって,そのsettingが3つあり,そのうちの2つはprinter classでUnidirectionalとBidirectionalであり,もう1つがvendor specificであることが分かった. USBのprinter classについて調べてみると,最後の1つはプリンターポートの規格であるIEEE 1284に対応していると推定でき,その制御の仕方を理解することが重要であることが分かった.
vendor specificの場合には,その制御方法はvendorが決めるのであるが,今回はIEEE 1284という規格を実現するためのものなので,おそらく共通の仕様になっていると予想した. いろいろなICの仕様を調べたら,control transferを使った制御の方法が分かってきた. それを手元にある変換ケーブルに試してみたら,多少の試行錯誤の後,うまく動かすことができた.
まず,usblpとしてシステムに使われていると,こちらからの制御は出来ないので,これを開放する. /etc/modprobe.d/blacklist.confにusblpをblacklistに加えて再起動すると,変換ケーブルを制御できる状態になる. あとはlibusbを使ってアクセスすれば良い. 基本的な手順としては,interface0のsetting2にして,auto modeをoffにしてから,双方向通信で制御信号も自由に扱えるBidirectionalモードにしたら,様々な制御が可能になる.
参考までに,rubyで書いたUSB-パラレル変換ケーブルを制御するプログラムは以下の通りである.
require "libusb" class UsbLp def initialize() usb = LIBUSB::Context.new device = usb.devices(idVendor: 0x0598, idProduct: 0x1001).first @handle=device.open @handle.claim_interface(0) @handle.set_interface_alt_setting(0,2) @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: 0x07f8, wIndex: 0x0000) # SET_1284_REGISTER IC control register to auto mode off @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: 0x0623, wIndex: 0x0000) # SET_1284_REGISTER extended control register to bidirectional status() end # 36pin attr_accessor :strobe # No.1, output, 0:5V,1:0V attr_accessor :data # No.2-9, in/out 0:0V,1:5V attr_reader :ack # No.10, input, 0:0V,1:5V attr_reader :busy # No.11, input, 0:5V,1:0V attr_reader :pe # No.12, input, 0:0V,1:5V attr_reader :slct # No.13, input, 0:0V,1:5V attr_accessor :autofxinit # No.14, output, 0:5V,1:0V attr_accessor :init # No.31, output, 0:0V,1:5V attr_reader :error # No.32, input, 0:0V,1:5V attr_accessor :slctin # No.36, output, 0:5V,1:0V # GND 16,19-30 def status() reg=@handle.control_transfer(bmRequestType: 0xc0, bRequest: 3, wValue: 0x0000, wIndex: 0x0000, dataIn: 7) # GET_1284_REGISTER sttsreg=reg[0].ord @error=sttsreg[3] #nFault @slct=sttsreg[4] #Select @pe=sttsreg[5] #PaperError @ack=sttsreg[6] #nAck @busy=sttsreg[7] #nBusy @ctrlreg=reg[1].ord @strobe=@ctrlreg[0] #Strobe @autofxinit=@ctrlreg[1] #AutoFd @init=@ctrlreg[2] #nInit @slctin=@ctrlreg[3] #SelectIn @data=reg[4].ord [@error,@slct,@pe,@ack,@busy] end def command() @ctrlreg=(@ctrlreg&0xf0)+@strobe+@autofxinit*2+@init*4+@slctin*8 @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: 0x0200+@ctrlreg, wIndex: 0x0000) # SET_1284_REGISTER control register [@strobe,@autofxinit,@init,@slctin] end def write(d=@data) @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: 0x0200+(@ctrlreg&~0x20), wIndex: 0x0000) # SET_1284_REGISTER control register to output @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: d, wIndex: 0x0000) # SET_1284_REGISTER data register @data=d end def read() @handle.control_transfer(bmRequestType: 0x40, bRequest: 4, wValue: 0x0200+(@ctrlreg|0x20), wIndex: 0x0000) # SET_1284_REGISTER control register to input status() @data end end #class
制御線については,statusで入力信号を読み取って,commandで出力信号を出し,データ線はreadとwriteで扱う. ここで,vendor idやproduct idはケーブルに合わせて変更して欲しい. 0が5Vのときと,1が5Vのときがあるので,注意して欲しい. 手持ちの変換ケーブルで動くことを確認しただけなので,IEEE 1284に対応したすべてのもので動くかは分からないが,動く可能性が高いのでは無いかと考えている. また,同様のプログラムを書けば,他の言語で制御することも可能であろう.
ioctlで制御できないかも試してみたのだが,rubyからはうまく行かなかった. Windowsでのusplpの解放に相当する操作のやり方は調べていないが,それができればUSBを直接叩くやり方が分かったので,windowsでも同様に制御できるはずである.
マイコンを使ってUSB機器を作ることもできるが,それを敷居が高いと感じる場合には,USB-パラレル変換ケーブルを使うという選択もあるのでは無いかと思う. 昔はそれなりの値段がしたようだが,最近では安いものは千円程度で入手できる. 変換ケーブルとコネクタを容易すれば,入出力8ピン,入力5ピン,出力4ピンを使うことができる. 例えば,この手法を使えば,以前作ったプリンターポート-GPIB変換と組み合わせれば,USB-プリンターポート-GPIB変換が可能になるだろう. 出力は5Vで,マイコンと同程度の電流は出ると思うので,LEDなどの制御もできるだろう.