ＲＸマイコンで実現するＵＳＢホスト（ＵＳＢメモリー編）

## はじめに

ＲＸマイコンにはＵＳＢ機能が備わっているデバイスが多数あります。

|タイプ|USB 2.0 FS|USB 2.0 HS|備考|
-------+------+------+----
|RX24T|✖|✖||
|RX66T|△|✖|144MHz 駆動|
|RX64M|〇|〇||
|RX71M|〇|〇||
|RX65N|〇|✖||
|RX72T|△|✖|192MHz 駆動|
|RX72M|〇|✖||

※「△」内蔵タイプと非内蔵タイプに分かれる
※ USB FS: フルスピード（12MBPS）
※ USB HS: ハイスピード（480MBPS）

---

「ホスト」にも「ペリフェラル」（クライアント）にもなる事が出来ます。
ルネサスのサンプルソースコードはそれなりに枯れているものの、自分のような環境だと、簡単に動かす事が出来ません。
※ルネサス製のコンパイラでコンパイルする前提で実装されているので自由度が少ないです。

- コンパイラ環境（gcc）を自分で構築して、独自のフレームワークを使っている場合ソースを修正する必要があります。
- 最終的には、C++ で実装したいと思っています。
- ルネサスの USB フレームワークは、ホストか、ペリフェラルかコンパイルオプションになっており、同居が出来ません。
- USB マスストレージクラスは、ルネサス社製の fatfs を使う事が前提となっていて、利便性が悪いです。   
※「r_tfat_rx」と「r_tfat_driver_rx」モジュールを使う前提です。
- 自分は、ChaN 氏の FatFs のソースコードを使いたいし、最新のバージョンで動かしたいです。

色々と注文はあるけどー、最低限の修正を行い、手っ取り早く動かしてみました。

