So-net無料ブログ作成
検索選択

F-RAM の RAMTRON を買ってきたので FX3 に接続してみる [USB]このエントリーを含むはてなブックマーク#

FX3 DVK と F-RAM を接続

F-RAM というのを入手したので、 FX3SPI FLASH の代わりにできないかと、接続してみました。

F-RAM

F-RAM

使用した F-RAM は、 FM25V20-PG という 2Mibit (256kiByte) の製品です。 DIP パッケージがうれしいじゃないですか。 うまく使えるようだったら、 SOIC パッケージを張り替えてもいいな。

接続表

F-RAM をブレッドボードに置いて、 FX3 DVK のピンヘッダとジャンプワイヤで接続します。 F-RAM のそれぞれの端子は、以下のように接続しています。

FM25V20信号名FX3 DVK
端子番号端子名ヘッダ名ピン番号
1/SSPI-SSN_UART-CTSJ1022
2QSPI-MISO_UART-TXJ1032
3/WVIO4--
4VSSGNDJ348
5DSPI-MOSI_UART_RXJ1042
6CSPI-SCK_UART-RTSJ1012
7/HOLDVIO4--
8VDDVIO4J342

J34 (SPI HEADER) にまとめられると良かったのですが、そのためには基板上の抵抗を取り外さなければなりません。今回は、 SPI-UART 切り替えヘッダの方を使いました。

ブートに使ってみる

SPI FLASH への書き込み

FX3 に接続された SPI FLASH には、 FX3 のファームウェアを格納してブート時にダウンロードすることができます。 そこで、 FX3 Software Development Kit (SDK) に付属しているユーティリティ Control CenterSPI FLASH と同じ要領でファームウェアを書込んでみます。 テストに使用したのは、FX3 DVK直結カメラキット "FX3DVK-Cam F1"の付属ファームウェアです。

すると、難なくファームウェアを書込むことができました。 何の障害もありません。 FX3 DVK をリセットすると、 F-RAM からブートしてカメラが起動しました。

それにしても、デフォルト状態の SPI ブートで使用される SPI クロックは、 10MHz のはずなのですが、こんないい加減なジャンパ配線でも動いちゃうものなのですね。

UsbSpiDmaMode プロジェクトを使ってみる

FX3 SDK には、多くのプロジェクト例が同梱されていて、その中に UsbSpiDmaMode というものがあります。 これは、 FX3 の SPI 端子に接続された外部デバイスを操作する時に参照するように用意されたプロジェクト例で、そのまま SPI FLASH とのインターフェイスに使用することができます。

このプロジェクト例では、コントロール転送でコマンド送ることですべての操作を行っています。 詳細は、プロジェクトディレクトリの "readme.txt" に書いてあります。 まず、さきほどファームウェアを書込んだままの状態で、「USBブート」を行い、 UsbSpiDmaModeRAM にダウンロードして、実行させてみました。

F-RAM からの読み出し

そして、 READ コマンド (0xC3) で先頭の 128 バイトを読み込んでみると、確かにファームウェアイメージが書かれているのが、確認できました。 まあ、このイメージからブートできるので、当然といえば当然です。

ERASE 操作は、可能か

ERASE操作

F-RAM という素子は、不揮発な RAM という位置づけをされています。 そのため、 ERASE という概念は、そもそも存在しません。 試しにコントロール転送から ERASE コマンドを送ってみましたが、問題は発生しません。


ERASE後のSTATUS表示 また、 STATUS コマンドを送信したところ、正常に ERASE が終了したことを示す"00"が返ってきました。

ファームウェアを読んでみると、この ERASE コマンドは、単に ERASE 操作を行い、 STATUS コマンドは WRITE IN PROGRESS (WIP) を返すだけの仕様になっているので、動作そのものに間違いはなさそうです。 そもそも、 F-RAM では、 SPI からデータを送った直後に、実際にメモリ素子に消去・書き込みを行うので、待ち時間はゼロです。 そのため、この WIP ビットも実装されておらず、常に "0" を返すようになっています。 つまり、消去されたつもりになっているのです。


ERASEしたけど消えてない

では、消去されたつもりの領域はどうなっているかというと、このように元のデータが残ったままになっています。 ERASEコマンドを実行したメモリ領域が、実は、消去されていない。 ここだけは、 SPI FLASH との互換性が無いので、注意が必要です。

消去せずに書き込み

いきなり書き込み

F-RAM は、不揮発な RAM なので、消去などせず、いきなり書き込みを行うことができます。 コントロール転送で書き込みコマンドを送信して、最初の11バイトだけに書き込みを行いました。


必要なぶんだけ、書き込み

そして、書き込まれた領域を読み込んでみると、最初の11バイトだけが変更されて、残りの部分については変化していないのがわかります。 もちろん、消去もされていません。

むすび

F-RAM という素子を FX3 DVK に接続して、どんな動きをするのか確認してみました。 ERASE コマンドが実装されていないことで、従来の SPI FLASH を想定していたファームウェアがそのまま使えないのではと考えていたのですが、 ERASE コマンドは適当に無視される仕様であることがわかりました。 ERASE によってデータが消去されずに残ってしまう点だけを注意すれば、 SPI FLASH 向けのファームウェアでも、ある程度は使用可能でしょうか。

参考文献


MC9S08JS8 にGUIアプリケーションを書く (3) [USB]このエントリーを含むはてなブックマーク#

2729707

ハードウェアが出来た。 In-Circuit Program も出来た。 次は、 USB アプリケーションに挑戦です。

前回の記事では、MC9S08JS8で作成したUSBデバイスをPCに認識させました。 今回は、GUIアプリケーションを作成します。

STEP3.1 : 開発環境の準備

アプリケーションの開発には、やはり開発環境が必要です。 トレーニング資料では、Visual C#が使用されていましたが、私は、Visual BASICを使ってみる事にしました。

どちらの言語を使う場合にもVisual Studio 2008 Express Editionを使うことができます。 以前、USB プロジェクト - サンプル・ソフトへの長い道のり(前編)Visual Studio 2008 Express Editionをダウンロードした時から一年以上の時がすぎました。 当時ダウンロードしたisoファイルから作成したDVDを使おうとしましたが、現在のバージョンはSP1になっているため、すでにインストールできない状態になっていました。 しかたなく、ダウンロードしなおしです。 日本語版が 931MB で、英語版が 748MB でした。

STEP3.2 : GUIアプリケーションの開発

開発環境が準備できたら、いよいよGUIアプリケーションの開発です。 これまで、GUIアプリケーションの開発は、かなりハードルが高かったと記憶しています。 そのため、比較的ライブラリが揃っているHIDの上にアプリケーションを構築しようとしてHIDデバイスをせっせと考えていました。

ただ、HIDデバイス向けのアプリケーションにしても結構な記述量(技量?)が必要です。 結局、サンプルプログラムの範囲を超えられませんでした。 今回は、あんなに苦労をしなくてもアプリケーションが開発できるらしいのです。

トレーニング資料に従って、新しいプロジェクトを作成します。 そして、 SimpleUSB という謎のコンポーネントを Visual Studio のコンポーネント一覧に入れます。 このコンポーネントは、機能は限定されるけれど、簡単にWinUSBを通じたアプリケーションを作成することが出来るラッパ・プログラムのようです。

WS000254.png

作成したGUI画面は、こんな風になりました。 USBに接続・切断するための"OPEN"ボタンと"CLOSE"ボタン、それに"LED0"の点灯・消灯を決める"LED0"チェックボックスがウィンドウ上の主な登場人物です。

下の方に表示されているコンポーネント"js8"は、"SimpleUSB"コンポーネントのインスタンスです。 このインスタンスを通じてUSBデバイスとデータのやり取りを行います。 記述したプログラムコードは、以下の通りです。

Public Class MainForm

    Private Sub openButton_Click( _
        ByVal sender As System.Object, _
        ByVal e As System.EventArgs _
    ) Handles openButton.Click
        If js8.DeviceConnected Then
            statusLabel.Text = "Already connected"
        Else
            statusLabel.Text = "Opening :"
            js8.OpenConnection()
        End If
    End Sub

    Private Sub closeButton_Click( _
        ByVal sender As System.Object, _
        ByVal e As System.EventArgs _
    ) Handles closeButton.Click
        Dim status As SimpleUSB.STATUS_CODE

        statusLabel.Text = ""
        status = js8.CloseConnection()
        statusLabel.Text += status.ToString
    End Sub

    Private Sub js8_onDeviceConnect() Handles js8.onDeviceConnect
        statusLabel.Text += "Connected :"
    End Sub

    Private Sub js8_onDeviceDisconnect() Handles js8.onDeviceDisconnect
        statusLabel.Text += "Disconnected :"
    End Sub

    Private Sub led0Cbox_CheckedChanged( _
        ByVal sender As System.Object, _
        ByVal e As System.EventArgs _
    ) Handles led0Cbox.CheckedChanged
        putLed()
    End Sub

    Private Sub putLed()
        Dim outBuffer(0) As Byte
        Dim status As SimpleUSB.STATUS_CODE

        outBuffer(0) = 0
        If led0Cbox.Checked Then
            outBuffer(0) = outBuffer(0) + 1
        End If
        status = js8.WriteData(1, outBuffer, 1)
        statusLabel.Text = status.ToString
    End Sub

