さて、LEDの点滅(通称Lチカ)がrubyスクリプトで直接できるようになったので、さっそくSPIデバイスを制御してみます。ターゲットは、タイトルにもあるようにアナログデバイセズのDDS(Digital Direct Synthesizer)です。これを自由に制御できるようになれば、いろいろ応用が効くはずです。
SPIデバイスを制御するには、最低限度としてはクロックとデータの2本があれば可能ですが、デバイスによっては他にも必要な場合があります。AD9859の場合には、他にIOUPDATEやRESETなど数本の制御線が必要ですが、それでも8本もあれば十分余裕があります。
制御するボードですが、まずは単純にDDSのみを制御するということで、ハムスクエアさんで数年前に販売されていたAD9859を使用したキットであるDDSBASEを使用します。FT232RLを使って制御するというのはもともとこのDDSBASEがやっていた方法です。このハードウェアをそのまま使い、制御をRubyスクリプトで行うというのが今回の趣旨になります。
AD9859もそうですがSPIデバイスは、その内部に制御用のレジスタを持っており、そこに必要な値を書き込む必要があります(DDSの場合は読み出しは不要です)。とはいえ、SPIの基本はクロック毎にビットをセットしていくことですから、まずはこれをコード化していきます。
ボードによって違いがありますから、ピンアサインはカスタマイズができるよう。また、コンフィギュレーションはたくさんのフラグやビットフィールドで構成されていますが、2進や16進の定数を書いてもわかりにくいので、これを設定するコードを容易に書けるようにしました。デフォルトでOKなものは書かずにすませて、明確にすべき値はその名称で意味がわかりやすくなるようにします。
現状のコードはこちらです。dds.rb
BitBangモードのインターフェースを複数のデバイスで共用できるようBitBangクラスに分離し、SPI関連の機能はSPIDeviceクラスに、AD9859固有の機能については、AD9859というSPIDeviceのサブクラスにしてみました。また複数のDDSを利用できるように留意しています。
SPIの制御ですが、ビットの制御ごとにポートへの出力を行うと時間がかかりますので、バッファリングをして、最後に一気に流し込むようにしています。これにより書き込みそのものは瞬時に終わります。ただし、最初にデバイスをオープンしたりするのに若干の時間を要します。
実際に利用する場合は、以下のようなコードを書きます。
require 'dds'
pins = { :sdio => 4, :sclk => 0, :ioupdate => 2, :iosync => 6, :reset => 7 }
mul = 16
fclk = 400e6
freq = 123.456e6
dds = AD9859.new(:pins => pins, :fclk => fclk, :refclk\_multiplier => mul)
dds.open
begin
dds.reset
dds.setup
dds.flush
dds.set\_frequency(freq)
dds.ioupdate
ensure
dds.close
end
コードは順に、ピン接続とBitBangのポートの対応の設定を定義から始まり、クリスタルは25MHzなので倍率を16とし、システムクロックを400MHzとなります。この設定でDDSのインスタンスを生成し、デバイスをオープン。DDSの初期化を行い、周波数を123.456MHzに設定して、レジスタの設定を動作に反映させるためにioupdateを行って完了です。
これをコマンドにするとこんな感じです。ddsbase.rb
コマンドライン引数として周波数などを与えて、下記のようにして動かします。数値お尻についているe6は指数表記で10^6すなわちM(メガ)の意味です。
$ ./ddsbase.rb 123.456e6
AD9859は振幅も設定可能なので、dBでレベルを設定可能にしてみました。
$ ./ddsbase.rb -- 10e6 -10
このように、周波数と振幅を即座にコマンドで設定することができます。
SPIの波形を採ってみました。コンフィギュレーションに2.5ms、周波数設定に1.5ms要しています。それぞれ、72bit, 40bit分ですのでSPIとしておよそ36us/bit、28kbps程度となります。
上記はデフォルトの状態でしたが、BitBangの速度そのものはbaudrateプロパティで250kbps程度まで上げることが可能です。さらに途中のバッファフラッシュも止めて一気に書き込んだときの様子です。今度はDDSの出力も同時に取ってみました。今度は250usec程度で書き込みできています。またAD9859のクロック逓倍のPLLが100usec程度で立ち上がっている様子がわかります。出力回路の周波数特性が平坦でないため周波数の立ち上がりで振幅が変化しています。
一応動画です。順に10MHz, 20MHz, 30MHz, -10dB, 100MHz, 1MHz, 10MHzと設定しています。
コマンドだけではなく、プログラムに組み込んだり、GUIを付けたりすることももちろん可能です。(自分的趣味の問題で、GUIよりコマンドラインのほうが便利なのでここではGUIの例は示しません)
単に周波数を設定するだけではなく、時間とともに周波数を変化させて(階段状に)スイープさせるなども、ちょこっと書き足せばすぐにできてしまいます。
さて、rubyでさくっとDDSを制御できるようになりました。次はgnuradioと組み合わせて使うために、同じことをpythonで実装してみたいと思います。
使用したDDSBASEボードは残念なことに既に販売されておらず、また出力回路の部分に弱点があるので、ちょっと使用方法に制限がありますが、何かしら試してみたいところです。
(追記)DDSBASEで添付されていたのは、FT232RLではなくFT245RLでした。いずれもBitBangモードで使用できるのですが、若干ピン接続が異なります。勘違いしてFT232RLでの結果を書いてしまいましたが、もしFT245RLを使用される場合はこの点をご留意下さい。FT245RLモジュールの方が素直なピン配列になっています。
- ハムスクエア https://hamshop.jpn.org/catalog/
- DDSBASE https://zao.jp/radio/dds-base/index.php
- アナログデバイセズAD9859 https://www.analog.com/jp/digital-to-analog-converters/direct-digital-synthesis-dds/ad9859/products/product.html
- AD9859紹介ビデオ https://videos.analog.com/video/japanese/1138085577001/AD9859-400MSPS10–/
- 実は数年前にrubyではなくJavaで試していたことがあります。その時はFTD2XX用のJavaバインディングを自作していました。https://github.com/edy555/ftd2xxj