5

STM32_USB-FS-Device_Lib_V3.2.1 USBライブラリでSTM32F105マイクロコントローラーを使用しており、VCPの例を目的(RTOSおよびシリアルAPIとの統合)に適合させています。

問題は、USBケーブルが接続されているが、Windowsホストでポートが開いていない場合、数分後、ポートが開くまでデバイスがUSB ISRに永続的に再入して、すべてが正常に動作し始めることです。

割り込みハンドラーをインストルメントしましたが、障害が発生すると、ISRハンドラーが終了し、すぐに再入することがわかります。これは、割り込みの終了時にOTG_FS_GINTSTSのIEPINTフラグがクリアされていないために発生します。この時点でのOTG_FS_DAINTには0x00000002(IEPINT1セット)が含まれていますが、DIEPINT1には0x00000080(TXFE)が含まれています。TXFEをクリアするOTGD_FS_Handle_InEP_ISR()の行が呼び出されますが、ビットはクリアされないか、すぐに再アサートされます。ホストのCOMポートが再度開かれると、割り込み終了時のOTG_FS_GINTSTSおよびOTG_FS_DAINTの状態は常にゼロになり、それ以降の割り込みは通常のレートで発生します。この問題は、データが出力されているが、ホストに開いているポートがない場合にのみ発生することに注意してください。ポートが開いているか、データが出力されていない場合、システムは無期限に実行されます。

VCPコードには、次の列挙値をとる状態変数があります。

  UNCONNECTED,
  ATTACHED,
  POWERED,
  SUSPENDED,
  ADDRESSED,
  CONFIGURED

そして、CONFIGURED状態を使用して、送信のためにデータをドライバーバッファーに入れるかどうかを決定します。ただし、CONFIGURED状態は、ホストがポートを開いてアプリケーションが接続されているときではなく、ケーブルが接続されているときに設定されます。Windowsがポートを開くと、割り込みのバーストが発生するため、このイベントで何らかの通信が発生しているように見えます。したがって、ホストがポートを開いているかどうかを検出できるかどうか疑問に思います。

おそらく次の2つのうちの1つが必要です。

  1. USBコードが最初にISRでスタックするのを防ぐため
  2. ホストがデバイス側からポートを開いているかどうかを判断し、開いているときにのみデータをプッシュして送信します。
4

7 に答える 7

7

パート (1) - 割り込みロックアップの防止 - は、ST サポートによる USB ライブラリのバグ修正によって促進されました。TxEmpty 割り込みを正しくクリアしていませんでした。

STサポートからの調査と支援の後、パート(2)の解決策を決定しました - ホストポートが開いているかどうかを検出します。通常、ポートが開かれると、DTR モデム制御ラインがアサートされます。この情報は CDC クラスのデバイスに渡されるため、これを使用して目的を達成できます。アプリケーションが DTR の動作を変更することは可能ですが、この場合、このデバイスに接続する可能性のあるクライアント アプリケーションのいずれかでこれが発生することはありません。ただし、回線コーディング (ボー、フレーミング) が設定されている場合にポートが開いていることを暗黙的に想定するバックアップ計画があります。この場合、閉鎖を検出する手段はありませんが、少なくとも、型にはまらないアプリケーションが私のデバイスで動作するのを防ぐことはできません。

具体的には、ST の VCP サンプル コードに関して、usb_prop.c に次の変更を加えました。

1) 以下の機能を追加しました。

#include <stdbool.h>
static bool host_port_open = false ;
bool Virtual_Com_Port_IsHostPortOpen()
{
    return bDeviceState == CONFIGURED && host_port_open ;
}

2) Virtual_Com_Port_NoData_Setup() の SET_CONTROL_LINE_STATE の処理を​​次のように変更しました。

else if (RequestNo == SET_CONTROL_LINE_STATE)
{
  // Test DTR state to determine if host port is open
  host_port_open = (pInformation->USBwValues.bw.bb0 & 0x01) != 0 ;
  return USB_SUCCESS;
}

3) 従来の DTR を操作しないアプリケーションで使用できるようにするために、Virtual_Com_Port_Data_Setup() の SET_LINE_CODING の処理も次のように変更しました。

  else if (RequestNo == SET_LINE_CODING)
  {
    if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
    {
      CopyRoutine = Virtual_Com_Port_SetLineCoding;

      // If line coding is set the port is implicitly open 
      // regardless of host's DTR control.  Note: if this is 
      // the only indicator of port open, there will be no indication 
      // of closure, but this will at least allow applications that 
      // do not assert DTR to connect.
      host_port_open = true ;

    }
    Request = SET_LINE_CODING;
  }
