要約
MAX3420Eによって、汎用マイクロコントローラを使用したUSB周辺機器の設計が簡単に行えるようになりました。このアプリケーションノートでは、MAX3420Eを紹介した後、このデバイスのSPIインタフェースに焦点を当てます。このアプリケーションノートは、ハードワイヤードSPIユニットを使用してデバイスにアクセスする方法、あるいは汎用I/Oピンをビットバンギングしてデバイスにアクセスする方法を示しています。また、MAXQ2000マイクロコントローラを使用したUSB設計のためのサンプルCコードを示しています。
はじめに
以前のユニバーサルシリアルバス(USB)に関するアプリケーションノートでは、USBがパーソナルコンピュータの新しい接続規格として有効であるという説明から始める必要がありましたが、ありがたいことにもはやその必要性はなく、この序文は短くてすみます。要するに、組み込み型のシステムをPCに接続したい場合の主流のルートはUSBであるということです。
マキシム製の新しいチップMAX3420Eを使えば、あらゆるシステムにUSBを簡単に追加することができるようになります。このアプリケーションノートでは、MAX3420Eの内蔵SPI(シリアル周辺機器インタフェース)インタフェースに焦点を当て、汎用SPI実装のためのサンプルCコードを紹介します。また最後に、簡単なUSB HID(ヒューマンインタフェースデバイス)であるWindowsベースの非常ボタンのためのコードを示します。
あらゆるシステムにUSBを追加
マイクロコントローラ(µC)の選択は、ほとんどの場合、組み込む周辺機器に基づいて行われます。USB機能を備えたプロセッサもありますが、ほとんどの場合、特にかなり低コストのバージョンではUSB機能を備えていません。I/Oと周辺機器が万全な組み合わせのマイクロコントローラを選択したもののUSBが欠如していたということはありませんか? また、USBを追加することで既存の開発ツールを継続して使用したいということはありませんか?
今回、マキシムの新しいMAX3420Eを使用することで、いずれのマイクロコントローラにもUSBを追加することができるようになりました。このチップは、USBのフルスピードトランシーバ、インテリジェントなUSBシリアルインタフェースエンジン(SIE)、および最大26MHzのSCLKクロック信号で動作可能なSPIスレーブインタフェースを提供します。MAX3420Eは、1つのコントロールエンドポイント、ダブルバッファ構造の2つの64バイトデータエンドポイント、および1つの64バイト割込みエンドポイントを備えたフルスピードのUSB周辺機器として動作します。
バスパワー駆動のウィジェット
図1は、一般的なUSB周辺機器のアーキテクチャを示しています。USB VBUSワイヤが5V電源を3.3Vレギュレータに供給し、これによってマイクロコントローラとMAX3420Eに電力を供給しています。外付け電源は不要です。SPIインタフェースは、3線、4線、または5線で構成することが可能です。表1は、フル5ピンのインタフェースを示しています。
Signal | MAX3420E Direction | Description |
MOSI | In | SPI master out, slave in |
MISO | Out | SPI master in, slave out |
SCLK | In | Serial clock |
SS# | In | Slave select |
INT | Out | Interrupt (level or pulse) |
アプリケーションが割込みを要求しない場合(MAX3420Eのすべての割込み条件は、レジスタビットを読み取ることでじかにテストすることが可能です)、INTピンをなくして4ピンのインタフェースが得られます。SPIマスタに双方向のデータインタフェース(同じ双方向のピン上にMOSI/MISO)がある場合、さらに別のピンによってインタフェースを削減することができます。したがって、割込みをサポートせず、双方向のデータピンがあるSPIインタフェースでは、3つのピンだけを使用します。
マイクロコントローラにSPIポートがない場合は、どうすればよいのでしょうか? 問題ありません。汎用I/Oピンをじかに切り替えることによって、ファームウェア駆動のSPIマスタを簡単に作ることができます。USBの強みは、セルフスロットル方式であるということです。つまり、SPI側のいずれのインタフェース速度にも自動的に対応するということです(これはUSB側のNAKハンドシェイクを使用して、「ビジー状態です。再試行してください」を指示することによって実行されます)。多くのUSB周辺機器、特に人間に接続される機器は、最も遅いSPIインタフェースであっても極めて敏感に動作することが可能です。
図1のマイクロコントローラがかなり小型で、10ピン以下の場合はどうでしょうか? USBチップにデータ送信するだけのために、これらの貴重なI/Oピンをすべて使用する必要があるのでしょうか? 答えは「イエス」です。これがまさに、MAX3420Eが4つの汎用入力と4つの汎用出力を提供している理由です。簡単に言えば、MAX3420Eには8つの汎用I/Oがあるため、データ送信に必要なピンに取って代わった後も、MAX3420Eによって今より多くのI/Oが追加されます。したがって、MAX3420Eを接続すれば、実際には今より多くのI/Oピンがシステムに備わることになります。
大きなチップ
MAX3420Eは、小さなシステムに制限されているわけではありません。図2は、大きなASIC、FPGA、DSP、およびその他の大きなチップにUSB機能を追加する方法を説明しています。大きなチップに追加することの明らかな理由は、大きなチップにはUSBが組み込まれていない場合があるということ、あるいは内部USBが必ずしも必要なものではないということが挙げられます。さらに、このアーキテクチャに当てはまるもう1つの理由は、大きなチップは、プロセスが微細化されていくにしたがい、USBで必要な3.3Vという「高」電圧を生み出すことがより難しくなるということです。低電圧のSPIインタフェースを備えた外付けのUSBチップは、これら設計課題に対する最適なソリューションとなります。低電圧インタフェースを動作するために、MAX3420Eには内部レベルシフタとVLピンが備わっており、SPIインタフェースの動作電圧を1.7V~3.6V間の任意の値に設定することができます。
絶縁型USB
上の図3に示すように、SPIインタフェースは光アイソレーションを配置しやすい場所にあります。これは、SPI信号が単方向であり、低コストの光カプラに対応した低周波数での動作が可能なためです。
SPIインタフェース
SPIは、2つのデータライン、シリアルクロック、およびチップセレクト信号を使用する簡単なシリアルインタフェースです。SPIマスタは、SS#をローに落として転送を開始し、次にシリアルクロックSCLKを駆動し、データはこのクロックに同期して、スレーブデバイスに対して同時に入出力されます。SPIマスタは、SS#をハイに戻すことで転送を終了します。
SPIインタフェースには4つのクロックモードがあり、CPOL(クロック極性)およびCPHA(クロックフェーズ)と呼ばれる2つのモード信号を反映しています。これらの信号は、(CPOL, CPHA)という書式で表現されます。インタフェースは、正エッジのSCKSとMOSIの両方のデータが、最初の正のクロックエッジの前に有効になることを想定しており、変更することなくモード(0,0)と(1,1)で動作可能です。この特性によって、MAX3420Eは、モードピンを必要とすることなく、これらのモードのいずれでも動作することができるようになります。
図4および図5は、マイクロコントローラ(後述するMAXQ2000)とMAX3420E間での同一データの転送を示しています。図4は、SPIモード(0,0)を使用し、図5はSPIモード(1,1)を使用しています。違いは、SCLK信号の非アクティブレベルであり、モード(0,0)ではローが、モード(1,1)ではハイが非アクティブレベルになります。
MAX3420Eは、コマンドバイトをすべての転送の最初のバイトとして受け取ります。コマンドバイトには、レジスタ番号と方向ビットが含まれます。2番目以降のバイトにはデータが含まれます。図4および図5で、コマンドバイトが同期入力される間に(MOSIピン)、MAX3420E(MISOピン)から入力される8ビットはUSBのステータスビットであり、コマンドバイトが同期入力されるたびに利用可能になります。この機能は、独立したデータピンMISOとMOSIを使用するインタフェースでのみ有効です。
SPIコード
MAX3420E用に汎用Cコードを記述するときの鍵は、別のモジュールで最小限のSPI動作を分離して、SPIインタフェース間にわたるこのモジュールだけをカスタマイズするということです。このモジュールでは、少なくとも以下の3つが必要となります。
- Initialize_SPI(SPIの初期化)
- Read byte(バイトの読取り)
- Write byte(バイトの書込み)
ここに挙げたアプリケーション例は、ハードウェアSPIユニットを使用しています。このようなユニットを持たないアプリケーションのために、最初に、ビットバンギングしたSPIインタフェースのための汎用Cコードについて取り上げます。
ビットバンギングしたSPI
Init SPI(SPIの初期化)
Initialize_SPI関数は、プロセッサ間にわたるほとんどの設定を変更します。この関数は、インタフェースが使用する特定のI/Oピンの割当て、方向の設定、さらに初期条件SS = 1とSCLK = 0の設定を担っています(モード(0,0)のSPIマスタを生成します)。
Read Register(レジスタの読取り)、Write Register(レジスタの書込み)
rregは、MAX3420Eレジスタを読み取るC関数です。マクロ(全部大文字)は、さまざまなマイクロコントローラの各I/O方式から関数を分離しています。マクロを使用することでコードが読みやすくなり、またプロセッサに依存しなくなります。wregは、MAX3420Eレジスタを書き込むルーチンです。
プロセッサを変更した場合、これらのルーチンを使用する少数のマクロを変更するだけで済みます。例として、以下のマクロは、ハードウェアSPIユニットが含まれていないマイクロコントローラ用のマクロです。
#define SCLK_HI OUTA = PINSA | 0x02; #define SCLK_LO OUTA = PINSA & 0xFD; #define SS_HI OUTA = PINSA | 0x04; #define SS_LO OUTA = PINSA & 0xFB; #define MOSI(v) OUTA = (PINSA & 0x7F) | (v & 0x80); #define MISO inval |= PINSA & 0x01; BYTE rreg(BYTE r) // Read a register, return its value. { int j; BYTE bv,inval; inval = 0; SS_LO bv = r<<3; // Left-shift the reg number, WRITE=0 for (j=0; j<8; j++) // send the register number and direction bit { MOSI(bv) // put out a bit bv <<= 1; // shift one bit left SCLK_HI SCLK_LO } for (j=0; j<7; j++) // get 7 bits and shift left into 'inval' { SCLK_HI MISO inval <<= 1; // shift in one bit SCLK_LO } SCLK_HI // one more bit, but don't shift 'inval' this time MISO SCLK_LO SS_HI return inval; // return the byte we read in } void wreg(BYTE r,BYTE v) // register, value { int j; BYTE bv; SS_LO bv = (r<<3)+2; // Left-shift the reg number, set the WRITE direction bit for (j=0; j<8; j++) // send the register number and direction bit { MOSI(bv) // put out a bit bv <<= 1; // shift one bit left SCLK_HI SCLK_LO } for (j=0; j<8; j++) // send the register data { MOSI(v) // put out a bit v <<= 1; // shift one bit left SCLK_HI SCLK_LO } SS_HI }
ハードウェアSPI
この項では、前述したMAXQ2000マイクロコントローラについて説明します。簡単に言うと、MAXQ2000はファミリの中でも低電力、16ビット、高性能の、主要なRISCプロセッサです。MAXQ2000の「Q」は、「quiet」を表し、このアーキテクチャが高感度のアナログ回路と適切に共存するように設計されていることを示しています。MAXQ2000はSPIポートが内蔵されており、特にMAX3420Eとの使用に適しています。以下の例では、MAXQ2000開発キットとMAX3420Eを使用して、簡潔で魅力あるWindowsウィジェットを構築しています。
MAXQ2000ハードウェアSPIユニットには、SCLK、MOSI、およびMISOが用意されていますがSS#はありません。SS#は多様な動作を示すため(たとえば、1つのバイトにアクセスする場合と多量のバイトにアクセスする場合の違い)、SS#には汎用I/Oピンを使用することをお勧めします。
MAXQのI/Oセル
図6は、基本的なMAXQ I/Oセルを示しています。I/Oポートのビットには、「port.bit」の形式でラベルが付けられています。ここで「p」はポート、「b」はビットです。この例では、I/Oポート5でビット3を取り上げています(ピンのラベルはP53になります)。
あらゆるI/Oセルにはフリップフロップがあり、この例ではPO5.3と呼ばれるビットを使用して書き込まれます。「O」は出力を表します。このフリップフロップには、いつでも書き込むことができます。フリップフロップがそのピンに接続されるかどうかは、方向ビットに依存しています。出力ピンを構成する場合には、グリッチを回避するため、ピンに接続する前にフリップフロップに書き込むことをお勧めします。
P53ピンの方向はPD5.3と呼ばれるビットで設定します。「D」は方向を表し、D信号はピンドライバの出力イネーブルとして機能します。1 = 駆動、0 = フロートになります。ピンの状態は、PI5.3と呼ばれるビットでいつでも読み取ることができます。ここで「I」は入力を表します。ピンの駆動方法(内部フリップフロップ(PD5.3 = 1)によるものか、あるいは他の外部フリップフロップ(PD5.3 = 0)によるものか)に関係なく、PIビットはピンの状態を示します。
この構造には、注目すべき機能があります。P53ピンが入力(PD5.3 = 0)として構成されている場合、フリップフロップの出力は出力として使用されないため、プルアップ抵抗のスイッチとして再利用することができます。D = 0のとき、O信号は、図6の点線とスイッチで示すように「プルアップ抵抗の接続」を表すように再定義されます。
一部のI/Oピンは、図6の下側のブロックに示すように、割込み機能を備えています。割込みブロックには、以下の3つの信号があります。
- フラグビット―割込みが要求されたときにセットされ、CPUによってリセットされます。
- エッジ選択ビット -正負いずれの信号の遷移が割込み要求を生じるのかを決定します。
- 特定のピンに対する割込みイネーブルビット。
このアプリケーション例では、正エッジで作動する割込み用にMAX3420EのINT出力ピンを構成します。MAXQ2000側では、MAXQ2000の割込みシステムを使用するのではなく、コードによって割込みのフリップフロップをじかにテストし、待ち状態のUSB割込みの有無を確認します。このプログラムは、押しボタンの状態を確認してUSBの要求に応答するだけであるため、ポーリングのループだけが必要となります。
Init SPI(SPIの初期化)
MAXQ2000のI/Oピンは、汎用I/Oと特殊機能のハードウェア(SPIユニットなど)で共有されています。特殊ハードウェアを使用するため、最初にハードウェアブロックを構成し、次にこのブロックをイネーブルにしてI/Oピンに接続することができるようにしています。以下に示すSPI_Init()ルーチンは、ピン方向を設定し、SPIインタフェースを設定してから、最後にこれをイネーブルにしています。
void SPI_Init(void) { // MAXQ2000 SPI port CKCN = 0x00; // system clock divisor is 1 SS_HI // SS# high PD5 |= 0x070; // Set SPI output pins (SS, SCLK, DOUT) as output. PD5 &= ~0x080; // Set SPI input pin (DIN) as input. SPICK = 0x00; // fastest SPI clock--div by 2 SPICF = 0x00; // mode(0,0), 8 bit data SPICN_bit.MSTM = 1; // Set Q2000 as the master. SPICN_bit.SPIEN = 1; // Enable SPI // MAX3420E INT pin is tied to MAXQ2000 P60; make it an input PD6 &= ~0x01; // PD6.0=0 (turn off output) }
Read Register(レジスタの読取り)、Write Register(レジスタの書込み)
以下に示す関数は、MAXQ2000のハードウェアSPIユニットを利用しているため、ビットバンギングのユニットよりも小さくて高速になります。
// Read a MAX3420E register, return its value. BYTE rreg(BYTE reg) { BYTE dum; SS_LO SPIB = reg<<3; // reg number w. dir=0 (IN) while(SPICN_bit.STBY); // loop if data still being sent dum = SPIB; // read and toss the input byte SPIB=0x00; // data is don't care, we're clocking in MISO bits while(SPICN_bit.STBY); // loop if data still being sent SS_HI return(SPIB); } // Write a MAX3420E register. void wreg(BYTE reg, BYTE dat) { SS_LO // Set SS# low SPIB = (reg<<3)+2; // send reg. number w. DIR bit (b1) set to WRITE while(SPICN_bit.STBY); // loop if data still being sent SPIB = dat; // send the data while(SPICN_bit.STBY); // loop if data still being sent SS_HI // set SS# high }
例:Windowsベースの非常ボタン
このUSB装置は、WindowsをベースとしたUSB HID(ヒューマンインタフェースデバイス)であり、ひとつの「非常(panic)」ボタンが備わっています。このボタンを押すと、すべてのアクティブなPCウィンドウが最小化され、デスクトップ画面が現れます。再び押すと、すべてのアプリケーションウィンドウが元の大きさに戻ります。
USBキーボードには興味深い機能が備わっています。キーボードをいくつか接続すると、すべてが同時に有効になります。このため、この非常ボタンは、標準のキーボードと連携して動作します。
PCがサスペンド状態にある場合、非常ボタンは新たな役割を果たします。つまり、PCのリモートウェイクアップボタンとして機能します。この動作は、PCがUSBからのウェイクアップに対応しているかどうかによって大きく異なります。対応しているPCもあれば、そうでないPCもあります。このボタンは、PCがこの機能を備えているかどうかを判断するのに役立ちます。
このコード例は、小さなUSBドーターボード(MAX3420Eを含む)を拡張コネクタに接続した状態のMAXQ2000開発キット上で動作します。
USBの詳細
このアプリケーションは、エニュメレーションの基本作業を行うUSBボイラープレートコードを備えています。このデバイスの特性は、このアプリケーションノートの末尾に添付したPanic_Button_Enum_Data.hの文字配列によって完全に記述されています。
このアプリケーションは2つのエンドポイントを使用しています。必須のCONTROLエンドポイントゼロとEP3-IN(シングルバッファ構造の64バイトエンドポイント)です。MAX3420Eには、ダブルバッファ構造の2つの64バイトエンドポイント(EP1-OUTとEP2-IN)がありますが、ダブルバッファ機能によるスループットの利点は、このアプリケーションでは必要ありません。
一般的なHIDについての誤解は、HIDデバイスが低速度でしか動作しないと思われている点です。このアプリケーションは、キーボードのように遅い機器であってもフルスピードで動作させることによって利点が得られることを実証しています。これは、キーボードが、1.5MHzではなく12MHzのパケットを送信することで、使用するバス帯域幅が少なくなることから明らかです。
割込みエンドポイントにはポーリング間隔があります。これは、USBホストがINエンドポイントにデータを要求する頻度を決定するものです。すべての間隔で、ホストがデバイスのエンドポイント3にIN要求を送信することを想定しています。図7は、これらの要求を処理する簡単なステートマシンを示しています。デバイスのエニュメレーションがいったん開始されると、マイクロコントローラはこのルーチンを繰り返し実行します。簡単にするため、このアプリケーションでは、アクティビティの有無について割込みピンをポーリングしています。他の作業をマイクロコントローラで動作させる場合、割込みに応答してDo_IN3関数を呼び出すことも可能です。
ステートマシンは2つのグローバル変数(stateおよびbutton)を使用しています。Cのマクロによって3つの状態(IDLE、RELEASE、およびWAIT)を定義しています。state変数は、IDLEに初期設定されます。変数buttonは、MAX3420EのGPIN0ピンに接続された押しボタンが押されるとハイに、押されていなければローになります。main()の無限ループは、ボタンチェックタイマをインクリメントし、期限切れになるとMAX3420E内のGPIOレジスタを読み取ってボタンの状態を判断します。これによって不要なSPIトラフィックが省かれます。
ボタンが稼動している間、状態図は左側の2つの分岐をたどり、何も実行しません。IDLE状態の間にボタンが押されると、この時点で、アクティブウィンドウをクリアするためのキーコードが送信されます。これは、08(Windowsキー) 00(予約済み) 07(文字d)のシーケンスです。次の状態がRELEASEに設定されて動作が完了します。
MAX3420Eは、このパケットをUSB上に送出すると直ちに、もう1つのEP3-IN割込み要求を生成します。これは、EP3-IN FIFOが再びデータの読込みに利用可能になったことを示します。図7の関数が再び実行されます。今回は、state = RELEASEであるため、関数は00 00 00のシーケンスを送信します。これは「キーが稼動している」ことを表します。次の状態はWAITに設定されます。これは「ボタンの解放待ち」を意味します。
この時点で関数は、WAIT状態の分岐を使用してボタンの解放を検知するだけです。ボタンが下がったままの状態では何も行われません。ボタンが解放されると、状態図は右側の2つの分岐をたどり、state変数を再度IDLEに初期設定して、次のボタンの押下に備えて関数を準備します。
ほとんど常時実行されるコードは極めて短いコードです。以下は、関数全体で、図7のフローチャートを実現しています。
void Do_IN3(void) { switch(state) { case IDLE: if (button) { wreg(rEP3INFIFO,0x08); // "Windows" prefix key wreg(rEP3INFIFO,0); wreg(rEP3INFIFO,0x07); // "D" key wreg(rEP3INBC,3); // arm it state = RELEASE; // next state sends the "keys up" code } break; // else do nothing (and the SIE will NAK) // case RELEASE: { wreg(rEP3INFIFO,0x00); // key up wreg(rEP3INFIFO,0x00); wreg(rEP3INFIFO,0x00); // key up wreg(rEP3INBC,3); // arm it state = WAIT; // next state waits for the PB to be unpressed } break; case WAIT: if (!button) state = IDLE; break; default: state = IDLE; } // end switch }
コード情報について
コードで補足すべき情報についていくつか説明します。
時間が重視されるUSBイベント
MAX3420Eは、10msの間、バスを「K」状態に駆動することによってリモートウェイクアップ信号を送出します。この時間をカウントする負担からSPIマスタを解放するため、MAX3420Eは内部的にこの信号(および、実際には、時間に依存する他のすべてのUSBイベント)の時間を測定し、間隔値に達すると、SPIマスタに割込みをかけます。SPIマスタは、これらのイベントに専用のタイマを使用する必要はありません。単に操作を開始して完了割込みを待つだけです。
ACKSTATビット
関数rregASおよびwregASは、rregとwregとは異なる内容を1つ実行します。つまり、SPIコマンドバイトのACK STATUSビットをセットします。SPIマスタ(この例ではMAXQ2000)は、このビットを使用し、現在のCONTROL転送サービスが終了したことをMAX3420Eに伝え、さらに、ステータス段にACKを送信することによってCONTROL転送を終了します。ACKSTATは、内部レジスタビットとして存在しますが、SPIコマンドバイトにこれを含めることによって、頻繁に使用されるこの動作の実行速度が速くなり、また使用するコードが少なくなります。
readbytes()およびwritebytes()関数
readbytes()とwritebytes()関数は、MAX3420Eのバースト機能を利用しています。バイトのアクセスごとに2つのSPIバイト(コマンドバイトとデータバイト)を送信するのではなく、最初にSS#をローにしてからコマンドバイトを送信し、次にバーストバイトを同期入力および同期出力し、最後にSS#をハイにしてSPI転送を終了します。
製品IDの検索場所
製品ID(PID)の文字列(Panic_Button_Enum_Data.h内)は、非常ボタンを初めて接続したときに短いメッセージで表示されます。このID文字列は、非常ボタンをHIDと識別するエニュメレーションプロセス時にポップアップ表示され、組み込みのWindowsドライバに関連付けされます。
その後の接続では、USBデバイスを接続したときに小さく鳴る「ba-deep」というWindowsのサウンドを除いて無音になります。デバイスの状態を確認したい場合は、図8に示す画面に進みます。この画面を表示するには、[マイコンピュータ]を右クリックし、[プロパティ]の[ハードウェア]タブ内の[デバイスマネージャ]ボタンを選択し、[ヒューマンインターフェイスデバイス]の項を開いてUSBヒューマンインタフェースデバイスを右クリックしプロパティを選択します。
USBの準拠性
コードを見ておそらく、「これは1つのボタンしかないUSBデバイスにしては作業が多い」と思われることでしょう。これは事実です。いずれのUSBデバイスにも、一定のオーバヘッドを伴うからです。ありがたいことに、USBは、極めて入念に仕様が定められているため、いずれのUSBデバイスについても、このエニュメレーションコードをテンプレートとして利用することができます(コピー/貼り付けなどを使用)。
勤勉な開発者はすべて、USB-IFによって設計が認定されることを望んでいます。認定されることによって、すべてのPC上で問題なく機能することが保証されるようになります。このアプリケーションは、USB-IFのウェブサイト上で開発者が入手可能なテストスイートにある、USB Command Verifier(USBCVバージョン1.2.1.0)とHIDのテストに合格しています。以下の図9は、この非常ボタンの採点表です。
結論
USB周辺機器を作成することが必要な場合には、MAX3420Eの使用を検討してください。このデバイスは小型でプログラムが容易、さらに無料サンプルコードが付属しています。MAX3420Eを使うと、設計にI/Oピンが追加され、SPIに対応するいずれのシステムにおいても良好に機能します。SPIは容易にビットバングすることができるため、この設計はすべてのマイクロコントローラに対応可能です。より高性能を望むのであれば、26MHzという高速なクロックでSPIインタフェースを動作させることもできます。
この記事に類似した内容が、2005年7月号の『Circuit Cellar』誌に掲載されています。