2007-08

にっき

開発日記


何故D+、D−をPB1,PB0に繋ぐ例が多いのだろうか?

  • IgorPlugUSBなどを見てもそうなっているようだ。
  • Igorさんの実装はAT90S2313、AT90S2323とATmega8の両方をサポート。
  • 90S2313(20ピン)ではPB1をPD2(INT0)にも接続する。
  • 90S2323(8ピン)はPB1がINT0になっていて、PDが存在しない。
  • 他にもTiny44、Tiny45などの14ピン品種、8ピン品種はPBのみであり、PDが存在しない。

というわけで謎が解けた

  • 8ピン品種と(アセンブラ)ソースをなるべく共用したい場合は、20ピン品種の1本が無駄になることなんて、たいした問題ではないのだ。
  • むしろアセンブラレベルでポートを書き分けるほうが面倒だ。(それに比べ配線1本の手間なんてわずかなもんだ)
  • その点、対比するとAVRUSBのCソースのカスタマイズ性はなかなかのものだ。

AVR_Monitでは、PORTBを完全フリーに出来るというメリットがあるのでconfig設定をPD3,PD2のほうに振ってしまった。

  • が、他の少ピン品種でやりたい場合はPB1,0に戻さなければならないわけだ。
  • もっとも、これは大した手間ではなく、usbconfigの書き換えと、ポート初期化を少し変えるだけで済む。

About IgorPlugUSB ...

  • D+、D−の配線が違うので、配線しなおし。
  • 秋月の片面ユニバーサルは半田はがしをやるとパターンが剥げやすい。
    • というか単純にこてがしょぼいということにして、実は半田の腕が落ちている言い訳。
  • AT90S2313だったけどそのままtiny2313に焼いてみた。
  • きっちり2kBだった。
    • 付属のApplicationを起動して、動作を確認。
    • しかし、TeraTermのCOM3:に出てこない。
    • そゆもの???

こゆものがあった。

  • 世の中便利に出来てるのね。
  • と思ったら、これシェアウェア?
    • デモは19200までで、57600にしたかったら製品を買えという落ちか。

つまり、こういうことか?

  • IgorPlugUSBは、まるでAVR_Monitのような切り口で作られていてDLLを呼び出せばAVRを自由に操れる。
  • ついでにシリアルポートもコントロールしているので、RS232C通信が出来る。
  • だが、付属のアプリケーションウィンドウ内とか、DLLを呼び出す形の端末ソフトを書かない限りRS232Cを受けられない。
  • COM:に変換するWin版ドライバーはシェアウェア。
  • Linuxカーネル2.6系ではIgorPlugUSBをCOM:にするドライバーが書かれている。

こゆこと?

  • 自分はつくづくAVR無知であることを思い知らされる毎日(びしっ!)

結局USB-シリアルコンバータは作れない!?

  • 頼みの綱だったIgorPlugはCOM:になれない。
  • AVR-CDCは最小限に切り詰めてなお256バイト以上足が出ている。tiny2313ではどうあがいても無理っす。
  • あとは、なんちゃってシリアルでも作る?実用性ないけど。

ComEmulDrvについて

http://mixw.net/related.htm#tncemu

  • こんなものがあった。
  • 物理ポートがCOM4まであるとする。
  • こいつはCOM5:とCOM6:を仮想的に作成。
  • 物理ポート、たとえばCOM1:とCOM2:をクロスケーブルで結んでみよう。どうなるか。
  • それと同じことをこのドライバーで行ない、COM5:の送信データはCOM6:で拾えるというわけだ。

こいつは便利かも。

  • だってWin32APIのCreateFile()でCOM5:をオープンするだけでしょ?
  • なんちゃんてCOMポートドライバーをCOM5に繋いでおけば、TeratermからCOM6経由でAVRに接続完了!ナイスアイディア!

試してみた。

  • COMポートに対するReadFileとWriteFileは競合するようだ(別スレッドで動かしても)。非同期モードを使うしかない。

