Forum: Mikrocontroller und Digitale Elektronik Frage an W.S. wegen USB CDC Implementierung STM32F103


von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo W.S.
Ich habe eine Frage zu deiner USB CDC Implementierung, die du hier schon 
ein paar mal veröffentlicht hast.

Und zwar habe ich den µC mit einem Netzteil versorgt. Der Pull-Up 
Widerstand an D+ ist fest mit 3,3V verbunden (Blue-Pill Board).

Das Programm sende jede Sekunde "Hello World" an den PC. Dann trenne ich 
das USB kabel im laufenden Betrieb. Nach einer gewissen Zeit hängt das 
Programm bei UsbCharOut() weil der Sendepuffer voll ist und voll bleibt.

Wenn ich dann das USB Kabel wieder mit dem PC verbinde kommt aber keine 
Neue Kommunikation mehr zustande. Der virtuelle COM Port wird nicht 
sichtbar, entsprechend kann ich ihn mit dem Terminalprogramm auch nicht 
wieder öffnen.

Hast du einen heissen Tip, wie ich es hinbekommen kann, dass ich das USB 
kabel später erneut verbinden kann ohne den µC resetten zu müssen?

von Patrick C. (pcrom)


Lesenswert?

Fuer deinen uP weisz ich nicht, aber bei mir (Cypress PSOC5LP) hatte ich 
genau dieses problem. Der DTR MUSZ von PC aus gesetzt werden musz bei 
die oefnung der Virtual Serial Port (CDC).
Also im PC programm (bei mir C#)

von Patrick C. (pcrom)


Lesenswert?

Sorry fuer den link zu ein anderes Forum in English aber ich denke 
dieser link beschreibt genau das gleiche problem :
http://www.avrfreaks.net/forum/how-detect-virtual-com-port-open

von Stefan F. (Gast)


Lesenswert?

> Der DTR MUSZ von PC aus gesetzt werden

Mein Problem ist, dass der Reconnect gar nicht funktioniert. Also gibt 
es auch keinen DTR. Die Sache mit dem DTR kenne ich bereits von der CDC 
Implementierung in Arduino.

von Stefan F. (Gast)


Lesenswert?

> dieser link beschreibt genau das gleiche problem

Nein, da geht es auch um die DTR Leitung. Mein Problem ist, das der USB 
Reconnect nicht funktioniert.

von Stefan F. (Gast)


Lesenswert?

Offensichtlich besteht hier ein Missverständnis.

Nach dem ersten Anstecken des USB Kabels funktioniert die Kommunikation 
zum PC.

Aber wenn ich das Kabel abstecke und dann nach längerer Zeit (5min) erst 
wieder anstecke, dann kommt keine neue Verbindung mehr zustande. Der 
virtuelle COM Port wird nicht angelegt.

Wenn ich das Kabel jedoch nur für kurze Zeit abstecke oder keine "Hello 
World" texte sende während es unterbrochen ist, funktioniert der 
Reconnect ohne Probleme.

von Stefan F. (Gast)


Lesenswert?

Ich habe das Experiment nochmal wiederholt und muss meine 
Fehlerbeschreibung ergänzen.

Wenn ich den virtuellen COM Port im Terminalprogramm schließe, bevor ich 
das USB kabel abziehe, wird er beim Reconnect wieder neu bereit gestellt 
und funktioniert dann auch.

Wenn ich es so lange abgesteckt lasse, dass der Sendepuffer voll läuft, 
funktioniert der Reconnect aus Windows Sicht auch (das hatte ich oben 
falsch geschrieben). Aber das Programm auf dem µC bleibt weiterhin 
hängen. Die LED blinkt nicht und das Terminalprogramm empfängt keine 
Daten.

von Stefan F. (Gast)


Lesenswert?

Ich habe inzwischen mal den Debugger eingesetzt, um zu prüfen, dass der 
µC wirklich in UsbCharOut() hängt. jetzt kommt der Witz: Mit der 
Optimierung -O0 tritt der Fehler nicht mehr auf!

Dafür meldet Windows jetzt bei -O1 und -O2 grundsätzlich bei jedem 
Anstecken, dass das Gerät nicht ordnungsgemäß funktioniert.

Mein eigentliches Problem mit dem Reconnect ist so gar nicht mehr 
nachvollziehbar. Dafür habe ich neues größeres Problem bekommen. Grmpf!

Einen Hardwaredefekt habe ich ausgeschlossen, mit Arduino und Cube HAL 
läuft es zuverlässig.

von Stefan F. (Gast)


Lesenswert?

Ich habe die Lösung für beide Probleme gefunden. Bin total Happy!

1) Wegen dem Reconnect: Die Variablen vom Puffer müssen volatile sein.
1
/* die Transfer-Puffer für zeichenweises In und Out über den USB */
2
#define txLen  256
3
volatile char UsbTxBuf[txLen];
4
volatile int  txr, txw;
5
6
#define rxLen  256
7
volatile char UsbRxBuf[rxLen];
8
volatile int  rxr, rxw;

