RX62NにUSB CDCをゼロから実装してみた

# はじめに
RX62NボードにUSB CDCのファンクション機能を実装してみました。
e2studioの場合は、FITを使えば便利ですが、それでは勉強にならないので、レジスタレベルでゼロから実装してみました。
このレジスタレベルからと言うところが投稿者のこだわりです。
この記事は、そのときの覚書です。
RX62Nは結構古いMCUですが、USBに関しては最近のRXマイコンもほぼ同じです。
余談ですが、RX62N搭載ボードは[秋月電子](https://akizukidenshi.com/catalog/c/crx/)で買えます。
なお、USB自体の説明は、投稿者の理解がまだ十分ではないので、割愛します。
## 仕様
|項目|仕様|備考|
|:-:|:-:|:-:|
|ターゲットボード|BlueBoard-RX62N 100pin|NGX Technologies製|
|MCU|R5F562N8BDFP|ROM:512kB,RAM:96kB|
|開発環境|e2stuido v2022.01||
|RTOS|使用しない||
|ツールチェイン|GCC for Renesa RX <font color="blue">8.3.0.29.201904</font>|バージョンに注意※1|
|エミュレータ|E1エミュレータ||
|その他のツール|usbview||

※1,8.3.0.29.201904以降のバージョンでは、optlibが使えなくなりました。
替わりのnewlibはoptlibに比べると数値計算が圧倒的に遅い。
## テストプロジェクト
TeraTermで入力した文字をエコーバックするだけの簡単なプログラムです。
ソースコード１式は、[VLX](http:esoftjapan.com)のダウンロードサイトにあります。どなたでも無料でダウンロードできます。
## 文献
1.RX62Nグループ、RX621グループ ユーザーズマニュアル　ハードウェア編
2.Universal Serial Bus Specification Revision 2.0
3.Universal Serial Bus Class Definitions for Communications Devices Revision 1.2 (Errata 1) 
4.Universal Serial Bus Communications Class Subclass Specification for PSTN Devices  Revision 1.2
# 苦労したところ
## RX62Nのマニュアルについて、反省を込めて
USB自体の理解が十分ではない状態でRX62のハードウエアマニュアル（文献1）を読んでも良く理解できません。
特に、RX62Nのハードウエア（USBモジュール）がやってくれるところと、
ユーザがプログラムで行うべきか所の区別が理解できていないままコーディングを行うと、たいていハマります。
今回は、ファンクション機能だけの実装だったので、ホスト機能の箇所は読み飛ばしていましたが、
ハードウエアマニュアルの28章の全体を熟読すると理解が深まります。これは、反省です。
## 用語
USBの世界ではホストが中心なので、INと言う場合、デバイスからホストへのデータ転送、
また、OUTはホストからデバイスへのデータ転送を意味します。
USB業界では常識かもしれませんが、初心者にとっては重要です。
## 割り込み
プログラムの全体構成は、簡単な初期化の後、実質的な処理は、割り込み処理ルーチンの中で行うことになります。
RX62NのUSB関連の割り込みは、各USBモジュールに対して、USBI、D0FIFO、D1FIFO、USBRと4種類があります。
最初は、違いが良く分かりませんでしたが、（文献1）の表28.15（1331ページ）に書いてありました。
本書では、DMAは使用しないのでUSBI0割り込みだけを実装すれば良いことがわかります。
## ディスクリプタの書き方
ディスクリプタの基本的な構造は、最初にディスクリプタ全体のバイトサイズを書き、
次にディスクリプタのタイプ、その後にディスクリプタ固有の記述が続きます。
DeviceディスクリプタとConfigurationディスクリプタは必須です。
文字列ディスクリプタはなくても大丈夫です。
CDC ACMデバイスの場合は、このクラス固有のディスクリプタが必要になります。
Configurationディスクリプタの中にまとめて書けば良さそうです。
CDC ACM用のディスクリプタの記述例は、ネット上で探せばたくさん見つかります。
## ベンダーID
USBのベンダーIDの取得は、[USB-IF](https://www.usb.org/)の会員になるか、または購入する必要があります。
いずれも有償で結構高いです。
0x6666がプロトタイプ用として広く使われているようですが、
USB-IFが公式に認めているかどうかは不明です。
本プロジェクトでもこれを使用していますが、問題があれば変更してください。
## Interruptエンドポイントは必要か?
CDC ACMは、コミュニケーションインターフェースとデータインターフェースの2種類のインターフェースを実装する必要があります。
コミュニケーションインターフェースは１個のInterruptエンドポイント、
データインターフェースは入力用と出力用にそれぞれBulkエンドポイントを割りつけます。
Interruptエンドポイントの方は、特に何もしないので、削除してもよさそうに思えますが、
これが無いとデバイス自体が、アクティブになりません。
Interrupt入力要求に対しては、ゼロ長のパケットを返すようにしています。
## ホストリクエストに対する応答
コントロール転送フェーズでは、ホストPCからさまざまリクエストが来ます。
CTRT割り込みの中で処理するのですが、SET_ADDRESS以外のリクエストに対しては、きちんと応答する必要があります。
### 64バイト以下のデータを返す場合
```
for (int i=0; i<packet_size; i++) {
	USB0.CFIFO.BYTE.L = *_buf;
	_buf += 1;
}
USB0.DCPCTR.BIT.PID = PID_BUF;
USB0.D0FIFOCTR.BIT.BVAL = 1; 
```
### 64バイト以上のデータを返す場合
```
for (int i=0; i<64; i++) {
	USB0.CFIFO.BYTE.L = *_buf;
	_buf += 1;
}
//PIPE0のBEMP割り込みを有効にする、残りはBEMP割り込みで行う
_remaining = nbyte-MAX_PACKETSIZE;
USB0.BEMPENB.BIT.PIPE0BEMPE = 1;
```
### トランザクションを終了する場合
```
USB0.DCPCTR.BIT.PID = PID_BUF;
USB0.DCPCTR.BIT.CCPL = 1;
```
## デバイス状態がサスペンドになってしまう
開発を始めた頃のお話です。
デバイス状態がアドレス状態にはなるものの、しばらくするとサスペンド状態になってしまいます。
原因は、CTRT割り込みの中でSTALLを返していたところがありました。
STALLを送信すると、USBモジュールはサスペンド状態になるんですね。
## TeraTermが接続状態になるのが遅い
TeraTermを起動したとき、接続状態になるのが異常に遅いという現象がありました。
コントール転送フェーズでパイプ0のBRDY割り込みに応答しないとこのようになるようです。
## BRDY割り込みが来ない
TeraTermから文字を入力するとBulk OutによるBRDY割り込みが発生すると思っていましたが、
割り込みが来ません。下記の２つの原因がありました。
### パイプの設定タイミング
パイプを設定するタイミングが重要です。最初のころは、初期化時に行っていました。
usbviewで見ると、USBシリアルデバイスとして正常に認識されており、コントロール転送はうまくいっているように見えます。
しかし、TeraTermの画面でキーボードから文字を入力しても、Bulk Outによる割り込み（BRDY）が発生しません。
この原因は、ホストPCからのリセット信号（D+とD-がVOL以下）によりデバイスがPowered状態からDefault状態になるとき、パイプの設定もリセットされるからです。
このため、パイプの初期化は、デバイスがDefault状態になった後で行う必要があります。
これについては、USB 2.0の規格書にもRX62Nのハードウエアマニュアルにもちゃんと書いてありました。
### PIDの設定
INTENB0やBRDYENB0の対応するビットを１に設定するだけでは、Bulk Out割り込みは発生しません。
これには、あらかじめBulk Out用パイプのPIDをBUF応答に設定しておく必要があります。
これは、次にパケットを受信したときに、ACKを返すと言う意味らしいです。
Bulk In用パイプの場合は、ホストに送信する必要が生じた時点で行えば良さそうです。
RX62のBUF応答の意味の理解が不十分でした。
## usbview
usbviewは、PCに搭載されているUSBデバイスの情報を表示するツールです。
詳細は[マイクロソフトのサイト](https://learn.microsoft.com/ja-jp/windows-hardware/drivers/debugger/usbview)を見てください。下記は、usbviewの出力です。
```
[Port11]  :  USB シリアル デバイス

Is Port User Connectable:	yes
Is Port Debug Capable:		no
Companion Port Number:		20
Companion Hub Symbolic Link Name: USB#ROOT_HUB30#4&28aeab80&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
Protocols Supported:
 USB 1.1:                         yes
 USB 2.0:                         yes
 USB 3.0:                         no

Device Power State:               PowerDeviceD0

       ---===>Device Information<===---
English product name: "RX62NPCDC"

ConnectionStatus:                  
Current Config Value:            0x01  -> Device Bus Speed: Full (is not SuperSpeed or higher capable)
Device Address:                  0x07
Open Pipes:                           

          ===>Device Descriptor<===
bLength:                         0x12
bDescriptorType:                 0x01
bcdUSB:                          0x0200
bDeviceClass:                    0x02  -> This is a Communication Device
bDeviceSubClass:                 0x00
bDeviceProtocol:                 0x00
bMaxPacketSize0:                 0x40 = (64) Bytes
idVendor:                        0x6666 = Vendor ID not listed with USB.org
idProduct:                       0x0001
bcdDevice:                       0x0000
iManufacturer:                   0x01
     English (United States)     "eSoft"
iProduct:                        0x02
     English (United States)     "RX62NPCDC"
iSerialNumber:                   0x03
     English (United States)     "001"
bNumConfigurations:              0x01

          ---===>Open Pipes<===---

          ===>Endpoint Descriptor<===
bLength:                         0x07
bDescriptorType:                 0x05
bEndpointAddress:                0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                    0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0010 = 0x10 bytes
bInterval:                       0xFF

          ===>Endpoint Descriptor<===
bLength:                         0x07
bDescriptorType:                 0x05
bEndpointAddress:                0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                    0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                       0x00

          ===>Endpoint Descriptor<===
bLength:                         0x07
bDescriptorType:                 0x05
bEndpointAddress:                0x02  -> Direction: OUT - EndpointID: 2
bmAttributes:                    0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0040 = 0x40 bytes
bInterval:                       0x00

       ---===>Full Configuration Descriptor<===---

          ===>Configuration Descriptor<===
bLength:                         0x09
bDescriptorType:                 0x02
wTotalLength:                    0x0043  -> Validated
bNumInterfaces:                  0x02
bConfigurationValue:             0x01
iConfiguration:                  0x04
     English (United States)     "config1"
bmAttributes:                    0x80  -> Bus Powered
MaxPower:                        0x19 =  50 mA

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x01
bInterfaceClass:                   0x02  -> This is Communications (CDC Control) USB Device Interface Class
bInterfaceSubClass:                0x02
bInterfaceProtocol:                0x00
iInterface:                        0x00
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 00 10 01 
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 06 00 01 
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x04
bDescriptorType:                   0x24
04 24 02 02 
  -> This is a Communications (CDC Control) USB Device Interface Class

          ===>Descriptor Hex Dump<===
bLength:                           0x05
bDescriptorType:                   0x24
05 24 01 03 01 

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x83  -> Direction: IN - EndpointID: 3
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                    0x0010 = 0x10 bytes
bInterval:                         0xFF

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x00
bNumEndpoints:                     0x02
bInterfaceClass:                   0x0A  -> This is a CDC Data USB Device Interface Class
bInterfaceSubClass:                0x00
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                    0x0040 = 0x40 bytes
bInterval:                         0x00

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 2
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                    0x0040 = 0x40 bytes
bInterval:                         0x00
```
# おわりに
最後まで読んでくれてありがとうございます。ご感想等などあれば、どんなことでも結構です。[Mail](http://esoftjapan.com/VLX/support/jp_support.php)を下さい。