なんちゃってシリアルアダプターの設計

  • シリアル受信バッファ
       0      1      2      3      4      5      6      7
    +------+------+------+------+------+------+------+------+
    | size |         受信データ(最大7バイトまで)         |
    +------+------+------+------+------+------+------+------+
    sizeフィールド下位4bitには0〜7までの数字(バイト数)が入る。
    sizeフィールド上位4bitにはRS232Cのラインステータスを入れる。
  • 受信バッファは2組〜4組用意してラウンドロビン。
  • 受信バッファにはRxDataの割り込みルーチンが直接データを入れる。
  • USBバルク(あるいはコントロール)受信のリクエストが来たら 有効で最も古い受信バッファを送信する。
  • シリアル送信データ
       0      1      2      3      4      5      6      7
    +------+------+------+------+------+------+------+------+
    | size |         送信データ(最大7バイトまで)         |
    +------+------+------+------+------+------+------+------+
  • コントロール転送のときは
       0      1      2      3      4      5      6      7
    +------+------+------+------+------+------+------+------+
    |ReqTyp| cmd  | 送信データ(最大4バイト)| wLength     |
    +------+------+------+------+------+------+------+------+
    cmdフィールド下位5ビットにはCMD_SENDコマンド(0x03)を入れる。
    cmdフィールド上位3ビットが送信データのサイズ。

SPIバスの使い方

ELM ChaNさんの説明

MICOM Journal

最大20MBPS!これは美味しい。

  • SPIはマスターがクロックを送り、スレーブとマスターの8ビットシフトレジスタの値を交換するやりかたでデータ転送する。
  • AVRのSPIプログラミングでは4バイト固定長でコマンドを送る。
  • データ受信は、わりと最後の1バイトの転送時に交換している。
    • コマンド解釈が完了しないとデータの返送はできないから。
    • コマンドは最初の2バイトで確定し、後ろの2バイトはデータだったりダミーだ。

これをRS232代わりにしようとすると、こんな感じか?

  • シリアル送信データ
       0      1    
    +------+------+
    | SEND | char |   SEND = 0xf1、NULL_SEND = 0xf0 とか固定。
    +------+------+
  • シリアル受信データ
       0      1    
    +------+------+
    | RECV | char |   RECV もSENDと同じ。
    +------+------+
  • 上記の2バイトペアを交換するイメージ。
  • 本当は9ビット受け取れるならMSBをデータ有効フラグに出来る。
  • RS232Cは自発的にデータが送られてくるが、SPIの場合、USBのように必ずマスターがスレーブに働きかける必要がある。
  • 2バイトのパケットを互いに交換する。
  • 送信データがない場合はNULL_SEND を送る。charは0にする。
  • 受信データがない場合もNULL_SEND を送る。charは0にする。

もしも、伝送路が文字列に限定され、^@(¥x00)は決して送られないか、捨ててよいというのであれば、1バイト単位の交換で出来る。

  • お互い、送りたいデータがなければ0を送る。
  • あるいは、エスケープ文字コードを1つ設定(めったに送られない文字、たとえば0xFDとか決める)して、エスケープ処理をする。
  • お互い、1バイト単位でバイナリーデータを送る。
  • 送るデータがなければ0を送る。
  • エスケープ文字が来た場合、
    0xFD , 0xF0 == バイナリーの 0x00 に置換。
    0xFD , 0xF1 == バイナリーの 0xFD に置換。
  • とか取り決めておく。

ATtiny2313の12MHzでSPI転送すると仮定して、

  • 3命令のループで1ビット送信を組んだ場合、SPIのマスタークロックが約4MHz
  • 1バイト送るのに約3マイクロ秒くらい?
  • オーバヘッドも考慮して5マイクロとすると、200kB/Sくらい。
  • もちろんAVRにそんな(200kBも)メモリーはないけど。
  • USB LowSpeedの物理速度くらいは超えられる。

むりやりRS232風にせずとも、

uchar read_data = spi_read( write_data );

みたく、交換形式でハンドシェイクすればいいのか。そうか。

  • それはAVR同士の送受信ではOKだが、TeraTermをつないでRS232風にやりとりする場合にちょっと困る。