2) Die Verzögerungsschleife mit dem NOP war zu klein:
1
void Nop (dword count)
2
{
3
    while (count)
4
    {
5
        asm volatile ("nop");
6
        asm volatile ("nop");
7
        asm volatile ("nop");
8
        asm volatile ("nop");
9
        asm volatile ("nop");
10
        asm volatile ("nop");
11
        asm volatile ("nop");
12
        asm volatile ("nop");
13
        asm volatile ("nop");
14
        asm volatile ("nop");
15
        asm volatile ("nop");
16
        asm volatile ("nop");
17
        --count;
18
    }
19
}

Bei 48Mhz Systemtakt habe ich herausgefunden, dass ich mindestens 9 nops 
brauche. Sicherhaltshalber habe ich paar mehr genommen. Klappt auch mit 
72Mhz.

von evtl (Gast)


Lesenswert?

Dieses Konstrukt soll laut des anderen Threads helfen:
1
void __attribute__((optimize("O0"))) Nop (dword count)
2
{ while (count) --count; }

von Stefan F. (Gast)


Lesenswert?

Meine Variante mit den 12 Nops:
1
 380                  .section  .text.Nop,"ax",%progbits
2
 381                  .align  1
3
 382                  .p2align 2,,3
4
 383                  .global  Nop
5
 384                  .syntax unified
6
 385                  .thumb
7
 386                  .thumb_func
8
 387                  .fpu softvfp
9
 389                Nop:
10
 390                  @ args = 0, pretend = 0, frame = 0
11
 391                  @ frame_needed = 0, uses_anonymous_args = 0
12
 392                  @ link register save eliminated.
13
 393 0000 68B1         cbz  r0, .L32
14
 394                .L34:
15
 395                  .syntax unified
16
 396                @ 610 "../src/USB.c" 1
17
 397 0002 00BF         nop
18
 398                @ 0 "" 2
19
 399                @ 611 "../src/USB.c" 1
20
 400 0004 00BF         nop
21
 401                @ 0 "" 2
22
 402                @ 612 "../src/USB.c" 1
23
 403 0006 00BF         nop
24
 404                @ 0 "" 2
25
 405                @ 613 "../src/USB.c" 1
26
 406 0008 00BF         nop
27
 407                @ 0 "" 2
28
 408                @ 614 "../src/USB.c" 1
29
 409 000a 00BF         nop
30
 410                @ 0 "" 2
31
 411                @ 615 "../src/USB.c" 1
32
 412 000c 00BF         nop
33
 413                @ 0 "" 2
34
 414                @ 616 "../src/USB.c" 1
35
 415 000e 00BF         nop
36
 416                @ 0 "" 2
37
 417                @ 617 "../src/USB.c" 1
38
 418 0010 00BF         nop
39
 419                @ 0 "" 2
40
 420                @ 618 "../src/USB.c" 1
41
 421 0012 00BF         nop
42
 422                @ 0 "" 2
43
 423                @ 619 "../src/USB.c" 1
44
 424 0014 00BF         nop
45
 425                @ 0 "" 2
46
 426                @ 620 "../src/USB.c" 1
47
 427 0016 00BF         nop
48
 428                @ 0 "" 2
49
 429                @ 621 "../src/USB.c" 1
50
 430 0018 00BF         nop
51
 431                @ 0 "" 2
52
 432                  .thumb
53
 433                  .syntax unified
54
 434 001a 0138         subs  r0, r0, #1
55
 435 001c F1D1         bne  .L34
56
 436                .L32:
57
 437 001e 7047         bx  lr

Und die Variante mit der Leeren Schleife ohne Optimierung:
1
 396                  .section  .text.Nop,"ax",%progbits
2
 397                  .align  1
3
 398                  .p2align 2
4
 399                  .global  Nop
5
 400                  .syntax unified
6
 401                  .thumb
7
 402                  .thumb_func
8
 403                  .fpu softvfp
9
 405                Nop:
10
 406                  @ args = 0, pretend = 0, frame = 8
