USB Frameworkは無事に動作したのだが、ループの中で常にCDCTxService()を呼び出し続けなければならない構造になっていて、改造しにくい。できればそんなものは割り込みなどで監視してほしいものである。
なので、もう少しコードを眺めてみることにした。そこでわかったこと、試してみたことは、
- usb_config.h の中に、
#define USB_INTERRUPTがあり、PIC24FJ256GB106ではUSBは割り込みで動作している。 - main.c の最後に、BOOL USER_USB_CALLBACK_EVENT_HANDLER()という関数があり、そこでUSB割り込み発生後のユーザー用のハンドラを記述できる。
EVENT_TRANSFERがパケット転送後、EVENT_TRANSFER_TERMINATEDがパケット転送中断後のイベントのようである。Start of Frame は EVENT_SOF 。 - 問題の面倒くさい処理 CDCTxService()は、usb_function_cdc.c の最後にある。
細かくは見ていないが、USBHandleBusy(CDCDataInHandle)を確認し、BUSYでなければ送信データをエンドポイントのサイズに分割しながらメモリ中にコピーして、USBTxOnePacket()を呼び出している。このUSBTxOnePacket()は、usb_device.h の中で
#define USBTxOnePacket(ep,data,len) USBTransferOnePacket(ep,IN_TO_HOST,data,len)
となっている。 - putUSBUART()は、mUSBUSARTTxRam()を呼び出している。その際、cdc_trf_stateがCDC_TX_READYかどうかをチェックしていて、CDC_TX_READYでない場合には、何もせずに帰ってくる。即ち、送信しようとしたデータは捨てられることになるが、事前にUSBUSARTIsTxTrfReady()をチェックしていれば、そのようなことは起きないはず。
- cdc_trf_stateはエンドポイントの初期化でCDC_TX_READYにセット、CDC_TX_COMPLETINGの状態でCDCTxService()が呼ばれるとCDC_TX_READYにセット、mUSBUSARTTxRam()などを呼ばれるとCDC_TX_BUSYにセットされるようである。整理すると、「CDC_TX_READY」→「(送信データセットで)CDC_TX_BUSY」→「(データの終わりのZeroLengthPacket送信で)CDC_TX_ZLP(省略あり)」→「CDC_TX_COMPLETING」→「CDC_TX_READY」となるようである。
- mUSBUSARTTxRam()は渡されたデータのポインタ(アドレス)、長さなどをモジュール内のグローバル宣言の構造体や変数などにコピーするだけ。実際の転送処理はCDCTxService()に任される形になる。
つまり、putUSBUART()などから送信データをポインタ渡しされたら、そのポインタをコピーしておき、CDCTxService()が呼ばれる度に状態をチェックして、送信データをエンドポイントのサイズに分割しながらコピー、送信する、という動作のようである。
これらのことから、以下のようにすれば概ね上手く行くのではないかと考えた。
- 送信データをセットする前に、USBUSARTIsTxTrfReady()をチェックして、CDCTxService()が抱えている未送信データの有無をチェックする。(CDC_TX_READYかチェックする)
- 送信データを準備し、putUSBUART()でそのポインタを教えてやる
- その直後に、一度だけCDCTxService()を呼び出し、送信を開始する。CDCTxService()はデータをエンドポイントのサイズに切り出して最初のパケットを送信する。
- USER_USB_CALLBACK_EVENT_HANDLER()の中のEVENT_TRANSFERのところに、CDCTxService()を追加して、1パケット送信ごとに自動的にCDCTxService()が呼ばれるようにする。割り込み処理内なのでネストしてしまうリスクがあるが、USBUSARTIsTxTrfReady()を実行してから送信処理をしていれば大丈夫なはず・・・。
ということで、実際に試してみた。確認のため、usb_config.hの
#define CDC_DATA_OUT_EP_SIZE 64
#define CDC_DATA_IN_EP_SIZE 64
を共に4に変更してみたところ、送信(PCから見ると受信)に関しては問題ないようである。
しかし、逆方向はファイルを流し込むと送受信を同時に行った際にデータを取りこぼしてしまう。もう少し調べなければ・・・。