20120405

なんちゃってオシロスコープ

久しぶりのUSB-1608FSネタ。

前回LEDを点滅させた頃には、実は既に取り込み自体は出来てたんですよ。
SnoopyProの結果から「使用チャンネル: 0番、サンプリングレート: 1Hz、レンジ: 10V」で取り込む場合には、色々省略すると
USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x43, 0x01])
USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x11, 0x00, 0x00, 0xc0, 0x03, 0x10, 0x00, 0x08, 0x95, 0x98, 0x04])
取り込みストップ、レンジ設定、チャンネル・サンプリングレート設定?した後に
USB1608FS.read(0x83, size, interface=1)
ただ、これで返ってくる値は
array('B', [238, 141, 240, 141, 240, 141, 239, 141, 239, 141, 239, 141, 239, 141, 239, 141, 239, 141, 239, 141, 240, 141, 87, 138, 203, 4, 117, 7, 171, 8, 248, 8, 129, 9, 42, 10, 161, 10, 219, 10, 99, 11, 33, 14, 0, 14, 159, 14, 9, 14, 133, 13, 99, 13, 170, 10, 143, 10, 8, 11, 11, 11, 134, 1])
例えばこんな感じで、どれが目的の値なのか分からない。
というのも、今回用いているUSB-1608FSは解像度?量子化レベル数?が16bitなもんで、2進数なら16桁、16進数なら4桁、10進数なら…まあ桁で表現しづらいですが0-65535の値を取り得る訳ですよ。
どうみても過剰な要素数(=64)の値が返ってきてるんですよ。


ここでSnoopyProに戻ると、値は異なりますが、先程の値に対応しそうな結果はこちら。


どうやら、個々の要素は16進数の2桁分みたい。
ということは、2個の要素をくっつければ1個分になるのではないかと。
それでも32個分のデータがあるわけですが…アナログ8チャンネル+デジタル8チャンネル…で、あ〜まだまだ余りますね。
よく分からないので、この点に関してはまた追々。


さてPythonの方に話を戻すと、目指すは、10進数*2要素→8bitの2進数*2要素に変換・結合→10進数(0-65535)→電位(V)。
10→2進数変換はbinを用いていい感じのヤツ作りました(下記のソースコードのdec2bin参照)。
2→10進数変換はPythonが勝手にやってくれる(0b10とか入力すると2とみなして処理してくれる)ので気にする必要はなし。
0-65535の値を電位に変換するにはサードパーティ製ドライバのソースコード(C言語)を参考に
Volt = (Decimal-32768)*10.0/32767
すると良さ気。
つまり32768(65535+1の半分)が0になるようにベースを調節してやって、レンジに合わせて1量子化レベルあたりの電位を掛けてやる、という具合。
図解するこんな具合。
公式の取扱説明書から拝借。


本当は、ソースコードでは更に補正値として1.17が掛けてあったのですが…まあとりあえず無視。


以上を踏まえ、サードパーティ製ドライバのソースコードを参考にしまくって作ってみたのが、タイトルにある「なんちゃってオシロスコープ」

usb1608fs_oscillo.py
import usb

def Initial():
     USB1608FS = usb.core.find(idVendor=0x09db, idProduct=0x007d)
     for i in range(7):
     if USB1608FS.is_kernel_driver_active(i):
          USB1608FS.detach_kernel_driver(i)
     USB1608FS.set_configuration()
     # ch0-0 1Hz 1sample 10V
     USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x43, 0x01])
     USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
     USB1608FS.ctrl_transfer(0x21, 0x09, 0x0, 0x0, [0x11, 0x00, 0x00, 0xc0, 0x03, 0x10, 0x00, 0x08, 0x95, 0x98, 0x04])
     return USB1608FS

def dec2bin(decimal, digits):
     binary = bin(decimal)[2:]
     if len(binary) < digits:
          binary = '0'*(digits-len(binary))+binary
     return binary

USB1608FS = Initial()

i = 0
while True:
     PIPE = i%6+1
     O = USB1608FS.read(0x80+PIPE+2, 0x40, interface=PIPE, timeout=1000+100)
     V = (int(dec2bin(O[1],8)+dec2bin(O[0],8),2)-0x8000)*10.0/0x7fff
     Vdisp = int(round(V*5.0+50))

     print ' '*(Vdisp-1)+'|'

     i = i+1


取り込みで沢山返ってくる値の内の第1、第2要素だけを抜き出してきてます。
ただ、他の要素も大体連動して同じような値になるので、結局のところどの要素に着目すればいいのやら…
チャンネル数を増やして、別々の値を掛けて…とかすれば分かるのでしょうけれども…それもまた追々。


matplotlibとか使って見栄えよく作ろうとしたんですけどね…アニメーション化の方法分からない…
でも、これはこれでシンプルでいいですよね?
16bitを全く活かせていない、7bit程度で十分な感じですけど。
ただ、こうなると、横スクロールさせたくなってしまいます。