11
 407                  @ frame_needed = 1, uses_anonymous_args = 0
12
 408                  @ link register save eliminated.
13
 409 0000 80B4         push  {r7}
14
 410 0002 83B0         sub  sp, sp, #12
15
 411 0004 00AF         add  r7, sp, #0
16
 412 0006 7860         str  r0, [r7, #4]
17
 413 0008 02E0         b  .L33
18
 414                .L34:
19
 415 000a 7B68         ldr  r3, [r7, #4]
20
 416 000c 013B         subs  r3, r3, #1
21
 417 000e 7B60         str  r3, [r7, #4]
22
 418                .L33:
23
 419 0010 7B68         ldr  r3, [r7, #4]
24
 420 0012 002B         cmp  r3, #0
25
 421 0014 F9D1         bne  .L34
26
 422 0016 00BF         nop
27
 423 0018 0C37         adds  r7, r7, #12
28
 424 001a BD46         mov  sp, r7
29
 425                  @ sp needed
30
 426 001c 80BC         pop  {r7}
31
 427 001e 7047         bx  lr

Sieht irgendwie unnötig kompliziert aus, erfüllt aber sicher auch seinen 
Zweck.

von temp (Gast)


Lesenswert?

Tja, das gleiche Problem mit der wackligen delay-Implementierung hatte 
ich hier auch schon paar mal berichtet. Alle Versuche W.S. zu einem 
Statement zu überreden endeten ohne Antwort. Es wird so sein wie ich 
schon vermutet habe, hier freut sich einer wenn andere Leiden.

von temp (Gast)


Lesenswert?

Kannst es ja auch mal so probieren:
1
void Nop (dword count)
2
{ 
3
  count<<4; // anpassen
4
  while (count) 
5
    {  
6
    --count; 
7
    __NOP();
8
    }
9
}

von temp (Gast)


Lesenswert?

Korrektur war zu schnell:
1
count<<=4; // anpassen

von Stefan F. (Gast)


Lesenswert?

> Kannst es ja auch mal so probieren

Das wären 16 Nops pro Durchlauf + 16 mal so oft dekrementieren. Das ist 
viel mehr als meine bereits großzügig gewählten 12 Nops.

von Stefan F. (Gast)


Lesenswert?

Ich hatte irgendwo mal gelesen, dass nur ein volatiles NOP in die 
Schleife rein muss weil sie sonst weg optimiert wird. Nun stellt sich 
heraus, dass das viel zu wenig war.

Egal, es läuft jetzt stabil. Ich denke, alle hier vorgeschlagenen Lösung 
funktionieren.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Stefan U. schrieb:
> Egal, es läuft jetzt stabil. Ich denke, alle hier vorgeschlagenen Lösung
> funktionieren.

Eine schlanke Alternative zu STs USB-Code hört sich interessant an :)

von temp (Gast)


Lesenswert?

Stefan U. schrieb:
> Das wären 16 Nops pro Durchlauf + 16 mal so oft dekrementieren. Das ist
> viel mehr als meine bereits großzügig gewählten 12 Nops.

Darum stand ja auch "anpassen" im Kommentar. Das __NOP() aus core_cm3.h 
hat den Vorteil, dass es je nach Compiler die richtige Implementierung 
macht. Beim gcc ist das dann:
1
static __INLINE void __NOP()                      { __ASM volatile ("nop"); }

Damit ist der Code dann auch unter Keil übersetzbar, der mit der gcc 
Syntax "asm volatile" nichts anfangen kann.

von Stefan F. (Gast)


Lesenswert?

Allerdings, diese Lösung ist ziemlich schlank.

Deswegen interessiere ich mich dafür. Liegt vielleicht auch daran, dass 
mein Einstieg in µC Programmierung mit 1kB Flash und 64 Bytes RAM 
begann.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Stefan U. schrieb:
> mit 1kB Flash und 64 Bytes RAM
> begann.

Mmhmm, da hatte mein damaliger 8032 schon mehr Speicher^^

Beitrag #5235163 wurde von einem Moderator gelöscht.
von Nico W. (nico_w)


Lesenswert?

Das delay sieht irgendwie eher wie nen Hack aus. Das sind da später im 
Code 25us Pause bei 48MHz(12*100/48).


Gibt es da eventuell irgendwo nen Ready-bit was man Abfragen kann?

von Stefan F. (Gast)


Lesenswert?

Ich glaube nicht. Delays findet man auch im Cube HAL Code und in in dem 
STM32 USB Tutorial. Scheint nicht anders zu gehen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.