なんちゃってシリアルアダプターのコーディング

  • コントロール転送版AVR_Monitが役に立った。
  • バルク転送版ファームのコマンド受信部をコントロール転送版に差し替え。
  • 両方のパケットを受け付けられるようになった。
  • monit.exeでAVR_Monitとして作動することを確認。
  • さらにバルクパケットの送受も出来た。

バルク転送でRS232Cの転送を行なう。

  • 受信については、受信割り込みで32バイトのFIFOに貯める。
  • バルク送信のタイミングでそれから7バイト以下を切り出してホストに送る。

ここまでは簡単だが、

  • 送信が意外と難しいことが判明。
  • ボーレートが低いと、送信されたバルクデータをさばけないでAVR側に溜まってしまう。
  • ホストから送るのを待ってもらうしかない。
  • ちょっとまって信号をどうやって返すか、という点と、送信できなかった場合のパケットをどこに保存するか、あるいは再送するかを決める必要がある。

しかたがないので

  • バルク転送は8バイト送って8バイト受けるというルールにする。
  • 常に返送パケットを必要とするようにする。
  • 前回の送信データがさばききれてないときは送信パケットの内容を破棄し、返送パケットに破棄したことを伝えるビットを入れる。
  • ホスト側は再送が必要になる。

このような実装にした場合のスループットは

  • UHCIでのバルク送信/受信の繰り返しだと1パケットに2mS掛かるようなので往復で4mS掛かる。
  • 1サイクルで送受できるデータは、USB側の都合だけだと7バイト以内。
  • 250回掛ける7バイト=1750バイト/秒=14000bps。

どうやったら57600bpsに出来るのだろう????


なんちゃってシリアルアダプターの動作試験

ここまで出来た。

ROM 1982 byte
RAM 119 byte
  • RAMが容量的にやばい.
  • ROMももうない。

どこまで書いたか?

  • RS232C受信割り込みでUSARTからデータを受け取りキューに貯める。
  • AVR_Monitのコントロール転送版にバルク転送を追加してシリアルデータのハンドシェイクを行う。
  • 送信バッファにランダムなデータを7バイト貯めてホストからバルク転送する。
  • AVR側は受け取って送信キューにセット。
  • メインループ内で送信キューを1文字づつ送信。
  • 受信データがあればバルク転送でホストに返送する。
  • AVRのピン2(RxD)とピン3(TxD)を接続し、なんとなくエコーバックしているところまで確認した。

通信速度は?

  • USBのバルク転送は1フレームに1回しか出来ない。
  • UHCIだと2フレームに1回だ(何故か判らないが)
  • 8バイトのバルクパケットを送受するのも同一フレームでは出来ずそれぞれの2倍のフレームを消費する。
  • よって、UHCIでは14000bps、SiSのOHCIに限りその2倍しか速度が出ない。

非同期でのUSB転送が出来れば速いのかもしれないが・・・


USB2.0PCIカードを試す。

同じマシンに2つのホストコントローラがある場合に、速度差があるかどうか調べる。

  • VIA VT6202のカードをSiS746Fに挿してみた。
  • ベンチ結果は以下のとおり
    USBホストコントローラコントロール転送バルク転送
    SiS7001 EHCI(SiS746Fオンボード)8000bytes/秒8000bytes/秒
    VT6202(PCI)2000bytes/秒4000bytes/秒
  • CPUはBarton(1800MHz)
  • 傾向的にはVT6202とVT8237(KM400などのサウスブリッジ)は大体同じ。
  • intelの865系も転送速度は同じくらい。
  • SiSだけが突出して速いのが相変わらず謎。
  • 最初はWindowsの設定の問題(レジストリ?)かとも思ったが、どうやらホストコントローラそのものが速度を決めているような感じ。
  • いくらLowSpeedが遅いからといって、1フレーム(=1mS)に8バイトのペイロードは4回以上送れるはず(1.5Mbps == 1500bit/frame ≒ 150byte /frame ,8バイトにオーバヘッドが3倍あっても6回は送れるだろ?)。
  • ということはだな、ホストコントローラの設計が思い切りタコなのか、
  • libusbかusbドライバーが1フレーム単位に1回しか同期処理出来ないような構造になっているかのどちらかだな。