SNSへはこちら

簡単USBマイコン EZ-USB FX2LP(2) - USB仕様を読む&簡易的な通信プログラムの実装

参考サイト

今回以降、以下のサイトを参考にさせていただきます。むしろこっちのほうが詳しいまである。

で、今回は

前回の記事では、EZ-USB の開発環境を整えることをしました。

本記事ではデフォルトの USB 仕様を見ることをしたいと思います。
そのためにはディスクリプタを読むのが不可欠ですよね。Cypress 公式のリファレンスマニュアルを読んでみましょう。以下は Appendix A. に記載されています。

デバイスディスクリプタ

結論から言うと、何も情報が無いです。Device-specific と規定されているので、つまりこれは一般的なデバイスでは無いことを意味します。つまり独自仕様というわけですよ。

インターフェイスディスクリプタ

こちらは1つのインターフェイスで構成されています。さらにその中で Alternate Settings が2つ存在します。

Alternate Setting 0

詳細は省略。ただ EP0 を主張しているだけです。

Alternate Setting 1

ここで EP0 以外のエンドポイントについて記述されます。詳細を見ていきましょう。

エンドポイント6つ分の設定があるようですね。

EP 番号 方向 トランザクションの種類 EP アドレス
1 OUT Bulk 0x01
1 IN Bulk 0x81
2 OUT Bulk 0x02
4 OUT Bulk 0x04
6 IN Bulk 0x86
8 IN Bulk 0x88

その他 Alternate Setting

省略。上と同じです。

以上、設定でした

以上は High-Speed でも Full-Speed でも共通なようです。そして、エンドポイントの設定はこれに見合うようにデフォルトでペリフェラルは設定済みのよう。いいですね。
そういえば何でこんなに用意周到に出来ているのかと言うと、言い忘れましたが EZ-USB は CPU よりも USB ペリフェラルのほうが偉い(もはやペリフェラルと呼ぶべきではない?)ので、要求に対応できるようになっていると考えられます。

プログラムを書いてみよう

では、とりあえず EP6 で通信させてみましょう。

#include "fx2regs.h"
#include <string.h>

#define SYNCDELAY __asm \
    nop\
    nop\
    nop\
    __endasm

int main(void) {
    CPUCS = 0x10;
    SYNCDELAY;
    while(1) {
        if( !(EP6CS & (1 << 3)) ) {
            const char mes[] = "Hello";
            for(int i = 0; i < strlen(mes); i++) {
                EP6FIFOBUF[i] = mes[i];
            }
            SYNCDELAY;
            EP6BCH = 0;
            SYNCDELAY;
            EP6BCL = strlen(mes);
        }
    }
}

ビルド。

$ sdcc -mmcs51 --std-c99 --opt-code-size -I../include main.c
$ cycfx2prog prg:main.ihx run delay:10 dbulk:6,-64,1
Using ID 04b4:8613 on 020.021.
Putting 8051 into reset.
Programming 8051 using "usb_test.ihx".
Putting 8051 out of reset.
Delay: 10 msec
Reading <=64 bytes from EP adr 0x86
  0x0000 48656c6c6f                                                             Hello
make: *** [flash] Segmentation fault: 11

いい感じっぽいですね。逆に EP2 等にデータを送信しようとしても、ディスクリプタで定められている EP2 の通信方向が OUT なので「方向がおかしい」というようなエラーを吐いて失敗します。

コード解説

簡単に行きます。

CPUCS = 0x10;
SYNCDELAY;

ここでは CPU クロックのスピードを 48MHz にしています。まあ気分ですねここは。
SYNCDELAY は、レジスタに値を書き込んだ後に必要な待ち時間を稼ぐための記述です。読み書きの間にこのディレイが必要になるレジスタがありますが、対象のものはユーザーマニュアルにあります。まあ面倒なんで取り敢えず脳死で全てディレイ入れればいいでしょう。
実体はコード上部のマクロで nop しているだけですね。どのくらい待てば良いのかはユーザーマニュアルの 15.15 をご参照あれ。

if( !(EP6CS & (1 << 3)) ) {
    const char mes[] = "Hello";
    for(int i = 0; i < strlen(mes); i++) {
        EP6FIFOBUF[i] = mes[i];
    }
    SYNCDELAY;
    EP6BCH = 0;
    SYNCDELAY;
    EP6BCL = strlen(mes);
}

if 文内部では、EP6CS レジスタの2bit目を見ています。このビット名は FULL といって、IN 方向の転送ではビジー状態を指すそうです。すなわち、エンドポイントの転送がホストから要求されていないということを指します。
なので if 文内部は、ホストから転送要求が出た時に処理されます。

EP に備え付けられている FIFO は各要素にアクセスが可能で、全てメモリ空間上にマッピングされています。なのでコード例のように EP6FIFOBUF のインデックスを指定してデータをコピーできます。

そして最後に転送データサイズを指定して処理が終わりです。EP6BCH / EP6BCL で、その長さを16bitで指定します。

今回はこんな感じです。
これまでに見たことのないような構造をしたマイコンなので(言い訳)、取り敢えず手探りでやりながらという感じで行っています。正直ここまで出来たら後は通常のマイコンと同じような気がするのですが、継続して調べて色々試していきたいかもです。