Frage

Ich verwende einen STM32F105-Mikrocontroller mit der USB-Bibliothek STM32_USB-FS-Device_Lib_v3.2.1 und habe das VCP-Beispiel für unsere Zwecke angepasst (Integration mit RTOs und serieller API).

Das Problem ist, dass, wenn das USB-Kabel angebracht ist, der Anschluss jedoch nicht auf dem Windows-Host geöffnet ist. Nach einigen Minuten wird das Gerät dauerhaft wieder in den USB-ISR eintreten, bis der Port geöffnet ist und dann alles normal funktioniert.

Ich habe Interrupt-Handler instrumentiert und kann sehen, dass der ISR-Handler beim Auftreten des Fehlers verlässt und dann sofort wieder eintritt. Dies geschieht, weil beim Ausgang aus dem Interrupt das Iepint -Flag in OTG_FS_GINTSTS nicht klar ist. Der zu diesem Zeitpunkt otg_fs_daint enthält 0x00000002 (Iepint1 -Set), während Dipint1 0x00000080 (TXFE) hat. Die Zeile in OTGD_FS_Handle_inep_isr (), die TXFE löscht, wird aufgerufen, aber das Bit löscht sich entweder nicht oder wird sofort erneut wiederhergestellt. Wenn der COM -Port auf dem Host wieder eröffnet wird, ist der Status von OTG_FS_GINTSTS und OTG_FS_DINING am Ende des Interrupts immer Null, und weitere Interrupts treten bei der normalen Geschwindigkeit auf. Beachten Sie, dass das Problem nur auftritt, wenn Daten ausgegeben werden, der Host jedoch keinen Port geöffnet hat. Wenn entweder der Port geöffnet ist oder keine Daten ausgegeben werden, wird das System auf unbestimmte Zeit ausgeführt. Ich glaube, je mehr Daten, die ausgeben, desto früher tritt das Problem auf, aber das ist derzeit anekdotisch.

Der VCP -Code verfügt über eine Statusvariable, die die folgenden aufgezählten Werte nimmt:

  UNCONNECTED,
  ATTACHED,
  POWERED,
  SUSPENDED,
  ADDRESSED,
  CONFIGURED

und wir verwenden den konfigurierten Status, um festzustellen, ob Daten zum Senden in den Treiberpuffer eingesetzt werden sollen. Der konfigurierte Status wird jedoch festgelegt, wenn das Kabel nicht angeschlossen ist, wenn der Host den Port geöffnet und eine Anwendung angeschlossen ist. Ich sehe, wenn Windows den Port öffnet, gibt es eine Ausbrüche von Interrupts, so dass es anscheinend eine gewisse Kommunikation bei diesem Ereignis auftritt. Ich frage mich, ob es daher möglich ist, festzustellen, ob der Host den Port geöffnet hat.

Ich brauche vielleicht eines von zwei Dingen:

  1. Um zu verhindern, dass der USB -Code in erster Linie im ISR stecken bleibt
  2. Um festzustellen, ob der Host den Port über das Gerätebetrieb aus dem Gerät geöffnet hat, und nur Daten zum Senden beim Öffnen voranzutreiben.
War es hilfreich?

Lösung

Teil (1) - Verhinderung des Interrupt -Sperrens - wurde durch einen USB -Bibliotheksfehler der ST -Unterstützung erleichtert. Es war nicht korrekt, den TXEMPTY -Interrupt zu klären.

Nach einigen Nachforschungen und Unterstützung durch ST -Unterstützung habe ich eine Lösung für Teil (2) festgestellt - festzustellen, ob der Host -Port geöffnet ist. Wenn ein Port geöffnet wird, wird die DTR -Modemsteuerlinie konventionell geltend gemacht. Diese Informationen werden an ein CDC -Klassengerät übergeben, sodass ich dies verwenden kann, um mein Ziel zu erreichen. Es ist möglich, dass eine Anwendung das Verhalten von DTR ändert. Dies sollte jedoch in keiner der Client -Anwendungen geschehen, die in diesem Fall wahrscheinlich mit diesem Gerät verbunden sind. Es gibt jedoch einen Backup-Plan, der implizit davon ausgeht, dass der Port offen ist, wenn die Liniencodierung (Baud, Framing) festgelegt wird. In diesem Fall gibt es keine Möglichkeit, Schließung zu erkennen, aber zumindest verhindert es nicht, dass eine unkonventionelle Anwendung mit meinem Gerät arbeitet, selbst wenn sie dann zum Abbrechen des Trennens abstürzt.

In Bezug auf STs VCP -Beispielcode habe ich speziell die folgenden Änderungen an USB_prop.c vorgenommen:

1) Die folgende Funktion fügte hinzu:

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

2) Modifizierte virtuelle_com_port_nodata_setup () Handhabung von set_control_line_state also:

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) Um die Verwendung mit Anwendungen zuzulassen, die DTR nicht betreiben, habe ich auch virtual_com_port_data_setup () Handhabung von set_line_coding so geändert: somit:

  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;
  }

Andere Tipps

