KSR CDI 第2幕 解決しました!

    どうもこんばんは。


    ハマっていたCDIのプログラムですが、とりあえず解決しました!!

    Microchipのサンプルプログラムのソースコードを変更してうまくUSBで通信できるようになりました。
    自分の理解の範囲内なので100%正しいか自信がないところもありますが、備忘録ということで状況を整理しておきます。


    今回、超長いです。
    あと画像なしのテキストオンリー。

    どのファイルのどの部分のコードを直せばよいのか?結果だけ知りたい人はLet’s Scroll!!


    いつもほぼ役に立たないしょうもない内容ばかりですが、今回ばかりは同じ現象でハマっている人にとってそこそこ有益な情報と思われます。
    ただ、需要がどの程度あるものか?たぶん日本国内でいいところ2~3人くらいだと思いますが...
    バイクブログとは思えないなようであることは自覚をしておりますのでおつきあい下さいとは言いません。
    興味の無い方はスルーでお願いします。


    1.問題の現象
    USBケーブルでPCとPICマイコンを接続してもPC側がUSBデバイスとして認識しない

    2.確認した内容
    2.1 USB通信の手順
    1) PIC24FはUSB2.0に準拠しており、今回、フルスピード(12Mbps)のHIDデバイスとしてプログラムを作成
    2) フルスピードの場合、ケーブル接続時PIC側が自動的にD+ラインをHi(=3.3V)に設定、D-ラインはLow(=0V)のまま
    3) この後、PC側が一定時間D+/D-ラインをLowとし、これによりPIC側が内部リセット
    4) PC側からコントロール転送という転送方式で通信を開始
    5) GetDevicerequestのコマンドが発行されて、PIC側が自分のプログラム内のDescriptorという情報を返信
    6) Descriptorの情報のやり取りの後でアドレス指定、”Configured State"の状態となり、最終的に通信が確立

    2.2 不具合の状況
    まず、USBケーブルを接続してもD+ラインがHiになりません。
    仕方がないのでD+ライン~Vcc(3.3V)間に1.5KΩのプルアップ抵抗を接続すると、PCからリセットがかかっているのが確認できました。(リセットはオシロスコープの波形で確認)

    U1OTGCONというレジスタにD+ラインの内臓プルアップ抵抗を有効にするビットがあるのですが、こいつを設定してもプルアップされたりされなかったりで動作が安定しません。

    2.3 ソースコードの調査
    IOポートにデバック用のLEDをつないでソースコードを追いかけていくと…

    ”usb_device.c”というソースファイルの783行目あたりの↓の処理に入らないことが判明。
    PCとの通信を開始しているにも関わらず、”USBTransactionCompleteIF”が真(=1)になりません。


      if(USBTransactionCompleteIE)
      {
        for(i = 0; i < 4u; i++) //Drain or deplete the USAT FIFO entries. If the USB FIFO ever gets full, USB bandwidth
        { //utilization can be compromised, and the device won't be able to receive SETUP packets.
          if(USBTransactionCompleteIF)
          {
          //Save and extract USTAT register info. Will use this info later.
            USTATcopy.Val = U1STAT;
            endpoint_number = USBHALGetLastEndpoint(USTATcopy);

              ・・・

          }//end if(USBTransactionCompleteIF)
          else
          {
            break; //USTAT FIFO must be empty.
          }
        }//end for()
      }//end if(USBTransactionCompleteIE)



    正常動作時、この一連の処理は↓の感じで流れます。

    PCからコマンド受信

    USBモジュールがU1STATレジスタを更新、同時にU1IRレジスタのBit3のTRNIFを1にセット

    TRNIF=1→USBTransactionCompleteIF=1なので、U1STATの情報を読み出す

    U1STATの情報によってエンドポイントとか入出力の方向が決まるのですが、そのU1STATの情報がセットされないorセット完了を知らせるTRNIFが1にならないことが問題のようです。


    3.そして解決へ
    さて、通信が確立されない状況とプログラムの挙動は把握できました。
    ここまではすぐにたどり着けたのですが、では原因は何か?というところが皆目見当がつきません。

    かなりハードウエアに近いレベルの話のような気がして配線を整理してUSBの信号ラインを最短でPICにつないだり、電源やGND周りの接続を見直したりとかいろいろトライしたのですが、一向に改善しません。
    ネット上にも特に参考になるような情報は見当たらないし...


    気になることはGPSデータロガーのプログラムではUSBデバイスとして認識しているということ。
    よって、Configuration Bitの設定を何度も見直したり、電源、GND周りの電圧や配線を見直したりしていました。
    時間だけがどんどん過ぎていきます。


    最後にGPSデータロガーのプログラムとの違いを一つづつ潰していったところ、ようやく原因にたどり着きました。
    原因となっていたコードはこれ↓。main.cというソースファイルの中にこんな処理があります。


    void SYSTEM_Initialize( SYSTEM_STATE state )

    {
        switch(state)
        {

                case SYSTEM_STATE_USB_START:
                    {
                     unsigned int pll_startup_counter = 600;
                        CLKDIVbits.PLLEN = 1;
                        while(pll_startup_counter--);
            }
                     ・・・

         
    PLLENを設定してwhile文で600回カウントする分待ち時間をとれ、というプログラムです。
    またその説明として↓のコメントが書いてあります。

       //On the PIC24FJ64GB004 Family of USB microcontrollers, the PLL will not power up and be enabled
                //by default, even if a PLL enabled oscillator configuration is selected (such as HS+PLL).
                //This allows the device to power up at a lower initial operating frequency, which can be
                //advantageous when powered from a source which is not gauranteed to be adequate for 32MHz
                //operation.  On these devices, user firmware needs to manually set the CLKDIV<PLLEN> bit to
                //power up the PLL.


    最初はふーん、って感じでスルーをしていたんですが、GPSデータロガーのプログラムではこんな処理は入れていないことに気が付きました。

    そこで、{}の中を全部コメントアウトしてGPSデータロガーのプログラムと同じように CLKDIV = 0x0020; でPLLENを設定するとビンゴ!
    USBの通信ができるようになりました。
    もうほんとにサクッと動いて拍子抜けするほどでした。
    ま、ソフといトウエアなんてそんなものなんでしょうけど。



    結局、Microchipのプログラムのままだと、CLKDIVのレジスタのうち、PLLENだけを設定していたことが原因だったようです。
    というか、ソフトウエアのプロフェッショナルからみたらそんなの当たり前だろって事かもしれませんが...

    修正方法は

    CLKDIVbits.PLLEN = 1; → CLKDIV=0x0020; に変更する
    CLKDIVbits.PLLEN = 1;を実行する前のどこかでCLKDIV=0x0000;のようにCLKDIVのPLLEN以外のbitを設定しておく

    のどちらかでよいみたいですね。
    while文のウエイトはあってもなくても正常に通信できるみたいですが、とりあえず残してあります。


    毎度のことですが、今回も長かった...
    これも毎度のことですが、結論はしょうもない話ということでした。


    ああ、また需要の無い記事を書いてしまいました...
    オチも何もないですが、とりあえず目出度し目出度しということで。


    関連記事

    コメント

    非公開コメント