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などの制御もできるだろう.