動作環境は、RX65N Envision Kit で、USB のホスト機能として、電源制御 IC とコネクタが実装済みとなっているので直ぐに使えます。
コンパイラは、gcc-6.4.0 を RX マイコン用にビルドしたもの。
参照：[gcc、g++を使ったルネサスＲＸマイコン開発](https://qiita.com/hira_kuni_45/items/a890e9ec613b88ca9a5f)

![IMG_1531.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/38ea2ce1-6894-41c0-50e7-336ddfad82cc.jpeg)

## Renesas USB BASIC(DRIVER)/HMSC の問題点

- 基本的に C++ から API を呼び出す宣言がされていない。
- 「やっぱり」と言うか、「const」が適切に運用されていない。
- #define を多用しており、良く使うようなキーワード（たとえば「USB」）が定義されている為、「当たる」。
- ホストとペリフェラル（クライアント）の同居が出来ない。
- ハードウェアーの依存をシンプルに解決する仕組みがあまり用意されていない。（割り込み制御など）
- 救いは、USB レジスタアクセスなどで、iodefine.h によるビットフィールドを利用していない為、コンパイラの依存が少ない。   
※ビットフィールドの操作は、バイトサイズ以外は規約違反で、処理系により動作が異なります。   
※USB レジスタのビット操作では、冗長だが、レジスタアクセス毎に API が実装されています。
- ドキュメントは比較的多く、利用方法に関する情報はそれなりに多いです。   
※ネットの記事は少ないように思います。

再利用を考えると、できれば、オリジナルコードは編集したくないが、上記の理由などから、修正は必要でした。

## gcc-6.4.0 でのビルド設定と要点

- 「#pragma」を消す。（#pragma は方言なので、本来コンパイラのテリトリーで囲む必要がある）
- 「__evenaccess」キーワードを無効にする。（このキーワードは、ルネサスのコンパイラ固有？）
- 「当たる」定義を変更する。   
※自分のフレームワークでは、USB などのキーワードは、名前空間の奥で定義されているが、#define 定義されており、仕方なくキーワードを変更。
- C++ から呼び出せるように、ヘッダーのプロトタイプ宣言に「extern "C"」を追加。
- C++ と当たる「bool、true、false」の #define があるので、避ける。
- 割り込み関係の操作 API をコメントアウトして外部で再定義。   
※割り込み関係など「r_usb_rx_mcu.c」では、ルネサスの BSP モジュールなどに依存しているので、自分のシステムに合わない。
- 今回は「実験」なので、DMA/DTC を使いませんでした。
- 「r_usb_basic_config.h」を適切に設定します。   
※このファイルは、フレームワークの動作を設定するファイルで、適切に設定する必要があります、ルネサスのドキュメントに詳しく書いてあります。   
- RX65N の場合、割り込みは、選択型割り込みＢグループで、USB0/USBI0(62) を定義する必要があります。
- 割り込みハンドラは「usb_hstd_usb_handler();」です。
- 割り込みレベルは、標準では「3」になっているようです。 
- USB クロックは、48MHz を供給する必要があるので、インターナルクロックを 240MHz にして、1/5 に設定する必要があります。   
※これらの設定は、自分のフレームワークでは、system_io クラスで自動で行われます。
- 最適化は「-O3」で行いました。

C ソースコードの警告を除くオプション

```
CC_OPT      =   -Wall -Werror \
                -Wno-unused-variable -Wno-uninitialized \
                -Wno-unused-function \
                -Wno-maybe-uninitialized \
                -Wno-unused-but-set-variable \
                -Wno-strict-aliasing \
                -Wno-sizeof-pointer-memaccess \
                -Wno-array-bounds \
                -fno-exceptions
```

割り込みなどのハンドラ（USB のインスタンスはメイン側にあるので、そこで定義）

```
    usb_err_t usb_module_start (uint8_t ip_type)
    {
        usb_io_.enable_mod();
        return USB_SUCCESS;
    }

    usb_err_t usb_module_stop (uint8_t ip_type)
    {
        usb_io_.enable_mod(false);
        return USB_SUCCESS;
    }

    void usb_cpu_usbint_init (uint8_t ip_type)
    {
        usb_io_.init_intr();
    }

    void usb_cpu_int_enable (usb_utr_t *ptr)
    {
        usb_io_.enable_intr();
    }

    void usb_cpu_int_disable (usb_utr_t *ptr)
    {
        usb_io_.enable_intr(false);
    }

    void usb_cpu_delay_1us (uint16_t time)
    {
        utils::delay::micro_second(time);
    }

    void usb_cpu_delay_xms (uint16_t time)
    {
        utils::delay::milli_second(time);
    }
```
※上記のように再定義して取り込んだ。（ハードウェアーの依存が高い API は上記のみだった）
※DMA、DTC を使う場合は、その関係レジスター操作をどうにかする必要がある。

## USB マスストレージと FatFs の関係

- ff13c を使用。
- USB HMSC は、USB メモリに対して低レベル API を提供します。
- それは、単純なセクター単位の R/W に集約されます。
- なので、それと FatFs の API を繋ぐだけです。
- USB はイベント駆動なので、イベントを監視するループを設けます。   
※今回は 1/100 秒毎に監視する仕組み。
- 「R_USB_GetEvent」API を使って変化するイベントのステートを監視します。
- USB メモリを接続すると「USB_STS_CONFIGURED」が返ります。
- この応答に対して、FatFs の f_mount を呼び出します。
- そうすると、FatFs はセクター単位で、ファイルシステムの情報を読み出します。   
※f_mount のパラメーターにより初期動作が異なります。
- なので、「disk_read、disk_write、disk_ioctrl」などを実装しておくだけです。   
※ioctrl は、特殊な操作なので、無くても問題無いと思えます。（今回は、「空」の状態です。）   
- USB メモリを抜くと「USB_STS_DETACH」が返るので、unmount の処理を実行してリソースを開放します。

```
    auto event = R_USB_GetEvent(&usb_.at_ctrl()); /* Get event code */
    switch (event) {
    case USB_STS_CONFIGURED:
        format("USB: CONFIGURED\n");
        {
            auto st = f_mount(&fatfs_, "", 1);
            if(st == FR_OK) mount_ = true;
            else mount_ = false;
        }
        break;

    case USB_STS_DETACH:
        format("USB: DETACH\n");
        f_mount(&fatfs_, "", 0);
        mount_ = false;
        break;

    default:
        break;
    }
```

## 実際の動作

RX65N Envision Kit には液晶がありますが、「実験」なので、シリアル入出力を接続して、TeraTerm でコマンド形式で制御しています。
※実用的な応用はこれからです・・

```
RTK5_USB sample start
USB0: enable_mod (1)...
USB0: init_intr(Vec: 128, No: 62, Lvl: 3)...
R_USB_Open: (0)
USB Host start...
USB Event: 14
# USB Event: 3
USB: CONFIGURED
USB Event: 14
# dir
       128 Jan  8 2019 11:23  autorun.inf
           Mar 11 2019 23:46 /boot
    408074 Jan  8 2019 11:23  bootmgr
   1452856 Jan  8 2019 11:23  bootmgr.efi
           Mar 11 2019 23:47 /efi
     82440 Jan  8 2019 11:23  setup.exe
           Mar 11 2019 23:47 /sources
           Mar 11 2019 23:53 /support
           Mar 11 2019 23:05 /System Volume Information
Total 9 files
# USB Event: 6
USB: DETACH
USB Event: 14
USB Event: 3
USB: CONFIGURED
USB Event: 14
# free
1688844/1967116 [KB] (85.8%)
# USB Event: 6
USB: DETACH
USB Event: 14
```

8GB の USB メモリ、ディレクトリーを取ったところ。
※実験コードなので、イベントのメッセージが含まれている。

```
# help
    dir [xxx]       list current directory
    pwd             current directory path
    cd [xxx]        change current directory
    free            list disk space
    write filename      test for write
    read filename       test for read
    time [yyyy/mm/dd hh:mm[:ss]]   set date/time
# write test.bin
Write: 'test.bin'
Write Open:  17 [ms]
Write: 272 KBytes/Sec
Write Close: 7 [ms]
# read test.bin
Read: 'test.bin'
Read Open:  1 [ms]
Read: 268 KBytes/Sec
Read Close: 0 [ms]
```

2GB の USB メモリで計測した速度です。
※ DMA、DTC を使えば、多少改善するものと思います。

## 最後に

今まで、USB は面倒そうなので敬遠していました。
※大きな問題は、ソースコードが CC-RX コンパイラ依存が高い点です・・

ですが、やってみると、ルネサスのサンプルのおかげで、意外と簡単な事が判りました。
今後さらに進めて、USB キーボードなどを接続するとか色々やってみたいと思います。
また、FreeRTOS も対応しているので、試してみたいと思います。

ソースコードは、GitHub にコミットしてあります。

[RX65N Envision Kit/USB sample](https://github.com/hirakuni45/RX/tree/master/RTK5_USB)