Forum: Mikrocontroller und Digitale Elektronik SAM3X8E virtueller COM post über CDC USB


von Dr. S. (petl)


Angehängte Dateien:

Lesenswert?

Hey Leute,

ich feile seit einiger Zeit an einer seriellen Schnittstelle zwischen 
Computer und mehreren SPI devices mittels eines SAM3X8E (auf einem 
Arduino Due board). Diese würde ich gerne mit max. 30MHz bespielen und 
brauche dafür vom PC eine (virtuelle) serielle Schnittstelle.

Ich hab mich jetzt mit den ASF Examples vom Atmel Studio 6.2 zu einem 
funktionierenden virtuellen COM Port über das "USB Device CDC Example - 
Arduino Due/X - ATSAM3X8E" aus dem Beispielkatalog durchgearbeitet. 
Meine main.c schaut folgendermaßen aus:
1
int main(void)
2
{
3
  sysclk_init();
4
  board_init();
5
  // Start USB stack to authorize VBus monitoring
6
  udc_start();
7
  spi_master_initialize();
8
9
  
10
  int len;
11
  const int BUF_SIZE = 4;
12
  char buf[BUF_SIZE];
13
  ui_com_tx_start();
14
  while(true){
15
    if(udi_cdc_is_rx_ready()){
16
      // blocks until BUF_SIZE bytes are received
17
      len = udi_cdc_read_buf(buf, BUF_SIZE);
18
      
19
      if (len == BUF_SIZE) continue;
20
      
21
      while (!udi_cdc_is_tx_ready()) {
22
        // Fifo full
23
      }
24
      udi_cdc_write_buf(buf, BUF_SIZE);
25
26
    }
27
}

Leider komme ich bei dieser Übertragung mit HTerm auf keine fehlerfreie 
Übertragung. Egal bei welcher Baudrate kommt das erste Byte nie zurück, 
die restlichen werden ab ca. 200000 Baud lückenhaft (siehe Screenshot).

Ich kann gerne die reslichen configs auch anhängen, die sind aber 
größtenteils unverändert und ich weiß nicht wo/was relevant ist.


Ich bin über jeden Input froh,
vielen Dank,
petl

: Verschoben durch User
von W.S. (Gast)


Lesenswert?

Dr. S. schrieb:
> Ich hab mich jetzt mit den ASF Examples vom Atmel Studio 6.2 zu einem
> funktionierenden virtuellen COM Port über das "USB Device CDC Example -
> Arduino Due/X - ATSAM3X8E" aus dem Beispielkatalog durchgearbeitet.

Das glaube ich dir nicht. Nee, nicht, daß du dich nicht durchgearbeitet 
hättest, sondern daß du zu einem funktionierenden VCP gekommen bist.

Normalerweise besteht die Funktion eines VCP auf Geräteseite ja eben 
darin, daß dort in asynchroner Weise Bytes reingeschrieben und 
rausgelesen werden können.

Der VCP muß also erstens zwischenpuffern und zweitens ein zum USB 
asynchrones Interface zum aufrufenden Programm bieten. Und genau DAS 
sehe ich bei deiner Quelle nun überhaupt nicht.

Folglich braucht es dich nicht zu wundern, daß erstens Bytes an irgend 
einer Stelle übersehen werden oder nicht abgeholt werden oder die 
Übertragung langsam ist oder hie und da ein Ack-Paket nicht gesendet 
wird oder dergleichen. Ich schätze, daß dein deviceseitiger 
USB-CDC-Treiber das Problem ist, weil er dich z.B. dazu verleitet, 
untätig auf der Stelle zu trampeln anstatt währenddessen etwas 
sinnvolles zu tun.

Dein Code besteht im Wesentlichen ja aus Trampeln:

immerzu:
{
  if RX hat empfangen
  { RX auslesen
    warten bis TX bereit
    TX beschreiben
  }
}

Das ist eben nicht gleichzeitig, sondern hakelig hintereinander. Und 
dein Puffer ist mal grad eben 4 Bytes lang und du willst ihn auch mit 
exakt 4 Bytes gefüllt haben, weil du auch exakt 4 Bytes senden willst.

Also geh mal in dich und überdenke deinen Algorithmus. Abgesehen davon 
sieht mir das aus wie ein simples Pingpong, also Echo auf alles was 
reinkommt - ohne jegliche Datenauswertung. Wozu soll das gut sein? 
Normalerweise hat man zwei Funktionen GetChar() und PutChar(char C), die 
von diversen Programmteilen zur Kommunikation über den Kanal benutzt 
werden - wo sind die?


Fast alle Hersteller lassen sich bei ihren USB-Core-Beschreibungen 
ellenlang darüber aus, wie man mit wechselnden Übertragungspuffern und 
geschicktem Hin- und Her-Schalten die vom USB bietbare Bandbreite 
ausnutzen kann - und dabei ist bereits von blockweise gebündelten Daten 
die Rede. Das Umsetzen in's Asynchrone ist dazu eine weitere, 
davorgeschaltete Sache. Vermutlich ist da noch ne Menge im 
Treibergetriebe deines µC zu tun.

W.S.

von Jim M. (turboj)


Lesenswert?

Dr. S. schrieb:
> while(true){
>     if(udi_cdc_is_rx_ready()){
>       // blocks until BUF_SIZE bytes are received
>       len = udi_cdc_read_buf(buf, BUF_SIZE);
>
>       if (len == BUF_SIZE) continue;


Äh, da überschreibtst Du recht oft Deinen Puffer ohne den Inhalt zu 
senden. Nur wenn wirklich zufällig BUF_SIZE Daten kommen gehts auch 
raus.

Wieso schmeisst Du nicht einfach alles so raus wie Du es liesst?

von Dr. S. (petl)


Lesenswert?

W.S. schrieb:
> Das ist eben nicht gleichzeitig, sondern hakelig hintereinander. Und
> dein Puffer ist mal grad eben 4 Bytes lang und du willst ihn auch mit
> exakt 4 Bytes gefüllt haben, weil du auch exakt 4 Bytes senden willst

Danke für die Antwort, ich hab das im Originalpost leider nicht 
reinformuliert, aber ich will immer Pakete in der Länge von 4byte 
übertragen.
Ich verliere aber leider auch bei 4byte Input das Erste, an einem 
Bufferoverflow scheiterts also glaube ich nicht.



> Abgesehen davon
> sieht mir das aus wie ein simples Pingpong, also Echo auf alles was
> reinkommt - ohne jegliche Datenauswertung. Wozu soll das gut sein?

Ich will eigentlich die Daten danach über SPI rausschieben, der Teil ist 
noch auskommentiert. Small steps ;)



> Äh, da überschreibtst Du recht oft Deinen Puffer ohne den Inhalt zu
> senden. Nur wenn wirklich zufällig BUF_SIZE Daten kommen gehts auch
> raus.

> Wieso schmeisst Du nicht einfach alles so raus wie Du es liesst?

Das habe ich auch so probiert, funktioniert aber leider nicht besser..

von Dumpbacke (Gast)


Lesenswert?

Dr. S. schrieb:
> aber ich will immer Pakete in der Länge von 4byte übertragen.

Dann wirst du dir ein Protokoll überlegen müssen, dafür wann
das Paket beginnt. Über die USB Schnittstelle allein wirst du
das nicht herausfinden.

von Dr. S. (petl)


Lesenswert?

Dumpbacke schrieb:
> Dann wirst du dir ein Protokoll überlegen müssen, dafür wann
> das Paket beginnt. Über die USB Schnittstelle allein wirst du
> das nicht herausfinden.

Der controller kann ja so lange input buffer checken bis er vier byte 
beisammen hat und dann weitermachen?

von Dumpfbacke (Gast)


Lesenswert?

Dr. S. schrieb:
> Das habe ich auch so probiert, funktioniert aber leider nicht besser..

W.S. hat es dir genau erklärt. Und du hast es offensichtlich
überhaupt nicht verstanden. Wenn bis dahin nicht die Alarmglocken
geklingelt haben dann wirst du mit deinem Vorhaben scheitern.

W.S. schrieb:
> Der VCP muß also erstens zwischenpuffern und zweitens ein zum USB
> asynchrones Interface zum aufrufenden Programm bieten.

von Dumpfbacke (Gast)


Lesenswert?

Dr. S. schrieb:
> bis er vier byte beisammen hat und dann weitermachen?

Das Fragezeichen spricht Bände.

Und die Antwort lautet: Nein.

von Dr. S. (petl)


Lesenswert?

Dumpfbacke schrieb:
> W.S. hat es dir genau erklärt. Und du hast es offensichtlich
> überhaupt nicht verstanden. Wenn bis dahin nicht die Alarmglocken
> geklingelt haben dann wirst du mit deinem Vorhaben scheitern.

Ich habe hier gefragt weil ich mich nicht auskenne, danke für die nette 
Antwort :(


> Ich schätze, daß dein deviceseitiger
> USB-CDC-Treiber das Problem ist, weil er dich z.B. dazu verleitet,
> untätig auf der Stelle zu trampeln anstatt währenddessen etwas
> sinnvolles zu tun.


Mein deviceseitiger Treiber ist einfach der Beispielcode, ich hab da 
nichts (zumindest nicht wissentlich) verändert.
Könnt ihr mich da in die richtige Richtung verweisen, oder gibt es da 
eine Anleitung wie ich das richtig biege?

von Dumpfbacke (Gast)


Lesenswert?

Dr. S. schrieb:
> Könnt ihr mich da in die richtige Richtung verweisen

Einen anderen Ansatz der Problemstellung/Klärung machen:

"Verdächtig" erscheint dass du immer 4 Bytes übertragen willst.

Also:

1) was willst du denn genau erreichen.
2) müssen es denn genau 4 Bytes sein?
3) welche Geschwindigkeitsanforderungen hast du?

