Hallo,
nach einer langen Reise durch alle möglichen Foren und Code-Sammungen
zum Thema CDC und Xmega habe ich feststellen müssen das es nichts gibt
was man eben schnell einbindet.
Das Problem:
ein atxmega32a4u soll per USB angebunden werden.
ein atxmega128a4u soll per USB angebunden werden.
Da es sich hier um einen CNC-Controller handelt ist eine einfache
serielle Übertragung das was einem zu erst einfällt. Na gut, wenn man
einen CNC-Controller baut, dann sollte es doch das einfachste sein einen
FTDI mit auf die Platine zu klatschen (Das nur um denen zuvorzukommen
die diese Idee als das gelbe vom ei betrachten!). Alle Lösungen mit FTDI
und PL23xx usw (egal ob on Board oder als usb to Serial-Adapter) haben
nur einen großen Nachteil: ein Timeout.
Viel werden davon noch nicht viel gehört haben, wer per USB die Daten zu
einem CNC-Controller überträgt und dort oft Wartezeiten (der Fräser ist
nun mal etwas langsamer als die Übertragung; zum Glück) hat, wir
feststellen das oft Daten verworfen werden. Und das nicht reproduzierbar
an unterschiedlichen stellen. Man kann das ganze nachvollziehen wenn man
seriell große mengen an Daten überträgt und die Statusleitungen einfach
einmal längere zeit setzt; plumps werden in einigen Sekunden 200 Bytes
an Daten übertragen (sagt die sende-Software) die nie ankommen.
Nimmt man eine Hardware-Serielle, wie früher üblich, gibt es dieses
Problem nicht. Aber welche PC's haben heute schon eine solche
Schnittstelle mit eingebaut? Die Anzahl der Möglichen PC's sinkt
täglich.
Aber da gab es doch was? Ja, seit XP (dort anfangs mit zusätzlichen
Treibern) kann man doch ein USB-CDC-Device ohne Treiber auf der
Windows-Seite anbinden! Diese Devices lassen auch eine nahezu beliebige
Verzögerung des Datenstroms zu.
Als erstes denkt man nun an eine Codesammung die es dafür doch geben
sollte. Ja, ASF, von Atmel selber bereitgestellt sollte das doch können.
Das erste Problem ist ASF selber... Ich habe seit Wochen an meinem Code
mit WINAVR gearbeitet. Der in der Zwischenzeit doch stattliche Code soll
nun in das AVR-Studio übertragen werden nur weil ich nicht mal 4k Code
für USB brauche? Das zweite Problem das ich den CDC-Code noch nicht
einmal übersetzen konnte (der Prozessor zu neu?) weil einige
Deklarationen nicht stimmen oder mit dem Code von ASF selber
Kollidieren.
ASF ist damit für mich ausgeschieden.
LUFA!!! Das ist es!! (so dachte ich)
Nein, Lufa ist so universell das es vermutlich neben USB auch noch
Toastbrot backen kann (hierfür habe ich aber noch nicht die
entsprechenden Einstellungen in LUFA gefunden.. aber wer weiß???)
Lufa hat sich als so unübersichtlich erwiesen (und zu dem für Xmega nur
als experimentell) das es nicht verwendbar war.
Aber da gab es doch noch jemanden der den LUFA-Code so
zusammengestrichen hat das er wirklich 'übersetzbar' ist. Leider ist
dieser Code von nonolithlabs.com nicht als CDC ausgelegt. Hier werden
Blöcke in PIPES übertragen die einen Code auf der Host-Seite erfordern.
Unter Linux noxh leicht machbar, unter Windows schon eine größere
Aktion. Zusätzlich wird dann noch die entsprechende Hostsoftware
benötigt die diese PIPES auch behandelt.
Ich will doch nur seriell Daten übertragen! (jammer)
Also habe ich mich hingesetzt und versucht eine CDC-Software zu
schreiben.
Winavr ist nun nicht zwingend auf dem neusten Stand (dennoch liebe ich
den pn davon).
Also erst mal den gcc aktualisiert (hierfür gibt es genügend zum
Download im Internet). Leider hat es sich gezeigt das diese GCC wohl
neuer sind, aber dennoch nicht die neuen Prozessoren unterstützen.
Ich habe dann den Toolchain von ATMEL genommen und diesen über den
Winavr drüberkopiert (vorher Sicherungen machen!!).
Ab diesem Zeitpunkt waren auch die ganzen Deklarationen für die neuen
Prozessoren da (sind die wirklich noch als neu zu bezeichnen?). Man
sollte auch nicht vergessen entsprechende Anpassungen für avrdude zu
machen (Anpassungen in der config-Datei) damit mit avrdude auch
programmiert werden kann.
Nach jetzt fast 3 Wochen ist nun die angehängte Codesammlung
rausgekommen.
Als VID und PID habe ich die von Atmel genommen die in deren Beispielen
verwendet wurden. Diese sind nur als Beispiel zu sehen! Jeder sollte
sich um gültige um frei verwendbare PID/VID's selber kümmern. Die
entsprechende .inf-Datei ist im Zip mit drin und wurde für die PID und
VID aus dem Beispiel angepasst.
In der main.c erfolgt die Initialisierung und dann kann der Code schon
verwendet werden. Ich habe auf einen Ringbuffer verzichtet weil es wohl
ehr selten ist das man wirklich damit auf eine Serielle Leitung dann
schreiben will. Man wird die seriellen Daten selber Interpretieren und
eigenen Funktionen dahinter legen.
1
while(1)
2
{
3
// manage usb-task for the endpoints (in loop)
4
EP_DEF_in(ep_out);
5
EP_DEF_out(ep_in);
6
EP_DEF_out(ep_note);
7
8
// handle data if flag are cleared
9
if(!cdc_rxb.flag)
10
{
11
if(cdc_rxb.bytes==cdc_rxb.len)
12
{
13
// all data prcessed; send the data in txd and wait for new data
Über einfache Flags wird gesteuert ob ein Buffer gefüllt werden soll
oder ob ein Buffer gefüllt ist damit er dann von der USB-Software
verarbeitet werden kann. Im USB-Code selber sind für die Endpoints
eigene Buffer verwendet. Das befreit den einfachen Nutzer von einem
ausgeklügelten Bufferhandling (Buffer müssen gesperrt werden,
Übertragungen darin stattfinden dann zugeordnet werden... eine schwer
nazuvollziehende Pointer-schlacht). Da die Inhalte immer umkopiert
werden, ist die Übertragungsrate etwas langsamer.
Die USB-Tasks für die Endpoints (EP_DEF_in(ep_out)) müssen in der
Mainloop immer wieder aufgerufen werden. Ich hatte diese zu beginn im
IRQ mit drin (jetzt ist da nur noch der Task für den EP0 drin) was
traumhafte datenraten ergeben hatte. Das locking war aber ungleich
komplexer. Und vorallem das 'Anschupsen' wenn kein IRQ ausgelöst wird
(alle Daten sind reingekommen, ich möchte nur was schicken) muss dann in
die IRQ'S mit eingreifen.
Zu den Datenraten:
Wird keine Byteweise Verarbeitung gemacht und ich empfange nur Daten,
komme ich auf 450kByte übertragungsrate.
Werden die Daten auch noch seriell umkopiert (hier in den ausgabebuffer)
und nicht zurück gesendet, dann komme ich noch auf ca. 95kBytes an
datenrate. Werden die Daten als echo gesendet, kann ich das nicht mehr
sagen weil mein Übertragungsprogramm mit der Darstellung der
zurückkommenden Daten so ausgelastet ist das es die Datenrate nicht mehr
darstellt ;-) (es dürften aber immer noch im hunderterkillobit-bereich
sein).
ich hoffe der Code hilft jemanden. vielleicht ist das auch die Basis
kleine weitere USB-Devices damit zu realisieren. HDI oder eine
Massenspeicher sollten damit nicht mehr all zu schwer sein.
bye woodym
Hallo,
so schnell kann es gehen...
hier gleich eine aktuallisierte Version. In der vorigen Version war ein
Quarz mit 8Mhz beim Clock eingestellt. Wer das Verwenden will wir nicht
zwingend einen Quarz mit der gleichen Frequenz haben. In der Version
hier wurden die Normalen Clockeinstellungen genommen die beim USB üblich
sind.
Der 32Mhz wird auf 48Mhz eingestellt und mit dem USB Start of Frame über
den DFFL synchronosiert. Die internen 2Mhz werden per PLL mal 16
genommen und stehen als SystemClock zur Verfügung.
Noch zum Datendurchsatz mit Echo.
1.5Mbyte werden in ca 34 Sekunden übertragen (ist auch abhängig von der
Art der Daten; wie oft muß die Ausgabe-Software scollen). Das macht ca.
44KBytes pro Sekunde.
bye woodym
Also LUFA lief bei mir praktisch auf Anhieb mit CDC. Auch wenn der XMEGA
Support noch Experimental ist, habe ich bisher keinerlei Probleme
festgestellt.
hallo Simon,
ja, es gab/gibt wirklich welche die damit keine Probleme haben. Das du
diese enttäuschende Erfahrung nicht machen musstest freut mich. Mir
konnte keiner darüber Auskunft geben unter welchen Voraussetzungen es
gehen soll (z.b. welche includes die richtigen sind, welcher GCC dafür
nötig ist). Genau genommen konnte ich LUFA für jeden Prozessor
übersetzen, nur nicht für den XMEGA128a4u. Als ich dann versuchte die
Definitionen für die Boards/Prozessoren entsprechend anzupassen, ging
auf Grund der unüberschaubaren Abhängigkeiten die Welt unter. Das mag
für jemanden der das mit entwickelt hat (ich weiß, dazu gehörst du
nicht) alles völlig easy sein. Für mich waren/sind das nicht
nachvollziehbare Prozesse gewesen. Zumal es ja wirklich nicht viel ist
wenn die Config bei usb mal durchlaufen ist. Dann sind ja 'nur' noch die
Endpoints die richtig beschrieben werden müssen.
Ach so, zum Anfang noch 'ja, es gab/gibt wirklich welche' ...
das ich nicht der einzige war/bin, sieht man an den Beiträgen in
AVR-Freaks usw..
Das auch viele andere das nicht so hinbekommen haben mit dem CDC sehe
ich an einigen Projekten die dann Zähneknirschend mit dem code von
nonolithlabs.com umgesetzt wurden (ist halt kein CDC).
Ich habe viel über USB gelernt durch das selber schreiben. Natürlich
habe ich auch auf die Erfahrungen von LUFA und ASF zurückgegriffen. Die
Ganzen Descriptor-Sachen sind von LUFA.
bye woodym
Hallo,
ich hab mal dein Zip-Datei ins AVR-Studio in einem eigenen Projekt
versucht zu kompilieren. Da ich will so wenig wie möglich ändern und
mich halt langsam an das Thema ranzutasten.
Jetzt gibt mir der Compiler einen Fehler aus "F_USB undecleared" in der
Datei usb_xm.c Zeile 272.
Wo definierst du die Variable??
Vielen Dank dass du dir die Mühe gemacht hast. hat mir schon ein bischen
weitergeholefen.
Die one Click Lösung ist doch immernoch ASF CDC... noch nie Probleme
gehabt, egal welcher Prozessortyp und Source in Atmel Studio einbinden
sollte nicht wirklich schwer sein, egal wieviel... oder du hast ganz wo
anders Probleme...
Grüße Basti
hallo 'xmega usb anfänger',
die F_CPU ist mit der F_USB in der makefile definiert. Wenn du eine
eigene makefile verwendest, mußt du darauf achten das diese Variablen
auch beim übersetzen mit übergeben werden.
@Basti
Ich freue mich für dich das du mit ASF zufrieden bist. Offensichtlich
hast du eine speziell für dich gemachte version (die aktuelle Version
die ich runtergeladen hatte, hat Prozessoren nicht unterstützt oder
einfach nicht fehlerfrei übersetzt.... mit neu installiertem und
aktuellem Studio), oder du verwendest nicht die Xmega-Prozessoren die im
ASF Buggi sind. Der hier vorgestellte Weg ist sicher auch nicht 100%ig.
Er gibt einem aber erst mal ein erfolgserlebnis welches einem mut zum
weitermachen gibt. Für mich war es einfacher den USB-Teil des Xmegas neu
zu schreiben als mich durch 100 erratas und asf-Bugfixings zu lesen in
der hoffnung ein übersetzbares ergebnis zu erhalten. Ich sprech noch
nicht mal davon das das dann auch noch funktionieren soll.
Ich freu mich wirklich für dich das es mit ASF funktioniert. Alle
anderen die die gleiche Erfahrung wie ich gemacht haben, haben hier die
möglichkeit eine Alternative zu verwenden.
bye woodym
Schaut lieber ins Datenblatt und machts gleich in ASM. Spart viel C+ASF
Studiererei. Den Vorteil den die ASF bringen soll wird von einem
Mehrfachen an Doku und Bürokratie locker zunichte gemacht.
wie installiere ich den die inf datei?
Mein Pc kennt keinen passenden Treiber.
Wähle ich die Inf datei welche in der Zip Datei enthalten ist aus, kommt
nur die Meldung, dass das "CDC Virtual Com konnte nicht installiert
werden"
hallo frank91,
Voraussetzung ist, das der Xmega bereits programmiert ist.
Wenn du den Xmega an die usb ansteckst, meckert dein Windows das es
dafür keinen Treiber hat und trägt nach deinem Treiber. Da gibst du ihm
an wo diese inf-datei ist.
Wie das bei Win8 aussieht kann ich nicht sagen.
Bei XP muss vorher der cdc-Treiber von Microsoft installiert sein der
eventuell Bestandteil von irgend einem ServicePack ist.
bye woodym
Hallo,
erst mal vielen Dank für deinen Code, ich finde ihn sehr nützlich und
habe seit längeren etwas ähnliches vor, doch nie geschafft.
Läuft auch auf einen xmega192a3u
Ich habe jedoch leider noch nicht ganz verstanden wie ich ein EP->Host
Transfer initialisiere.
Nur als Beispiel ich habe einen kleinen UART RX Interrupt der folgenden
Code ausführt wobei c das Eingegangene Byte ist:
1
if(c=='\n')
2
{
3
cdc_txb.data[cdc_txb.bytes]='\n';
4
cdc_txb.data[cdc_txb.bytes+1]='\r';
5
cdc_txb.bytes+=2;
6
return;
7
}
8
cdc_txb.data[cdc_txb.bytes]='1';
9
cdc_txb.data[cdc_txb.bytes+1]=c;
10
cdc_txb.bytes+=2;
Ich hätte erwartet das in irgendeinen Interrupt oder innerhalb der main
loop, jetzt erkannt wird das sich der Puffer verändert hat und die Bytes
werden gesendet.
Das passiert gar nicht oder nach einer Code Modifikation von mir die
aber wahrscheinlich nicht sehr gescheit ist manchmal.
Das gleiche gilt auch wenn ich immer nur ein Byte fülle anstatt gleich
2.
2 Bytes sende ich nur um sicher zu gehen das die Zeichen nicht durch
meinen uart verschluckt werden.
Das ursprüngliche Echo Programm von dir funktioniert immer einwandfrei.
Beste Grüße
Felix
Ok, vielleicht habe ich meine Frage schon selber beantwortet.
Das direkt im IRQ in den Buffer zu schreiben scheint keine Gute Idee zu
sein. Weil die Reihenfolge der Aufrufe wichtig ist.
Ich habe nochmal deine main modifiziert.
So funktioniert es sehr schön, man könnte ebenfalls noch einen Buffer
anlegen in den man mehrere Bytes schreiben könnte. Welche dann in der
Main kopiert werden. Das kein Problem sein.
Wenn du magst könntest du mir noch ein wenig auf die Sprünge helfen.
Was müsste ich tun um die Bytes direkt im IRQ in den Buffer zu
schreiben?
Und sieht meine main für dich irgendwie Problematisch aus:
1
while(1)
2
{
3
4
// manage usb-task for the endpoints (in loop)
5
EP_DEF_in(ep_out);
6
EP_DEF_out(ep_in);
7
EP_DEF_out(ep_note);
8
9
// handle data if flag are cleared
10
11
if(is_new)//Nimmt ein neues Byte (Vom uart gespeichert) entgegen.
12
{
13
cdc_txb.data[cdc_txb.bytes]=new_char;
14
cdc_txb.data[cdc_txb.bytes+1]='1';//Nur zum testen
hallo Felix,
das ist jetzt schon ein bischen her das ich das gemacht hatte. Wenn ich
deinen Code richtig verstehe, bekommst du per IRQ ein zeichen und setzt
dir dann ein flag (is_new). Dann schnappst du dir das Zeichen und
schiebst es in den tx-buffer für den USB.
Das wird nur bedingt funktionieren. Wenn die Daten schneller von der
Seriellen kommen als sie vom USB verarbeitet werden können, dann
überschreibst du dir dein Zeichen. Das abarbeiten in EP_DEF_out kann u.U
schon etwas brauchen.. zumindest bist die Flags wieder auf 'ich bin
Frei' sitzen.
Ich habe dort einen einfachen Ringbuffer für die Serielle verwendet und
schmeiße alle Daten in einem Block raus. Der Aufwand für die USB ist für
ein Zeichen fast genauso groß wie für z.B. 20 Zeichen.
als Ringbuffer habe ich das verwendet:
ring.c
1
#include <stdlib.h>
2
#include <string.h>
3
#include <avr/pgmspace.h>
4
#include <avr/interrupt.h> // AVR specific functions to access the program memory
void ring_init(Ring_buffer *ring, unsigned int len);
43
44
#endif
45
// end of file
Der Ringbuffer hat auch eine Signalisierung für die Buffer damit man
RTS/CTS setzen kann. Der Ringbuffer läßt sich optimieren durch 8 bit
Pointer. Da ich hier aber Buffer verwende die größer als 255 Bytes sind
habe ich diese Verriante genommen.
ich hoffe das hilft dir.
bye woodym
Vielen Dank für die wirklich schnelle und umfangreiche Antwort.
Leider habe ich meine Frage wohl nicht genau genug formuliert.
Entschuldige. Also ein Ring Buffer zu schreiben ist für mich kein
Problem, und eine hübsche UART Routine habe ich auch. Deinen Ring Buffer
finde ich ganz hübsch.
Was mir daran nicht so gut gefällt ist das du so oft dereferenzieren
musst um auf die Ring Daten zuzugreifen. Aber mit sollchen sachen bin
ich mir oft etwas zu pingelig. Und schneller als die von Atmel (ASF)
bist du alle Male :) Was ich dir noch empfehlen kann ist DMA und nur
zweierpotenzen als zugelassene Größe.
DMA ist auf dem XMEGA keine Zauberrei und macht wirklich spaß, lohnt
sich aber natürlich nur wenn man zumindest hin und wieder etwas größere
Datenmengen speichern will.
Ich habe eigentlich für alles eigene Routinen geschrieben. Bis auf für
USB. Das wollte ich als nächstes aber mir fehlt in letzte Zeit zu oft
die Zeit.
Was ich eher meinte ist ob es möglich ist, bereits in der (UART oder
sonstwas) IRQ Routine die Daten in USB Buffer zu schreiben. Dieses 3
fache hin und her kopiere fühlt sich etwas unsauber an finde ich. Obwohl
ich natürlich ganz klar die Vorteile sehe.
Das Zeichen verloren gehen werden, weil ich sie selbst überschreiben
könnte ist klar. Ich wollte es halt möglichst einfach halten. Was ich
dich eher prüfen lassen wollte, ist ob ich die USB Geschichte richtig
benutze. Oder ob ich dort in irgendwelche hässlichen Zustände kommen
könnte.
Grüße
Felix
Hallo nochmal!
Hätte nochmal eine Frage.
Es gibt:
cdc_rxb.len und cdc_rxb.bytes
wenn die ungleich sind liegen neue Daten an. Soviel habe ich verstanden.
1. Wenn ich jedoch wissen möchte wie viele Bytes reingekommen sind, kann
ich dann einfach:
n = cdc_rxb.len - cdc_rxb.bytes;
machen?
2. Und von &cdc_rxb.data[cdc_rxb.bytes] die nächsten n Bytes lesen?
3. Was sind cdc_rxb.bytes und cdc_rxb.len überhaupt?
Es scheint ja so gemacht zu werden. Verstehe jedoch nicht wie das
möglich sein soll, irgendwann muss es doch mal einen wrap geben und man
kann dann nicht einfach weiter lesen. Oder wird darauf einfach geachtet
beim USB Buffer füllen...?
Besten Dank
Felix
hallo felix,
also erst mal zu deiner anderen frage....
du kannst auch im irq Daten schreiben. du mußt nur sicherstellen das du
erst die Daten reinschreibst und dann erst das flag schreibst. das flag
wird genommen um zu entscheiden ob Daten da sind oder nicht.
die bytes sind nur da, um bereits Daten reinlaufen zu lassen ohne das
eine Aktion ausgeführt wird (wenn es aus einem ringbuffer kommt).
das ist der code in usb_cdc.c
das ist nicht optimal, aber etwas universeller gehalten weil damit ja
auch andere enpoints erstellt werden können (hid usw.).
wenn du die task aufrufst
EP_DEF_in(ep_out);
EP_DEF_out(ep_in);
EP_DEF_out(ep_note)
wird bei den out-endpoints nachgesehen ob irgend was zu senden ist. ist
das flag gesetzt, dann wird der buffer kopiert und eine Übertragung
angestoßen.
bei den in-endpoints wird nachgesehen ob der usb irgend was asynchrones
will und ob Daten eingetroffen sind. ist der buffer frei, werden die
Daten in den in-buffer kopiert und das flag gelöscht.
d.h. das flag ist für dich ein Indikator ob ein block von Daten gesendet
werden kann oder empfangen wurde.
trägst du erst 10 bytes ein und setzt das flag nicht (beim senden), dann
passiert auch nichts. du kannst dann data[len] auch weitere Daten
eintragen bis zu maximallänge des buffers und/oder buffers für den
endpoint.
dann setzt du das flag und stößt dann erst die Übertragung an.
du solltest möglichst schnell den buffer räumen für den empfang, weil
erst wenn da reingeschrieben werden kann, geht die usb-übertragung
weiter.
man könnte den buffer auch als endpoint-buffer direkt nehmen (schneller
beim senden weil einmal die Daten nicht umkopiert werden müssen), wäre
dann aber bis zum ende der usb-übertragung blockiert. das wiederum würde
die datenvorbereitung langsamer machen.
du kannst das ganze auch im irq machen, weil ein setzen eines bytes und
das lesen 'atomic' ist, also nicht unterbrochen werden kann. beim flag
sollte man nicht += oder |= usw machen weil das zwei zugriffe sind und
nicht sichergestellt ist das da zwischen lesen und schreiben kein irq
dazwischenfunken kann.
um nochmal explizit die frage mit dem wrap zu beantworten. hier gibt es
kein wrap weil immer ein datenblock übertragen wird, kein ring.
schauen wir uns doch nochmal das an:
1
if(!cdc_rxb.flag)
2
{
3
if(cdc_rxb.bytes==cdc_rxb.len)
4
{
5
// all data prcessed; send the data in txd and wait for new data
es kommen Daten rein vom usb. damit wird cdc_rxb.len gesetzt. wenn die
reinkommen, wird cdc_rxb.bytes auf 0 gesetzt.
cdc_rxb.len ist somit ungleich cdc_rxb.bytes.
es wird ein byte rausgeholt (cdc_rxb.data[cdc_rxb.bytes]) und in den
ty-buffer kopiert (cdc_txb.data[cdc_txb.bytes]). dann werden die bytes
erhöht.
das umkopieren innerhalb der schleifendurchgänge geht solange bis
bytes==len ist. also so viele byte kopiert wurden die auch eingelaufen
sind.
das ist alles fein für den loop-test. überträgst du reale Daten, dann
kannst du auch einen ganzen block reinschieben und das flag setzen.
cdc_rxb.bytes wird dafür nicht wirklich benötigt. übertragen werden
cdc_rxb.len Daten.
bye woodym
Hi woodym,
Vielen Dank für deinen Beitrag. Dein Code hilft einem USB Neuling sehr.
Ich habe versucht den ASF USB und den LUFA zu durchblicken und bin auch
versunken...
Ich habe eine Frage: Ich habe deinen Code am Laufen in einem XMEGA256A3U
und der funktioniert auch tadelos. Aber das geht nur in der
Application-Mem-Section. Sobald ich versuche den Code in der
Boot(loader)-Section laufen zu lassen funktioniert die USB-Enumerierung
nicht mehr. ("USB Gerät wurde nicht erkannt")
Um den Code für die Boot-Section umzubauen habe ich einerseits im Linker
die Section umgestellt (.text=0x20000), das BOOTRST Flag auf BOOTLDR
gesetzt und im main nach dem IRQ-Level enable den IRQ Vector vebogen
(temp = PMIC.CTRL | PMIC_IVSEL_bm; CPP = CCP_IOREG_gc; PMIC.CTRL =
temp;). Aber irgend etwas habe ich noch vergessen.
Kannst du mir einen Tipp geben wo ich suchen muss?
Schöne Grüße
Wolfgang
hallo Wolfgang,
das ganze ist schon ein paar Tage her ;-)... ich versuche es dennoch.
Wenn das USB als solches mal erkannt wird, dann würde ich vermuten das
die ganzen IRQ's usw. schon mal laufen. Zumindest funktioniert die
Initialisierung.
Der ganze Descriptor-Teil liegt in Progmem. Wo legt der Compiler den den
Teil hin? Der sollte dann auch im Bootloader liegen. Wenn dem nicht so
ist, wäre das schon eine mögliche Fehlerquelle und würde auch das
verhalten erklären.
Die andere Frage die sich mir stellt, ob das pgm_read_byte (in
decriptor.c) bei über 128k Flash so einfach funktioniert oder ob man da
nicht Flash-blöcke umschalten muß damit die Pointer da richtig
funktionieren.
Ich meine mich erinnern zu können das bei den großen Flash-Speicher im
Bootloader immer besondere Aktionen gemacht werden müssen damit diese
dann auch laufen.
bye woodym
Hi woodym,
Ich war leider wieder etwas im Ausland unterwegs deswegen bin ich immer
etwas delayed... Aber danke für deine Infos! Ich versuche mich gerade in
die PROGMEM Thematik genauer einzulesen und es schaut wirklich sehr
danach aus als kann das auf großen XMEGAs zum Problem werden.
Momentan habe ich einen Bootloader der mich zwar nicht sehr glücklich
macht, aber erstmal funktioniert (DFU von Atmel gepacht im Hex-Editor
auf individuellen Bootloader Start-Pin, das kompilieren des Atmel DFU
Source Codes schaffe ich im GCC auch nicht).
Sobald ich meine primäre Applikation fertig habe werde ich mich des
Bootloaders nochmal annehmen und melde mich.
Danke
Wolfgang
hallo,
ich habe da etwas gefunden:
https://lists.gnu.org/archive/html/avr-gcc-list/2010-06/msg00066.html
Hier hat auch jemand das Problem das 'progmem' in den ersten 64k
platziert werden.
Vielleicht ist es um einiges einfacher das PROGMEM einfach weg zu lassen
und es im Memory einfach durch den Compiler initialisieren zu lassen. Du
verlierst dann etwas Speicher, was aber im bootloader nicht wirklich
relevant sein dürfte.
In der Umsetzung PROGMEM weg lassen und wenn read aufgerufen einfach
direkt auf den speicher zugreifen. Alternativ kannst du auch über den
Pointer darauf zugreifen der (auch im read) ja vorhanden ist.
bye woodym
Hallo woodym,
dein Code funktioniert auch bei mir ohne Probleme. Nur leider habe ich
es nicht geschafft, ihn für meine Zwecke anzupassen.
Das Ziel ist es eine Zeichenkette an den µC zu senden, dort in einem
String zu speichern und diesen weiterzuverarbeiten. Die Zeichenkette
kann unterschiedlich lang sein (max. 6 Zeichen) und endet immer mit
einem '\r'.
Ich denke, dazu brauche ich eine while-Schleife, in der ich nacheinander
die empfangenen Zeichen in ein String speicher, solange bis ein '\r'
empfangen wurde. Das habe ich auch versucht, allerdings ohne Erfolg. Das
mag daran liegen, dass ich noch nicht genau verstanden habe, wann ich
welche Flags setzen muss. Die gespeicherte Zeichenkette möchte ich dann
auch wieder zurückschicken. Mein Code dazu sieht so aus:
hallo Matthias,
wie wird denn das Telegramm auf die Reise geschickt? Kommen da immer
alles als Block?
Wenn dem so wäre, dann kannst du gleich im Buffer nach deiner Kennung
suchen und auf die Funktionen verzweigen. Kommt es aus einer unbekannten
Quelle (z.B. ein Terminalprogramm) dann mußt du wirklich die einzelnen
Daten erst einmal zwischenspeichern.
Deine erste Frage steht ja im Code... "//wann genau muss ich das immer
aufrufen?". Die kurze Antwort IMMER!.
Das ganze USB-Zeug funktioniert ja vollkommen asynchron. es kann zu
jeder Zeit was reinkommen oder etwas zum Senden sein. Das Senden ist
relativ einfach, weil ich weiß ja wenn etwas zu senden ist. Schlimmer
wird es wenn Notifikation übermittelt werden oder ich etwas empfangen
soll.
Der USB-Controller ist so eingestellt, das er für den empfang von Daten
bereit ist. Die Daten laufen im Prinzip ohne mein Zutun ein. Irgend wie
muß ich aber die frohe Kunde erhalten das sich bei den Daten etwas getan
hat (oder eine Benachrichtigung über einen Statuswechsel). Der Aufruf
von dem hier tut genau das:
1
EP_DEF_in(ep_out);
2
EP_DEF_out(ep_in);
3
EP_DEF_out(ep_note);
Man könnte auch die expliziten Funktionen für die möglichen Zustände
aufrufen, das erfordert aber das man genau weiß was zu tun ist. Also
wird das in Universelle Funktionen zusammengefasst die dann entsprechend
das tun was nötig ist. Im Eingangsposting habe ich auch beschrieben das
es per IRQ abgewickelt werden kann, jedoch die Synchronisation ungleich
komplexer wird. Also wird bei jedem durchlauf einfach nachgesehen ob
sich etwas getan hat und entsprechend darauf reagiert.
Also das in ein Main-Loop reinpacken dann sollte das ausreichend sein.
Man kann das in etwa so beschreiben: Je seltener das aufgerufen wird, um
so langsamer ist auch die Datenübertragung. Frühestens bei Jedem Aufruf
der Funktionen kann ich auf Stati-Wechsel der USB-Schnittstelle
reagieren.
Das ist deutlich langsamer als mit IRQ und verbraucht mehr Resourcen,
ist aber erheblich unkomplizierter zu implementieren. Hier war das Ziel
möglichst einfach an eine Funktionierende CDC zu kommen. Optimieren kann
man wenn man versteht was abläuft oder es nötig ist.
Bei deinem Code sehe ich ein paar Fallen; genau genommen Zwei
wesentliche.
Ich übersetze einmal: Kopiere solange von Line ind cdc_rxb.data fis ein
\r erreicht wird.
Was ist wenn kein \r kommt? und warum \r.... hängt da ein MAC dran?
sonst wäre ein \n ehr üblich (Unix \n oder PC \n\r oder \r\n) aber das
wiederum was du der Schnittstelle gesagt hast was er als Abschlußzeichen
nehmen soll.
Hier nochmal der ursprüngliche code für die Main-Loop.
1
while(1)
2
{
3
// manage usb-task for the endpoints (in loop)
4
EP_DEF_in(ep_out);
5
EP_DEF_out(ep_in);
6
EP_DEF_out(ep_note);
7
8
// handle data if flag are cleared
9
if(!cdc_rxb.flag)
10
{
11
if(cdc_rxb.bytes==cdc_rxb.len)
12
{
13
// all data prcessed; send the data in txd and wait for new data
Hole dir das aktuelle Byte aus den empfangenen Daten raus in 'res'. Da
bei diesem Beispiel alle empfangenen Daten wieder zurück gesendet werden
sollen wird 'res' auch wieder in den Ausgabebuffer geschrieben.
In deinem Fall könntest du einfach in Line[i] schreiben, vorausgesetzt
wurde vor dem 'while' auf 0 gesetzt. Ich schmücke das ein bischen aus...
1
if(i<sizeof(Line)-1)
2
{
3
Line[i]=res;
4
}
5
if(res=='\r')
6
{
7
guck_was_in_Line_drin_ist();
8
// oder einfach Switch mit dem ersten Zeichen in Line
9
Switch(*Line)
10
{
11
case 'A': //die A-Funktion
12
a-Funktion(Line);
13
break;
14
case 'B': //die B-Funktion
15
b-Funktion(Line);
16
break;
17
case 'C': //die C-Funktion
18
c-Funktion(Line);
19
break;
20
default:
21
// nene... nur gueltige Funktionen machen was
22
break;
23
}
24
// Line wurde komplett abgearbeitet. Als naechstes muß eine neue Line kommen, darum Line wieder von vorne anfangen.
25
i=0;
26
}
Der code vor dem 'else' sorgt dafür das ein Transmit angestoßen wird
wenn der Ausgabebuffer gefuellt ist und der Transmitter frei ist
(!cdc_txb.flag).
1
// das kann alles auch an anderer stelle veranlaßt werden. ich muß den receiver nicht erst freigeben wenn ich Daten sende.
2
// ich muß auch erst Daten senden wenn ich sie reingepackt habe wenn es sich nicht um einen echo-test handelt
3
if(!cdc_txb.flag)
4
{
5
// soviel soll gesendet werden
6
cdc_txb.len=cdc_txb.bytes;
7
// das sorgt dafuer das die Daten gesendet werden
8
cdc_txb.flag=1;
9
// das sorgt dafuer das der Receiver wieder Daten aufnehmen kann.
hach, ich sehe gerader ich habe ein ++ vergessen.
falsch
1
Line[i]=res;
richtig
1
Line[i++]=res;
Der Code ist jetzt aus der hohlen Hand... also ungetestet, soll ja nur
ein Anhaltspunkt sein.
Der Main-Loop ist schon die Schleife in der du die einzelnen Zeichen
abarbeiten kannst. Und wirklich CPU-Resourcen werden hier nicht
vergeudet weil meistens an den paar IF's vorbeigerutscht wird. Davon
abgesehen sollte es auch mit deine Schleifen gehen wenn sichergestellt
ist das Line genügend groß ist das es die Zeichen aufnehmen kann, das
auch nur in den Buffer geschrieben wird wenn Platz ist und das dein Line
mit dem '\r' synchronisiert wird.
Letzteres mache ich in dem code oben mit i=0. Auch wenn das erste
Telegramm in die Hosen geht, spätestens beim 2. fängt Line immer mit
deiner Telegrammkennung an. Einfach ist es wenn man bei einem Telegramm
ein Startzeichen definiert. Da kann dann auch Müll kommen, bei jedem
Startzeichen wird von neuem versucht das Telegramm zu Interpretieren.
>A12345\r>B123456\r>C222\r>D\r
'>' ist das Startzeichen, \r das Endzeichen.
bye woodym
vielleicht nochmal die flags:
cdc_txb.flag
Eine 0 zeigt an ob der Transmitter frei ist. Ist das der Fall, kann ein
neues Senden angestoßen werden. Dazu müssen die Daten in cdc_txb.data
stehen und cdc_txb.len muß mit der Anzahl der Bytes gesetzt sein. Wird
dann cdc_txb.flag auf 1 gesetzt und dann
EP_DEF_in(ep_out);
EP_DEF_out(ep_in);
aufgerufen, werden die Daten gesendet. Wurden die Daten auch wirklich
verschickt, wird cdc_txb.flag wieder auf 0 gesetzt.
Das setzen/löschen der flags erfolgt durch die aufrufe von
EP_DEF_in(ep_out);
EP_DEF_out(ep_in);
cdc_rxb.flag
Eine 0 Zeigt das Daten eingegangen sind. Der Buffer cdc_rxb.data ist mit
den Daten beschrieben. Die Anzahl der Daten ist in cdc_rxb.len
eingetragen.
Setze ich das Flag auf 1, weiß der Aufruf von
EP_DEF_in(ep_out);
EP_DEF_out(ep_in);
das der Buffer frei ist (alles wurde verarbeitet) und für weitere Daten
verfügbar ist.
Hallo woodym,
vielen Dank für die ausführliche Erklärung! Ich werde das heute Abend
mal durcharbeiten und melde mich dann wieder. Bis dann.
Gruß
Matthias
Hallo woodym,
ja, die Daten kommen immer als Block und werden von einem eigenen
Programm gesendet (mit Visual Studio programmiert). Ich sende am Ende
jeden Blocks ein '\r'. Du hast Recht, ein '\r' ist unüblich. Ich habe
einfach ein Zeichen genommen, bei dem ich weiß, dass es an keiner
anderen Stelle der Zeichenkette vorkommt. Ich werde das aber eventuell
doch auf '\n' ändern, danke für den Hinweis.
Jetzt zum Code: Wenn man einmal verstanden hat wie es genau
funktioniert, ist es wirklich einfach. Ich habe deinen Vorschlag
umgesetzt und arbeite die einzelnen Zeichen jetzt in der Main-Loop ab
und nicht in einer gesonderten while-Schleife. Das ist besser und
funktioniert auch optimal.
Deinen Vorschlag ein Startzeichen zu definieren werde ich ebenfalls
berücksichtigen, dann kann ich sicher sein, dass auch wirklich alles
übertragen wurde und es kann zu keinen Fehlern bei der Interpretation
der Daten kommen.
Danke nochmal für deine Hilfe!
Gruß
Matthias
Hallo woodym,
vielen Dank für dieses schlankere CDC. Ich habe es auf einen xmega32a4u
problemlos portiert. Nun ja, fast problemlos, deshalb diese Nachfrage:
in ep_def_in wird bei Transition Complete dann der Handler aufgerufen:
LACR16(&(c->STATUS), USB_EP_BUSNACK0_bm | USB_EP_TRNCOMPL0_bm | USB_EP_OVF_bm); // note: LACR16 means load and clear
8
p->bank=1;
9
}
10
}
Das passiert unter 'ATOMIC', d.h. Interrupt sind in der Zeit komplett
zu. Ist das unbedingt nötig und wenn ja, warum? Eigentlich hat man doch
Ruhe, solange USB_EP_TRNCOMPL0 nicht gelöscht worden ist.
Ich möchte wegen der Bedienung anderer Interrupts die globalen
Interrupts so wenig wie möglich und so kurz wie möglich sperren.
Danke
Wolfgang
Hallo,
Ich bin gerade im Urlaub deswegen kann ich mir das nicht genau ansehen.
Soweit ich das noch im Kopf habe ist das deswegen weil der Händler, wenn
definiert, auch die Pointer umschaltet. Damit da nichts kracht muss hier
dafür gesorgt werden das nichts klemmt und gegenseitig überschreibt. Ist
kein Händler definiert, spielt das keine Rolle, dann ist das nur die if
Abfrage während die geblockt wird.
Wenn ich das noch richtig weiß ist das auch alles egal (man braucht das
Atomic nicht) wenn das nicht über Interrupt gemacht wird wie im
Beispiel. Dann wird das alles sowieso sequenziell abgearbeitet.
Ich hoffe ich konnte mit der Antwort helfen.
Ach so, auf dieser Basis habe ich vor ein paar Monaten ein Multi CDc
gemacht mit 5 seriellen... Funktioniert prima ( ich bin stolz ;-) ).
Bye woodym
Hallo,
ich habe da mal ein bischen durch den Code gelesen. Was mir noch nicht
klar ist: Das CDC Interface besteht ja aus 3 Endpoints (note, in, out).
in/out ist klar, aber mit dem note habe ich Verständnisschwierigkeiten:
Hinterlegt sind in usb_handle_ring_note Routinen zum Stellen/Abfragen
von DCD/DSR. Die Behandlung von LineEncodung erfolgt aber im
Endpoint[0], also auf dem Device-Endpoint, offenbar also nicht per
CDC-Interface, sondern global für das gesamte Device.
Du hast doch da schon mal einen composite aus mehreren CDCs gebaut -
sind diese denn dann unabhängig voneinander (bezogen auf LineEncoding) ?
Und das mit dem Interrupt-Blocken scheint mir bei speziell bei USB_EP_PP
= 1 (also Wechselbuffer) nicht nötig zu sein.
Servus Wolfgang
Hallo,
ich versuche, das mit HID zu kombinieren. Dazu habe ich die
Descriptortabelle für die Devicekonfig entsprechend erweitert. Aber da
gibt es dann Probleme: die Tabelle wird dadurch länger als die 64 Bytes
des EP0. Wie hast Du das gelöst?
Wolfgang K. schrieb:> Wie hast Du das gelöst?
Wie man das bei USB immer löst: in mehr als ein Paket aufteilen.
Falls es genau 64 Byte sind, musst du das berühmt-berüchtigte
ZLP (zero-length packet) nachschieben, damit die andere Seite das
Ende erkennt.
@Jörg Wunsch
als ich meine multi-Serielle gemacht hatte, dann hätte mir die Info Tage
an Arbeit gespart ;-)
@Wolfgang K.
ich habe das realisiert in void USB_ep0_send_progmem (in usb_xm.c)
aus diesem:
Danke für den Tipp, das ZLP war das Problem. Jetzt wird die Combidevice
eingelesen. Auf dem HID bekomme ich noch einen STALL, aber das ist das
nächste Problem ...
Servus Wolfgang
Hallo zusammen!
Danke für diesen hervorragenden Beitrag!
Ich habe deinen Code ins Atmel Studio 7 importiert und die Defines aus
deinem makefile übernommen. Läuft hier auf einem XMEGA128A1 einwandfrei.
Das lauffähige Projekt ist im Anhang.
Folgende Dinge sind allerdings komisch:
1. Die VirtualSerial.inf läuft auf meinem Host (Win7 64Bit) nicht. Ich
habe stattdessen von Atmel den Treiber für die Virtuelle Brücke (EDBG
Virtual COM Port) manuell geladen.
2. Wenn ich die Optimierung von -01 auf -O0 stelle, dann geht es nicht
mehr: Irgendwo scheint der Code zu langsam zu werden. Aber ohne
Optimierung ist das Debugging einfacher, so optimiert er alle möglichen
Variablen weg. Hab versucht, lokal die Optimierung abzuschalten (mit
#define _DEBUG), aber das geht nicht, hat wohl nichts mit der
Optimierung zu tun.
3. Bei dem CDC-Beispiel aus ASF
(das hier:
asf.atmel.com/docs/3.31.0/common.services.usb.class.cdc.device.example.s
tk600_atxmega128a1u/html/index.html)
funktioniert die Verbindung weiter, wenn man zwischenzeitig einen
Haltepunkt setzt und dann weiter laufen lässt. Scheint wohl eine
Fehlerresistenz eingebaut zu sein, die hier fehlt, oder wie ist das???
Danke und Grüße!
Christoph
Christoph M. schrieb:> 2. Wenn ich die Optimierung von -01 auf -O0 stelle, dann geht es nicht> mehr: Irgendwo scheint der Code zu langsam zu werden.
Wundert mich nicht.
Wenn du Variablen oder Zwischenergebnisse im Debugger brauchst,
dann definier' die entsprechenden Variablen vorübergehen "volatile".
Das schaltet dann nur deren Optimierung aus, nicht aber die komplette.
> 3. Bei dem CDC-Beispiel aus ASF> funktioniert die Verbindung weiter, wenn man zwischenzeitig einen> Haltepunkt setzt und dann weiter laufen lässt.
Glaub' ich nicht.
Wenn das Device beim Host bekannt ist, bleibt es auch erstmal bekannt,
solange keine Daten zum Device zu senden sind, auch wenn der Code
angehalten ist. Sowie er aber reagieren müsste, bricht der Kram
auseinander.
Das ist eigentlich eins der Hauptprobleme mit jeglichen USB-Devices
in Mikrocontroller-Firmware; daher sehen wir immer alternativ noch
eine richtige UART vor (sofern möglich), die man während des
Debuggens benutzen kann.
hallo,
zu Punkt 1) kann ich nichts sagen, bei mir funktioniert in inf auf 32
Bit XP, 64 Bit Win7 und 64 Bit win10.
Zu Punkt 2) .... ich habe noch nie andere Optimierung versucht.
Zu Punkt 3) Ich könnte mir vorstellen das ASF den Treiber per Interrupt
eingebunden hat. Dann dürften das ganze Handling auch unabhängig von
Breakpoints funktionieren. Wenn das, wie bei meinem Beispiel, per
polling eingebunden ist, kann das nicht mehr gehen weil der USB dann
nicht mehr frei wird weil keiner mehr die Flags wegnehmen kann. Das ist
das einzige was ich mir hier vorstellen kann.
bye woodym
Hallo Jörg, hallo Woodym,
hab das Programm komplett auseinandergenommen und analysiert... es ist
doch alles etwas anders:
Jörg W. schrieb:>> 2. Wenn ich die Optimierung von -01 auf -O0 stelle, dann geht es nicht>> mehr: Irgendwo scheint der Code zu langsam zu werden.> Wundert mich nicht.
Grund sich zu wundern: Der XMega ist nämlich auch ohne Optimierung
schnell genug, die USB-Kommunikation zu betreiben. Ohne Optimierung
scheitert es nur an der Konfiguration des µCs, d.h. dort wo die Code
Protection abgeschaltet wird. Dieser Mechanismus geht nur mit -O1. Alles
andere läuft auch mit -O0.
In dem Quellcode, den ich gleich anhänge, habe ich die Optimierung lokal
mit #pragma GCC optimize ("O1") aktiviert, den Rest des Programmes lasse
ich mit -O0 laufen.
>> 3. Bei dem CDC-Beispiel aus ASF>> funktioniert die Verbindung weiter, wenn man zwischenzeitig einen>> Haltepunkt setzt und dann weiter laufen lässt.>Glaub' ich nicht.
Ist aber so, zumindest zur Hälfte: Das RX läuft weiter, egal wie lang
der Haltepunkt war. Ich habe jetzt auch dieses Beispiel modifiziert, und
auch hier läuft zumindest der RX-Kanal weiter. Du kannst es gerne selber
ausprobieren.
Der TX geht nach dem Haltepunkt nicht mehr. Ich habe einen Graphen des
Programms erstellt und die Programmsequenzen analysiert, weil ich auch
das Senden nach einem Haltepunkt gerne wiederbeleben würde.
Ich weiß soviel:
1. Von einer beliebigen Wartezeit in der while(1) Schleife in main wird
TX nicht gekillt. D.h. ich kann fünf Sekunden lang die Endpunkte nicht
mehr bedienen, das macht nichts, es bleibt stabil. Also Woodym: Das
Pollen ist überhaupt kein Problem!
2. Laut USB-Spec (http://www.beyondlogic.org/usbnutshell/usb6.shtml)
muss der µC gewisse Anfragen des Host innerhalb von 50 mSek beantworten.
3. Es gibt keinen Programmcode, der regelmäßig auf Host-Anfrage
ausgeführt wird. Das habe ich durch Loggen der Funktionsaufrufe
herausgefunden. Also denke ich, dass das Einfrieren der µC-USB-Hardware
die Ursache für den TX-Abbruch ist. Vieleicht gibt hier der Host-Treiber
auf und pollt diesen Endpunkt nicht mehr oder so???
Übrigens ist das, was im Programm "Ringpuffer" oder ring_tx heißt,
eigentlich nur ein normaler Puffer ohne Ringstruktur. Hatte mich anfangs
etwas verwirrt.
Ich stell euch mal die Übersicht und alles was ich hab hier rein :)
Grüße,
Christoph
Hier noch wie oben versprochen meine main.c:
Man kann mit "A..." eine LED toggeln, mit "B..." einen Text im
Dauerfeuer zum Host senden und mit allen anderen Strings "x..." ein Echo
bekommen. Die Optimierung ist lokal differenziert, global aber für diese
Tests ausgeschaltet. Prozessor ist XMega128a1, Leiterplatte
selbstentworfen. Also LEDs wahrscheinlich nicht dort, wo sie bei euch
sind! Mit dieser main.c geht das Empfangen vom Host weiterhin, das
Senden ist (warum?) hinüber.
1
#include<stdio.h>
2
#include<string.h>
3
#include<stddef.h>
4
#include<stdlib.h>
5
#include<avr/io.h>
6
#include<avr/interrupt.h>
7
#include<avr/pgmspace.h>
8
9
#include"main.h"
10
#include"usb_cdc.h"
11
#include<util/delay.h>
12
13
14
voidinit(void)
15
{
16
CLK.PSCTRL=0x00;// no division on peripheral clock
Jörg W. schrieb:> Christoph M. schrieb:>> Das RX läuft weiter, egal wie lang der Haltepunkt war.>> Auch dann, wenn der Host in dieser Zeit etwas sendet?
Hab ich gerad mal live für dich getestet... ja. Auch wenn der Host zum
gestoppten µC sendet, geht es danach wieder! Die gesendet Bytes gehen
verloren, aber alles nach dem Haltepunkt kommt wieder korrekt an.
VG
HI
Würde auch gern etwas mit USB rumspielen und euere Programme als Anfang
nehmen, aber ich krieg es nicht zum Laufen. Hab dieses
USB-ganz-einfach-Zip genommen, aber diesen Fehler bekommen:
hallo,
das sieht so aus als würden die Definitionen für den USB nicht vorhanden
sein.
Nach meiner Kenntnis hat der atxmega128a1 kein USB, in so fern wären die
Fehlermeldungen richtig. Der atxmega128a1u , der hat USB.
Ich verwende auch den Programmers Notepad (darum hatte ich mich bei der
Diskussion wegen dem Debug rausgehalten). Hierfür hatte ich mehrfach den
gcc aktuallisieren müssen, da der beim Notepad mitgelieferte gcc zu alt
war und die neuen Prozessoren überhaupt nicht richtig definiert waren
(z.B. ohne USB).
Michael D. schrieb:> Würde auch gern etwas mit USB rumspielen und euere Programme als Anfang> nehmen, aber ich krieg es nicht zum Laufen. Hab dieses> USB-ganz-einfach-Zip genommen, aber diesen Fehler bekommen:
Ja, du musst 128a1u nehmen, mit 128a1 geht es nicht!
Außerdem musst du F_CPU mit 32MHz und F_USB mit 48MHz definieren. Sieht
so aus, als hättest du F_CPU auf 8MHz stehen.
VG Christoph
@woodym:
Welche Version hast du jetzt und wie hast du das gemacht? Meine ist
"avr-gcc (WinAVR 20100110) 4.3.3" nach dem Update und er kennt den
128a1u immer noch nicht :(
Michael D. schrieb:> nach dem Update
Wie willst du einem WinAVR, welches 2010 das letzte Mal aktualisiert
worden ist, denn auch ein "Update" verpassen?
Da musst du schon mal auf eine andere Toolchain umstellen, wenn du
aktuelle Devices benutzen möchtest, und das olle WinAVR aufgeben.
Früher war eben nicht alles besser …
Michael D. schrieb:> Welche Version hast du jetzt und wie hast du das gemacht? Meine ist> "avr-gcc (WinAVR 20100110) 4.3.3" nach dem Update und er kennt den> 128a1u immer noch nicht :(
Ich würd sagen, das hat nichts mit der Toolchain oder dem Compiler zu
tun, vielmehr mit den eingebundenen Bibliotheken. Denn es fehlen die
controllerspezifischen Definitionenen und Funktionen.
Du kannst dir ja die Mühe machen, und die Bibliotheken von Atmel Studio
zu deinem Programmers Notepad rüberschubsen. Oder du installierst Atmel
Studio und das Projekt müsste sich sofort übersetzen lassen.
VG
Christoph
Christoph M. schrieb:> Ich würd sagen, das hat nichts mit der Toolchain oder dem Compiler zu> tun, vielmehr mit den eingebundenen Bibliotheken.
Der Compiler muss den Controller schon auch kennen, denn er muss aus
der Angabe „-mmcu=atxmega128a1u“ intern eine Reihe von Parametern
ableiten.
Jörg W. schrieb:> Wie willst du einem WinAVR, welches 2010 das letzte Mal aktualisiert> worden ist, denn auch ein "Update" verpassen?
Ja, gar nicht. Ist mir auch im Nachhinein erst aufgefallen....
Kopf->Tisch
WInAVR aufgeben ist wohl die Lösung, aber Atmel Studio will diese Xmegas
bei mir nicht erkennen oder standardmäßig mitinstallieren. Aber das
kommt dann in einen anderen Thread.
Christoph M. schrieb:> Du kannst dir ja die Mühe machen, und die Bibliotheken von Atmel Studio> zu deinem Programmers Notepad rüberschubsen. Oder du installierst Atmel> Studio und das Projekt müsste sich sofort übersetzen lassen.
Die aktuelle avr-gcc toolchain gibts auch komplett separat als Download
von Atmel, ohne beigepacktes Studio. Das kann er installieren um die
verstaubte WinAVR Toolchain zu ersetzen.
So, bin jetzt auch soweit, dass das Ding mit dem Programm von chrito
sich als USB meldet. Es wird aber nicht erkannt(unknown device) vom
Windoof, egal mit welcher Optimierung. Was ist noch gleich die Lösung
dafür?
1
Um das hinzubekommen hab ich übrigens manuell ein Update ausgeführt
2
und Daten (zB iox128a1u.h und andere) der avr8-Toolchain von Atmel in
3
den WinAVR-Ordner geschmissen. Jetzt wird das Ding erkannt und der Code
#define USB_DEF_PID 0x2404 // PID for USB (Atmel CDC)
Dann hält Windows dein Gerät für ein Atmel Board und installiert den
Treiber automatisch.
Hat es eigentlich jemand schonmal unter Linux getestet?
VG
Christoph
Christoph M. schrieb:> Hat es eigentlich jemand schonmal unter Linux getestet?
Dem sind die IDs egal, es ist ja ein CDC, also wird es als
modemähnliches serielles Gerät registriert – von jedem anderen
Betriebssystem genauso. Sogar von Windows 10, immerhin können die
da jetzt auch schon. ;-)
Linux is eben geil und läuft wenn es soll.
Ganz anders mein Board: Es meldet sich zwar an am USB-Port, aber es wird
nicht erkannt (unknown device). Die PID und VID wird scheinbar nicht
übertragen, egal welche man ihm übergibt. Woran hängt das denn wieder?
Oder liegts an Windows?
Michael D. schrieb:> Woran hängt das denn wieder?
Um das rauszufinden, darfst du nicht nur die Definition von ein paar
Makros ansehen, sondern musst auch gucken, wo diese wirklich
verwendet werden.
USB-Debugging ist nicht so ganz trivial, vor allem, weil man die
Echtzeit-Abfolge der Firmware nicht stören darf, sonst ist der Host
sofort eingeschnappt.
Was sich bewährt hat: einen großen RAM-Puffer irgendwo anlegen, in dem
man verschiedene Trace-Informationen hinterlässt während der
Verarbeitung der Setup-Nachrichten. Nachdem das ganze USB-Geraffel
des Hosts dann durch ist, kann man mit dem Debugger die Chose mal
anhalten und sich in diesem Puffer in Ruhe ansehen, was zuvor passiert
ist.
Wenn du einen Linux-Host zur Verfügung hast, dort kann man per
usbmon den Traffic mit Wireshark aufzeichnen und ansehen (auch denen
eines Windows-Gasts in einer VM). Das ist recht hilfreich.
Hallo Michael!
Ich hab gerade nochmal das Projekt getestet, das ich hochgeladen habe.
Es funktioniert auf anhieb.
Daher:
Hast du ein aktuelles Atmel Studio Komplett installiert?
Womit hast du's jetzt kompiliert?
Nutzt du einen XMEGA 128A1U oder vergleichbar?
Ist der USB-Port richtig angeschlossen?
Funktioniert dein Board richtig, Spannungsversorgung usw. alles schick?
VG
Christoph
Hallo Michael,
hier nochmal mit anderen VIDs und PIDs, so müsste es auch von Windows
sofort erkannt werden.
"A..." schaltet eine LED an PQ0 an und aus
"B..." sorgt dafür, dass der Controller ca. einmal pro Sekunde "Hallo
Welt! " an den Host schickt.
Sollte so direkt laufen. Falls nicht, ist was anderes faul.
VG
HI
Kompiliert an meinem Rechner (mit der avr-gcc toolchain) und ausgetestet
mit meinem normalen Windows und einem anderen -> unbekanntes Device mit
PID und VID 0000. Ein Ubuntu zum Testen hat gar nix angezeigt.
Oeffnen des Projekts mit einem Atmel Studio 7 im Rechnerraum der Uni hat
nicht funktioniert: "The project 'USB_ganz_einfach' contains a device
'ATxmega128A1U' which is not supported by Atmel Studio." Klingt
vertraut. An irgendeinem Rechner hats dieses Board aber schon mal
erkannt. Bin mal suchen....
Okey, der Rechner kanns jetzt. Erkennt das Board und kann es
programmieren. Hab nur "#define F_CPU 30000000UL" und "#define F_CPU
42000000UL" dazugebaut, weil er mich da angemeckert hat. Aber auch hier
tritt der Fehler wieder auf - unknown device, PID und VID wie im Bild
Null.
Edit wegen der Nachfrage: Das Board mit dem atxmega128A1U hab ich so bei
Atmel gekauft, USB fuer Debugger und USB fuer Target (zwei verschidene
Stecker) waren schon drauf. Strom is genug da, kann da auch Leds dran
betreiben und schalten.
--> Was auch immer faul ist: Es nervt und ich hab keinen Schimmer woran
es liegt....
Hallo,
ich habe mir mal das USB_ganz_einfach_2.zip geschnappt.
Da ich mit dem Programmers Notepad arbeite, habe ich ein Makefile dazu
gepackt. Den Prozessor habe ich auf den ATXMEGA32A4U eingestellt (den
habe ich hier gerade liegen) und das ganze übersetzt.
Beim Übersetzten noch kleine Anpassungen (der 32A4U hat keinen Port Q).
Programmieren und Läuft.
Ich kann also nicht nachvollziehen das es sich um ein problem im
Quellcode handelt.
Ich vermute das die Aktuallisierung des Compilers nicht vollständig ist.
Ich habe das übrigens ähnlich gemacht.... Habe mir eine Kopie meines
WINAVR angelegt, das Studio installiert und einfach den Compiler mit
allen includes und Libs in WINAVR reinkopiert.
bye woodym
woodym schrieb:> Ich vermute das die Aktuallisierung des Compilers nicht vollständig ist.
Woraus schlussfolgerst du dieses?
Wenn der Compiler den Chip kennt, dann muss er dafür auch korrekten
Code übersetzen können. Wenn er ihn nicht kennt, geht einfach gar
nichts (hatten wir ja am Anfang).
hallo,
das schlussfolger ich daraus das der code 1:1 ( portmodifikation
ausgenommen ) bei mir funktioniert ;-)
und aus dem umstand das ich am anfang auch probleme hatte bei der
aktuallisierung des gcc. ich hatte es am anfang versucht mit "schauen
wir uns die änderungen an". das komplette rüber kopieren hatte da erst
funktioniert.
woodym schrieb:> das schlussfolger ich daraus das der code 1:1 ( portmodifikation> ausgenommen ) bei mir funktioniert ;-)
Eine sehr gewagte Schlussfolgerung.
Hab jetzt ein Beispiel von einem Kollegen bekommen. Ich kann damit
zumindest (über python und das ASF) mich mit dem µC unterhalten. Nicht
ganz das was ich wollte (eine Konsole wäre besser gewesen, will
SCPI-Befehle schicken), aber besser als nix.
Das kompiliert einwandfrei, meldet sich unter dem angegebenen
Device-Namen und so. Keine Ahnung was das Problem mit euerem Programm
war, aber das jetzt geht ohne Probleme. Rätselhaft.
(Ich werd es euch wohl nicht schicken, nicht dass der Kollege was
dagegen hat.)
Michael D. schrieb:> Keine Ahnung was das Problem mit euerem Programm war
Du weißt: Bugs, die man nicht aufgeklärt hat, tauchen genau dann wieder
auf, wenn man es am wenigsten gebrauchen kann …
Hallo an alle,
ich habe die USB-CDC Version auch bei mir problemlos zum laufen
gebracht.
Bei meiner Anwendung benötige ich in 2 Punkten Hilfe, bzw. Ideen.
Vergrößern der Buffer von CDC_TXRX_EPSIZE auf 256Bytes.
Meine Packete sind ca. 200Bytes lang. Muss ich mir einen weiteren Buffer
bauen oder kann ich das relativ einfach mit der bestehenden Struktur
lösen?
Wie versende ich das Ganze als stream (fdev_setup_stream(stream,
_usb_putc, _usb_getc, _FDEV_SETUP_RW);) Mit USB habe ich bisher noch
nichts gemacht,d aher bitte ich um ein paar Ideen.
Danke
Martin J. schrieb:> Vergrößern der Buffer von CDC_TXRX_EPSIZE auf 256Bytes.> Meine Packete sind ca. 200Bytes lang.
Hallo Martin, soweit ich weiß darf der Puffer maximal 64 Byte groß sein,
das ist bei USB so. D.h. du müsstest deine 200 Bytes in kleinere
Päckchen segementieren und auf der Gegenseite wieder zusammenbauen.
VG
Christoph
Martin J. schrieb:> Wie versende ich das Ganze als stream (fdev_setup_stream(stream,> _usb_putc, _usb_getc, _FDEV_SETUP_RW);) Mit USB habe ich bisher noch> nichts gemacht,d aher bitte ich um ein paar Ideen.
usb_putc wird ja von der Applikation mit einzelnen Bytes aufgerufen.
Sinnvoll ist es, innerhalb von usb_putc dann die Bytes erstmal zu
sammeln, entweder bis die 64 Bytes für die maximale Endpoint-Größe
zusammen sind, oder bis ein Timeout verstrichen ist.
Letztlich arbeiten auch fertige USB-Seriell-Wandler (wie FTDI) nach
dieser Methode, nur dass man bei denen vom Host aus den Timeout
konfigurieren kann.
Hallo,
hab mir das ganze jetzt so aufgebaut.
Gibt es irgendwo ein Register, wo ich sehe, ob eine USB Verbindung da
ist? Bzw. kann ich eigentlich einfach kontinuierlich Daten in den
Sendebuffer legen auch wenn keine Verbindung aufgebaut ist?
Grüße Martin
Martin J. schrieb:> Gibt es irgendwo ein Register, wo ich sehe, ob eine USB Verbindung da> ist?
Spätestens alle 1ms müsste ein SOF Token ankommen, wenn das ausbleibt
hat jemand den Stecker gezogen.
Martin J. schrieb:> Gibt es irgendwo ein Register, wo ich sehe, ob eine USB Verbindung da> ist?
Nein, die Hardware kann das nicht tracken.
Die Software könnte schon. Erstens weiß sie, wann sie eine Adresse
zugeteilt bekommen hat und die anderen Standard-Requests des Hosts
so durchgelaufen sind. Davon weißt du allerdings erstmal nur, dass
der Host das USB-Gerät überhaupt eingebunden hat. Deshalb muss es
noch lange keinen geben, der sich auf dem Host auch für das Gerät
interessiert (Layer 7 gewissermaßen).
Um herauszufinden, ob es auch eine Applikation gibt, die den Port
geöffnet hat, benutzen wir folgendes:
1
caseSET_CONTROL_LINE_STATE:
2
DTE_present=(pRequest->wValue&1)!=0;
3
break;
(Keine Ahnung, wo das codemäßig in den Xmega-Treiber passt, es
sollte genügen, eine Idee dafür zu bekommen.)
> Bzw. kann ich eigentlich einfach kontinuierlich Daten in den> Sendebuffer legen auch wenn keine Verbindung aufgebaut ist?
Natürlich nicht.
Hallo,
hat es schon jemand geschafft den CDC-USB-Treiber unter 4K Programmgröße
zu bekommen, so dass man den auch als bootloader mit verwenden könnte?
Im Netz gibt es zwar verschiedene Versionen von USB Bootloadern aber die
basieren meist auf dem ASF und sind zu groß oder verwenden kein CDC und
benötigen so einen speziellen Treiber.
jup... Guckst du da:
/* XBoot Extensible AVR Bootloader
*/
/*
*/
/* tested with ATXMEGA64A3, ATXMEGA128A1, ATXMEGA256A1, ATXMEGA32A4
*/
/*
*/
/* xboot.c
*/
/*
*/
/* Alex Forencich <alex@alexforencich.com>
*/
/*
*/
/* Copyright (c) 2010 Alex Forencich
*/
/*
*/
Ich kann dir leider die Dateien nicht geben (von hier aus kann ich das
nicht Zippen), aber den Auszug aus dem header habe ich. Nach dem sollte
man suchen können ;-)
bye woodym
leider sehe ich in dem USB treiber noch nicht genug durch, aber wie
könnte man den so reduzieren, dass es ein CDC-USB treiber bleibt, aber
kleiner als 4K benötigt.
momentan ist der Treiber CDC-USB mit einer einfachen Echo-Funktion 5,8K
groß
Vielleicht noch ein paar Infos hierzu.
Ich verwende den auf dem 32A4U erfolgreich. Beim Programmieren muß man
alles beachten was beim Programmieren des Bootloaders nötig ist (ich
meine das muß man extra angeben).
Es gibt die Möglichkeit eine LED zu definieren die dann schön flackert
wenn Daten übertragen werden. Um den Bootloader beim start zu aktivieren
benötigt man irgend einen Eingangspin der sagt das der Bootloader
angesprochen werden soll (z.B. Taster).
Wenn ich mich noch recht erinnere kann der Bootloader nicht den
Bootloader-bereich selber Programmieren.
bye woodym
Vielen Dank. bin schon dabei.
wie bekommst du den in den Speicher?
Wenn ich es compiliere bekomme ich eine Größe von 4240 bytes, es ist
somit zugroß :-(
Ich baue es mit dem aktuellen AVR Studio.
der Unterschied war, bei dir im Makefile ist noch folgende Option drin.
ifeq ($(MAKE_BOOTLOADER), yes)
LDFLAGS += -nostartfiles
endif
Was bedeutet das?
Wenn ich das noch richtig weiß, bedeutet diese Option das die normale
Initialisierung nicht stattfinden soll.
Jedes normale Programm bekommt eine kleine Initialisierung davor
gepackt. Da werden Vektoren gesetzt, Bereiche in den Speicher kopiert
und Variablen initialisiert. Für den Bootloader wird das nicht zwingend
benötigt wenn bei der Umsetzung bereits das alles berücksichtigt wurde.
Das spart dann ein paar hundert Bytes ein.
bye woodym
Danke danke.
Es funktioniert wunderbar!!
Lange gesucht, selber probiert und jetzt endlich eine Lösung :-)
Noch ein paar kleine Erweiterungen reinpacken und schon ist's perfekt.
Noch eine Frage, das lesen (0,03s) und schreiben(1,34s) geht ja richtig
schnell, warum ist das verifying(22,74) dagegen so relativ langsam?