于 2011-03-25T12:19:13.230 に答える
2

CDC_Transmit_FS を採用することで別の解決策を見つけました。_write 関数を上書きすることで、printf の出力として使用できるようになりました。

最初に接続状態をチェックし、次にビジー ループで USB エンドポートを介して送信を試みます。これは、USB がビジーである場合に送信を繰り返します。

dev_state が USBD_STATE_CONFIGURED でない場合、USB プラグが切断されていることがわかりました。プラグが接続されているが、VCP ポートが PuTTY またはシロアリ経由で開いていない場合、2 番目のチェックは失敗します。

この実装は、RTOS および CubeMX HAL アプリケーションでうまく機能します。ビジー ループは優先度の低いスレッドをブロックしなくなりました。

uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)
{
    uint8_t result = USBD_OK;


    // Check if USB interface is online and VCP connection is open.
    // prior to send:
    if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
            || (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
    {
        // The physical connection fails.
        // Or: The phycical connection is open, but no VCP link up.
        result = USBD_FAIL;
    }
    else
    {

        USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len);

        // Busy wait if USB is busy or exit on success or disconnection happens
        while(1)
        {

            //Check if USB went offline while retrying
            if ((hUsbDevice_0->dev_state != USBD_STATE_CONFIGURED)
                        || (hUsbDevice_0->ep0_state == USBD_EP0_STATUS_IN))
            {
                result = USBD_FAIL;
                break;
            }

            // Try send
            result = USBD_CDC_TransmitPacket(hUsbDevice_0);
            if(result == USBD_OK)
            {
                break;
            }
            else if(result == USBD_BUSY)
            {
                // Retry until USB device free.
            }
            else
            {
                // Any other failure
                result = USBD_FAIL;
                break;
            }

        }
    }

    return result;
}

CDC_Transmit_FS は _write によって使用されます。

// This function is used by printf and puts.
int _write(int file, char *ptr, int len)
{
    (void) file; // Ignore file descriptor
    uint8_t result;

    result = CDC_Transmit_FS((uint8_t*)ptr, len);
    if(result == USBD_OK)
    {
        return (int)len;
    }
    else
    {
        return EOF;
    }
}

よろしくベルンハルト

于 2016-01-13T12:52:48.450 に答える
1

おそらく次の2つのいずれかが必要です。

  1. 最初のインスタンスで USB コードが ISR にスタックするのを防ぐには
  2. ホストがデバイス側からポートを開いているかどうかを判断し、開いている場合にのみ送信するデータをプッシュします。

オプション 2 の代わりにオプション 1 を実行してみてください。Windows と Linux では、COM ポートを開いて、制御信号を設定せずに使用することができます。 COM ポートが開いています。

適切にプログラムされたデバイスは、USB ホストがデータのポーリングを停止したからといって、機能を停止することはありません。これは、適切に処理する必要がある通常のことです。たとえば、コードを変更して、エンドポイントに使用できるバッファー領域がある場合にのみ、USB ホストに送信されるデータをキューに入れることができます。空きバッファ領域がない場合は、特別なエラー処理コードが含まれている可能性があります。

于 2015-08-26T00:14:47.120 に答える
1

非常に多くの検索と一種のリバース エンジニアリングを行った後、最終的にオープン ターミナルとその終了を検出する方法を見つけました。CDC クラスには 3 つのデータ ノードがあり、1 つはコントロール ノードで、残りの 2 つはデータ入力ノードとデータ出力ノードです。ターミナルを開くとコードがコントロール ノードに送信され、ターミナルを閉じるとコードが送信されます。 . これらのコードを取得して、データ送信タスクを開始および停止するだけです。送信されるコードは、ターミナルを開閉するためのそれぞれ 0x21 と 0x22 です。 .その関数は CDC_Control_FS です。これで、 0x22 と 0x21 を解釈できるように関数を展開するだけです。

于 2015-08-24T21:58:43.497 に答える
0

PCポートの開閉を検出するという同じ要件があります。私はそれが次のように実装されているのを見ました:

開いていることが検出されました:

  • DTR がアサートされました
  • CDC一括転送

クローズが検出されました:

  • DTR のアサート解除
  • USB「プラグが抜かれました」、スリープなど

これはかなりうまく機能しているようですが、確実に機能することを確認するには、より徹底的なテストが必要です.

于 2011-04-13T00:43:02.823 に答える