von Dr. S. (petl)


Lesenswert?

Dumpfbacke schrieb:
> 1) was willst du denn genau erreichen.
> 2) müssen es denn genau 4 Bytes sein?
> 3) welche Geschwindigkeitsanforderungen hast du?

1) Daten über SPI in mehrere DAC scheiben
2) 3byte daten + 1byte info
3) 3-30MHz wären angestrebt

von Jim M. (turboj)


Lesenswert?

Dr. S. schrieb:
>> Das ist eben nicht gleichzeitig, sondern hakelig hintereinander. Und
>> dein Puffer ist mal grad eben 4 Bytes lang und du willst ihn auch mit
>> exakt 4 Bytes gefüllt haben, weil du auch exakt 4 Bytes senden willst
>
> Danke für die Antwort, ich hab das im Originalpost leider nicht
> reinformuliert, aber ich will immer Pakete in der Länge von 4byte
> übertragen.
> Ich verliere aber leider auch bei 4byte Input das Erste, an einem
> Bufferoverflow scheiterts also glaube ich nicht.

Womit stellst Du sicher dass der PC auch wirklich die 4 Bytes am Stück 
sendet? Einfach HTerm über USB CDC reicht da offenbar nicht - Du hast 
beim VCP Treiber AFAIK keine Kontrolle darüber wie die Bytes über USB 
wirklich übertragen werden.

