コレは何?
RTAIでD/A変換ボードを使うための解説です。 RTAIでモータ,ロボットを制御するためにDACボードは必須です。 ここでは,Interface社製の PCI-3340 (8ch 16bit) をRTAI上で動作させることを考えます。 本節では PCI-3340 について述べますが,他の型番についても少しいじれば応用可能と思われます。

D/A変換ボードの外観
ハードウェア/ソフトウェア構成例
使用したパソコンのハードウェアおよびソフトウェア構成は以下の通りです。
ハードウェア構成:Pentium4 + 845G + Intel EtherExpressPro + PCI-3340上記で動作を確認しています。
ソフトウェア構成:kernel-2.6.23 + Slackware-12.2 + rtai-3.7
PCI-3340
PCI-3340はインターフェース社が出荷しているD/Aコンバータボードであり, 一般的なPCのPCIに刺すことで,自作プログラムから好きな電圧を自由に出力できるようになります。 このDACによってモータの電流指令値もしくはトルク指令値を与えてモータの制御が行えます。 実際の制御には位置センサや速度センサ,加速度センサ等々も必要ですので, A/Dコンバータボードやエンコーダカウンタボードも一緒に使うことになりますが,これらについては別の項目で述べます。
アドレスを調べる
まずPCIバスに刺したPCI-3340がどの番地に居るのか,「scanpci」若しくは「lspci」を使って調べます。
scanpci -v上のを打ち込むと以下のような画面が出てきます。(scanpciがなければlspci)

scanpci -v の実行結果
刺さってるデバイスごとにずらずら上記の結果が出てくるはずです。 その中で,黄色い線が引いてある部分に注目して, Interface Corp,vendor ID 0x1147, device ID 0x0d0c のものを探します。 0x1147はインターフェース社を意味し,0x0d0cはPCI-3340を意味しています。(0x0d0cを10進数にするとなんと3340!)
ここで,BASE0の欄のaddrの後の数字(緑で囲った部分)がベースアドレスなので,これをどこかにメモします。 このアドレスを基にしてin/out関数で叩いていきます。
DACsettings関数 (D/A変換ボード設定用)
D/A変換ボードの初期設定用関数を以下に置いておきます。 この関数は PCI-3340用です。 そのまま使えるはずです。 他の型番のD/A変換ボードでもちょっとした改造で使えると思われますが,IF社のI/O公開資料を参照してください。 これを参考にして好きなように改良,新造してください。
設定後に100μsの待機がありますが,これはIO公開資料に「出力レンジを設定した後に、最低100μsのウェイトが必要です。」 と書いてあるのでusleepが挿入してあります。 (とはいうものの,各々のチャネルのレンジの設定後に待機するのか?全チャネルのレンジの設定後に待機するのか?よく分かりません…)void DACsettings(unsigned int BASE0){ // DACの設定を行う関数 iopl(3); // I/O全アドレス空間にアクセス許可 //------- DAC 設定 // DAC チャネル 1 outb(0x00,BASE0+0x07); // CH1 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 2 outb(0x01,BASE0+0x07); // CH2 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 3 outb(0x02,BASE0+0x07); // CH3 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 4 outb(0x03,BASE0+0x07); // CH4 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 5 outb(0x04,BASE0+0x07); // CH5 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 6 outb(0x05,BASE0+0x07); // CH6 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 7 outb(0x06,BASE0+0x07); // CH7 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 // DAC チャネル 8 outb(0x07,BASE0+0x07); // CH8 設定 outb(0x03,BASE0+0x06); // 出力電圧範囲を±10Vに設定 usleep(100); // 設定後に必要な100μsの待機 outb(0x03,BASE0+0x05); // 全チャネル同時出力設定 return; }
DACout関数 (電圧出力用)
D/A変換ボードから指定した電圧を出力する関数を下に置いておきます。
ここで「IIbyteHi」と「IIbyteLo」の二つの関数がありますが,これは2バイトデータの上位および下位1バイト分を抽出して出力する関数です。 この関数は以下のように実装します。void DACout(unsigned int BASE0, unsigned short DACdata[8]){ // DACから指定した電圧を出力する関数 //------- DAC 出力 // DAC チャネル 1 outb(0x00,BASE0+0x02); // CH1 設定 outb(IIbyteLo(DACdata[0]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[0]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 2 outb(0x01,BASE0+0x02); // CH2 設定 outb(IIbyteLo(DACdata[1]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[1]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 3 outb(0x02,BASE0+0x02); // CH3 設定 outb(IIbyteLo(DACdata[2]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[2]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 4 outb(0x03,BASE0+0x02); // CH4 設定 outb(IIbyteLo(DACdata[3]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[3]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 5 outb(0x04,BASE0+0x02); // CH5 設定 outb(IIbyteLo(DACdata[4]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[4]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 6 outb(0x05,BASE0+0x02); // CH6 設定 outb(IIbyteLo(DACdata[5]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[5]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 7 outb(0x06,BASE0+0x02); // CH7 設定 outb(IIbyteLo(DACdata[6]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[6]),BASE0+0x01); // DAC出力 上位 // DAC チャネル 8 outb(0x07,BASE0+0x02); // CH8 設定 outb(IIbyteLo(DACdata[7]),BASE0+0x00); // DAC出力 下位 outb(IIbyteHi(DACdata[7]),BASE0+0x01); // DAC出力 上位 outb(0x01,BASE0+0x05); // 全チャネル同時出力実行 return; }
この関数で上位と下位に分けてからDACに放り込んでいます。unsigned short IIbyteLo(unsigned short in){ // 2byteデータの下位1byteを抽出して出力 return 0x00FF & in; } unsigned short IIbyteHi(unsigned short in){ // 2byteデータの上位1byteを抽出して出力 return 0x00FF & (in >> 8); }
使い方
上記の関数の実際の使い方について説明します。 まずscanpciで調べたBASE0の値をどこかに定義しておきます。 次にRTAIの制御ループに入る前の部分で,DACsettings関数を実行します。 その次に,制御ループの中でDACout関数で所望の電圧を出力します。 ということで以下のような感じになります。
電圧は 0x0000で-10V,0xFFFFで+10Vになります。 なので実際に使うときは換算ゲインを掛けましょう。 また,下位の制御系(例えばどこかのメーカ製のサーボアンプ/ドライバ)で電流制御やトルク制御を行っている場合は, 電流指令値やトルク指令値から出力電圧値に変換する必要もあります。 この換算ゲインについては他で説明したいと思います。 それからioplとin/out関数を使うにはroot権限が必要なので注意して下さい。#define BASE0 ここにscanpciの値 RTAI実時間スレッド{ unsigned short data[8]={0}; DACsettings(BASE0); // DACの設定 while(1){ なにかの制御プログラム data[0]=CH1の所望の電圧 data[1]=CH2の所望の電圧 data[2]=CH3の所望の電圧 ・ ・ ・ DACout(BASE0, data); // 電圧出力実行 } }
ということで,上記の手法によりRTAI上でも非常に簡単にDACが使えるようになります。
コーディング例
具体的なコーディング例は,ARCS のソースコードを参照して下さい。 C++のクラスにする場合は,こんな感じにしておくと便利です。 (2012/01/25追記)
研究室の横の倉庫 - Side Warehouse of Laboratory
Copyright(C), Side Warehouse, All rights reserved.