質問

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

問題は、USBケーブルが接続されているが、ポートがWindowsホストで開いていない場合、数分後にデバイスがPORTが開くまで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

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

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

  1. 最初のインスタンスでUSBコードがISRに閉じ込められないようにするため
  2. ホストがデバイスの端からポートを開いているかどうかを判断し、開いたときに送信のためにデータをプッシュするかどうかを判断します。
役に立ちましたか?

解決

パート(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)Modified 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;
  }

他のヒント

非常に多くの検索と一種のリバースエンジニアリングの後、私は最終的に開いた端子を検出する方法とそれが終了した方法を見つけました。 CDCクラスには3つのデータノードがあり、1つはコントロールノードであり、他の2つはデータとデータアウトノードであることがわかりました。現在、端末を開くときにコードがコントロールノードに送信され、閉じるとコードが送信されます。 。私たちがする必要があるのは、それらのコードを取得し、それらによってデータ送信タスクを開始して停止することです。送信されるコードは、それぞれ端末を開閉するために0x21および0x22です。USB_CDC_IF.Cには、それらのコードを受信および解釈する関数があります(スイッチケースがあり、変数CMDは私たちが話しているコードです) 。その関数はCDC_CONTROL_FSです。ここに、私たちがしなければならないのは、0x22と0x21を解釈するようにその関数を拡張することだけです。ここに、あなたは今、あなたのアプリケーションでポートが開いているかどうかを知っています。

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

  1. 最初のインスタンスでUSBコードがISRに閉じ込められないようにするため
  2. ホストがデバイスの端からポートを開いているかどうかを判断し、開いたときに送信のためにデータをプッシュするかどうかを判断します。

2の代わりにオプション1を実行しようとする必要があります。WindowsとLinuxでは、コムポートを開き、制御信号を設定せずに使用することができます。 comポートが開いています。

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

CDC_TRANSMIT_FSを採用することにより、別のソリューションを見つけました。 _write関数を上書きすることにより、printfの出力として使用できるようになりました。

最初に接続状態をチェックし、次に忙しいループでUSB Endportを送信しようとします。これは、USBがビジーである場合に送信を繰り返します。

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

この実装は、RTOと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;
    }
}

よろしくベルンハルト

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

検出されたオープン:

  • DTRは主張しました
  • CDCバルク転送

検出されたクローズ:

  • DTRがDEASTERED
  • USB「プラグを抜かれていない」、睡眠など

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

免責事項:私はキューブによって生成されたコードを使用し、その結果、HALドライバーで動作します。以前にここで提案されたソリューションは、私のために働かないので、私はそれを見つけました。それは良くありませんが、いくつかの目的のために機能します。

PACKETをCDC_TRANSMIT_FSで送信し、TXSTATEが0に設定されるまで待機しようとすると、開いていないポートの間接的な兆候の1つが発生します。だから私の解決策は、いくつかのタイムアウトを修正することです。

uint16_t count = 0;
USBD_CDC_HandleTypeDef *hcdc =
        (USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;

while (hcdc->TxState != 0) {
    if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
        //here it's clear that port is not opened
    }
}

問題は、ポートを開こうとすると、デバイスがパケットを送信しようとした後、実行できないことです。したがって、私が使用するルーチン全体:

uint8_t waitForTransferCompletion(void) {

    uint16_t count = 0;
    USBD_CDC_HandleTypeDef *hcdc =
             (USBD_CDC_HandleTypeDef*) USBD_Device.pClassData;

    while (hcdc->TxState != 0) {
        if (++count > BUSY_TIMEOUT) { //number of cycles to wait till it makes decision
            USBD_Stop(&USBD_Device); // stop and
            MX_USB_DEVICE_Init(); //            init device again
            HAL_Delay(RESET_DELAY); // give a chance to open port
            return USBD_FAIL; // return fail, to send last packet again
        }
    }

    return USBD_OK;
}

問題は、ポートが開かれている間に送信を中断しないためではなく、タイムアウトがどれほど大きいかということです。 busy_timeoutを3000に設定しましたが、今では機能します。

変数を確認して修正しました hUsbDeviceFS.ep0_state。接続されている場合は5、接続されていない場合、または切断された場合は4に等しくなります。しかし。 HALにはいくつかの問題があります。プログラムが開始されたとき、それは5に等しくなります。次のステップは、プログラムの開始時にそれを修正しました

        /* USER CODE BEGIN 2 */
            HAL_Delay(500);
            hUsbDeviceFS.ep0_state = 4;

...

私はHALを学びたいという願いを持っていません - この投稿が開発者によって見られることを願っています。彼らはHALを修正するでしょう。それは私の問題を解決するのに役立ちました。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top