Und wenn zuerst 1 Byte alleine kommt, wirft Dein geposteter Code das 
kurzerhand weg. Das zu reparieren überlasse ich dem OP als 
Hausaufgabe,
mit dem Hinweis: Wo müssen die restlichen 3 Bytes hin..?

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Dr. S. schrieb:
> Mein deviceseitiger Treiber ist einfach der Beispielcode, ich hab da
> nichts (zumindest nicht wissentlich) verändert.
> Könnt ihr mich da in die richtige Richtung verweisen, oder gibt es da
> eine Anleitung wie ich das richtig biege?

Was willst du hören, ohne sogleich beleidigt zu sein?
Die korrekte Antword würde lauten: zuerst lies die Dokumentationen zum 
USB und zum Unterthema CDC, sodann das RefManual zu deinem Chip bis du 
das Ganze verstanden hast. Aber so wie ich die Leute heutzutage kenne, 
ist sowas zuviel verlangt.

So.
Eben deswegen hänge ich dir mal was für den Anfang dran.
Zum Lesen und Verstehen.
Es ist NICHT für deinen Chip geschrieben, weswegen die blutigen 
Low-Level-Units bei dir garantiert nicht ohne Editieren laufen werden.

Aber schon der nächste Level gio.c ist fast hardwareunabhängig (nur die 
low-level Treiber dort benennen, die auch tatsächlich vorhanden sind!) 
und alles oben drüber ist hardwareunabhängig. Da hast du ein simples 
Kommandoprogramm, womit du erstmal über die COM-Ports deiner Wahl 
(inclusive dem Virtuellen per USB) mit dem PC kommunizieren kannst.

Beim USB-Treiber mußt du vermutlich die größte Axt schwingen, denn der 
Core, der in deinem µC steckt, ist sicherlich weitgehend anders als der, 
dn die Leute von ST benutzen. Der ist m.M.n herzlich krötig - sowohl von 
der Auslegung der Transferbuffer her als auch von der verxorten 
Bedienung der SIE. Auf so eine Logik muß man erstmal kommen!
Aber: Das ist bei ST gründlich dokumentiert - wenngleich auch schwer zu 
kauen. Jetzt ist es deine Brause, in der Doku zu deinem Chip 
nachzulesen, wie das dort geht. Die Grundprinzipien am USB sind aber 
immer gleich.

Ach ja, ich verwende auf der PC-Seite keinerlei speziellen Treiber, 
sondern den usbser.sys vom Betriebssystem. Siehe usb.h - dort ist das 
beschrieben. Das hat den Vorteil, daß ich so ziemlich allen Eigenbau mit 
Nuvoton, LPC, STM32 ohne Treibergefummel dranstecken kann.

W.S.

Beitrag #5070408 wurde von einem Moderator gelöscht.
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.