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
intmain(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
intlen;
11
constintBUF_SIZE=4;
12
charbuf[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
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.
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?
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..
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.
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?
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.
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?
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?
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
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..?
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.