Hallo,
auf http://www.fischl.de/usbasp/ steht derzeit:
* Planned: serial interface to target (e.g. for debugging).
Genau das möchte ich umsetzen.
Dazu habe ich mir folgendes überlegt:
Da der ISP-Programmieradapter schon die SPI-Leitungen belegt, soll das
Target über SPI mit dem USBasp reden, welches dann mit USB weiter mit
dem Hostrechner redet.
Um die den Overhead im Target so gering wie möglich zu halten, möchte
ich
das Target als SPI master und das USBasp als slave konfigurieren.
Denn dann kann man im Target komfortable aussuchen wann man die
Debugging Informationen sendet, als Slave können die
Lese/Schreibanfragen zu den unpassendsten Zeiten kommen.
Da das USBasp auch schon ein USB slave ist, also insgesamt 2
Schnittstellen
als Slave bedient, benötigt es einen Puffer um die beiden Schnittstellen
zu synchronisieren.
Der Puffer funktioniert, dieser kann auch schon per USB ausgelesen
werden.
Immer wenn ich Bytes hineinschreibe, kann man sie am Hostrechner
emfangen.
Die SPI Schnittstelle selber funktionert auch, ich kann also Daten von
dem Target zu dem USBasp senden.
Das Problem ist die Synchronisation der beiden Schnittstellen:
Da das USBasp im SPI-slave modus arbeitet, nimmt es die Daten per
Interrupt an.
Wenn aber so ein Interrupt in das usbPoll() an der unpassenden, da
zeitkritischen Stelle einschlägt, gibt es Fehler im USB Stack.
Wie geht man jetzt am besten vor?
* Man könnte mit noch einer Leitung dem Target signalisieren, wann SPI
erwünscht ist und wann nicht.
* Ignorieren der SPI Nachrichten während der aktiven USB Phase.
* Doch den USBasp als SPI-Master und das Target als slave konfigurieren?
Was ist aus Benutzersicht am einfachsten?
Die Kommunikation über einen Verlust-behafteten Kanal oder die ein
bestimmtes Timing zum Senden der Debugnachrichten?
Ich habe schon Code geschrieben, dieser funktioniert allerdings nur
sehr begrenzt, da eben dieses Synchronisationsproblem auftaucht.
https://github.com/stefanbeller/USBasp
Der Code kann auf 2 USBasps getestet werden (eins als echtes USBasp, das
andere als Target, welches hin und wieder Daten per SPI verschickt.)
Gruß,
Stefan
Hallo!
Zuerst mein Kompliment, ich finde es klasse, dass sich mal jemand an
diese Baustelle herantraut!
Stefan B. schrieb:> Die SPI Schnittstelle selber funktionert auch, ich kann also Daten von> dem Target zu dem USBasp senden.
:-)
> Das Problem ist die Synchronisation der beiden Schnittstellen:> Da das USBasp im SPI-slave modus arbeitet, nimmt es die Daten per> Interrupt an.> Wenn aber so ein Interrupt in das usbPoll() an der unpassenden, da> zeitkritischen Stelle einschlägt, gibt es Fehler im USB Stack.
Klingt so, als bräuchtest du ein USIBR (USI Buffer Register), wie ich es
vom ATtiny85 her kenne. Gibts aber wohl bei den ATmegas nicht... oder?
Was passiert, wenn du die Interrupts während dieser "zeitkritischen
Stelle" sperrst?
> * Man könnte mit noch einer Leitung dem Target signalisieren, wann SPI> erwünscht ist und wann nicht.
Fürs Target zu aufwändig auszuwerten, find ich.
> * Ignorieren der SPI Nachrichten während der aktiven USB Phase.
Du meinst, die Nachrichten, die das Target sendet? Ich hab nicht
verstanden, was die "aktive USB-Phase" ist. Ist das nur dann, wenn der
USBasp zum Programmieren genutzt wird? Dann wär das sicher kein Problem
für den User.
> * Doch den USBasp als SPI-Master und das Target als slave konfigurieren?
Auch wieder zu aufwändig für das Ziel, denk ich...
Markus W. schrieb:> Klingt so, als bräuchtest du ein USIBR (USI Buffer Register), wie ich es> vom ATtiny85 her kenne. Gibts aber wohl bei den ATmegas nicht... oder?>> Was passiert, wenn du die Interrupts während dieser "zeitkritischen> Stelle" sperrst?
Ein USIBR habe ich nicht in dem Datenblatt des Atmega8 finden können.
Bisher sah die Hauptschleife so aus:
1
for(;;){
2
usbPoll();
3
}
Wenn ich die Interrupts während des usbPoll() ausstelle, dann kommen in
derzeit keine Nachrichten mehr an, es kann also maximal eine Nachricht
(1Byte) geschickt werden. Jedes weitere Byte würde das bisherige in dem
SPDR Register überschreiben.
Der USB Host pollt regelmäßig alle angeschlossenen Geräte, diese müssen
innerhalb von 50ms antworten, sonst wird das Gerät abgemeldet.
Dieses Antworten passiert innerhalb des usbPoll(). die usbPoll Funktion
hingegen, muss Zyklen-genau sein laut deren Dokumentation, um die Zeiten
auf dem USB Bus zu einzuhalten.
Ich habe bisher die Hauptschleife so erweitert:
1
for(;;){
2
SPCR&=~(1<<SPIE);
3
usbPoll();
4
SPCR|=(1<<SPIE);
5
if(prog_state==PROG_STATE_SERIAL){
6
if(--blink_counter==0){
7
toggleLedRed();
8
blink_counter=(unsignedint)(1UL<<15);
9
}
10
}
11
}
Weiterhin könnte man noch soetwas wie pollSPI() einfügen in der Funktion
wird dann auf die Nachrichten von dem Target gewartet.
Lange Rede, kurzer Sinn:
Falls der SPI Interrupt deaktiviert wird, kann man nie sicher sein, ob
Nachrichten von dem Target bei dem USBasp ankommen. Man kann sogar
ziemlich sicher sein, dass Packetverluste (heh, Byteverluste) auftreten
und muss definitiv ein Data-Link-Layer Protokoll einsetzen, welches die
Übertragung sicherstellt.
Alternativ kann man versuchen die usbPoll() Funktion zu sezieren und nur
an den wirklich zeitkritischen Stellen den SPI Interrupt deaktivieren.
Bisher habe ich die USB Schnittstelle nicht näher untersucht.
> Du meinst, die Nachrichten, die das Target sendet? Ich hab nicht> verstanden, was die "aktive USB-Phase" ist. Ist das nur dann, wenn der> USBasp zum Programmieren genutzt wird? Dann wär das sicher kein Problem> für den User.
Mit der "aktiven USB-Phase" meinte ich die usbPoll() Funktion, die also
nicht nur während des Flashen des Target, sondern die ganze Zeit
regelmäßig ausgeführt wird.
Gruß,
Stefan
OK, danke, nun hab ichs begriffen. :-)
So ganz einfach wird die Sache vermutlich nicht sein, sonst hätten die
Entwickler von damals es schnell erledigt. Kann also gut sein, dass ein
gehöriger Schluck Aufwand drinsteckt und du deine Ergänzungen in die
Funktion usbPoll() integrieren musst.
Du könntest aber auch mal bei fischl.de direkt anklopfen, vielleicht
haben die noch eine Idee, wo man anpacken sollte.
Bitte meld dich wieder, es interessiert mich brennend, ob du mit dem
Vorhaben weiterkommst. Vielleicht schau ich auch mal in den Quellcode,
schadet ja nichts, auch wenn ich AVR-Assembler viel besser kann als
AVR-C.
Markus W. schrieb:> Bitte meld dich wieder, es interessiert mich brennend, ob du mit dem> Vorhaben weiterkommst. Vielleicht schau ich auch mal in den Quellcode,> schadet ja nichts, auch wenn ich AVR-Assembler viel besser kann als> AVR-C.
Heh, bei mir ist es genau andersherum. Ich kann C viel besser als
Assembler jeglicher Art.
Das usbPoll() scheint mir auch nur eine Wrapper Funktion für die
Assembler Sachen zu sein.
Stefan B. schrieb:> Heh, bei mir ist es genau andersherum. Ich kann C viel besser als> Assembler jeglicher Art.
Du kriegst das bestimmt hin.
> Das usbPoll() scheint mir auch nur eine Wrapper Funktion für die> Assembler Sachen zu sein.
Sieht nicht unbedingt so aus. Die Funktion ist in der Datei usbdrv.c
definiert:
1
USB_PUBLICvoidusbPoll(void)
2
{
3
scharlen;
4
uchari;
5
6
len=usbRxLen-3;
7
if(len>=0){
8
/* We could check CRC16 here -- but ACK has already been sent anyway. If you
9
* need data integrity checks with this driver, check the CRC in your app
10
* code and report errors back to the host. Since the ACK was already sent,
Noch ein Gedanke:
Du schreibst, dass es Probleme gibt, wenn über SPI ein Byte ankommt und
damit ein Interrupt ausgelöst wird, während die oben genannte
USB-Funktion läuft. Kann es sein, dass die betreffende Interrupt-Routine
einfach zu lang ist, also zu viel Zeit braucht? Vielleicht kann man die
verkürzen oder optimieren.
Magst du sie posten?
Zu den Zeitkritischen Sachen:
In der Datei usbdrvasm12.asm steht:
> Do not link this file! Link usbdrvasm.S instead, which includes the> appropriate implementation!> Since almost all of this code is timing critical, don't change unless you> really know what you are doing! Many parts require not only a maximum number> of CPU cycles, but even an exact number of cycles!
Jetzt müssen wir untersuchen, in welcher Datei die
Interruptsperren/freigaben eingebaut werden müssen.
Die usbPoll() Funktion ist zu weit oben im USB STack angesetzt, die
Assemblerdateien wohl zuweit unten.
Danke!
Ich hab mal durchgezählt, die ISR schluckt 52 Takte. Das ist ziemlich
lang für zwei Zeilen Code. C arbeitet leider sehr viel über den Stack,
jedes push braucht 2 Takte - jedes pop auch.
Gibt es bei C die Möglichkeit, den Compiler anzuweisen, bestimmte
Register nicht zu benutzen? Falls ja, könnte man die ISR evtl. in
Assembler schreiben, sie würde dann weniger als halb so lang laufen.
Zum C-Code:
Die Übersetzung des Compilers find ich ok, lediglich das zweite "push
r0" und das erste "pop r0" sind überflüssig. Aber darauf kommt es
wahrscheinlich auch nicht mehr an.
In welchem Wertebereich bewegt sich comStop? Könntest du statt mit
comBuffer[comStop] auch mit einem Zeiger arbeiten (z.B. *comPointer++ )?
Falls ja, würde das eine 16-Bit-Addition in der ISR sparen. Außerdem
könnte Post-Inkrement verwendet werden (statt "st Z,r25" dann "st
Z+,r25"), das spart die zweite Addition ("subi r24,0xff") ein. Ob das
alles das Problem löst, weiß ich natürlich nicht... :-(
Oliver J. schrieb:> Mit ISR_NOBLOCK sollte also alles im grünen Bereich sein.
Hm. Das bedingt, dass alles Zeitkritische des bisherigen Programms sich
nur in Interrupt-Routinen befindet. Davon war ich nicht ausgegangen,
aber du hast völlig Recht, das ist einen Versuch wert!
Ein Problem könnte es geben, wenn die ISR(SPI_STC_vect) aufgerufen wird,
während sie noch läuft, aber das ist sehr unwahrscheinlich.
Markus W. schrieb:>> In welchem Wertebereich bewegt sich comStop? Könntest du statt mit> comBuffer[comStop] auch mit einem Zeiger arbeiten (z.B. *comPointer++ )?> Falls ja, würde das eine 16-Bit-Addition in der ISR sparen. Außerdem> könnte Post-Inkrement verwendet werden (statt "st Z,r25" dann "st> Z+,r25"), das spart die zweite Addition ("subi r24,0xff") ein. Ob das> alles das Problem löst, weiß ich natürlich nicht... :-(
Im ersten Beitrag ist der bisherige Code verlinkt:
1
// choose size to 2^8, so comStart, comEnd can wrap around as ringbuffer
Markus W. schrieb:> Hm. Das bedingt, dass alles Zeitkritische des bisherigen Programms sich> nur in Interrupt-Routinen befindet.
In der usbPoll-Funktion steckt sicher nichts Zeitkritisches:
Quelle usbdrv.h (20120109):
1
USB_PUBLICvoidusbPoll(void);
2
/* This function must be called at regular intervals from the main loop.
3
* Maximum delay between calls is somewhat less than 50ms (USB timeout for
4
* accepting a Setup message). Otherwise the device will not be recognized.
5
* Please note that debug outputs through the UART take ~ 0.5ms per byte
6
* at 19200 bps.
7
*/
Markus W. schrieb:> Ein Problem könnte es geben, wenn die ISR(SPI_STC_vect) aufgerufen wird,
Das könnte man über ein Globales Flag abfangen:
Oliver J. schrieb:> Mit ISR_NOBLOCK sollte also alles im grünen Bereich sein.
Heh, Ich sollte die Doku besser lesen. Danke für den entscheidenen
Hinweis.
Ich habe den SPI Interrupt auf ISR_NOBLOCK gesetzt und das Auslesen von
Daten aus dem Target über das USBasp funktioniert jetzt.
Wenn zuviele Daten per SPI versandt werden, gibt es immer noch
Packetverlust, aber das ist ja normal bei Überlast. Bei einer Last von 2
Bytes in 10 ms (unregelmäßig aufgeteilt, dh. 9.6 ms lang nichts und dann
2 Bytes kurz hintereinander) funktioniert es.
Als nächsten Schritt könnte Ich einen Treiber für die Hostseite
bereitstellen, derzeit habe ich nur ein rudimentäres Python Skript
welches die Daten entgegennimmt und in der Konsole ausgibt.
https://github.com/stefanbeller/USBasp/blob/master/host/testcommunication.py
Was erwartet ein Benutzer von einem Hostseitigen Treiber für so eine
Kommunikation? Vielleicht einfach nur ein Programm, welches die Daten
auf stdout ausgibt, sodass man diese in anderen Programmen
wieterverarbeiten kann?
Andere Detailfrage:
Soll die rote LED, die sonst den Programmiervorgang anzeigt, während der
SPI Kommunikation auch konstant an sein? Derzeit habe ich sie auf
Blinken eingestellt, sodass man das Programmieren und die SPI
Kommunikation voneinander unterscheiden kann.
Stefan B. schrieb:> Danke für den entscheidenen Hinweis.
Dem Dank an Oliver schließ ich mich an!
> Ich habe den SPI Interrupt auf ISR_NOBLOCK gesetzt und das Auslesen von> Daten aus dem Target über das USBasp funktioniert jetzt.
Schööön!
Wie liest du eigentlich? So ähnlich wie mit dem Befehl hier?
cat /dev/irgendwas
> Wenn zuviele Daten per SPI versandt werden, gibt es immer noch> Packetverlust, aber das ist ja normal bei Überlast. Bei einer Last von 2> Bytes in 10 ms (unregelmäßig aufgeteilt, dh. 9.6 ms lang nichts und dann> 2 Bytes kurz hintereinander) funktioniert es.
Hmmm... der Puffer müsste mit 256 Bytes auf jeden Fall groß genug sein.
Woran liegt das? Eventuell noch ein Bug?
Viele werden später Daten zeilenweise mit printf() versenden wollen, da
gäbs dann schon ein kleines Problem...
> Als nächsten Schritt könnte Ich einen Treiber für die Hostseite> bereitstellen, derzeit habe ich nur ein rudimentäres Python Skript> welches die Daten entgegennimmt und in der Konsole ausgibt.> https://github.com/stefanbeller/USBasp/blob/master/host/testcommunication.py
"Treiber" klingt so nach Windows – da halte ich mich mal raus. :-)
> Andere Detailfrage:> Soll die rote LED, die sonst den Programmiervorgang anzeigt, während der> SPI Kommunikation auch konstant an sein? Derzeit habe ich sie auf> Blinken eingestellt, sodass man das Programmieren und die SPI> Kommunikation voneinander unterscheiden kann.
Mir würde das Blinken gefallen, dann sieht man, dass Daten kommen. Sie
blinkt nur, wenn der angeschlossene Mikrocontroller Daten sendet oder
zumindest einen Takt anlegt, oder? Wenn sie IMMER blinken würde, wärs
vielleicht doch zu nervig. :-)
Mit welchem USBasp experimetierst du eigentlich? Ich hab zwei
verschiedene China-USBasps hier rumliegen, wär natürlich toll, wenn
deine Software dann auf beiden laufen würde. :-)
Markus W. schrieb:> Hmmm... der Puffer müsste mit 256 Bytes auf jeden Fall groß genug sein.> Woran liegt das? Eventuell noch ein Bug?> Viele werden später Daten zeilenweise mit printf() versenden wollen, da> gäbs dann schon ein kleines Problem...
Da nun die SPI isr unterbrochen werden kann und ich nicht weiß wie lange
die USB isr braucht, können Bytes verloren gehen, wenn man die zu
schnell hintereinander sendet.
Ich habe nicht ausgemessen, wie klein der minimale zeitliche Abstand
zwischen zwei Bytes sein muss, um definitiv 'Packetverlust' zu
vermeiden,
320µs als Wartezeit waren halt einfach zu bekommen. ;)
Meine USBasps sind aus dem Shop von Ullrich Radig, die Schaltpläne
findet man hier
http://www.ulrichradig.de/home/index.php/avr/usb-avr-prog
Um die Erweiterung schnell testen zu können, habe ich einfach die
Hauptschleife umgebaut, siehe
https://github.com/stefanbeller/USBasp/blob/master/firmware/main.c#L371
(clockWait wartet 320µs und war schon da)
Zu dem Treiber... Naja ich benutze derzeit in python die python-usb lib
version 0.4.3-1 (Standard in Ubuntu 12.04)
Da USB Kommunikation über sogenannte Setup Packete erfolgt, können pro
USB Packet nur 8 Byte Nutzdaten an den Hostpc gesendet werden.
(Es gibt noch sogenannte Bulk Transfers oder Interrupt Transfers, aber
da USB Neuland für mich ist, benutze ich die Packete die auch alle
anderen Befehle des USBasps benutzen.)
Und in python habe ich jetzt die Funktionen
Stefan B. schrieb:> Ich habe nicht ausgemessen, wie klein der minimale zeitliche Abstand> zwischen zwei Bytes sein muss, um definitiv 'Packetverlust' zu> vermeiden,> 320µs als Wartezeit waren halt einfach zu bekommen. ;)
Kann man dem Anwender sowas wie eine maximale Taktfrequenz fürs SPI als
Richtwert geben? Ich nehm an, dass es kein Problem ist, wenn es keine
Wartezeit zwischen den Bytes gibt, wenn die SPI-Frequenz entsprechend
gering ist. Das wär für den User am einfachsten zu verstehen und
umzusetzen. Auch printf() und andere Routinen halten sich dann daran.
> Meine USBasps sind aus dem Shop von Ullrich Radig, die Schaltpläne> findet man hier> http://www.ulrichradig.de/home/index.php/avr/usb-avr-prog
Werd ich mir anschauen, danke! Ich nehm an, du setzt mit deiner Software
auf dem letzten Entwicklungsstand von fischl.de auf (Mail 2011)?
> Um die Erweiterung schnell testen zu können, habe ich einfach die> Hauptschleife umgebaut, siehe> https://github.com/stefanbeller/USBasp/blob/master/firmware/main.c#L371> (clockWait wartet 320µs und war schon da)
Ich schau morgen mal rein. Danke.
> Zu dem Treiber... Naja ich benutze derzeit in python die python-usb lib> version 0.4.3-1 (Standard in Ubuntu 12.04)
Schon drauf, wenn man Ubuntu neu installiert? Ich denk jetzt einfach mal
an die Neulinge...
> (Es gibt noch sogenannte Bulk Transfers oder Interrupt Transfers, aber> da USB Neuland für mich ist, benutze ich die Packete die auch alle> anderen Befehle des USBasps benutzen.)
Mach da bloß keinen Aufwand, es geht erst einmal darum, Textausgaben auf
den Schirm zu bringen. Je einfacher, desto leichter lässt sich die
Software hinterher pflegen.
> Und in python habe ich jetzt die Funktionen> (...)> Die Funktion getData müsste noch so umgebaut werden, sodass man einen> kontinuierlichen Bytestrom bekommt.
Als einfacher Anwender stelle ich es mir ideal vor, wenn man ein Binary
hat, das Linux-mäßig auf stdout schreibt. Dann kann man es beliebig in
Pipes einbauen usw.
Vielleicht kann avrdude schon von Haus aus sowas? Ich hab keine Ahnung.
Stefan B. schrieb:> Da nun die SPI isr unterbrochen werden kann und ich nicht weiß wie lange> die USB isr braucht, können Bytes verloren gehen, wenn man die zu> schnell hintereinander sendet.
Wenn Daten verloren gehen, dann muss man die Geschichte irgendwie
synchronisieren. Ich hätte dazu folgende Idee:
Beide Geräte sind abwechselnd Master oder Slave. Also: Ein
Multimaster-Bus.
Target-AVR | Datenrichtung | usbasp | Aktion
------------+-----------------+-------------+---------------------------
---
Master | --> | Slave | Byte wird versendet
Slave | <-- | Master | usbasp schickt ACK
Zuerst wartet der usbasp auf die Daten und danach wartet der Target-AVR
auf das Acklowledge-byte. Und das immer im Wechsel. Dabei sendet niemand
zu viel und man hat immer Zeit die Bytes abzuholen, ohne dass ein
Overrun auftritt.
Könnte das nicht so funktionieren?
Gruß Oliver
Oliver J. schrieb:> Target-AVR | Datenrichtung | usbasp | Aktion> ------------+-----------------+-------------+--------------------------- ---> Master | --> | Slave | Byte wird versendet> Slave | <-- | Master | usbasp schickt ACK
Das ist eine gute Idee für eine Flusssteuerung. Würde bestimmt prima
funktionieren.
Das schränkt aber die Einsatzmöglichkeiten von Stefans Projekt ein. Ziel
sollte es sein – so denke ich – Testausgaben und andere Ausgaben per
printf() zu ermöglichen.
Oliver J. schrieb:> Wenn Daten verloren gehen, dann muss man die Geschichte irgendwie> synchronisieren. Ich hätte dazu folgende Idee:>> Beide Geräte sind abwechselnd Master oder Slave. Also: Ein> Multimaster-Bus.
Woher weiß das Gerät (beide), wann es den Status umschalten soll?
Wenn das Target zufällig ziemlich lange keine Daten verschickt und dann
viele Daten in kurzer Zeit?
Ich denke so ein Multimasterbus verkompliziert die ganze Sache nur.
(Das wäre auf dem USBasp kein Problem, obwohl auch da wegen des
zeitkritischen USB Stacks die Umschaltung getimed werden muss.
SPI ist ein voll Duplex Bus, das bedeutet, das immer wenn A ein Byte
nach B sendet, auch automatisch ein Byte von B nach A gesendet wird.
Man könnte also im SPI-Slave (USBasp) ein ACK als nächstes zu sendendes
Byte in die SPI Register schreiben sobald das Byte gesichert ist.
Bisher sieht der Interrupt so aus
1
ISR(SPI_STC_vect,ISR_NOBLOCK)
2
{
3
comBuffer[comStop]=SPDR;
4
comStop++;
5
}
Als Erweiterung wird jedes Byte invertiert und im nächsten
Nachrichtenaustausch als ACK zurückgesendet:
1
ISR(SPI_STC_vect,ISR_NOBLOCK)
2
{
3
constucharrecv=SPDR;
4
comBuffer[comStop]=recv;
5
comStop++;
6
SPDR=~recv;
7
}
Nun kann das Target selber feststellen ob die SPI-Nachrichten zu schnell
in dem USBasp auflaufen
> Dabei sendet niemand> zu viel und man hat immer Zeit die Bytes abzuholen, ohne dass ein> Overrun auftritt.>> Könnte das nicht so funktionieren?
Heh, "niemand sendet zuviel", das ist meiner Meinung nach einfacher
umsetzbar mit der Idee von Markus, nämlich eine maximale Taktfrequenz
anzugeben, mit der Daten gesendet werden dürfen.
> Als einfacher Anwender stelle ich es mir ideal vor, wenn man ein Binary> hat, das Linux-mäßig auf stdout schreibt. Dann kann man es beliebig in> Pipes einbauen usw.> Vielleicht kann avrdude schon von Haus aus sowas? Ich hab keine Ahnung.
Wahrscheinlich müsste man dann avrdude erweitern, mal schauen was sich
da machen lässt :)
Das Problem auf dem PC ist auch folgendes:
Wir können keine Annahmen über die Anzahl der gesndeten Nachrichten von
dem Target machen, außer die maximale Taktfrequenz als obere Schranke.
Daher muss man auf der PC Seite eigentlich ununterbrochen USB-Control
Nachrichten verschicken, mit der Bitte die Daten aus dem Puffer des
USBasps zum Host zu senden.
Meine Pythonlösung (ist übrigens nicht standardmäßig installiert) hat
daher eine CPU Auslastung von 100% :(
Mit avrdude wäre das nicht anders.
>>> Gruß Oliver
Gruß Stefan
Stefan B. schrieb:> SPI ist ein voll Duplex Bus, das bedeutet, das immer wenn A ein Byte> nach B sendet, auch automatisch ein Byte von B nach A gesendet wird.
Wie stellst du dir das genau vor?
> Woher weiß das Gerät (beide), wann es den Status umschalten soll?> Wenn das Target zufällig ziemlich lange keine Daten verschickt und dann> viele Daten in kurzer Zeit?
Ich dachte mir das so:
Der usbasp ist quasi immer Slave. Er wird nur kurz zum Master wenn er
das Ack verschickt. Das würde ich vom Hauptprogramm aus machen, um auch
gleich danach wieder umschalten zu können.
Der Target-AVR sendet ein Byte und schaltet um auf Slave. Wenn er das
ACK bekommt, dann schaltet er wieder auf Master und sendet das nächste
Byte, wenn vorhanden.
Hmm das wird aber nichts, wenn der USB-Interrupt feuert, bevor der
USB-asp wieder auf Slave geschalten hat. Dann würde nämlich wenn der
Target-AVR der Meinung ist, dass er wieder senden darf, der usbasp immer
noch ein Master sein. Das kann so nicht funktionieren.
Gruß Oliver
Ich glaube ich habe die Handshake Lösung gefunden:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=107060&start=0
Der Master setzt in seinem Telegramm das Busyflag, sendet es heraus und
pollt dann solange die MISO-Leitung, bis der Slave sein Byte in das SPDR
herein geschrieben hat und damit das Busyflag toggelt. Dann kann der
Master das nächste Byte senden. Kommt einem Handshake über die
MISO-Leitung gleich.
Man könnte einfach gestalten indem man nur 7 Datenbit verwendet und das
höchste dann als Busyflag nutzt. Da könnte man dann immer 7 Bit in beide
Richtungen übertragen. Null wird als kein Zeichen gewertet. Da müsste
man aber vom Target-AVR aus den USBasp dann ständig pollen, wenn in
beide Richtungen übertragen werden soll. Für ASCII reichen 7 Bit denke
ich.
Gruß Oliver
Denkt bitte dran, dass es hinterher für den Anwender möglichst einfach
sein soll. printf() auf dem Mikrocontroller soll möglichst ohne
Änderungen oder Rücksichtnahme auf ein Flusssteuerungsprotokoll
fehlerfrei funktionieren.
Wahrscheinlich geht das wirklich nur mit der Vorgabe einer maximalen
Taktfrequenz.
Was mir vorhin auch durch den Kopf ging: Wenn der USBasp Master ist,
kann die Übertragung trotzdem in Gegenrichtung stattfinden, ja, es stört
auch nicht, wenn sie nur in Gegenrichtung läuft. Der USBasp schickt
dann nur den Takt, mehr nicht.
Beim regelmäßigen Wechsel zwischen Master uns Slave muss auch immer der
Takt-Master wechseln, das heißt, man braucht Sicherheitsvorkehrungen auf
der Taktleitung – z.B. nach dem Open-Kollektor-Prinzip. Das
verkompliziert die Sache.
Trotzdem wär es am einfachsten, wenn der Mikrocontroller der Master
bleiben darf – eben mit langsamem Takt.
Markus W. schrieb:> Denkt bitte dran, dass es hinterher für den Anwender möglichst einfach> sein soll.
Wenn man dem Anwender eine Lib oder eine fertige putc-Funktion gibt, ist
dem das sicher zum Schluss egal, wie die Kommunikation intern
funktioniert.
> printf() auf dem Mikrocontroller soll möglichst ohne> Änderungen oder Rücksichtnahme auf ein Flusssteuerungsprotokoll> fehlerfrei funktionieren.
Warum sollte das kompliziert sein. Einfach ein spi_putc gemäß der
avr-libc bauen und stdout darauf umleiten. Die putc Funktion blockiert
halt solange bis der Slave nicht mehr busy ist.
> Trotzdem wär es am einfachsten, wenn der Mikrocontroller der Master> bleiben darf – eben mit langsamem Takt.
Das kann er bei dem MISO-Polling definitiv.
Ich denke, dass man das tatsächlich so machen kann/sollte, wie ich es
bereits vorhin beschreiben habe (MISO-Busy-Polling). Für eine Richtung
ist das fast schon trivial. Damit hätte man den maximal möglichen
Durchsatz und kann auch noch buffer-overflows verhindern, wenn der Slave
seinen Zustand erst auf nicht busy setzt, wenn Platz im Ringpuffer ist.
Wenn ich heute Abend etwas Zeit habe, dann probiere ich das mal aus.
PS. Wäre das denn nicht schön, wenn man den maximal möglichen Durchsatz
erreichen kann und nicht um ein einziges Byte bangen müsste?
Gruß Oliver
Man könnte auch 8 Bit versenden und nutzt ein Toggeln des MSB im usbasp
für den Handshake. Da müsste man nur den Kommunikationsstart irgendwie
synchronisieren. Vielleicht reicht es aus, wenn man das erste Byte nach
dem Reset des Target-AVR einfach heraus schreibt und danach erst
Handshake betreibt.
Gruß Oliver
Oliver J. schrieb:> Warum sollte das kompliziert sein. Einfach ein spi_putc gemäß der> avr-libc bauen und stdout darauf umleiten. Die putc Funktion blockiert> halt solange bis der Slave nicht mehr busy ist.
Hallo!
Denke doch bitte auch an die Leute, die z.B. einen ATtiny13 in Assembler
programmieren. Gerade für die ist das USB-SPI sehr wertvoll, weil sie
nur schwer auf andere Wege umsteigen können (die kleinen Tinys haben
keinen UART). Hier kostet jedes Byte wertvollen Platz, und eine Lib
lässt sich da kaum "einbinden".
Ich seh es deswegen genauso wie Markus: maximale SPI-Geschwindigkeit
vorsehen
Hubi schrieb:> Hier kostet jedes Byte wertvollen Platz, und eine Lib> lässt sich da kaum "einbinden".
Wenn man das Handshake nicht verwenden will, dann schreibt man halt
einfach Zeichen raus und gut ist.
> Ich seh es deswegen genauso wie Markus: maximale SPI-Geschwindigkeit> vorsehen
Ich bin eher für die sichere Variante.
Gruß Oliver
Die Hostseite ist soweit fertig.
Es werden exakt die empfangenen Bytes auf stdout ausgegeben.
Derzeit bin ich dabei auszumessen, mit welcher maximalen Taktrate
das SPI des USBasp befeuert werden kann.
Dabei stelle ich fest, dass hin und wieder manche USB Packete
anscheinend doppelt ankommen.
Stefan B. schrieb:> Die Hostseite ist soweit fertig.> Es werden exakt die empfangenen Bytes auf stdout ausgegeben.
Gratuliere! :-)
> Dabei stelle ich fest, dass hin und wieder manche USB Packete> anscheinend doppelt ankommen.
Verschwinden im Subraum? ;-)
Das heißt, manche Zeichenfolgen erscheinen dann doppelt am Bildschirm?
Ich mach mir grad Gedanken, ob sich dein Host-Script später vielleicht
auch auf Bash oder C portieren lässt. Vielleicht wage ich mich an einen
Versuch – aber das dauert noch.
Wie stellt ihr euch eigentlich den Betrieb dieses Projektes vor? Damit
diese Geschichte überhaupt funktionieren kann, darf nicht die
Resetleitung des Target-AVR auf /SS des usbasp gehen. Bei allen
AVR-Schaltungen, die ich kenne (Ausnahme: usbasp), ist aber Reset auf
den Programmierstecker geführt. Reset ist aber normalerweise keinGPIO.
Gruß Oliver
Soweit ich den USBASP verstanden habe, soll die Erweiterung eine echte
UART zum Target bereitstellen. Dies wäre ein nettes Feature, aber nicht
unbedingt erforderlich, da es ein billiger USB-UART-Adapter (gibt es
auch mit TTL-Pegel) auch tut.
SPI zwischen Target und Umsetzer (ASP), noch dazu mit Flusssteuerung, da
AVR-SPI mangels Empfangspuffer nicht so richtig Slave-fähig ist, halte
ich für nicht unbedingt erstrebenswert.
Ein kleines Platinchen mit MAX232 und 4 Pins zum AVR (Vcc, GND, RXD,
TXD) habe ich sowiso immer an einem RS232-Verlängerungskabel hängen, da
ich dieses Interface regelmäßig zum Parametrieren verschiedener
AVR-Schaltungen nutze. Dies ermöglicht in Verbindung mit HTERM
problemloses Debuggen über UART sowie mittels spezieller kleiner
VB6-Programme komfortables Parametrieren des Targets.
...
Oliver:
Soweit ich mich an den Schaltplan erinnere, wird die Resetleitung des
Targets vom USBasp immer dann (und nur solange) auf GND gezogen, wenn
programmiert wird. Danach ist die Leitung wieder auf VCC, und man kann
die Zielschaltung mit den 5 Volt aus USB betreiben. Manche USBasps
lassen sich sogar auf 3,3 Volt umschalten, so dass man auch
Zielschaltungen programmieren und versorgen kann, die mit 3,3 Volt
arbeiten, weil sie z.B. ein LCD oder eine SD-Karte ansteuern.
Also: alles im grünen Bereich. :-)
Hannes Lux schrieb:> Dies wäre ein nettes Feature, aber nicht> unbedingt erforderlich, da es ein billiger USB-UART-Adapter (gibt es> auch mit TTL-Pegel) auch tut.
Klar, mit einem eigenen RS232-Umsetzer gehts auch. Das bedingt aber
1. dass der Mikrocontroller einen UART hat (einige haben das nicht),
2. dass du (wie du auch schreibst) einen MAX232 brauchst, also
zusätzliche Hardware, und
3. dass dein PC eine serielle Schnittstelle bzw. einen Adapter dafür
braucht.
Das von Stefan angestoßene Projekt kommt ohne 1., 2. und 3. aus.
Deswegen ist es ja so genial. :-)
Markus W. schrieb:> Also: alles im grünen Bereich. :-)
Muss denn der Target-AVR nicht am usbasp den SS-Pin gegen Masse ziehen,
um ein Byte übertragen zu können? Wie soll er das mit seinem Reset-PIN
tun?
Oder soll das Ganze nur für usbasps und Spezialschaltungen sein?
Gruß Oliver
Martin Wende schrieb:> Der usbasp wird sich eben einfach immer angesprochen fühlen, so viel> Leitungen gibt der ISP nunmal nicht her.
Wie soll das funktionieren, wenn die Resetleitung des Targets auf 5V
liegt und diese Leitung mit dem SS vom usbasp verbunden ist. Um als
Slave Daten empfangen zu können, muss SS auf low-Pegel liegen.
Gruß Oliver
Das ist ein super interesantes Thema! Bin grad darauf gestoßen als ich
nach etwas gesucht habe, wie ich mit dem USBasp Adapter Signale von
meinem µC ablesen kann.
Gibt es schon irgendwelche Updates?
So wie ich es verstanden habe wurde am originalen Layout des Adapters
nichts geändert, oder? Nur die Firmware wird neu geschrieben.
Bin jedenfalls sehr gespannt wie das hier weitergeht!
Matthias Kesenheimer schrieb:> Gibt es schon irgendwelche Updates?
Hallo Matthias,
auch ich hoffe noch auf Updates. Fürchte aber, der Thread-Ersteller ist
im wohlverdienten Pfingsturlaub.
Matthias Kesenheimer schrieb:> So wie ich es verstanden habe wurde am originalen Layout des Adapters> nichts geändert, oder? Nur die Firmware wird neu geschrieben.> Bin jedenfalls sehr gespannt wie das hier weitergeht!
Genau, es gab keine Hardware Änderung.
(Ich benutze die USBasp Hardware aus dem Shop von Ulrich Radig.)
Die Firmware wurde nur minimal verändert.
Stand der Dinge:
Vom dem Targetavr kann man per SPI Daten verschicken.
Dieser Bytestream wird genauso auf dem Hostrechner auf stdout
ausgegeben.
Für meinen Anwendungszweck ist das genug, was würdet ihr da noch
erweitern?
Stefan B. schrieb:> Matthias Kesenheimer schrieb:>> Ist das dann die aktuelle Firmware, mit der das funktioniert?>> genau das ist es.
Super!
Ist die Firmware-Basis von Mai 2011? Will heißen, funktioniert damit
auch das "-B"-Kommando, oder hast du eine ältere Firmware als Basis
verwendet?
Gibt es noch irgendwelche Probleme mit Bytes, die verloren gehen?
Stefan B. schrieb:> Ich habe die anderen Kommandos nicht explizit getestet, zumindest das> flashen eines weiteren usbasps mit dieser Firmware funktioniert. ;)
Guuut! :-)
Magst du bei fischl.de anfragen, ob sie deine Verbesserungen übernehmen?
Die meisten User suchen ja direkt bei fischl, wenn sie ein
Firmware-Update brauchen.
Die Hex-Dateien in USBasp/bin/firmware müsste man dann auch noch
entsprechend updaten.
Langfristig findet sich sicher jemand, der dein Host-Script in C
übersetzt und nach Windows portiert. Aber ok, ich persönlich brauch kein
Windows. :-)
Markus W. schrieb:> Magst du bei fischl.de anfragen, ob sie deine Verbesserungen übernehmen?> Die meisten User suchen ja direkt bei fischl, wenn sie ein> Firmware-Update brauchen.
Vor 11 Tagen (also als ich gefühlt fertig war), habe ich fischl eine
mail geschrieben, er wolle sich das mal ansehen, habe aber 2 Wochen lang
keine Zeit. Die Zeit ist fast vorbei, mal sehen ob was kommt.
Ulrich Radig habe ich auch auf diesen Thread hingewiesen, habe aber noch
keine Antwort bekommen.
Zusätzlich habe ich die Firmware kompiliert und gepushed.
https://github.com/stefanbeller/USBasp/tree/master/bin/firmware
Ich persönlich würde so vorkompilierte Software für den PC nicht einfach
so aus dem Web nutzen. Auf Mikrocontrollern macht man nicht viel falsch,
aber generell für meinen Computer möchte ich es entweder selbst
kompilieren, oder aus einer vertrauenswürdigen Quelle herunterladen.
(zb. Repositories einer großen Distribution.)
Das selberkompilieren bringt nochmal einen gewisses Vertrauen in den
Code,
da man es ja reviewen kann. ;) (Das tue ich nur manchmal, zumindest ein
Überfliegen, ob offensichtliche Probleme bestehen.)
Gruß,
Stefan
usbdrv/usbdrv.h:455:6: Fehler: Variable »usbDescriptorDevice« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
6
usbdrv/usbdrv.h:461:6: Fehler: Variable »usbDescriptorConfiguration« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
7
usbdrv/usbdrv.h:467:6: Fehler: Variable »usbDescriptorHidReport« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
8
usbdrv/usbdrv.h:473:6: Fehler: Variable »usbDescriptorString0« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
9
usbdrv/usbdrv.h:479:5: Fehler: Variable »usbDescriptorStringVendor« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
10
usbdrv/usbdrv.h:485:5: Fehler: Variable »usbDescriptorStringDevice« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
11
usbdrv/usbdrv.h:491:5: Fehler: Variable »usbDescriptorStringSerialNumber« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
12
usbdrv/usbdrv.c:70:14: Fehler: Variable »usbDescriptorString0« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
13
usbdrv/usbdrv.c:80:14: Fehler: Variable »usbDescriptorStringVendor« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
14
usbdrv/usbdrv.c:89:14: Fehler: Variable »usbDescriptorStringDevice« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
15
usbdrv/usbdrv.c:111:14: Fehler: Variable »usbDescriptorDevice« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
16
usbdrv/usbdrv.c:142:14: Fehler: Variable »usbDescriptorConfiguration« muss konstant sein, um mit »__attribute__((progmem))« in Nur-Lese-Abschnitt gelegt zu werden
Trippelpost, yay!
@Matthias Kesenheimer
Ich habe die relevanten Stellen mal mit dem const keyword versehen, bei
mir kompiliert das immer noch ohne Fehler. Vielleicht kannst du den
jetzigen Stand mal ausprobieren?
Stefan B. schrieb:> Welche Version des avr-gcc hast benutzt du?
Ich habs mit der Version 3 und mit 4 probiert.
1
avr-gcc --version
2
avr-gcc (GCC) 4.7.0
Stefan B. schrieb:> Ich glaube hier> Beitrag "Re: Fiese Zeiger Hacks und PROGMEM"> wird dieser Fehler diskutiert.
Ok, das kann sein, ich kann leider nicht so arg viel damit anfangen :)
Stefan B. schrieb:> Trippelpost, yay!>> @Matthias Kesenheimer> Ich habe die relevanten Stellen mal mit dem const keyword versehen, bei> mir kompiliert das immer noch ohne Fehler. Vielleicht kannst du den> jetzigen Stand mal ausprobieren?
Versuch ich gleich, moment...
Gut, wenn ich dann die Firmware geflasht habe, wie lese ich dann die
Daten von meinem µC ab? Was muss ich also avrdude mitteilen, dass mein
µC die Daten an stdout sendet?
Dann werden die Ausgaben auf den Bildschirm angezeigt.
Alternativ kannst du die Daten anstelle auf den Bildschirm auch in eine
Datei (hier daten.bin) leiten:
1
cd ./host/
2
python pipeout.py > daten.bin
Auf dem Mikrocontroller versendest du die Daten per SPI, also zum
Beispiel so etwas (ungetestet):
1
#include<avr/io.h>
2
#include<util/delay.h>
3
4
intmain(void)
5
{
6
SPCR=(1<<SPE|1<<MSTR);// aktiviere SPI als Master.
Hallo Zusammen
Sorry das ich mich kurz einmische, ohne alles gelesen zu haben, ich bin
gleich wider weg;-)
Vor einem halben Jahr habe ich selbst USBAsp erweitert um eine Serielle
Kommunikation über UART zu erhalten. Ich habe dazu auch noch ein kleines
"Terminal" geschrieben, um das ganze unter Linux anzusprechen.
Für mich reichts, ich habe den Code auch wider zurück an Fischl
gesendet, der hat gesagt beim nächsten Release wird dieser eingebaut.
Da dies offenbar noch nicht geschehen ist veröffentliche ich hiermit den
Code unter der GPL.
Die Kommunikation hat funktioniert. Die Konsole ist eher dürftig.
Ich arbeite momentan nicht mehr daran. Falls jemand diesen Code
weiterverwendet, bitte schreibt mir doch kurz ein Mail oder eine
Nachricht im Forum. Ich werde ggf. irgendwann auch mal wieder an dem
Code weiterentwickeln.
mfg Andreas
@Stefan:
Danke für die ausführliche Anleitung! Ich hab es bis jetzt noch nicht an
Hardware ausprobieren können, sondern nur mal das Pythonscript gestartet
und geschaut was dabei rauskommt. Es treten ein paar Fehler auf, aber
ich denke mal, das kommt daher, dass mein USBasp adapter noch nicht
richtig geflasht wurde. Würde nur mal gerne wissen ob ich bis jetzt
alles richtig gemacht habe :)
Das ist das was beim starten des Skriptes ausgegeben wird:
1
Traceback (most recent call last):
2
File "pipeout.py", line 28, in <module>
3
handle = getHandle()
4
File "pipeout.py", line 9, in getHandle
5
busses = usb.busses()
6
File "/Library/Python/2.6/site-packages/usb/legacy.py", line 333, in busses
7
return (Bus(),)
8
File "/Library/Python/2.6/site-packages/usb/legacy.py", line 329, in __init__
9
self.devices = [Device(d) for d in core.find(find_all=True)]
10
File "/Library/Python/2.6/site-packages/usb/core.py", line 846, in find
11
raise ValueError('No backend available')
12
ValueError: No backend available
Treten die Fehler wegen dem noch nicht geflashten USBasp auf?
Ich habe kurz in die Lösung von Andreas B. geschaut und gebe zu die
sieht technisch eleganter aus. Ich werde Sie auf jeden Fall mal
ausprobieren.
(Wahrscheinlich erst nächstes Wochenende.)
@bartimaeus
Heh, das ist das Problem mit 'mal eben zusammengehacktem Code'.
Keine Doku und nix ;)
Ich habe folgende Packete installiert (Ubuntu 12.04)
1
$ dpkg -l |grep libusb
2
ii libusb-0.1-4 2:0.1.12-20 userspace USB programming library
3
ii libusb-1.0-0 2:1.0.9~rc3-2ubuntu1 userspace USB programming library
4
ii libusb-1.0-0-dev 2:1.0.9~rc3-2ubuntu1 userspace USB programming library development files
5
ii libusb-dev 2:0.1.12-20 userspace USB programming library development files
6
ii libusbmuxd1 1.0.7-2 USB multiplexor daemon for iPhone and iPod Touch devices - library
7
$ dpkg -l |grep usb |grep python
8
ii python-usb 0.4.3-1 USB interface for Python
Ich nehme an du brauchst ähnliche Packete insbesondere python-usb in der
Version 0.4.3 (Die Autoren behaupten die Version 1.0, welche komplett
neu geschrieben ist, sei kompatibel. Also sollte es auch mit der
neueren Version gehen.)