PICでUSBを!(知識ゼロからのスタートです)
PIC18F14K50のUSB機能を100%自前のソフトで制御する試みです。しかもアセンブラで!
当記事は2009年12月から「TTLでCPUをつくろう!」というタイトルの もとにほとんど毎日連載をしてきたものを再編集したものです。 2011.7.11

前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆PIC18F4550のUSB送受信の仕組み

このあたりでPIC18F4550のUSB送受信の仕組みについて簡単に説明をしておくことにします。

[第55回]

●WriteFile()の続きです

本日もWriteFile()についてのお話です。
[第52回]で、WriteFile()関数を使って、文字列を連続してUSB(HID)で送信するテストを行いました。
送信側のWindowsマシンのプログラムは、とくにタイミングなどを考慮せずに連続してWriteFile()を繰り返し実行しました。

一方、受信側のPIC18F4550は、その文字列をUSB(HID)で受信する度に、その受信したデータをRS232C(RS485)で当社のBASICボードZB25Kに送信しました。
RS232Cのボーレートは9600ボーですが、USB(HID)の通信速度とは比較になりません。

RS232Cでは1バイト8ビットのデータを送信するのに、スタートビットとストップビットを付加しますから、1文字あたり10ビットの送信が必要です。
9600ボーは9600ビット/secと考えることができますから、960文字/secの送信速度になります。

Windowsマシンが1回のWriteFile()の実行でPIC18F4550に送信した文字列は約40バイトですから、これはUSB(HID)では1msecで送信してしまうことのできるデータ量です。
しかしそれを受信したPIC18F4550が、RS232Cで同じ文字数のデータを送信するには、40/960≒0.042ですから、40msec以上もかかってしまいます。

WindowsマシンとPIC18F4550との間でハンドシェイクができていなかったら、PIC18F4550からZB25KへのRS232C送信はまともに行うことはとてもできません。
PIC18F4550はUSBで送られてくるデータを受信するバッファをもってはいますが、このテストで使ったテストプログラムの仕様では、受信バッファはHIDでの受信データ1回分64バイトのバッファしかありませんから、連続してデータが送られてきたら、HIDのデータを受信するだけで精一杯で、それをRS232Cで送信する余裕はとてもありません。

しかし[第52回]で行った連続送信のテストでは、Windowsマシンから連続して送信した文字列データは、その全てが誤り無くZB25Kに送られました。
ということは、USB(HID)では、ホストからHIDデバイスにデータを送信するときに、デバイス側が受信できる状態になるまで、ホストからの送信を待たせておくことができる、ことになります。

実はPIC18F4550のプログラムで、ホストからの送信に対して、READY/NOT READYの状態にすることができるはずで、それがうまく機能するかどうかのテストでもあったのです。
その結果は、ご覧いただいた通りで、どうやらうまくいったようですので、とりあえずホストからデバイスへのHID送信については、実用レベルにまできた、と考えてよいのでは、と思います。

PIC18F4550がホストからの送信を待たせるための仕組みについては、いずれPIC18F4550側のプログラムの説明をするときに、詳しく説明をすることになろうか、と思います。
ですのでここでは簡単にその概略のみを説明することにいたします。

●PIC18F4550のUSB送受信の仕組み

PIC18F4550はチップ内にUSB通信に必要な全てを備えています。
ですから外付けの回路は全く必要ありません。
ダイレクトにUSBラインに接続することができます([第54回]PIC18F4550回路図)。

USB信号はD+、D−の2線で伝達される差動信号です。
ホストのUSBコネクタからはD+、D−のほかに、V+、GNDの2線が出ていて、V+、GNDは+5Vで最大500mAの電源としてデバイス側が使うことができますが、D+、D−信号の電圧は約3Vです。

PIC18F4550の内部で、ホストからの受信信号はD+、D−の差動信号からロジックレベルのシリアル信号を生成し、またホストへの送信信号は、ロジックレベルのシリアル信号をD+、D−の差動信号に変換してホストに送出します。

●USB Serial Interface Engine(SIE)

PIC18F4550は内部に、CPUとは別に独立して動作するUSBコントローラ(USB Serial Interface Engine,USBSIE)を持っています。
このUSBコントローラ(SIE)に対して、プログラムによって、必要なパラメータさえ与えておけば、USBコントローラ(SIE)は、CPUの動作とは全く関係無く、ホストとの間で自動的にデータの送受信を行います。
この送受信は、[第32回]から[第35回]で説明をしましたUSBのプロトコルにしたがって、USBコントローラ(SIE)が自動的にこなしてくれますから、プログラムがそれに携わる必要は全くありません。


[出典]Microchip社PIC18F4550DataSheet

●USBRAM

PIC18F4550は内部に、USBの送受信バッファとして利用できる1KBのメモリを持っています。
このメモリはCPUからもUSBコントローラからもアクセスできる「双方向」のメモリです。
その1KBのメモリは任意の範囲だけをUSBバッファとして使うこともまた使わないこともでき、USBバッファとして使わない範囲のメモリは、通常のプログラムのワークエリアとして使うことができます。


[出典]Microchip社PIC18F4550DataSheet

●Buffer Ownership