Nach so viel Suchen und einer Art Reverse Engineering fand ich schließlich die Methode, um das offene Terminal zu erkennen, und auch die Beendigung. Ich fand heraus, dass in der CDC -Klasse drei Datenknoten vorhanden sind, einer Steuerknoten und die anderen beiden sind Daten in und Daten aus Knoten. . Alles, was wir tun müssen, ist, diese Codes zu erhalten und unsere Datenübertragungsaufgaben zu starten und zu stoppen. Der gesendete Code ist jeweils 0x21 bzw. 0x22 zum Öffnen und Schließen des Terminals. In der USB_CDC_IF.c gibt es eine Funktion, die diese Codes empfängt und interpretiert (es gibt einen Switch -Fall und die Variable -CMD ist der Code, über den wir sprechen). Die Funktion ist cdc_control_fs. Hier sind wir, jetzt müssen wir nur noch diese Funktion erweitern, damit sie die 0x22 und 0x21 interpretiert. Da sind Sie, jetzt wissen Sie in Ihrer Bewerbung, ob der Port geöffnet ist oder nicht.

Ich brauche vielleicht eines von zwei Dingen:

  1. Um zu verhindern, dass der USB -Code in erster Linie im ISR stecken bleibt
  2. Um festzustellen, ob der Host den Port über das Gerätebetrieb aus dem Gerät geöffnet hat, und nur Daten zum Senden beim Öffnen voranzutreiben.

Sie sollten versuchen, Option 1 anstelle von 2. unter Windows und Linux zu machen, können Sie einen COM-Anschluss öffnen und ohne die Steuersignale festlegen, was bedeutet Com Port ist geöffnet.

Ein gut programmiertes Gerät lässt sich nicht aufhören zu funktionieren, nur weil der USB -Host die Umfragen für Daten gestoppt hat. Dies ist eine normale Sache, die richtig behandelt werden sollte. Sie können beispielsweise Ihren Code so ändern, dass Sie nur Daten an den USB -Host anstellen, wenn für den Endpunkt Pufferraum verfügbar ist. Wenn es keinen kostenlosen Pufferraum gibt, haben Sie möglicherweise einen speziellen Fehlerbehandlungscode.

Ich habe eine andere Lösung gefunden, indem ich cdc_transmit_fs übernommen habe. Es kann jetzt als Ausgabe für Printf verwendet werden, indem sie die Funktion _write überschreiben.

Zuerst überprüft es den Verbindungsstatus, dann versucht es, USB -Endport in einer geschäftigen Schleife zu senden, die sich wiederholt, wenn USB beschäftigt ist.

Ich habe herausgefunden, ob Dev_State nicht USBD_State_Configured ist, der USB -Stecker ist getrennt. Wenn der Stecker angeschlossen ist, aber kein VCP -Anschluss über Putty oder Termite geöffnet ist, schlägt der zweite Scheck fehl.

Diese Implementierung funktioniert für mich für RTOs und Cubemx HAL -Anwendung. Die geschäftige Schleife blockiert keine Threads mit niedriger Priorität mehr.

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 wird von _write verwendet:

// 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;
    }
}

Grüße Bernhard

Ich habe die gleiche Anforderung, den PC -Anschluss offen/schließen zu erkennen. Ich habe gesehen, dass es wie folgt implementiert wurde:

Offen erkannt von:

  • DTR behauptet
  • CDC -Bulk -Übertragung

Schließen von:

  • DTR Deasserted
  • USB "Unplugged", Schlaf usw.

Dies scheint einigermaßen gut zu funktionieren, obwohl gründlichere Tests erforderlich sind, um zu bestätigen, dass es robust funktioniert.

Haftungsausschluss: Ich verwende Code, der von Cube generiert wird, und als Ergebnis funktioniert er mit HAL -Treibern. Lösungen, die hier zuvor vorgeschlagen wurden, funktionieren nicht für mich, also habe ich einen gefunden. Es ist nicht gut, funktioniert aber für einige Zwecke.

Eines der indirekten Anzeichen für nicht geöffneten Ports entsteht, wenn Sie versuchen, Paket per CDC_TRANSMIT_FS zu übertragen, und dann warten, bis TXState auf 0 ist. Wenn der Port nicht geöffnet ist, kommt es nie zu. Meine Lösung ist also, einige Zeitüberschreitungen zu beheben:

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
    }
}

Das Problem ist auch, dass, wenn man versucht, Port zu öffnen, nach dem Versuch, ein Paket zu senden, nicht möglich ist. Daher verwende ich die ganze Routine:

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;
}

Die Frage ist, wie groß der Zeitpunkt sein muss, um das Getriebe nicht zu unterbrechen, während der Port geöffnet wird. Ich habe Busy_Timeout auf 3000 gesetzt, und jetzt funktioniert es.

Ich habe es behoben, indem ich eine Variable überprüfte hUsbDeviceFS.ep0_state. Es entspricht 5, wenn es angeschlossen ist, und 4, wenn sie nicht verbunden sind oder getrennt wurden. Aber. Es gibt einige Probleme im Hal. Es gleich 5, als das Programm begann. Die nächsten Schritte haben es zu Beginn eines Programms festgelegt

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

...

Ich habe keine Wünsche, die Hal zu lernen - ich hoffe, dieser Beitrag wird von Entwicklern sehen und sie werden die Hal reparieren. Es hat mir geholfen, mein Problem zu beheben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top