はじめに
この記事は「Arduinoを使って学ぶ、Win/Macドライバ開発講座」のシリーズ初回の記事になります。
シリーズタイトルにもあるように、この講座ではArduinoを使ってWindowsおよびMacのドライバ開発の基礎を解説していきます。
今回のシリーズで使うArduinoはArduino Uno Rev3です(公式紹介ページ)
このデバイス用のドライバを作成して、専用アプリからドライバを通してArduinoを操作するのが今回の目的です。
なお、Arduino Uno Rev3をUSB接続した場合にCDCクラス(Communication Device Class)の標準ドライバがインストールされますが、本シリーズではUSBデバイスのドライバを開発するのが目的のためArduinoをCDCデバイスとしては扱わず、vendor-specificとみなして開発を行います。
開発対象
今回はWindowsとMacで同一の機能実現するためにそれぞれのOS用に以下を開発対象とします。
- Arudino用ドライバ
- デバイス操作用アプリ
実現する機能は以下です。
- アプリから値を送信し、その値によりArudino上のLEDの光の強さを変化させる。
目的はドライバの開発なのでArduinoの機能はシンプルにしてます。
WindowとMacの開発環境
以下の環境で開発を行いました。
Windows | Mac | |
---|---|---|
OS | Windows10 20H2 (OSビルド19042.985) | MacOS (11.4) |
開発環境 | Visual Studio 2019(16.8.4) | Xcode (12.5) |
フレームワーク | UMDF(User-Mode Driver Framework) | DriverKitフレームワーク (XCode 12.5) |
ソフトウェア開発キット | WDK(10.0.18346.1000) | - |
Arduinoデバイス情報
ドライバを設計する前に、開発対象になるデバイスがどのようなデバイスか調べる必要があります。
開発対象のArduino Uno Rev3がどのようなデバイスなのか知らないと、ドライバでなにを実装すればいいのか分かりません。
前述しましたが、Arduino Uno Rev3は、USB-CDC互換デバイスであるため、CDCの規格に則ったインターフェースとなっているはずです。
PC接続するとUSBデバイスとして認識されますので、実際にどういったインタフェースを持っているのかを確認してみようと思います。
デバイスのUSB情報をMicrosoftが配布しているUSBViewを使って見ていきましょう。
デバイスディスクリプタ
まずはじめにデバイスディスクリプタの情報です。
ドライバに必要となるものをいくつかピックアップします。
項目 | 説明 | 値 |
---|---|---|
bDeviceClass | デバイスクラス | 0x02 CDCクラス |
idVendor | ベンダーID | 0x2341 |
idProduct | プロダクトID | 0x0043 |
bNumConfigurations | コンフィグレーション数 | 0x01 |
bDeviceClassは0x02
であり、これはCDCクラスのデバイスを表しています。
CDCクラスについて、このブログが非常に分かり易いのでおすすめします。
idVendor:0x2341
とidProduct:0x0043
はドライバとデバイスのマッチングに必要な情報です。
この情報を使ってデバイスに適切なドライバが選択されます。
bNumConfigurationsは0x01
であり、コンフィグレーションを1つのみ持っていることを表しています。
次に、コンフィグレーションディスクリプタの情報を見てみましょう。
コンフィグレーションディスクリプタ
コンフィグレーションディスクリプタではインターフェースの数が分かります。
項目 | 説明 | 値 |
---|---|---|
bNumInterfaces | インターフェース数 | 0x02 |
Arduino Uno Rev3にはインターフェースが2つあるようです。
インターフェースの情報を見ていきましょう。
インターフェースディスクリプタ
インターフェースは2つあるため、インターフェースディスクリプターも2つあります。
主要な項目を以下にまとめます。
項目 | 説明 | インターフェース0 | インターフェース 1 |
---|---|---|---|
bInterfaceNumber | インターフェース番号 | 0x00 | 0x01 |
bNumEndpoints | エンドポイント数 | 0x01 | 0x02 |
bInterfaceClass | インターフェースクラス | 0x02 CDC Control | 0x0A CDC-Data |
1つ目のインターフェース(インターフェース0)はインターフェースクラスが0x02
であり、CDC Controlクラスのインターフェースということが分かりました。
こちらのインターフェースはデバイスの設定や管理を行うためのインターフェースですが、本講座ではCDCとしてのコントロールは行わないため、使用しません。
その代わりに、デバイスの初期設定などには、既定のエンドポイントであるEndpoint0を使用します。
既定エンドポイントについては、以下を参照ください。
"既定のエンドポイントについて"
2つ目のインターフェース(インターフェース1)はインターフェースクラスが0x0A
でありCDC-Dataクラスのインターフェースです。
このインターフェースを使ってデバイスとデータのやり取りができます。
エンドポイントが2つあり、このエンドポイントを使ってデバイスと通信ができます。
最後にインターフェース1のエンドポイントの情報を見てみましょう。
エンドポイントディスクリプタ
エンドポイント | 説明 | エンドポイント1 | エンドポイント2 |
---|---|---|---|
bmAttributes | 属性 | 0x02 Bulk | 0x02 Bulk |
bEndpointAddress | エンドポイントアドレス | 0x04 Out | 0x83 In |
wMaxPacketSize | 最大パケット長 | 0x0040 | 0x0040 |
属性は下位2ビットにて、転送方式を表します。
- 0x00:Control 転送
- 0x01:Isochronous 転送
- 0x10:Bulk 転送
- 0x11:Interrupt 転送
bEndpointAddressは最上位ビットで転送方向を表し、下位4ビットにてエンドポイントアドレスを表します。
- bit7:IN=1、OUT=0
- bit 3-0:エンドポイントアドレス
両方ともデータのやり取りを行うためのバルク転送用のエンドポイントがということが分かります。
エンドポイント1のアドレスは0x04
なため、バルクアウトです。
エンドポイント2のアドレスは0x83
なため、バルクインです。
この講座ではデバイスにデータを送るだけで受け取ることはないのでバルクアウトのエンドポイント1のみを使います。バルクインのエンドポイント2は使いません。
そのため、今回使うエンドポイントのアドレスは0x04
になります。
デバイスの初期化処理(CDCデバイスの設定)
USBデバイスは、一般的にドライバからデバイスに対して何らかの転送を行うことでデバイス側を初期化し、使用可能な状態になります。
vendor-specificのデバイスである場合は独自の初期化処理を定義することができますが、今回はArduinoを使用するため、初期化処理としてはCDCデバイスとしての初期化処理が必要となります。
CDCの初期化処理としては、SetLineCoding
とSetControlLineState
の2つのリクエストが最低限必要となります。
SetLineCoding
ではデバイスのシリアル通信を設定します。
SetControlLineState
はフロー制御の信号を設定します。
ドライバの開始時にこの2つのリクエストをコントロール転送でデバイスに送信することで、これ以降にバルク転送ができるようになります。
CDCのリクエストに関する細かい説明は省略しますが、以下のデータをコントロール転送することで初期化することが可能です。
リクエスト | bmRequestType | bRequest | wValue | wIndex | wLength | データ |
---|---|---|---|---|---|---|
SetLineCoding | 0x21 | 0x20 | 0 | 0 | 7 | {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x80} |
SetControlLineState | 0x21 | 0x22 | 0 | 0 | 0 | - |
ArduinoのCDCのリクエストについて興味のある方は、前述のブログやこの記事をご参考ください。
Arduino用コード
Arduinoに書き込むコードはこの記事で紹介しているソース(リンク)を基に、複数の種類のコマンドを処理できるように変更しました。
現時点ではLEDの光の調整コマンドしか実装していませんが、後のことを見越して拡張性を持たせました(gist)。
バルク転送で受け取ったデータの値によってArduino Uno Rev 3のPIN13に挿してあるLEDの光を調整します。
コマンド種別と値が格納されている構造体のデータを受け取って、コマンド種別を確認してLEDの光調整コマンドだった場合、LEDの光の強さを構造体の値に合わせます。
おわりに
デバイスの詳細を調べることでデバイスと通信するためにドライバで実装する機能が判明しました。
- コントロール転送でのCDCデバイス設定
- バルクアウト転送でのデータの送信
次回からはこの情報を基に早速ドライバの開発に取り掛かります。
参考資料
https://docs.arduino.cc/hardware/uno-rev3
https://android.serverbox.ch/?p=549
https://blog.oino.li/posts/usbcdc/
https://docs.microsoft.com/ja-jp/windows-hardware/drivers/usbcon/usb-control-transfer