USBバッファとして使う(あらかじめプログラムでそのように定義します)範囲のメモリは、CPUとUSBコントローラ(SIE)が、同時にはアクセスしない仕組みが必要です。
CPUとUSBコントローラ(SIE)がお互いに相手の状況とは全く無関係にUSBバッファをアクセスしたら、めちゃくちゃになってしまいます。
で、PIC18F4550では、そこのところの交通整理をするために、CPUとUSBコントローラ(SIE)の間で「USBバッファへのアクセス権」とでもいうべき機能が用意されています。それがBuffer Ownershipです。
そのアクセス権(Ownership)の設定は、当然のことながらCPUに優先権があって、プログラムで設定ができるようになっています。
下記引用文の中でBDとあるのは、Buffer Descriptorのことです。BDについてはこのあとの項目で説明をします。


[出典]Microchip社PIC18F4550DataSheet

それほど難しい文章ではありませんから、ぜひ皆様もお読みになってくださいませ。
次の項目で説明するBDnSTATレジスタのビット7(UOWNビット)を0にする(clear)と、BDとバッファのアクセス権はCPUが所有し(is ”owned” by the microcontroller core)、UOWNビットを1にする(set)と、アクセス権はUSBコントローラ(SIE)が所有します(are ”owned” by the USB peripheral)。

UOWNビットが0のときは、USBコントローラ(SIE)はバッファにアクセスすることができません。
このときはUSBコントローラ(SIE)はホストからデータを受け取ることも、ホストに対してデータを送ることもできません。
ここが重要なところです(今回は、ここのところを説明するために、長々と引用をしています)。
つまり、UOWNビットを0にして、バッファのアクセス権をCPUが持つようにしている間は、ホストがデータをWriteFile()で送信しようとしても、それを待たせておくことができるわけです。

UOWNビットが1のときには、USBコントローラがホストからのアクセスに応えて自動的にバッファのデータを送出したり、ホストからのデータをバッファに書き込みます。
ホストとの間でのデータの転送が完了すると、UOWNビットは0になります。

ここで注意しなければならないことは、UOWN=1のとき、つまりバッファのアクセス権がUSBコントローラ(SIE)にあるときには、メモリアクセスをブロックする仕組みは働かない(No hardware mechanism exists to block access when the UOWN bit is set)、ということです。
CPUはそのときでもバッファにアクセスできてしまいますから、たとえばUSBコントローラがバッファにアクセスしているときに、CPUがバッファの値を読み出そうとしたりすると、不正確な値が読み出されてしまうなどの可能性があります。
ですからUOWNビットが1である間は、CPUがバッファをアクセスすることのないようにプログラムする必要があります。

●Buffer Descriptors(BD)

エンドポイント毎にバッファアドレスやバッファのサイズ(バイト数)を設定するためのレジスタで、アドレス0400〜04FFのメモリに置きます。
エンドポイントあたり4バイトを割り当てますので、最大64個のエンドポイントを定義することができます。
USBのエンドポイントは0〜15の16個なのですが、なぜ最大64個なのかについては、もう少しあとの項目で説明をします。

第1バイト(BDnSTAT)はそのエンドポイントのバッファの状態や性格などを定義する、ステータスレジスタです。
このビット7が上で説明したUOWNビットです。

第2バイト(BDnCNT)はバッファのサイズ(バイト数)です。
ということは、バッファの最大サイズは256バイト、ということになりますが、じつは256バイトよりも大きいバッファを定義することもできます(最大1023バイト)。
その場合のバッファサイズのビット8とビット9はBDnSTATレジスタのビット0とビット1を使います(下図REGISTER17−5のBC8とBC9)。

あれ?
でもPIC18F4550のUSBバッファメモリは、このBD定義エリアの0400〜を含めても、07FFまでの1024バイトしかありませんから、実質的には、1023バイトのバッファを使うことは不可能のようです。

まあ、でも、私の場合には、HIDでの使用が前提ですから、そうするとバッファサイズは64バイトですから、バッファが足りなくなることはありません。

第3バイト(BDnADRL)はバッファの下位アドレスで、第4バイト(BDnADRH)は上位アドレスです。


[出典]Microchip社PIC18F4550DataSheet

●BDnSTAT

BDnSTATのビット7(UOWN)を0にすると、BDとUSBバッファのアクセス権をCPUが所有します。
そのときのビット6〜ビット0が示す意味は、ビット7(UOWN)が1のときにビット6〜ビット0が示す意味とは別の内容になります。

こちらがUOWN=0(CPUモード)のときのBDnSTATです。


[出典]Microchip社PIC18F4550DataSheet

こちらがUOWN=1のときのBDnSTATです。


[出典]Microchip社PIC18F4550DataSheet

各ビットの意味については、説明が深くなりすぎますから、ここでは省略いたします。
いずれそのうち詳しく説明するときもあるかと思います。多分ですけれど。

●エンドポイントバッファが最大64個の意味

USBのエンドポイントは0〜15の16個なのですが、なぜ最大64個なのかについての説明です。
エンドポイントは、それぞれにIN(ホスト←デバイス)とOUT(ホスト→デバイス)を分けて定義することもありますから、その場合には32個になりますし、さらにそれぞれをDATA0とDATA1に分けてバッファを用意するとした場合には64個のバッファを使うことになります。ですから最大64個になります。



簡単に説明をするだけのつもりだったのですが、毎度のことで、結構深くなってしまいました。
CPUをつくろう!第482回(2010.4.21upload)を再編集

PICでUSBを![第55回]
2011.7.11upload

前へ
次へ
目次へ戻る
ホームページトップへ戻る