End Class
WS000255.png

実行したら、こんなアプリケーションになりました。 "OPEN"ボタンでUSBデバイスに接続し、"LED0"チェック・ボックスでLEDを制御します。 少しサイズが大きいのですが、このリンクを "UsbGuiTrain01.zip" という名前で保存すると、プロジェクト・ディレクトリを再現することが出来ます。

参考サイト

USBJM_TRAINING: How to make a graphical user interface (GUI) for USB communication
今回の記事は、ここで見つけたお話を基にしています。 私がGUI開発に使用したのは、 Visual BASIC でしたが、このサイトでは Visual C# が使用されています。 お好みに合わせて、お使いください。

参考文献

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

  • 作者: Jan Axelson
  • 出版社/メーカー: Lakeview Research
  • 発売日: 2005/06/15
  • メディア: ペーパーバック

MC9S08JS8 にGUIアプリケーションを書く (2) [USB]このエントリーを含むはてなブックマーク#

2729707

ハードウェアが出来た。 In-Circuit Program も出来た。 次は、 USB アプリケーションに挑戦です。

前回の記事では、MC9S08JS8でUSBデバイスを作成しました。 今回は、PCにこのUSBデバイスを認識させます。

STEP2 : デバイス・ドライバの作成

これまでの GUI アプリケーション開発で最大の難関は、デバイス・ドライバの作成でした。 Window$の仕様を正しく理解して、それなりのプログラムを書く必要があります。 最初にDEMOJM向けのサンプルアプリケーションを見つけた時からデバイス・ドライバとその記述がネックになっていました。

トレーニング資料で使用しているのは、WinUSBでした。 WinUSBなら、以前USB プロジェクト - 懲りずにWinUSBに手を出すでかじった事があります。 その時は、アプリケーションにデバイス・ドライバ並みの記述量が必要になるので、深追いしなかったのでした。 今回は、できるようになるのかな。

トレーニング資料によると、WinUSBを使うときの難関は、アプリケーションの開発などではなく、INFファイルの作成なのだそうです。 へ~、そうなんだ。 INFファイルは、Window$にデバイス・ドライバのインストール方法を知らせるための手がかりが書いてあるファイルであると認識しています。 そのため、機械にやさしく、人間には厳しい記述が並んでいます。 トレーニング資料では、freescaleが作成した INF ジェネレータというアプリケーションを使って簡単にINFファイルを作成していました。 こりゃあ、ラクチンだ。

WS000250.png

USBデバイスをPCにつなぐと「新しいハードウェアの検出ウィザード」が動き出します。


WS000251.png

ここでは、自前のデバイスドライバを使います。


WS000252.png

私が改造したファームウェアは、最低限の改造しかしていません。 デバイス・デスクリプタもそのままなので、 PC からはトレーニング資料のものと同じデバイスとして認識されます。 トレーニング資料には、出来上がった INF ファイルもあります。 今回は、これをそのまま使ってインストールを続けます。


WS000253.png

デバイス・ドライバがインストールされるのを待ちます。 理由はわかりませんが、インストールには数分の時間がかかりました。 ちょっと、長すぎるんじゃないの?

つづく

参考サイト

USBJM_TRAINING: How to make a graphical user interface (GUI) for USB communication
今回の記事は、ここで見つけたお話を基にしています。

参考文献

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

  • 作者: Jan Axelson
  • 出版社/メーカー: Lakeview Research
  • 発売日: 2005/06/15
  • メディア: ペーパーバック


MC9S08JS8 にGUIアプリケーションを書く (1) [USB]このエントリーを含むはてなブックマーク#

2729707

ハードウェアが出来た。 In-Circuit Program も出来た。 次は、 USB アプリケーションに挑戦です。

STEP0 : 簡単にアプリケーションを作る方法を見つけた

以前、 freescale のサンプルアプリケーションを使おうとしたりWinUSB に手を出してみたりJan Axelson さんのサンプルアプリケーションを改造してみたり、 いろいろとやってきましたが、どうも簡単にアプリケーションを使うための方法が見つかりませんでした。

時はすぎて、 MC9S08JS8 のための資料を集めていたときに、ふと、こんなタイトルが目に付きました。

How to make a graphical user interface (GUI) for USB communication

「USB通信のためのGUIの作り方」

これだよ、私が追い求めていたのは。 FTF2008 で使用されたトレーニングをまとめた資料のようです。 しかも、ステップ・バイ・ステップの動画付き。 さっそく、追いかけてみました。

STEP1.1 : ファームウェアの作成

この資料は、GUIアプリケーションを作成することを目的として作られています。 そのため、USBデバイスを作成する部分は、仕様を中心に簡単に説明されているにすぎません。

そこで、今回の実験でも DEMOJM に搭載された MC9S08JM60 向けに作成されたファームウェアを私が作成した MC9S08JS8 基板で動作するため最低限の改造をしました。 改造のポイントは、以下の通り。

  • typedef.hの改定

    typedef.hというファイルがありまして、このプロジェクトで使用されているデータ型を宣言しています。 中には、byteなどの標準で宣言されているような型も含まれているので、ファイル名も含めて改定しました。 ファイル名は、Usb_Type.hになっています。 ただ、本当に必要な部分がどこなのか、区別が付かなかったので、まだ余計な部分が残っていると思います。 いつか、使わなくてもいいようにしてやろう。

  • FslTypes_File.hの廃業

    もうひとつ、FslTypes_File.hという型宣言ヘッダファイルがありました。 こちらは、UINT8形式の型宣言を行っています。 プログラム本体の記述をbyte形式に統一して、このヘッダ・ファイルは、廃業しました。

  • ADCモジュールの削除

    MC9S08JM60には、A/D変換モジュールが内蔵されていて、Bulk型エンドポイントからデータを送り出すことが出来ます。 MC9S08JS8には、A/D変換モジュールが内蔵されていないので、A/D変換に関連する部分はすべて削除しました。 ただし、エンドポイントは残してあります。

  • GPIOおよびKBIの割り当て変更

    このプログラムでは、KBIを使ったキー入力4本とGPIOを使ったLED出力8本が定義されています。 MC9S08JS8には、MC9S08JM60ほど多くのポートが装備されていないので、それぞれの接続先を変更しています。

    機能端子備考
    SWITCH0PTA4/KBIP4KBI付きGPIO入力
    SWITCH1PTA5/KBIP5KBI付きGPIO入力
    SWITCH2PTA6/KBIP6KBI付きGPIO入力
    SWITCH3PTA7/KBIP7KBI付きGPIO入力
    LED0PTA0GPIO出力
    LED1PTA1GPIO出力
    LED2PTA2GPIO出力
    LED3PTA3GPIO出力
    LED4PTB0/IRQ*GPIO出力
    LED5PTB1/RESET*GPIO出力
    LED6PTB2/BKGD/MSGPIO出力 出力専用
    LED7PTB3/BLMS*GPIO出力 出力専用

    ポート数に限りがあるので、LED4-LED7のあたりの割り当てがかなり厳しくなっています。 現状の評価ボードでは、LED0しか実装されていませんので、必要になったら拡張していきます。

このリンクを "JS06.zip" という名前で保存すると、プロジェクト・アーカイブを再現することができます。

STEP1.2 : ファームウェアの書き込み

トレーニング資料では、 DEMOJM 評価ボードを使用しているので、評価ボード上の MULTILINK を使用することができます。 私の基板も MULTILINK を接続することを前提としてBDMコネクタを装備しましたが、今回は使用しません。 なぜなら、MC9S08JS8 をUSBにつないだで書いたように、内蔵 bootloader プログラムがあるからです。 もちろん、デバッグはできませんが、今回も bootloader を利用してプログラムを書き込みます。

bootloader GUIプログラムなどの詳細は、前回の記事を参照してください。 先のプロジェクト・アーカイブには、コンパイル済みS19ファイルが含まれていません。 書き込みだけ行いたい方は、このリンクを"JS06.s19"という名前で保存するとコンパイル済みコードが再現できますのでお使いください。

つづく

参考サイト

USBJM_TRAINING: How to make a graphical user interface (GUI) for USB communication
今回の記事は、ここで見つけたお話を基にしています。

参考文献

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

Usb Complete: Everything You Need To Develop Custom Usb Peripherals (Complete Guides Series)

  • 作者: Jan Axelson
  • 出版社/メーカー: Lakeview Research
  • 発売日: 2005/06/15
  • メディア: ペーパーバック

USBプロジェクト - HIDデバイス(5) [USB]このエントリーを含むはてなブックマーク#

1593486

複合USBデバイスが、できちゃいました。 まだ、何も仕事はしてくれません。

デスクリプタを整備する

要は、複合デバイスに見えるデスクリプタを記述して、ホストの要求に応じて適切なデスクリプタを返すだけでおしまいです。 記述したコンフィグレーション・デスクリプタは、こんな構成になっています。

  • コンフィグレーション・デスクリプタ
    • 自前インターフェース・デスクリプタ (#0)
      • 自前HIDデスクリプタ
      • 自前エンドポイント・デスクリプタ (IN-EP2)
      • 自前エンドポイント・デスクリプタ (OUT-EP2)
    • キーボード・インターフェース・デスクリプタ (#1)
      • キーボードHIDデスクリプタ
      • キーボードエンドポイント・デスクリプタ (IN-EP1)

そして、GET_DESCRIPTORリクエストの処理部分でwIndexの値により、該当するインターフェースのレポート・デスクリプタを返すようにswitch文を追加しました。

キーボードの実装は、無い

キーボードとして動作する部分の実装は、一切行っていません。 TX1Eさえ、アサートしていません。 そのため、トランザクションが到着しても、ひたすらNAKが返っているはずです。 特に文句も言われていないから、こんなもんでいいんだろう。

ついでに、キーボードのレポート・デスクリプタから、"OUTPUT"部分を取り除いてしまいました。 そもそも、SET_REPORTリクエストへの応答処理も書いていません。 果たして、SET_REPORTリクエストが来なかったのか、それともDATAステージのSTALL応答で十分なのか、深くは追求していません。

PCが認識したデバイス

PCがどのようにこのデバイスを認識したのかは、デバイスマネージャで見るとわかります。

WS000133.png

このように、「複合デバイス」という仮想デバイスの下に「ヒューマン・インターフェース・デバイス」がぶら下がり、それらが「HID自前デバイス」と「HIDキーボード・デバイス」であることがわかります。 何だ、こんなに簡単な事だったんだ。

HIDD_ATTRIBUTESには、インターフェースが無い

これを、VisualBASICのプログラムで操作すると、LCDに表示が現れます。 でも、このプログラムでは、インターフェース番号を指定している所がありません。 VendorIDとProductIDは、HIDD_ATTRIBUTES構造体に入っていますが、インターフェース番号は入っていません。 何か、他に指定方法があるのでしょうか?

プロジェクト・アーカイブ

いつものように、HID08.zipという名前で保存してお使いください。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - HIDデバイス(4) [USB]このエントリーを含むはてなブックマーク#

1593486

MC908JB16のエンドポイント#2は、双方向で使えるらしい。

デバイスファームウェアを改造

前回のHID06では、IN-エンドポイント#1とOUT-エンドポイント#2を使っていました。 ところが、エンドポイント#2は、同時に双方向で使えるとの指摘を受けましたので、IN-エンドポイント#1の代わりにIN-エンドポイント#2を使うようにファームウェアを改造しました。

要するに、エンドポイント・デスクリプタと"TX1"と書いてレジスタを"TX2"に書き換えただけです。 これだけで、あっというまにエンドポイント#1を使わないファームウェアが出来上がりました。 プロジェクトは、これをHID07.zipという名前で保存すると解凍できるようになります。

次は、余ったエンドポイント#1を使って、複合HIDデバイスを考えてみよう。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - 複合デバイスを考えた [USB]このエントリーを含むはてなブックマーク#

1469810

Tsuneoさんのコメントを手がかりに複合デバイスについて考えました。 いや、考えただけですから。

要求仕様

hamayanさんからの要求仕様は、「キーボード・デバイスでありながら、PCの指示で電飾がコテコテ光る装置」でした。 これを満たすためには、「同一USBポートにキーボード・デバイスと電飾デバイスを割り当てる」必要があります。

物理的にUSBハブを付けると仕様は満たしますが、それでは、あまりにも不恰好。 その代わりに、一つの物理USBポートに複数の論理USBポートを割り当てると解決します。 この論理USBポートの事をインターフェースと呼ぶらしいです。

必要なのは、PCに複数のインターフェースが存在することを知らせることと、 到着したコントロール転送リクエストが、どのインターフェースに宛てられたものかを判断し、適切に割り振る機能であると認識しました。

デスクリプタは、どうする

Tsuneoさんの書き込みを勝手に引用すると、デスクリプタはこうなるらしいです。

  • デバイス・デスクリプタ
  • コンフィグレーション・デスクリプタ
    • キーボード・インターフェース・デスクリプタ (#0)
      • キーボードHIDデスクリプタ
      • キーボードINエンドポイント・デスクリプタ (EP1)
    • 電飾インターフェース・デスクリプタ (#1)
      • 電飾HIDデスクリプタ
      • 電飾INエンドポイント・デスクリプタ (EP2)
  • ストリング・デスクリプタ
  • キーボードHIDレポート・デスクリプタ
  • 電飾HIDレポート・デスクリプタ

コンフィグレーション・デスクリプタは、ひとかたまりでPCに伝達されます。 そのため、"GET_DESCRIPTOR"リクエストでどのインターフェースのデスクリプタが要求されたかという事は考えなくても構いません。 問題になるのは、コンフィグレーション・デスクリプタの外に出されてしまった「HIDレポート・デスクリプタ」の方です。

"GET_DESCRIPTOR"リクエストで要求されたのがキーボードのデスクリプタなのか、電飾のデスクリプタなのかを知るための手がかりは、SETUPパケットの"wIndex"に書いてあります。 標準デスクリプタが要求されたときには、このフィールドにはZEROまたは言語IDが入ります。 ところが、HIDクラス・デスクリプタが要求された場合には、インターフェース番号が入ります。 つまり、このフィールドを見て、PCに返すHIDレポート・デスクリプタを返せば良いということがわかります。

SET_REPORTリクエストへの対応が必要

MC908JB16には、EP0以外のエンドポイントが二つしかありません。 つまり、複数のHIDインターフェースを使う場合には、HIDで使用するインタラプトOUTエンドポイントを用意するわけにはいかないので、"SET_REPORT"リクエストの処理が必須になります。

"SET_REPORT"リクエストの場合も、インターフェース番号が"wIndex"フィールドに入ってきます。 このフィールドを見て、どのインターフェースに宛てた"SET_REPORT"リクエストなのかを判断し、しかるべき処理を行います。


何だかできそうな気になってきました。 他に忘れ物は無いかな?

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - HIDデバイス(3) [USB]このエントリーを含むはてなブックマーク#

1516092

調子にのって、他のマシンにつないでみました。 接続時の写真は、ありません。

KNOPPIX5.1のエニュメレーション

最初のホストは、KNOPPIX5.1マシン(Pentium II 233MHz)です。

記号意味
GDDGET_DESCRIPTORリクエストがDEVICEデスクリプタを要求しました。
SaSET_ADDRESSリクエストが届きました。
GDD再度GET_DESCRIPTORリクエストがDEVICEデスクリプタを要求しました。
GDCGET_DESCRIPTORリクエストが2回CONFIGURATIONデスクリプタを要求しました。
GDC
GDS GET_DESCRIPTORリクエストがSTRINGデスクリプタを2回要求しました。
GDS
ScSET_CONFIGURATIONリクエストが届きました。
GDRGET_DESCRIPTORリクエストがREPORTデスクリプタを要求しました。

コンソールは、こんな感じ。 すっかり、HIDデバイスだと思っています。

Apr 10 22:59:07 noritan-kpx kernel: usb 1-1: new low speed USB device using uhci_hcd and address 2
Apr 10 22:59:07 noritan-kpx kernel: usb 1-1: configuration #1 chosen from 1 choice
Apr 10 22:59:08 noritan-kpx kernel: usbcore: registered new interface driver hiddev
Apr 10 22:59:08 noritan-kpx kernel: hiddev96: USB HID v1.01 Device [noritan.org LCD Display] on usb-0000:00:07.2-1
Apr 10 22:59:08 noritan-kpx kernel: usbcore: registered new interface driver usbhid
Apr 10 22:59:08 noritan-kpx kernel: drivers/usb/input/hid-core.c: v2.6:USB HID core driver
Apr 10 23:10:35 noritan-kpx kernel: usb 1-1: USB disconnect, address 2

玄箱のエニュメレーション

MontaVistaを入れたままの玄箱/HGでも試してみました。

記号意味
SaSET_ADDRESSリクエストが届きました。
GDDGET_DESCRIPTORリクエストが2回DEVICEデスクリプタを要求しました。
GDD
GDCGET_DESCRIPTORリクエストが2回CONFIGURATIONデスクリプタを要求しました。
GDC
ScSET_CONFIGURATIONリクエストが届きました。

STRINGデスクリプタを見てもくれません。 どうも、HIDをご存知ないようです。 /var/log/messagesは、こんな感じです。

Apr 10 23:09:32 NORITAN-BOX kernel: hub.c: new USB device 00:0e.0-1, assigned address 7
Apr 10 23:09:32 NORITAN-BOX kernel: usb.c: USB device 7 (vend/prod 0x100/0x0) is not claimed by any active driver.
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: beep is defined as "off"
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: usb device is added
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: vendor:0x100 product:0x0 Dclass:0x0 Dsubclass:0x0 Dprotocol:0x0 Iclass:0x3 Isubclass:0x0 Iprotocol:0x0
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: The device match nothing in map file
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: Please change MODULE in following line to the appropriate module name, add it to /etc/murasaki/murasaki.usbmap
Apr 10 23:09:32 NORITAN-BOX murasaki.usb[12855]: MODULE 0x0081 0x100 0x0 0 0 0x0 0x0 0x0 0x3 0x0 0x0 0x00000000
Apr 10 23:09:36 NORITAN-BOX kernel: usb.c: USB disconnect on device 00:0e.0-1 address 7
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: beep is defined as "off"
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: usb device is removed
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: vendor:0x100 product:0x0 Dclass:0x0 Dsubclass:0x0 Dprotocol:0x0 Iclass:0x3 Isubclass:0x0 Iprotocol:0x0
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: The device match nothing in map file
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: Please change MODULE in following line to the appropriate module name, add it to /etc/murasaki/murasaki.usbmap
Apr 10 23:09:36 NORITAN-BOX murasaki.usb[12856]: MODULE 0x0081 0x100 0x0 0 0 0x0 0x0 0x0 0x3 0x0 0x0 0x00000000

usbmapファイルに何やら書き込むと所望のドライバが返事をしてくれるらしいです。 ここから先は、未知の世界ですね。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - HIDデバイス(2) [USB]このエントリーを含むはてなブックマーク#

1593486

MC908JB16で制御するLCDモジュールに文字を表示させました。 まずは、有名なご挨拶から。

起動時のトランザクション

トランザクションをLCDモジュールに記号で表示するようにしました。 このため、起動時のトランザクションを確認することが出来ます。

so-netは、WMVファイルを受付けないので、yahooにリンクしています。 yahooは、サービスを停止したため、YouTubeに引っ越しました。 速すぎて表示が読めないので、書き起こしました。

記号意味
GDDGET_DESCRIPTORリクエストがDEVICEデスクリプタを要求しました。
SaSET_ADDRESSリクエストが届きました。
GDD再度GET_DESCRIPTORリクエストがDEVICEデスクリプタを要求しました。
GDCGET_DESCRIPTORリクエストが2回CONFIGURATIONデスクリプタを要求しました。
GDC
GDS GET_DESCRIPTORリクエストがSTRINGデスクリプタを4回要求しました。
GDS
GDS
GDS
GDD再度GET_DESCRIPTORリクエストがDEVICEデスクリプタを要求しました。
GDCGET_DESCRIPTORリクエストが2回CONFIGURATIONデスクリプタを要求しました。
GDC
ScSET_CONFIGURATIONリクエストが届きました。
GDRGET_DESCRIPTORリクエストがREPORTデスクリプタを要求しました。

赤いLEDが点灯しているのは、どこかで"STALL"が発行された事を示しています。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - USBCV実行失敗からの脱出 [USB]このエントリーを含むはてなブックマーク#

BlueBack.png

USBCVを実行したら、ブルーバック画面になって、ブルーになってしまいました。 せっかくだから、ネタにしてしまおう。

USBCVを実行したら

USBCVは、USBデバイスの準拠度を検査するソフトウェアです。 USB.orgToolsからダウンロードできます。 HIDデバイスが出来たので、これの適応度合いを測るべくノートPCで実行したのですが、反応が無くなって、しばらくしてから、いわゆるブルー・バック画面が出てきてしまいました。 確か、hcdevice.sysでSTOPが実行されたと表示されていました。

ALT+CTRL+DELも効きません。 こうなると、手出しが出来ないので、電源ボタンを長押しして、再起動をかけました。

USB機器全滅

再起動はできました。 「Windowsは、深刻なエラーから復帰しました。」と 偉そうなメッセージが出てきましたが、当初の予想(警告?)どおり、マウスが動きません。 ぜんぜん、復帰して無いじゃん。 USB機器を挿した時の「ピポッ」の音も出ないので、完全にUSBデバイスを受け付けない状態になったと思われます。

WS000129.png

ノートPCだったので、キーボードとタッチパッドは、なんとか動きます。 デバイス・マネージャを開けてみると、見慣れないデバイスが並んでいます。 この"Intel EHCI Compliance Test Tool"というのが犯人ですね。 プロパティを開けてみました。

テスト・ツールのプロパティ

WS000130.png

プロパティによると、Intel製のドライバのようです。 どうも、このデバイス・ドライバを復旧させれば直りそうです。 そこで、いくつか方法を考えました。

「システムの復旧ウィザード」を使う

デバイス・ドライバの状態を元に戻すために、節目節目でスナップ・ショットが取られています。 これを使って、デバイス・ドライバを元に戻せば良さそうです。 そう思って、ヘルプを探し回り、「システムの復旧ウィザード」というものが存在するところまでは思い出したのですが、肝心の「ウィザード」を呼び出す方法が見つかりません。 えぇい、役に立たないヘルプめ。

「ドライバのロールバック」を使う

デバイス・ドライバのプロパティには、「ロールバック」というボタンがありました。 デバイス・ドライバを元に戻すという意味であるとは、理解しましたが、USBCVがWindowsの推奨する方法で以前の状態のデバイス・ドライバを保存してくれているのかどうか、わかりません。 もし、ロールバック・ボタンを押して、「元に戻せません。Intelドライバも無くなっちゃいました。」という結果になるのを恐れて、このボタンを使う度胸がありませんでした。

「削除」を使う

USBデバイスのドライバの時と同じように、デバイス・ドライバを削除すると、新しいデバイス・ドライバが自動的に検索されるはずです。 その時に"Windows XP"のインストール・ディスクをあてがってやれば、復旧すると思われます。 でも、削除した時にDVDドライブが動作する保証はありません。

天の助け

第一報をコメントに記入していたおかげで、コメント欄にTsuneoさんの返答をもらいました。

「USB(Universal Serial Bus)コントローラ」の下に

- Intel EHCI Compliance Test Tool

というのがあるか、もしくは

- 黄色いマークの付いたEnhanced Host Controller

というインスタンスがあるでしょう。 このインスタンスをダブルクリックで開けて、「ドライバー」タブに行きます。 「ドライバの更新」か「ドライバのロールバック」で元のMSのドライバに戻します。


WS000131.png

おぉ、これは明快なお答えだ。 「ロールバック」でいいんですね。 「ロールバック」ボタンを押すとこんなメッセージが出てきます。

もちろんです。 ロールバックしてくれるなら。 「はい」をクリックすると、


WS000132.png

できました。 "Microsoft"のデバイス・ドライバが戻ってきました。 いつもの「ピポッ」の音も出ました。


なぜ、USBCVが落っこちたのかは、良くわかっていません。 簡単に再インストールしても許されるUSBCV実行専用マシンが欲しいな。

早々と明確なお答えをいただき、感謝のしようもありません。 この場を借りてお礼申し上げます。


USBプロジェクト - HIDデバイス(1) [USB]このエントリーを含むはてなブックマーク#

1516092

設計に時間を要しましたが、ようやく、HIDデバイスができました。

動作中写真は、後日。

HIDのプロトコル

このプログラムは、HIDのレポートとして、送信された8バイトのデータの先頭1バイトの文字コードを液晶モジュールに表示するものです。 ただし、デバッグのための文字も一緒に液晶モジュールに表示されるので、まだ、ごちゃごちゃしています。

また、8バイトのデータをホストに返す機能もありますが、今の所、意味のあるデータを載せていないので、データが到着するのを確認することしかできません。

動作の確認は、WindowsアプリケーションにはBASICが良く似合うで紹介した、"hidclass_vs5.zip"を使用しました。 これで、液晶モジュールに文章を出させるには、根気が続きませんでした。

プロジェクト・ファイル

え~と、いちおうプロジェクト・アーカイブを作成しましたが、切れ端やら、残骸やらが残ったままのプログラムなので、読みたい方だけお読みください。 "HID06.zip"という名前で保存すると再生できるはずです。 協調マルチタスクにしてあるつもりです。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - USB制御液晶モジュールのその後 [USB]このエントリーを含むはてなブックマーク#

1516092

USBで制御させようと思っていた「USB制御液晶モジュール」ですが、液晶表示の機能のテストをしていませんでした。 ひとまず、文字を表示させてみました。 液晶モジュールには、サンライク社のSC1602Bを使用しています。

端子の割り当て

SC1602Bには、4-bitバスモードと8-bitバスモードがあります。 MC908JB16には、多くの端子があるので、贅沢に8-bitモードを使用するつもりだったのですが、そのままではMON08インターフェースがつながらないことがわかりました。

DB2を制御しようとしていたPTA2は、MON08接続時には、"LOW"レベルを保持しなくてはなりません。 SC1602BのDB2にはプルアップ抵抗がつながっているようで、テスタで0.8Vの電圧が出ていました。 このマイコンのVILは、最大0.3VDDなので、十分に"LOW"レベルだと思います。 10KΩのプルダウン抵抗を追加したらつながるようでした。

真の原因は良くわからないけど、4-bitモードを使用すべくDB3からDB0の線を切ってしまいました。 切っちゃったら、何とかなったので、いいことにしてしまおう。

また、CN2-11はPTC1だと思っていたのですが、実際にはPTC0がつながっていましたので、"E"端子は、PTC0で制御するようにしました。

マイコンと液晶モジュールの接続
SC1602B側 USBDEV基板側
#端子名#端子名
1VDDCN4-14VDD
2VSSCN2-14VSS
3VO--
4RSCN2-5PTE0
5R/WCN2-6PTE2
6ECN2-11PTC0
7DB0-N/C
8DB1-N/C
9DB2-N/C
10DB3-N/c
11DB4CN2-7PTA4
12DB5CN2-8PTA5
13DB6CN2-9PTA6
14DB7CN2-10PTA7

基板上には、LEDが4個搭載されます。 こちらは、前回から変更ありません。

マイコンとLEDの接続
#端子名LED
CN4-8PTD2
CN4-9PTD3
CN4-10PTD4
-PTD5青(USBDEV上)

表示テスト

一秒ごとに数字を表示していくプログラムを作成してみました。 そうか、SC1602って、勝手にスクロールしてくれるんじゃなかったのか。

//==============================================================
//  $Id: main.c,v 1.1 2008/04/06 02:43:58 noritan Exp $
//  $Source: /mnt/cvs/rep/CW/JB01/Sources/main.c,v $
//==============================================================
#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */

//--------------------------------------------------------------
//  Time constants
//--------------------------------------------------------------
#define   _CPU_CLOCK_   (12000000UL)
#define   _BUS_CLOCK_   (6000000UL)
#define   PERIOD_PS     (1)
#define   PERIOD_1m00   ((_BUS_CLOCK_ / PERIOD_PS / 1000) - 1)

//--------------------------------------------------------------
//  Alias for I/O ports.
//--------------------------------------------------------------
#define   LCD_RS        PTE_PTE0
#define   LCD_RW        PTE_PTE2
#define   LCD_E         PTC_PTC0
#define   LCD_DB        PTA
#define   LCD_DDRRS     DDRE_DDRE0
#define   LCD_DDRRW     DDRE_DDRE2
#define   LCD_DDRE      DDRC_DDRC0
#define   LCD_DDRDB     DDRA

dword systime;

//--------------------------------------------------------------
//  MSEC sleep.
//--------------------------------------------------------------
void msleep(dword reltim) {
  dword expire = systime + reltim;
  while (systime <= expire) ;
}

//--------------------------------------------------------------
//  Periodic interrupt
//--------------------------------------------------------------
void interrupt VectorNumber_TIM1Ovr isrTim1Ovf()
{
  T1SC_TOF = 0;                 // Clear flag.
  systime++;                    // Increase system time.
  PTD_PTD5 = (systime & 0x40)?(1):(0);
}

//--------------------------------------------------------------
//  Toggle E clock.
//--------------------------------------------------------------
static void lcd_toggleE(void) {
  LCD_E = 1;                    // assert LCD enable
  LCD_E = 0;                    // negate LCD enable
}

//--------------------------------------------------------------
//  Get a byte.
//--------------------------------------------------------------
byte lcd_getByte(void) {
  byte data;
  LCD_E = 1;                    // assert LCD enable
  data = LCD_DB;              // Get Status.
  LCD_E = 0;                    // negate LCD enable
  return data;
}

//--------------------------------------------------------------
//  Get two nibbles.
//--------------------------------------------------------------
byte lcd_getNibbles(void) {
  byte data;
  LCD_E = 1;                    // assert LCD enable
  data = LCD_DB & 0xF0;         // Get MSB part.
  LCD_E = 0;                    // negate LCD enable
  LCD_E = 1;                    // assert LCD enable
  data |= (LCD_DB >> 4);        // Get LSB part.
  LCD_E = 0;                    // negate LCD enable
  return data;
}

//--------------------------------------------------------------
//  Get status word.
//--------------------------------------------------------------
byte lcd_getStatus(void) {
  byte status;
  
  LCD_RS = 0;                   // Select command.
  status = lcd_getNibbles();    // Get Status as nibbles.
  return status;
}

//--------------------------------------------------------------
//  Get data word.
//--------------------------------------------------------------
byte lcd_getData(void) {
  byte data;
  
  LCD_RS = 1;                   // Select register.
  data = lcd_getNibbles();      // Get Data as nibbles.
  return data;
}

//--------------------------------------------------------------
//  Check if LCD is busy.
//--------------------------------------------------------------
byte lcd_isBusy(void) {
  return (lcd_getStatus() & 0x80);
}

//--------------------------------------------------------------
//  Send two nibbles
//--------------------------------------------------------------
void lcd_sendNibbles(byte data) {
  // Set MSB nibble
  LCD_DB = (LCD_DB & 0x0F) | (data & 0xF0);
  LCD_DDRDB |= 0xF0;            // Drive DB(7:4) as output
  lcd_toggleE();                // Toggle LCD E clock.
  // Set LSB nibble
  LCD_DB = (LCD_DB & 0x0F) | ((data << 4) & 0xF0);
  lcd_toggleE();                // Toggle LCD E clock.
  LCD_DDRDB &= 0x0F;            // Release DB(7:4) as input
}

//--------------------------------------------------------------
//  Send one bytes
//--------------------------------------------------------------
void lcd_sendByte(byte data) {
  LCD_DB = data;                // Set a byte.
  LCD_DDRDB = 0xFF;             // Drive DB as output
  lcd_toggleE();                // Toggle LCD E clock.
  LCD_DDRDB = 0x00;             // Release DB as input
}

//--------------------------------------------------------------
//  Send a byte as a command
//--------------------------------------------------------------
void lcd_sendCommand(byte command) {
  LCD_RS = 0;                   // Select command.
  LCD_RW = 0;                   // Prepare for WRITE.
  lcd_sendNibbles(command);     // Send as nibbles.
  LCD_RW = 1;                   // Prepare for READ.
}

//--------------------------------------------------------------
//  Send a byte as a data
//--------------------------------------------------------------
void lcd_sendData(byte data) {
  LCD_RS = 1;                   // Select data.
  LCD_RW = 0;                   // Prepare for WRITE.
  lcd_sendNibbles(data);        // Send as nibbles.
  LCD_RW = 1;                   // Prepare for READ.
}

//--------------------------------------------------------------
//  Locate cursor 
//--------------------------------------------------------------
void lcd_cursor(byte addr) {
  while (lcd_isBusy()) ;        // Wait for instruction ends.
  lcd_sendCommand(addr | 0x80); // Send an address set command.
}

//--------------------------------------------------------------
//  Initialization sequence
//--------------------------------------------------------------
void lcd_init(void) {
  void putc(char);

  LCD_E = 0;                    // clear LCD enable
  LCD_RS = 0;                   // clear register select.
  LCD_RW = 1;                   // Prepare for READ.
  LCD_DDRE = 1;                 // configure LCD_E as output.
  LCD_DDRRS = 1;                // configure LCD_RS as output.
  LCD_DDRRW = 1;                // configure LCD_RW as output.
  
  PTD_PTD5 = 1;
  PTD_PTD4 = 1;
  PTD_PTD3 = 1;
  PTD_PTD2 = 1;
  DDRD_DDRD5 = 1;               // Enable BLUE
  DDRD_DDRD4 = 1;               // Enable GREEN
  DDRD_DDRD3 = 1;               // Enable YELLOW
  DDRD_DDRD2 = 1;               // Enable RED
  
  // Initialization sequence.
  LCD_RS = 0;                   // Select command.
  LCD_RW = 0;                   // Prepare for WRITE.
  msleep(100);                  // wait for 100ms
  LCD_DB = (LCD_DB & 0x0F) | (0x30);  // 1st command.
  LCD_DDRDB |= 0xF0;            // Drive DB(7:4) as output
  lcd_toggleE();                // Toggle LCD E clock.
  PTD_PTD4 = 0;
  msleep(10);                   // wait for 10ms
  lcd_toggleE();                // Toggle LCD E clock.
  msleep(1);                    // wait for 1ms
  LCD_DB = (LCD_DB & 0x0F) | (0x20);  // 4-bit mode.
  lcd_toggleE();                // Toggle LCD E clock.
  LCD_DDRDB &= 0x0F;            // Release DB(7:4) as output
  LCD_RW = 1;                   // Prepare for READ.
  msleep(1);                    // wait for 1ms

  while (lcd_isBusy()) ;        // Wait for instruction ends.
  lcd_sendCommand(0x28);        // Set DL as 4-bit, N as 2 lines, F as 5x7 dots.
  
  while (lcd_isBusy()) ;        // Wait for instruction ends.
  PTD_PTD3 = 0;
  lcd_sendCommand(0x10);        // Set S/C as move, R/L as left.
  
  while (lcd_isBusy()) ;        // Wait for instruction ends.
  PTD_PTD2 = 0;
  lcd_sendCommand(0x0F);        // Set D as ON, C as ON, B as blink.
  
  while (lcd_isBusy()) ;        // Wait for instruction ends.
  lcd_sendCommand(0x06);        // Set I/D as INC, S as no Shift.

  while (lcd_isBusy()) ;        // Wait for instruction ends.
  lcd_sendCommand(0x01);        // Clear Display.
}


void main(void) {
  byte    i;
  byte    x;
  
  systime = 0;

  CONFIG =
    0b01100001;
  //  |||||||+---   COPD=1    No COP function
  //  ||||||+----   STOP=0    No STOP instruction
  //  |||||+-----   COPRS=0   Default COP rate
  //  ||||+------   SSREC=0   No STOP
  //  |||+-------   LVID=0    Enable LVI function
  //  ||+--------   URSTD=1   Disable RESET by USB
  //  |+---------   LVI5OR3=1 Configure for VDD=5V
  //  +----------   LVIDR=0   Enable LVI for VREG

  T1MOD = PERIOD_1m00;  // Set modulo period
  T1SC =
    0b01110000;
  //  |||||+++---   PS=000  prescaler x1
  //  ||||+------   Reserved
  //  |||+-------   TRST=1  Reset counter
  //  ||+--------   TSTOP=1 Disable counter
  //  |+---------   TOIE=1  Enable OVF interrupt
  //  +----------   Not affected
  T1SC_TSTOP = 0;       // Enable counter
  
  EnableInterrupts; /* enable interrupts */
  
  lcd_init();
  x = 0;
  for (;;) {
    for (i = 0; i < 10; i++) {
      msleep(1000);
      PTD_PTD3 = 0;
      while (lcd_isBusy()) ;
      lcd_sendData(i + '0');
      if (++x >= 16) {
        while (lcd_isBusy()) ;
        lcd_sendCommand(0x02);  // Return home
        x = 0;
      }
      PTD_PTD3 = 1;
      PTD_PTD2 = ~PTD_PTD2;
    }
  } /* loop forever */
  /* please make sure that you never leave main */
}

USBプロジェクト - ファームウェアに立ち返る (13) [USB]このエントリーを含むはてなブックマーク#

1475712

USB処理のためのステートマシンを設計します。 昨日の図を全面的に見直し、書き直しました。

状態遷移図

UsbControlHandlerState.png

Tsuneoさんに書いてもらった、プログラムの断片から、状態遷移図を書いてみました。 しまった、初期状態が無いや。

IDLE状態で、SETUPトランザクションを検出したら、SETUPパケット解析を行い、リクエストの種類によって三つの状態に分岐します。 ここでは、HIDに最低限必要なリクエストだけを扱います。

コントロール読み出し転送

UsbControlInTransferSequence.png

GET_DESCRIPTORGET_REPORTなどのコントロール読み出し転送の場合、ep0InStart()で返送データを準備してから、READ_DATA状態に遷移します。 そして、全てのデータを送り終えたことをep0InCompleteで確認したら、READ_STATUS状態に遷移します。 STATUS-OUTトランザクションが終わった事をep0OutReadyによって確認したら、IDLE状態にもどります。

データ無しコントロール転送

UsbControlNoDataTransferSequence.png

SET_ADDRESSSET_CONFIGURATIONなどのデータ無しコントロール転送の場合、ep0InStart()でSTATUSデータを準備し、NODATA_STATUS状態に遷移します。 そして、STATUS-INトランザクションの終了を待ちます。 STATUS-INトランザクションが終わったら、アドレス設定などの実際の処理を行い、IDLE状態に戻ります。

コントロール書き込み転送

UsbControlWriteTransferSequence.png

SET_REPORTなどのコントロール書き込み転送の場合、WRITE_DATAに遷移します。 この状態では、ep0OutReadyポーリングされます。 すべてのデータを転送しおえたら、ep0InStart()でSTATUSデータを準備し、WRITE_STATUS状態に遷移します。 この状態では、STATUS-INトランザクションの終了時に発行されるep0InCompleteがポーリングされます。 STATUS-INトランザクションの終了を確認したら、受信したデータを使った実際の処理を行い、IDLE状態に戻ります。

このシーケンス図では、SETUPパケットの解析に時間がかかりすぎたために、解析中に次のDATA-OUTトランザクションが到着しています。 SETUPDATA-INは、別のバッファを持たせるつもりなので、このぐらいのタイミングであれば、正常に転送が行われるはずです。


こんなもんで、いいかな?

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - ファームウェアに立ち返る (12) [USB]このエントリーを含むはてなブックマーク#

1475712

最後に残ったコントロール書き込み転送のシーケンスを考えます。

シーケンス図

UsbControlWriteTransferSequence.png

シーケンス図が見るからに忙しそうです。

他のコントロール転送同様にSETUPトランザクションが到着すると割り込みサービスルーチンが応答し、メイン・ルーチンによってポーリングされます。 この後、メイン・ルーチンでSETUPパケットを解析します。

さらに、ホストからはDATA-INトランザクションが到着します。 EP0-INは、いつSETUPトランザクションが到着しても対応できるように、このDATA-INトランザクションも割り込みで応答します。

このように、次々と割り込みサービスルーチンがデータを受け取るため、デバイスの都合でデータを送出していたほかのコントロール転送とは違い、あまり時間的な余裕がありません。

トランザクション単位で受信すると

このシーケンス図では、トランザクションごとにメイン・ルーチンがデータを受け取っています。 このため、一回の転送に対して何度もメイン・ルーチンの手をわずらわせる事になります。 そこで、バッファに1転送分のデータを受け取らせる処理を割り込み処理ルーチンに記述し、メイン・ルーチンには、転送の終了だけを知らせる方式も考えてみました。

この場合、1転送分のデータを入れておくためのバッファを用意しなくてはなりません。 RAMをふんだんに使えるほど容量は多くないので、アプリケーションで使用するバッファにそのまま書き込むとします。 すると、SETUPの構文解析が終わってから、DATA-INトランザクションが終了するまでの間にバッファへのポインタを引き渡さなくてはなりません。 これは、時間的に厳しい方式であると思います。

コントロール書き込みは、無くてもいいか。いいよね。

以前、Tsuneoさんに指摘されたように、エンド・ポイントEP2を追加するとコントロール書き込み転送をサポートしなくてもHIDデバイスとして機能させることができます。 そこで、コントロール書き込み転送については、「考えるだけにして、実装しない」という事にしようと思います。


さあ、役者はそろった。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - ファームウェアに立ち返る (11) [USB]このエントリーを含むはてなブックマーク#

1475712

昨日に続き、SET_ADDRESSリクエストなどで使用される、データ無しコントロール転送のシーケンスを考えてみました。

シーケンス図

UsbControlNoDataTransferSequence.png

データ無しコントロール転送をポーリングするシーケンスを書きました。 昨日のシーケンスと異なり、DATAステージがありません。 この転送では、SETUPトランザクションに全てのデータが含まれています。 そのため、他のデータの授受を行うことなく、STATUSトランザクションを発行すると転送が終わります。 STATUSトランザクションは、コントロール読み出し転送のDATAステージの処理を流用しています。

メインルーチンは、STATUS応答をEP0-INバッファに書き込んだら、転送の終了をポーリングによって待ち、転送完了により実際の処理を行います。 ポーリングを使っているため、転送終了を待つ時間、原則として他の仕事をすることができません。 もったいないな。

ポーリングに代わる方法は無いか

このモデルでは、ポーリングによって転送の終了を待っているので、待ち時間の間、メインルーチンでフラグを監視しなくてはなりません。 これは、重い処理です。

解決策のひとつは、割り込み処理ルーチンに処理を書いてしまう事です。 しかし、この方法は、割り込み処理ルーチンでの処理時間が長くなってしまい、結果としてトランザクションに対する応答時間が長くなってしまいます。 リアル・タイム・オペレーティング・システム(RTOS)であれば、コントロール転送とアプリケーションの二つのタスクに分割するところですが、今の所、RTOSを導入・開発する予定はありません。

もうひとつの解決策として考えたのが、タイマ割り込みを使って、周期的にフラグを確認する方法です。 この方法によると、最大応答時間を割り込み周期で規定できます。 ただし、複雑な処理を行わせると、USB割り込み処理が遅くなってしまいます。

周期的タイマ割り込みを使う事にすると、コントロール転送の処理は、逐次、自動的に処理を行うことが出来ます。 このため、アプリケーション側には、特にコードを書く必要はなく、アプリケーションの処理に専念することができます。


次回は、コントロール書き込み転送、または、タイマ割り込みを使ったUSBの処理について考える予定です。

2008-04-02 20:39 コメント欄の内容を追記しました。

Tsuneoさんからのコメント

>しかし、この方法は、割り込み処理ルーチンでの処理時間が長くなってしまい、結果としてトランザクションに対する応答時間が長くなってしまいます。

多重割込みの無いMCUではこれが悩みの種ですね。 割込みで速い応答時間を保証するため、割込みでの処理を出来るだけ軽くする必要があります。そうすると、タスクスケジューリングに安易に割込みが使えなくなる。メインでスケジューリングをやるか(協調マルチタスク)、なるべく軽いタスクスイッチを割込みに組込む(RTOS)ことになります。

>このモデルでは、ポーリングによって転送の終了を待っているので、待ち時間の間、メインルーチンでフラグを監視しなくてはなりません。 これは、重い処理です。

ポリングが重いかどうかは、コーディングによりますね。 こういう最小ポーリングループだと重いでしょう。フラグが立つまでの時間をすべてポーリングが喰っていますから。

void usb_task( void )
{
  while ( ! setupReady ); // 最小ポーリングループ
  setupReady = 0;
  setup_parser();
  ...
}

しかし、上の最小ポーリングループをメインループに解放してやれば、ポーリング中に他のタスクも実行でき、ポーリングに費やす実行時間は相対的に減ります。

void main( void )
{
  //
  // 初期化
  //
  while(1) { // メインループ
    usb_task();
    application_task();
  }
}

void usb_task( void )
{
  if ( ! setupReady ) return; // ポーリングループを解放
  setupReady = 0;
  setup_parser();
  ...
}

これがメインループを使った協調マルチタスクの原型です。RTOSよりもお手軽なので小規模のシステムでは良く使われます。しかし、 ポーリング周期(タスクの応答時間)がメインループで実行する他のタスクによって決まる - 最大ポーリング周期は、他のタスクの最大実行時間 という大きな制約があります。それぞれのタスクの一回の実行時間を短く保つため、タスクの中身を細かく分割する必要があります。分割は概ね原型のように時間のかかりそうな待ちループの部分で行います。分割されたサブタスクを順次実行するため、ステートマシンやデシジョンツリーが使われます。

例えばUSBでは、

// USBタスクステートマシン
enum {
  st_USB_IDLE,
  st_USB_NODATA_STATUS,
  st_USB_READ_DATA,
  st_USB_READ_STATUS,
  ....
};

unsigned char usb_task_state = st_USB_IDLE;

unsigned char * usb_TX_buf;
unsigned char usb_TX_count;

void usb_task( void )
{
  switch( usb_task_state ) {
    case st_USB_IDLE:
      if ( ! setupReady ) return;
      setupReady = 0;
      setup_parser();
      ....
      break;

    case st_USB_NODATA_STATUS:
      ....
      usb_task_state = st_USB_IDLE;
      break;

    case st_USB_READ_DATA:
      if ( usb_TX_count >= setupData.wLength )
        usb_TX_count = setupData.wLength;
      else {
        usb_add_ZLP = FALSE;
        if ( (usb_TX_count & (EP0_MAXPACKETSIZE - 1)) == 0 )
          usb_add_ZLP = TRUE;
      }
      usb_Write_TX( usb_TX_buf, usb_TX_count, usb_add_ZLP );
      usb_task_state = st_USB_READ_STATUS;
      break;
    ....
    default: break;
  }
}
>もうひとつの解決策として考えたのが、タイマ割り込みを使って、周期的にフラグを確認する方法です。 この方法によると、最大応答時間を割り込み周期で規定できます。

タイマによるフラグは最小応答時間を規定して定期的なタスクの実行を助けます。しかしこの場合も、それぞれのタスクの最大実行時間は、タイマの周期よりも短かくする必要があります。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - ファームウェアに立ち返る (10) [USB]このエントリーを含むはてなブックマーク#

1475712

さあ、いよいよ、わからんようになって来たぞ。

GET_DESCRIPTORリクエストに代表されるコントロール読込み転送のシーケンスを考えます。

やっぱり、割り込みは必要だった

EP0-OUTトランザクション以外には割り込みは必要ないのではないかと考えて試行錯誤をしてきました。 しかし、デスクリプタのように複数のDATA-INトランザクションを送出する場合には、割り込みを使わないとメインルーチンの負荷が重くなってしまい、プログラムが複雑になってしまうことがわかってきました。

そこで、DATA-INシーケンスの開始トリガをメインルーチンで発行したら、あとは自動的にデータが送信されるモデルを作りました。

シーケンス図

UsbControlInTransferSequence.png

このモデルでは、メインルーチン、割り込みサービスルーチン(isr)、EP0-OUT、EP0-IN、ホストオブジェクトとみなして、データとイベントの受け渡しを表現しています。

メインルーチンは、isrセットされたsetupReadyフラグををポーリングして、SETUPデータの解析(parser)を開始します。 データの解析が終わったら、デスクリプタへのポインタと長さを指定して、ep0InStart関数を呼び出します。

この関数の中では、エンドポインタに最初のデータを詰めて、TX0E=1として、DATA-INトランザクションを開始します。 もし、TX0E=1とする前にDATA-INトランザクションが始まっても、NAKが返されるので、ホストを待たせることができます。

二回目以降のデータは、isrが詰めます。 そのため、この間、メインルーチンは、他の仕事をすることが出来ます。

STATUS-OUTが到着したら、isrが返答します。 この時にep0InCompleteフラグが発行されるので、メインルーチンが処理の終了を認識することが出来ます。 メインルーチンが、コントロールIN転送の終了に興味を持つ状況は私には想像できないので、きっとこのフラグは、使われることはないでしょう。

デスクリプタのトランザクションへの分割

MC908JB16のエンドポイントのバッファは、8バイトの大きさしかありません。 そのため、デスクリプタを8バイト以下のトランザクションに分割しなくてはなりません。

USB2.0の仕様書、"8.5.3 Control Transfers"と"8.5.3.2 Variable-length Data Stage"に分割の仕方が記述されています。 先に最大サイズのトランザクションを送り、残りをサイズの小さなトランザクションで送ります。 状況により、データ長の大きさが以下のように決まります。

送信すべきトランザクション
送信要求フラグ残りサイズ可変長トランザクション
NO--送信終了
YES0NO送信終了
YES0YES0バイト送信
YES1-7-1-7バイト送信
YES≤ 8-8バイト送信

「可変長」は、SETUPトランザクションで要求された大きさよりも送信するデスクリプタの大きさが小さかった場合に適用されます。 サイズの大小によって、最後の長さゼロのトランザクションを送るかどうかを決定しなくてはなりません。 なんと、面倒な。

すべてのデータを送り終えたら送信要求フラグを落とします。


コーディングが間に合わない。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - ファームウェアに立ち返る (9) [USB]このエントリーを含むはてなブックマーク#

1475712

GET_DESCRIPTORの処理を考え始めました。 サンプル・プログラムには、簡単に書いてあるのですが、仕様書を読むと色々と制約があるようです。

GET_DESCRIPTORで対応すべきデスクリプタ

標準デバイスリクエストで対応すべきデスクリプタは、USB2.0仕様書の"9.4.3 Get Descriptor"に記述があります。 low-speedデバイスの場合には、デバイス・デスクリプタ、コンフィグレーション・デスクリプタ、ストリング・デスクリプタの三つのリクエストに対応する必要があります。 これ以外のデスクリプタには、対応することは出来ますが、必要なのか、不要なのか、可能なのか、判断がつきません。

では、HIDデバイスとしては、どうかというと、HID1.11仕様書の"7.1.1 Get_Descriptor Request"に書いてありそうです。 HID関連のデスクリプタには、HIDデスクリプタ、レポート・デスクリプタ、フィジカル・デスクリプタの三つがありますが、どれが必須なのか不明確です。 HIDデスクリプタは、標準デスクリプタのコンフィグレーション・デスクリプタに組み込まれています。 だから、単体での対応はしなくてもいいのかな?

フィジカル・デスクリプタは、"6.2.3 Physical Descriptors"に完全にオプションであると書いてあり、「必要が無ければ、ここは読み飛ばしていいよ」とだけ書いてあります。 つまり、必須ではないのは解るですが、その場合、GET_DESCRIPTORリクエストにSTALLで返答すべきか、長さ0のパケットで返答すべきかという、詳しいことは書いてありません。

レポート・デスクリプタは、サンプル・プログラムでも対応しています。 これがないと、トランザクションの長さが決まらないので、必要だとは思います。 でも、本当にこれだけで良いのか、根拠がわからないのです。

bmRequestTypeも見た方がいいと思うんだけど

サンプル・プログラムでは、bRequestだけを見て、GET_DESCRIPTORリクエストであることを認識しています。 つまり、デバイス・リクエストもクラス・リクエストも同じハンドラが処理しています。 きっと、bmRequestTypeも見た方が良いに違いないと考えて、こんなプログラムになっています。

byte parseSetup(void) {
  switch (setupBuffer.bmRequestType & 0x60) {
    case RT_STANDARD:  // Standard Request Decoder:
      switch (setupBuffer.bRequest) {
        :
        case GET_DESCRIPTOR:    // 6
          return getDescriptorSetup();
        :
      }
      break;
    case RT_CLASS:  // Class Request Decoder:
      switch (setupBuffer.bRequest) {
        :
        case GET_DESCRIPTOR:    // 6
          return getClassDescriptorSetup();
        :
      }
      break;
    default:    // Unsupported Request
      return ERR_STALL;
  }
}

GET_DESCRIPTORハンドラが、二つの関数に分かれてしまいました。 それぞれ、標準とHID特有のデスクリプタの処理を行っています。 きっと、これで正しい処理になるんだろうけど、かなり煩雑です。 まあ、今は、妥協せずにこのまま続けます。

2008-03-30 13:55 コメントで頂いた情報を転記

このHIDデバイスで実装すべきGET_DESCRIPTOR

コメントに頂いたように、以下のデスクリプタを実装すれば十分との事です。

このHIDデバイスのGETDESCRIPTORの実装
bmRequestType wValue wIndex 備考
%1000_0000 (Standard Device) $01_00 (device:0) $00_00 (ZERO) デバイス・デスクリプタを返します。
$02_00 (configuration:index0) $00_00 (ZERO) コンフィグレーション・デスクリプタを返します。インデックス0だけに応答します。
$03_0X (string:index0-2) $04_09 (LangID US English) ストリング・デスクリプタを返します。文字列を二つ定義したので、インデックス0から2の範囲で応答します。
%1000_0001 (Standard Interface) $21_00 (HID:ZERO) $00_00 (interface0) HIDデスクリプタを返します。インターフェース0にだけ応答します。
$22_00 (report:index0) $00_00 (interface0) レポート・デスクリプタを返します。インターフェース0にだけ応答します。また、インデックス0だけに応答します。

USB2.0仕様書の"9.6.7 String"によると、 ストリング・デスクリプタで有効な言語ID一覧は、ストリング・デスクリプタのインデックス0に入っています。 この一覧だけは、指定された言語IDに関わらず同じものが返ってきます。 すると、GET_DESCRIPTORリクエストの実装では、言語IDのValidationは、省略したほうが賢明なのかな?

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

USBプロジェクト - ファームウェアに立ち返る (8) [USB]このエントリーを含むはてなブックマーク#

08-03-28_13-13.jpg

そんなに急いで、何処へ行く。

USBにとって、割り込みが必要な仕事はどれなんだろう。

急ぎの仕事は何ですか?

「少なくともSETUPトランザクションだけは、取りこぼしが無いように」と考えて、必要のない部分を割り込みハンドラから出してみました。 その結果、ほとんどの仕事は、割り込みハンドラに居る意味がないとわかってきました。 あれ?急ぎの仕事って、どれだったっけ。

SETUPを取りこぼす原因となっていたのは、EP0に到着した、OUTトランザクションです。 このトランザクションを早く処理しないと、次に来るかもしれないSETUPトランザクションを取りこぼす可能性があります。 でも、割り込みまで駆使して処理しなくてはならないのは、これだけなんですよね。

データ無しコントロール転送の場合

データ無しコントロール転送は、SETUPトランザクションに続いて、STATUS-INトランザクションが到着します。 このとき、TX0E=0としておくと、マイコンはホストに対してNAKを返します。 従って、あわてて処理をする必要はありません。

データ付きコントロールIN転送の場合

データ付きコントロールIN転送は、SETUPトランザクションに続いて、DATA-INトランザクションが到着します。 ここでも、TX0E=0としておくと、マイコンはホストに対してNAKを返します。 従って、あわてて処理をする必要はありません。 割り込みハンドラの外で、エンドポイントのバッファにデータを詰めてからTX0E=1とするだけで、データがホストに渡ります。

その後のSTATUS-OUTトランザクションに対する応答は、次に発生するであろうSETUPトランザクションが迫ってきているかもしれないので、すみやかに処理する必要があります。

コントロールOUT転送の場合

コントロールOUT転送は、SETUPトランザクションに続いて、DATA-OUTトランザクションが到着します。 エンドポイントにSETUPパケットが残ったままの状態(RXD0F=1)にしておくと、マイコンは、ホストに対してNAKで応答します。 ここだけ考慮するなら、割り込みハンドラ内で早めにRXD0FR=1とする事には意味がないし、しないほうが良いはずです。 ところが、後で述べるトランスファの中断という事態を考えるとSETUPパケットをそのまま残すわけにもいきませんので、すみやかに処理を行うことになります。

転送の最後にSTATUS-INトランザクションが到着しますが、これもTX0E=0としておくと、マイコンは、ホストに対してNAKで応答します。 急いで受信したDATA-OUTトランザクションの処理がすっかり終わって、暇になってからTX0E=1にするだけで十分です。

トランスファが中断される場合

処理中のトランスファが中断される場合には、予期せずSETUPトランザクションが到着する事態も考えられるので、EP0-OUTバッファは、常にあけておく必要があります。 そのため、RXD0Fイベントを割り込みで優先的に処理する必要があります。

インタラプト転送の場合

インタラプト転送は、エンドポイントを片方向で使用するため、TX1ERX2Eをマイコンの都合で制御することができます。 そのため、割り込みで急いで処理する必要はありません。

まとめると

こうして、整理して考えてみると真に割り込みでの処理が必要なのは、SETUPEP0-OUTが到着するRX0DFイベントだけという図式が見えてきます。 割り込み機能が装備されているから、他のイベントにも割り込み処理が必要かと思っていたら、他のイベントは、アプリケーションの都合に合わせて動作させても良いみたいです。

これをアプリケーションに適用する時の問題点は、アプリケーション・タスクと割り込みタスクに加えて、USBの入出力タスクという新たなタスクが必要になるというところです。 あまり、手をかけずに「マルチ・タスク」みたいな事をさせるには… Visual BASIC の DoEvents 式にすると、可能は可能だな。 UNIX で言うところの yeild() ですね。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

仙台駅から電車に乗って~ [USB]このエントリーを含むはてなブックマーク#

08-03-28_13-13.jpg

きょう~わたぁしは~ 旅にでまぁす~

久々に新幹線に乗る用ができたので、車内でMC908JB16データシートのUSBの項を読みました。

SUSPENDとRESUME

SUSPENDとRESUMEについての解説を読みました。 バスがINACTIVEになった状態が3msec以上続いたら、SUSPEND状態に入ると書いてあります。 そのときには、SUSPNDビットをセットして、低消費電力状態にして、最大500uAの規格を満たせとも書いてありました。 500uAを達成するためには、マイコンはSTOP状態を使うしかなさそうです。

SUSPEND状態からは、hostがバスの状態を20msec以上反転してから、End-Of-Packet(EOP)状態(D+端子とD-端子の双方を2ビット時間LOWに引くこと)にすることで、各デバイスが目覚める(RESUME)のだそうです。 じゃあ、バスの状態を反転させただけだったら、デバイスは目覚めてはいけないのか? 反転状態が20msecの間継続しなかったら、デバイスは目覚めてはいけないのか?

この疑問に対しては、20msecの根拠が書いてあるので、ある程度推測することができます。 20msecという時間は、全てのデバイスが目覚めるために必要な時間であると書いてあります。 そのため、実際には、バスの状態が反転したときに、全てのデバイスがお目覚めシーケンスを開始して、20msec後に全てのデバイスが出揃うという事になるのでしょう。

hostは、EOP状態にしてから、3msec以内にパケットを送ります。 そうしないと、デバイスが再びSUSPEND状態に入ってしまうからです。 このため、デバイスは、最短20msecでSUSPEND状態から抜け出し、パケットを受信する準備を整えなくてはなりません。 PLLがロックする時間は、数十msecなので、これは厳しい値です。 本当に間に合うのかな?


今日は、乗車時間が短かったので、SUSPENDとRESUMEを読むのが精一杯でした。


USBプロジェクト - ファームウェアに立ち返る (7) [USB]このエントリーを含むはてなブックマーク#

1516092

今日は、「SETUPトランザクションが到着したら、残りのデータは破棄すべきなのか」について考えるのココロだ~

SETUPトランザクションを常に優先すべきか

前回の記事に対するコメントで、SETUPトランザクションは、無視すべきではなく、到着時にバッファに残っているデータを破棄すべきとの指摘を受けました。 全ての場合において、本当にそうなのかと考えて、検証してみました。

UsbDiscardSetupOrStatus.png

このシーケンス図は、INコントロール転送を受けたデバイスが、DATA-INステージの処理に手間取って、次のSETUPステージを取りこぼした例です。 DATA-INステージでは、例によってRX0E=0としているので、SETUPステージは、無視されています。

もし、SETUPステージが常時受け付け可能で、これを優先して受け付けたとすると、 cpuは、STATUSステージの受信を認識することができません。 ところが、hostは、STATUSステージを送信し、ACKを受け取ることが出来ます。 つまり、cpuhostで情報の不一致が起こることになります。

何が問題なのか

私が考えるに、 問題は、SETUPパケットのバッファとOUTパケットのバッファが同じところにあるからではないでしょうか。 もし、別々のバッファがあれば、OUTパケットの処理に手間取っても、別のバッファでSETUPパケットを受け取ることが出来ます。

ただし、その場合でも、OUTパケットとSETUPパケットのうち、どちらが先に到着したかによって処理が異なってくるので、「優先すべきパケットは、コッチだ。」フラグが欲しくなります。

バッファのデータは、さっさと引き取るべし

MC908JB16は、そういった構成になっていないので、とにかく、到着したトランザクションを遅滞無く処理していくことが必要になると思います。

  • エンド・ポイントのデータは、さっさと一時バッファに引き取り、次のトランザクションに備える。
  • 割り込みハンドラ内で処理をしない。

割り込みハンドラ内でも、「待った」のきかないSETUPを確実に処理するために、全ての割り込み処理が終わるまで、割り込みハンドラを抜けない工夫も必要なのではないでしょうか。

USB_isr(void) {
  for (;;) {
    if (UIR1_RXD0F) {   // EP0 OUT packet
       :
      continue;
    }
    if (UIR1_TXD0F) {   // EP0 IN packet
       :
      continue;
    }
    if (UIR1_RXD1F) {   // EP1 OUT packet
       :
      continue;
    }
    :
    break;
  }
}

これ以上は、処理速度の速いエンジンかプロセッサを持ってくるしかないでしょうか。

付録 : USBプロジェクト索引

参考文献

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)

  • 作者: インターフェース編集部
  • 出版社/メーカー: CQ出版
  • 発売日: 2006/07
  • メディア: 単行本

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。