Nicht jedem Otto-Normal-Endanwender von µC-Schaltungen steht ein
Flashgerät oder eine serielle Schnittstelle (UART) zur Verfügung, um
Daten zum Mikrocontroller zu übertragen oder gar ein Firmware-Update
durchzuführen. Was aber ein jeder PC-Anwender hat, ist eine Soundkarte.
Das Programmpaket SOUNDRX kann Daten mit mehr als 300 Zeichen pro
Sekunde über die Soundkarte vom PC an einen ATMega übertragen. Das Paket
enthält dafür ein Beispielprogramm sowohl für den µC als auch PC. Ebenso
ist ein Bootloader für den µC und das dazugehörige PC-Programm
vorhanden, um ATMegas mit mind. 2KB Bootloadergröße über ein simples
Audiokabel zu flashen.
Dieses Programmpaket ist damit für Endanwenderschaltungen gedacht, um
Firmware-Updates zu ermöglichen, ohne sich direkt eine komplette
Entwicklungs-Hardware anschaffen zu müssen.
Die gegenwärtige Übertragungsgeschwindigkeit ist noch nicht das Optimum.
An der Verbesserung der überaus simplen Emfängerschaltung wird noch
gearbeitet, um die gegenwärtige Übertragungsrate von ca. 3000bd (wenn
man es mit einer UART-Üertragung vergleicht) zu erhöhen.
Der beigelegte Bootloader verbraucht im Moment auch noch ca. 1,6 KB an
Code. Durch eine Binärübertragung kann der Speicherplatzverbrauch noch
unter 1KB gedrückt werden, um den SOUNDRX-Bootloader auch für kleinere
ATMegas einsetzen zu können. Im Moment wird die HEX-Datei noch im
Klartext übertragen und auf dem µC interpretiert. Verlagert man diese
Interpretation auf den PC, sollte sich der Bootloader auf unter 1KB
drücken lassen.
Näheres dazu haben wir im Artikel
http://www.mikrocontroller.net/articles/SOUNDRX
zusammengefasst.
Gruß,
Robert und Frank
Hmm. Jeder PC hat heute eine USB-Schnittstelle.
Entweder mutet man dem Endanwender zu, sich einen USB/Seriell-Wandler
für 10 Euro zu besorgen, mit dem dann jeder Bootloader über die serielle
Schnittstelle gefüttert werden kann...
oder, man integriert in die µC-Schaltung sowieso einen FT232 bzw.
ähnlichen Chip, um eine Kommunikation über USB zu ermöglichen, dann
stellt sich das Problem erst gar nicht.
Eine dieser beiden Lösungen ist m.E. auf jeden Fall einfacher, als ein
spezielles Kabel für das Softwareupdate und entsprechende Treiber für
die Soundkarte zu installieren.
Oliver Döring schrieb:> Entweder mutet man dem Endanwender zu, sich einen USB/Seriell-Wandler> für 10 Euro zu besorgen, mit dem dann jeder Bootloader über die serielle> Schnittstelle gefüttert werden kann...>> oder, man integriert in die µC-Schaltung sowieso einen FT232 bzw.> ähnlichen Chip, um eine Kommunikation über USB zu ermöglichen, dann> stellt sich das Problem erst gar nicht.
Die Schaltung mit dem LM741, kostet unter 50 Cent. Das schaffst Du nicht
mit einem USB->Seriellwandler zu unterbieten. Außerdem sparst du den
MAX232 in Deiner Schaltung. Stattdessen brauchst Du nur einen LM741 + 2
Widerstände + 2 100nF Kerkos + 3,5mm Klinkenbuchse
> Eine dieser beiden Lösungen ist m.E. auf jeden Fall einfacher, als ein> spezielles Kabel für das Softwareupdate und entsprechende Treiber für> die Soundkarte zu installieren.
Der Witz ist doch gerade, dass Du kein spezielles Kabel brauchst. Du
nimmst einfach Dein Audio-Kabel, dass zu Deinem PC-Lautsprecher geht.
Das Ende steckst Du einfach in die Klinkenbuchse - fertig.
Ein spezieller Treiber ist auch nicht nötig, da es über den ganz
normalen Windows-Soundtreiber geht, der bereits installiert ist.
Achja, übrigens, wir haben gerade mal das Windows-Programm derart
erweitert, dass es die HEX-Datei als WAVE-Datei speichert. Diese kann
man dann mit einem beliebigen Abspielprogramm (z.B. Winamp oder auch
Irfanview) an den µC schicken. Wenn man als Entwickler die Wave-Datei an
den Endanwender schickt, kann dieser mit einem beliebigen
Windows-/Linux-/Apple-/MP3-Player das Firmware-Update auf den µC
flashen.
So haben wir gerade eben mit Winamp unseren ATMega geflasht :-)
Update kommt demnächst...
Gruß,
Frank
Das ist doch mal eine kreative Art, µC zu flashen.
Braucht man eigentlich den OPV unbedingt? Es wäre ja noch schöner, wenn
man höchstens drei Widerstände zusätzlich bräuchte.
Dennis S. schrieb:> Das ist doch mal eine kreative Art, µC zu flashen.
Finde ich auch :-)
Übrigens: die Idee ist eigentlich 30 Jahre alt. Damals hat man in den
C64/ZX-Spectrum die Programme/Daten mit einem Kassettenrecorder geladen.
Hier passiert eigentlich genau dasselbe: Die Daten werden in (hörbare)
Töne gewandelt und dann an den µC übertragen.
> Braucht man eigentlich den OPV unbedingt? Es wäre ja noch schöner, wenn> man höchstens drei Widerstände zusätzlich bräuchte.
Der Audiosignalpegel reicht leider nicht aus, dieses ohne Verstärkung
direkt auf den µC zu geben.
Hmm, wenn das ganze jetzt noch mit Frequenzen zwischen 300-2600 Hz läuft
und mit Phasendrehungen zurecht kommt gäbe es dafür evtl. noch eine
interessante Anwendung. Man könnte solch ein Update über ein Telefon
machen. Sprich man hat an einer Stelle einen Anrufbeantworter der das
Programm als Ansage hat, und sagt der Person, dass sie einfach den Hörer
auf die mit Leiterbahn gemachte Spule auf der Platine legen soll und bei
der Nummer anrufen soll. Einfacher geht es wirklich nicht mehr.
@odbs: Nur sehr wenige Leute sind in der Lage USB stabil zum Laufen zu
bekommen. Ich gehöre nicht dazu.
Christian Berger schrieb:> Hmm, wenn das ganze jetzt noch mit Frequenzen zwischen 300-2600 Hz läuft> und mit Phasendrehungen zurecht kommt gäbe es dafür evtl. noch eine> interessante Anwendung. Man könnte solch ein Update über ein Telefon> machen. Sprich man hat an einer Stelle einen Anrufbeantworter der das> Programm als Ansage hat, und sagt der Person, dass sie einfach den Hörer> auf die mit Leiterbahn gemachte Spule auf der Platine legen soll und bei> der Nummer anrufen soll. Einfacher geht es wirklich nicht mehr.
Nette Idee. Im Moment werden Frequenzen zwischen 1200 Hz bis 6000 Hz
verwendet. Diese könnte man durch Ändern der Preprozessor-Konstanten
SNDRX_F_SAMMPLES von derzeit 12000 auf einen niedrigeren Wert erreichen.
Die Baudrate würde dann von derzeit ca. 3000 Bd auf ca. 300 Bd sinken,
also vergleichbar mit den damaligen Akkustikkopplern. Aber ich glaube,
es ist trotzdem heutzutage einfacher, dem Anwender die Wave-Datei per
Mail zuzusenden und ihn zu bitten, diese mit dem WinAmp oder
Windows-Media-Player abzuspielen ;-)
Achja: Vorher nicht vergessen: Der Lautstärke-Regler muss auf mindestens
3/4 oder besser noch auf Vollausschlag!
Ach ja, die ATMegas in SMD haben häufig einen differentiellen Eingang
für den Wandler, den man auch auf x10 und x200 schalten kann. Damit
könnte man sich eventuell den Verstärker sparen.
Christian Berger schrieb:> Ach ja, die ATMegas in SMD haben häufig einen differentiellen Eingang> für den Wandler, den man auch auf x10 und x200 schalten kann. Damit> könnte man sich eventuell den Verstärker sparen.
SOUNDRX benutzt einen digitalen Pin und keinen DA-Wandler. Den meintest
Du doch?
Sehr schönes und kreatives Prinzip.
Einzige Frage:
Haben nicht die meisten Sound-Ausgabe-Geräte ein fest angeschlossenes
Kabel, also kein Stecker-Stecker-Kabel?
Was ich noch besser Fände: Sound über KondensatorMikrofon aufnehmen,
Frequenzanalyse (1=10KHz 0=1KHz) und so programmieren.
Also so ähnlich wie der weiter oben beschriebene Ansatz mit der
Leiterschlaufe auf der Platine..
Grüße Tüftler
Tueftler schrieb:> Haben nicht die meisten Sound-Ausgabe-Geräte ein fest angeschlossenes> Kabel, also kein Stecker-Stecker-Kabel?
Ja, einige Brüllwürfel für den PC haben ein fest angeschlossenes Kabel.
Dann nimmt man dann einfach das beim Monitor mitgelieferte Audiokabel.
Die meisten haben ja mittlerweile integrierte Lautsprecher. Hier hat das
Kabel immer an beiden Enden eine grüne 3,5mm Klinke.
> Was ich noch besser Fände: Sound über KondensatorMikrofon aufnehmen,> Frequenzanalyse (1=10KHz 0=1KHz) und so programmieren.
Das sollte gehen. Wenn man an den OPV ein Mikrofon anschließt und eine
genügende Verstärkung da ist, müsste man das Mikro auch vor den
Lautsprecher halten können. Allerdings ist das, was da am Lautsprecher
über SOUNDRX ausgegeben wird, nicht gerade ohrenfreundlich. Ich würde
jedenfalls dann fluchtartig den Raum verlassen ;-)
Neue Version 1.1.0 verfügbar.
Damit kann man nun mit sndtx.exe auch eine WAVE-Datei erzeugen, statt
die HEX-Datei direkt "abzuspielen". Dies geht folgendermaßen:
sndtx.exe -f Dateiname.hex Dateiname.wav
Die erzeugte WAVE-Datei kann dann mit einem beliebigen WAVE-Player
abgespielt und damit an den µC übertragen werden.
Download-Datei und Artikel wurde entsprechend aktualisiert.
Gruß,
Robert und Frank
Tolle Sache. Nach dem Denkanstoß von euch habe ich es mit einem Pic
implementiert, jedoch nicht Modemcodiert sondern Pulscodiert.
Braucht nur 1 pin, keine zusätzliche HW, und FW-Update ist mittels MP3
Player oder PC machbar. Nicht schlecht und unschlagbar Preiswert in HW.
Die Idee ist zwar nicht neu, aber gut. Vor allem wenns auch mit nem
popeligen MP3-Player funzt. Hat schon fast was von einem C64 mit
Datasette. Nur halt den technischen Möglichkeiten angepasst :-))
Weiter so :-)
Ich habe nun die OPV-Schaltung durch einen simplen Transistor + 2
Widerstände + 1 Kerko ersetzt. Erste Tests damit ergeben:
1. Signal-Empfindlichkeit ist wesentlich höher
2. Die Geschwindigkeit kann auf ca. 960 Zeichen erhöht werden, das
entspricht dann ca. 9600 Bd bei einer vergleichbaren UART-Verbindung.
Ich teste heute abend noch ein wenig die Grenzen aus, bevor ich ein
Update des Artikels mache...
Bis später,
Frank
Neue Version 1.2.0 ist online:
- Neue Eingangsschaltung bestehend aus nur einem einem Kerko, drei
Widerständen und einem Transistor
- Geschwindigkeitserhöhung auf über 1000 Zeichen/sec. Das entspricht
einer Baudrate von über 9600 Bd, wenn man die Übertragungsrate mit
UARTs/RS232 vergleicht.
- Bugfix in sndtx.exe beim Interpretieren der HEX-Datei (bei kurzen
Zeilen)
Der Artikel und die Download-Dateien wurden aktualisiert, siehe:
http://www.mikrocontroller.net/articles/SOUNDRX
Viel Spaß,
Frank
Hallo Frank,
ein großes Lob an euch. Die Datenübertragung via Soundkarte ist auch ein
Projekt, welches ich schon lange realisieren wollte.
Ein Frage: Geht das ganze wirklich via MP3? Durch die Komprimierung und
die verlustbehaftete Kompression kann ich mir das nicht vorstellen.
Gruß,
chris
Hallo Chris,
chris schrieb:> Ein Frage: Geht das ganze wirklich via MP3? Durch die Komprimierung und> die verlustbehaftete Kompression kann ich mir das nicht vorstellen.
Das ist eher ein Missverständnis: man kann es mit einem MP3-Player
abspielen, weil dieser in der Regel auch WAV-Dateien abspielen kann. MP3
als Datenformat wird wohl wirklich nicht funktionieren, da (wie Du schon
sagtest), MP3 tatsächlich verlustbehaftet ist.
Möchte man die WAV-Datei komprimieren, muss man schon ein verlustfreies
Kompressionsprogramm nehmen, z.B. zip.
Gruß,
Frank
Mp3 codiert geht mit bestimmten Voraussetzungen. Deshalb habe ich auch
Pulscodierung verwendet. Codierung OOK z.B ein IR Protocoll wie das von
Sony. Da wird nur zwischen Musik und Pause unterschieden und das lässt
sich hervorragend mit Mp3 komprimieren.
Chris schrieb:> Mp3 codiert geht mit bestimmten Voraussetzungen. Deshalb habe ich auch> Pulscodierung verwendet. Codierung OOK z.B ein IR Protocoll wie das von> Sony. Da wird nur zwischen Musik und Pause unterschieden und das lässt> sich hervorragend mit Mp3 komprimieren.
SOUNDRX benutzt (wie IR-Fernbedienungen auch) ein
Pulse-Distance-Protokoll, Timing siehe SOUNDRX-Artikel:
http://www.mikrocontroller.net/articles/SOUNDRX
Wir haben es ausprobiert: WAVE-Daei erzeugt, mit LAME nach MP3 codiert
und anschließend mit WinAmp ausgegeben. Ergebnis:
- WAVE-Datei geht
- MP3-Datei geht nicht
Jedenfalls bei 1000 Zeichen pro Sekunde. Vielleicht geht es mit
niedrigeren Sampleraten. Aber was gewinnst Du dadurch? Nichts, denn die
Datenmenge, die zum µC übertragen wird, bleibt dieselbe.
Gruß,
Frank
Neue Version 1.3.0 ist online.
Änderungen:
- Binärübertragung für Bootloader, dadurch Verdoppelung der
Flash-Geschwindigkeit. Damit ist Geschwindigkeit vergleichbar
mit der eines üblichen ISP-Programmers.
- Neues PC-Flash-Programm sndflash.exe
- Abschalten des Ringbuffers nun möglich mit SNDRX_RINGBUFSIZE = 0
- Optimierung der ISR-Statusvariablen
Der Artikel und die Download-Dateien wurden aktualisiert, siehe:
http://www.mikrocontroller.net/articles/SOUNDRX
Viel Spaß,
Robert und Frank
Hallo Zusammen,
vielleicht ließe sich die Datenrate verdoppeln, wenn man die Bits in die
Flanken kodiert.
Jetzige Datenrate im Mittel:
44100/((2+2+3+5)/2)=44100/6=7350 Baud
Bei Codierung in Flankenwechsel ( low=2 samples hight=4 samples )
44100/((2+4)/2)=14700 Baud
Gruß,
chris
Hi Vlad,
Vlad Tepesch schrieb:> kann es sein, dass in dem Archiv die Bootloader-Sourcen fehlen?
Sorry, Du hast recht. Ich habs korrigiert und sndrx-bootloader.c
angefügt. Liegt jetzt als V. 1.3.1 in der Download-Datei.
Uns fehlen nch ca. 70 Bytes, um den Bootloader unter 1KB zu bekommen.
Vielleicht findest Du (als Tüftler) ja noch eine Optimierungstelle? ;-)
Gruß,
Frank
Hallo,
finde die Idee super!! Man könnte noch R3 weglassen und den internen
pullup nutzen, für die Sparfüchse unter uns :) (R2 muss natürlich
angepasst werden)
chris schrieb:> vielleicht ließe sich die Datenrate verdoppeln, wenn man die Bits in die> Flanken kodiert.
Du meinst, vom Puls-Distance-Protokoll auf Manchester-Codierung
wechseln?
> Jetzige Datenrate im Mittel:> 44100/((2+2+3+5)/2)=44100/6=7350 Baud
Deine Rechnung verstehe ich nicht. Ein Byte, das gleich viele 0en und
1en hat, kostet:
4 * 4T + 4 * 7T = 16T + 28T = 44T
Dann ist die Übertragungsrate im Mittel: 44100 / 44 = 1002 Bytes pro
Sekunde. Das ist vergleichbar mit einer UART-Übertragungsrate von 10020
Bd, da dort noch Start- und Stopbits übertragen werden müssen (10 Bits
pro Byte).
Bei SOUNDRX haben die Bytes kein Start- und Stopbit, nur der ganze
Datenblock hat jeweils eins. Das kann man aber bei der Rechnung
vernachlässigen.
Damit übertrage ich real (im Mittel) 1020 Zeichen pro Sekunde. Diesen
Wert zeigt mit sndtx.exe auch an.
> Bei Codierung in Flankenwechsel ( low=2 samples hight=4 samples )> 44100/((2+4)/2)=14700 Baud
Das müsstest Du mal näher ausführen - wie gesagt, habs nicht verstanden.
Gruß,
Frank
Sparfuchs schrieb:> Man könnte noch R3 weglassen und den internen pullup nutzen, für die> Sparfüchse unter uns :)
Stimmt, Du hast vollkommen recht! Ich werde ihn für die nächste Version
"wegoptimieren" ;-)
> (R2 muss natürlich angepasst werden)
Der kann so bleiben, der Wert ist relativ unkritisch.
Gruß, Frank
Hallo Frank,
>Jetzige Datenrate im Mittel:>44100/((2+2+3+5)/2)=44100/6=7350 Baud
in meiner Rechnung ist ein kleiner "Leichtsinnsfehler"
Es muss natürlich heißen:
44100/((2+2+2+5)/2)=44100/6=8018 Bit/Sekunde
In der Nachrichtentechnik wird meistens die Einheit Bit/Sekunde
angegeben.
Die Datenübertragungsrate in Bytes/Sekunde ( ohne Protokolloverhead )
wäre dann
8018 Bit/Sekunde / 8 Bit= 1002 Byte/Sekunde
>Du meinst, vom Puls-Distance-Protokoll auf Manchester-Codierung>wechseln?
Nein, das würde nichts bringen. Ich mache am besten mal ein Beispiel. Es
soll diese Bit-Folge codiert werden:
010110
Daraus wird
--_--__-
_ = Low-Pegel, - = High-
Die Bit-Information liegt im Abstand der Flanken.
Gruß,
chris
Ähm, Tchuldigung, der Browser stellt meine Graphik falsch dar.
Also noch mal mit anderen Symbolen ( Punkt= low, Strich=High )
010110
Daraus wird
.--.--..-.
Jeder Punkt und jeder Strich ist genau 2 Takte lang.
Frank M. schrieb:> Sparfuchs schrieb:>>> Man könnte noch R3 weglassen und den internen pullup nutzen, für die>> Sparfüchse unter uns :)>> Stimmt, Du hast vollkommen recht! Ich werde ihn für die nächste Version> "wegoptimieren" ;-)
Auf diese Weise lassen sich auch durch Schalten den Pins auf GND die
paar Hunderd Mikroampere einsparen, die sonst durch den Pullup fließen.
Dann bleibt noch der Basisstrom.
Hallo Chris,
chris schrieb:> in meiner Rechnung ist ein kleiner "Leichtsinnsfehler"> Es muss natürlich heißen:> 44100/((2+2+2+5)/2)=44100/6=8018 Bit/Sekunde
Okay, das passt dann wieder, die von Dir genannten 7350 Bd erschienen
mir nur zu wenig. :-)
Ich habe bewusst Zeichen/Sekunde statt Bit/Sekunde angegeben, weil man
sonst eine SOUNDRX-Übertragung (8 Bit/Zeichen) schlecht mit einer
UART-Übertragung (10 Bit/Zeichen) vergleichen kann.
8018 Bd bei SOUNDRX sehen schlechter aus als 9600 Bd bei UART,
tatsächlich schafft aber SOUNDRX mehr Zeichen pro Sekunde (1002
Zeichen/sec) rüber als ein UART (960 Zeichen/sec).
Um die tatsächliche Übertragungsgeschwindigkeiten besser zu vergleichen,
rechne ich lieber mit Zeichen/sec :-)
> Also noch mal mit anderen Symbolen ( Punkt= low, Strich=High )> 010110> Daraus wird> .--.--..-.
Ich versuche mal, es zu verstehen. Ich sehe da folgende Flankenabstände:
1. Bit: low - >high: Abstand 1 (ich sehe aber die Flanke "davor" nicht)
2. Bit: high -> low: Abstand 2
3. Bit: low -> high: Abstand 1
4. Bit: high -> low: Abstand 2
5. Bit: low -> high: Abstand 2
6. Bit: high -> low: Abstand 1
Okay, da kommt die Bitfolge 010110 raus. Aber was mache ich beim
allerersten Bit? Da nehme ich eine virtuelle Flanke high->low an?
Da bei 8 Bit pro Byte nach dem 8. bit wieder Low-Pegel gegeben ist,
müsste das funktionieren :-)
Für die 6 Bit brauchst Du dann 20 Takte. Ich habe nämlich beim Testen
auch gelernt, dass die Soundkarte nur vernünftige Pegel liefert, wenn
ich diesen mindestens für 2 Takte konstant halte. Sonst gibt das Murks
am Ausgang. Das passt dann ja auch mit Deiner Aussage:
> Jeder Punkt und jeder Strich ist genau 2 Takte lang.
Ich brauche im Moment für die Folge 010110: 3 x 4 + 3 x 7 insgesamt 33
Takte, d.h. Deine Methode würde einen Faktor von 1,65 an Geschwindigkeit
rausholen, also statt 1002 Bytes/sec wären das dann 1653 Bytes/sec.
Ich glaube, das werde ich mal ausprobieren, gefällt mir :-)
Gruß,
Frank
P.S.
Es gibt noch eine andere Möglichkeit, die Geschwindigkeit nochmal zu
verdoppeln: Stero statt Mono, kostet dann aber 2 Input-Pins am ATmega
;-)
>Okay, da kommt die Bitfolge 010110 raus. Aber was mache ich beim>allerersten Bit? Da nehme ich eine virtuelle Flanke high->low an?>Da bei 8 Bit pro Byte nach dem 8. bit wieder Low-Pegel gegeben ist,>müsste das funktionieren :-)
Du könntet als Startsequenz z.b. 10xNull + 1 mal Eins senden ( in
Flankencodierung ). Dein Algorithmus müsste dann einfach die
Zeitabstände messen: Für die 10 Nullen müssen zehn kurze Zeitabstände
nacheinander auftauchen, dann der lange Abstand für die 1. Danach kannst
Du den ganzen Datenstrom Senden.
>Für die 6 Bit brauchst Du dann 20 Takte. Ich habe nämlich beim Testen>auch gelernt, dass die Soundkarte nur vernünftige Pegel liefert, wenn>ich diesen mindestens für 2 Takte konstant halte.
Das habe ich mir fast gedacht, als ich die Beschreibung im Wiki gelesen
habe. Vielleicht könnte man die Datenrate mit der Flankencodierung noch
etwas erhöhen: 2 Takte für die 0 und 3 Takte für die 1.
Dann käme man auf
44100/((3+2)/2)=17640 Bit/Sekunde
also 2200 Byte/Sekunde.
Frank M. schrieb:>> Uns fehlen nch ca. 70 Bytes, um den Bootloader unter 1KB zu bekommen.
70? bei mir (avr-gcc (WinAVR 20100110) 4.3.3) kompiliert das ding mit
1060 Bytes, also nur 36 zu viel
> Vielleicht findest Du (als Tüftler) ja noch eine Optimierungstelle? ;-)
Ich hab sogar recht einfach 60Bytes wegbekommen.
1
AVR Memory Usage
2
----------------
3
Device: atmega168
4
5
Program: 1000 bytes (6.1% Full)
6
(.text + .data + .bootloader)
7
8
Data: 45 bytes (4.4% Full)
9
(.data + .bss + .noinit)
Allerdings bringt das jetzt 2 Warnungen, die aber normalerweise (dazu
siehe unten) ignoriert werden können
1
../sndrx-bootloader.c: In function 'main':
2
../sndrx-bootloader.c:209: warning: function declared 'noreturn' has a 'return' statement
3
../sndrx-bootloader.c:209: warning: 'noreturn' function does return
was etwas unschön bei deinem Bootloader ist:
er springt einfach hart nach 0x0000 ohne vorher wieder aufzuräumen.
Das Anwendungsprogramm findet also eine andere Umgebung vor, als es
normalerweise erwarten würde. Wenn ich es nicht auf die schnelle
übersehen habe sogar mit aktiven Timerinterupt.
Was ich nicht weiß und was zu überprüfen wäre, ist:
was übernimmt die crt?
initialisiert die Arbeitsregister oder verlässt sie sich auf die
Standardregisterwerte? falls letzteres packst du mit dem Bootloader auch
Daten auf den Stack, die nie wieder abgeräumt werden.
Genau hier könnte aber sogar meine Attribut-definition helfen. die
sagt dem Compiler, "dies ist eine Funbktion, die nie mehr verlassen
wird". Dieser kann daraufhin vezichten, register oder
Rückspruingadressen zu retten.
edit:
ok, dem stack hilft das natürlich auch nicht komplett, er ist nur
eventuell etwas leerer
vor allem bei den kleinen Tinys ist dieser Trick extrem nützlich, da
damit meist 20-50 Bytes gespart werden können
chris schrieb:> Das habe ich mir fast gedacht, als ich die Beschreibung im Wiki gelesen> habe. Vielleicht könnte man die Datenrate mit der Flankencodierung noch> etwas erhöhen: 2 Takte für die 0 und 3 Takte für die 1.
Auch eine Idee, werde ich ausprobieren.
Aber ich habe da insgesamt zur Flankencodierung noch einen Punkt als
Bedenken anzumelden:
Ich habe die momentanen Taktzeiten für ein mögliches Signal bewusst mit
einem Abstand von 3 Takten gewählt, nämlich 4, 7, 10, 13 für "0", "1",
"Stop" und "Start", damit auch ein µC ohne Quarz noch zuverlässig
zwischen den einzelnen Taktzeiten unterscheiden kann.
SOUNDRX erkennt nämlich:
3 bis 5 T als 4 T -> "0"
6 bis 8 T als 7 T -> "1"
9 bis 11 T als 10 T -> "Start"
12 bis 14 T als 13 T -> "Stop"
SOUNDRX arbeitet also mit einer Toleranz von +/- 1T.
Wenn man diese Toleranz auch bei der Flanken-Kodierung beibehalten will,
sieht das nicht mehr ganz so gut aus mit der Geschwindigkeitssteigerung.
Dein obiges Beispiel für 6 Bits schreibe ich mal anders, nämlich in
1T-Einheiten (kurz = 2T, lang = 4T):
..----..----....--.. = 20T
Das bläht dann die Geschichte folgendermaßen auf, wenn man den 3er
Abstand beibehalten will (kurz = 2T, lang = 5T):
..-----..-----.....---.. = 24T
Damit liegt dann die Geschwindigkeits-Steigerung bei "nur" noch 33/24 =
1,375.
Ich habe da auch noch über eine Möglichkeit der
Geschwindigkeitssteigerung nachgedacht, die man eventuell dann noch mit
Deiner Flankencodierung (gibt es dafür eigentlich einen Fachbegriff,
unter dem man Literatur dazu findet?) kombinieren könnte.
Zur Veranschaulichung meiner Idee betrachte ich erstmal das
Pulse-Distance-Protokoll, denn ich finde das anschaulicher. Der
Kernpunkt ist, dass ich immer direkt 2 Bits auf einmal betrachte,
nämlich die Bitfolgen 00, 01, 10 und 11.
Bei dem bisherigen Verfahren sind das:
00: --..--.. = 4T + 4T = 8T
01: --..--..... = 4T + 7T = 11T
10: --.....--.. = 7T + 4T = 11T
11: --.....--..... = 7T + 7T = 14T
=====================================
Summe: 44T
Nun codiere ich die Bitpaare wie folgt:
00: --.. = 2T + 2T = 4T
01: --..... = 2T + 5T = 7T
10: --........ = 2T + 8T = 10T
11: --........... = 2T + 11T = 13T
======================================
Summe: 34T
Bei gleichmäßigem Vorkommen dieser Bitmuster "verbrate" ich pro Byte
dann 34 Takte - bei gleichem Abstand der möglichen Zeiten von 3 Takten
(für die Toleranz).
Das entspricht einer Geschwindigkeitssteigerung von 44/34 = 1,3.
Vielleicht kann man dieses Verfahren mit der Flankencodierung
kombinieren... mal schauen.
Vlad Tepesch schrieb:> 70? bei mir (avr-gcc (WinAVR 20100110) 4.3.3) kompiliert das ding mit> 1060 Bytes, also nur 36 zu viel
Bei mir sind es auch 1060 Bytes, ich hatte die 70 aus dem Kopf
hingeschrieben und bewusst ein "ca." davorgeknallt, weil ich den exakten
Stand nach endlosen Optimierungen am Wochenende heute morgen auch nicht
mehr parat hatte.
> Ich hab sogar recht einfach 60Bytes wegbekommen.
Klasse! Wenn ich das richtig sehe, liegt der Unterschied ledliglich bei:
1
intmain(void)__attribute__((noreturn));/* saves some Bytes but produces warning */
> Allerdings bringt das jetzt 2 Warnungen, die aber normalerweise (dazu> siehe unten) ignoriert werden können
Unschön, aber vielleicht kriege ich die auch noch weg :-)
Vielleicht einfach main() als void deklarieren? Probiere ich aus.
> was etwas unschön bei deinem Bootloader ist:> er springt einfach hart nach 0x0000 ohne vorher wieder aufzuräumen.
Muss er aufräumen? Die C-Runtime am Anfang setzt doch sowieso
Stackpointer etc. wieder neu beim Start. Oder etwa nicht? Ich denke mal,
dass alles zu Beginn automatisch wieder neu initialisiert wird...
> Das Anwendungsprogramm findet also eine andere Umgebung vor, als es> normalerweise erwarten würde. Wenn ich es nicht auf die schnelle> übersehen habe sogar mit aktiven Timerinterupt.
Die Interrupt-Vektoren werden zurückgesetzt. Aber Du hast Recht: Der
Timerinterrupt muss wieder deaktiviert werden. Ist mir nicht
aufgefallen, weil ich bisher Anwendungsprogramme geflasht habe, die
sowieso den Timer 1 benutzen :-)
> Was ich nicht weiß und was zu überprüfen wäre, ist:> was übernimmt die crt?> initialisiert die Arbeitsregister oder verlässt sie sich auf die> Standardregisterwerte? falls letzteres packst du mit dem Bootloader auch> Daten auf den Stack, die nie wieder abgeräumt werden.
Glaube ich nicht. Der Stackpointer wird gewiss re-initialisiert.
> Genau hier könnte aber sogar meine Attribut-definition helfen. die> sagt dem Compiler, "dies ist eine Funbktion, die nie mehr verlassen> wird". Dieser kann daraufhin vezichten, register oder> Rückspruingadressen zu retten.
Eben, da hast Du vollkommen recht. Ich werde das ausgiebig testen und
mir bei der Gelegenheit die Initialisierungen der C-Runtime mal näher
anschauen.
Vielen Dank!
Frank
Frank M. schrieb:>> Allerdings bringt das jetzt 2 Warnungen, die aber normalerweise (dazu>> siehe unten) ignoriert werden können>> Unschön, aber vielleicht kriege ich die auch noch weg :-)> Vielleicht einfach main() als void deklarieren? Probiere ich aus.
auf die idee bin ich noch nicht gekommen.
eine Warnung geht weg, aber es kommt eine neue dazu.
1
../sndrx-bootloader.c:172: warning: return type of 'main' is not 'int'
2
../sndrx-bootloader.c: In function 'main':
3
../sndrx-bootloader.c:209: warning: 'noreturn' function does return
und man spart 4 Byte ;)
Frank M. schrieb:> Klasse! Wenn ich das richtig sehe, liegt der Unterschied ledliglich bei:
und ein entferntes return, sonst gibt es noch eine Warning
Vlad Tepesch schrieb:> Allerdings bringt das jetzt 2 Warnungen, die aber normalerweise (dazu> siehe unten) ignoriert werden können
Die habe ich jetzt auch wegbekommen, nämlich mit:
1
int
2
main(void)
3
{
4
main_loop();
5
}
wobei ich den Code von main() nach main_loop() geschoben habe.
Das musste ich machen, weil gcc darauf beharrt, dass main() keine
void-Funktion sein darf.
Die Funktion main_loop habe ich dann als
void main_loop (void) _attribute_ ((noreturn));
deklariert. Um dem Compiler dann klarzumachen, dass sich mainloop()
wirklich nicht beendet, habe ich noch eine Endlosschleife einbauen
müssen, um die letzte Warnung auch noch wegzubekommen:
1
void
2
main_loop(void)
3
{
4
uint8_tch;
5
uint8_ttemp;
6
7
sndrx_init();// initialize SOUNDRX
8
9
#if USE_LED == 1
10
led_init();// initialize LED port
11
LED_ON;// switch LED on
12
#endif
13
14
temp=MCUCR;// set interrupt vectors to bootloader section
15
MCUCR=temp|(1<<IVCE);
16
MCUCR=temp|(1<<IVSEL);
17
18
sei();// enable interrupts
19
20
while(1)
21
{
22
if(sndrx_poll(&ch,3000)==1&&ch=='$')// wait 3 seconds for the dollar character...
23
{
24
#if USE_LED == 1
25
LED_OFF;// got it, switch LED off
26
#endif
27
if(!boot_update_flash())// flash now
28
{
29
continue;
30
}
31
#if USE_LED == 1
32
LED_OFF;// switch LED off
33
#endif
34
}
35
36
cli();// disable interrupts
37
38
temp=MCUCR;// reset interrupt vectors (set to application section)
39
MCUCR=temp|(1<<IVCE);
40
MCUCR=temp&~(1<<IVSEL);
41
42
asmvolatile("jmp 0x0000");
43
}
44
}
Damit kann gcc erkennen, dass sich mainloop() wirklich nicht beendet.
Das ergab dann nochmals eine Ersparnis von 16 Bytes, so dass wir jetzt
bei 984 Bytes Code für den Bootloader sind :-)
Die while-Schleife hat auch noch den Vorteil, dass der Bootloader auch
dann erst die Applikation startet, wenn die Übertragung fehlerfrei
geklappt hat. Bis dahin kann man immer weiter probieren, ohne den µC
resetten zu müssen.
Gruß,
Frank
Vlad Tepesch schrieb:> sehr gut. Das muss ich mir unbedingt merken.
Ich habe mal eben den mainloop-noreturn-Trick für ein Tiny2313-Projekt,
wo ich schon immer an der 2KB-Grenze herumschrabbte, ausprobiert: 34
Byte Ersparnis. Ist zwar hier nicht soviel, aber gibt mir wieder etwas
Luft für Erweiterungen :-)
> Ich werde das mal hier hinzufügen:> http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung
Gute Idee.
du könntest dir mal den Tip mit den Volatiles im Verlinkten Beitrag
anschauen.
da könnte es auch noch Potential geben.
hab mir den Code aber nicht allzu genau angeschaut
Vlad Tepesch schrieb:> du könntest dir mal den Tip mit den Volatiles im Verlinkten Beitrag> anschauen.> da könnte es auch noch Potential geben.
Danke für den Tipp, aber vom Puffern der volatile-Variablen mach ich
schon lange extensiven Gebrauch. :-)
Ich habe noch ein paar Dutzend Bytes einsparen können:
1. In sndrx-bootloader.c:
1
#define SNDRX_AS_INCLUDE // bad trick: save program space by including sndrx.c so we can use static functions
2
#include"sndrxconfig.h"
3
#include"sndrx.h"
4
#include"sndrx.c"
2. In sndrx.c:
1
#ifdef SNDRX_AS_INCLUDE // if used as include....
2
#define SNDRX_STATIC static // we can declare functions as static
3
#else // this saves program space
4
#define SNDRX_STATIC
5
#endif
Dadurch konnte ich dann sämtliche sndrx-Funktionen mittels SNDRX_STATIC
als static-Funktionen definieren - aber nur dann, wenn man sndrx.c
mittels "#include" in das main-Modul einfügt.
Beispiel:
Alt:
1
voidsndrx_init(void)
2
{
3
...
4
}
Neu:
1
SNDRX_STATICvoidsndrx_init(void)
2
{
3
...
4
}
gcc kann dann die sndrx-Funktionen als inline-Funktionen weiter
optimieren. Hat nochmal 20 Bytes gebracht.
Durch die #defines kann sndrx.c sowohl als externes C-Modul dazugelinkt
werden oder auch includiert werden. Im ersten Fall stehen dann die
Funktionen als "extern" zur Verfügung, im zweiten Fall als "static".
Vielleicht ist das auch noch einen Abschnitt in
http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung
wert?
Gruß,
Frank
P.S.
Mittlerweile sind wieder ein paar der eingesparten Bytes für diverse
Bugfixes draufgegangen (z.B. Abschalten des Timers vor Srpung in die
Applikation etc.). Aber mit 1012 Bytes sind wir weiterhin unter 1KB.
Das Update kann ich aber wohl erst später hochladen, im Moment ist der
Upload von Dateien auf µC.net defekt...
Frank M. schrieb:> Ich habe noch ein paar Dutzend Bytes einsparen können:>> [...]>> Dadurch konnte ich dann sämtliche sndrx-Funktionen mittels SNDRX_STATIC> als static-Funktionen definieren - aber nur dann, wenn man sndrx.c> mittels "#include" in das main-Modul einfügt.
Das includieren ist nicht wirklich schön, machen wir ja aber in der
wordclock auch ;)
Wenn man ein Makefile manuell erstellt (ich hasse makefiles), das nicht
jedes sourcefile extra compiliert (komischerweise machen das alle
automatischen generatoren, die ich bisher ausprobiert habe (avrstudio 4,
eclipse, jörgs mfile)), sondern alle in einen gcc-Aufruf packt kann man
whole program optimization benutzen, die sollte das inlinen nur einmal
gebrauchter funktionen auch gebacken bekommen, unabhängig davon, in
welchem src-File sie stecken. Einziger NAchteil: wird ein File geändert,
muss das ganze Projekt neu gebaut werden, was bei den kleinen Projekten
aber normalerweise irrelevant ist. Vorteil: Warnungen in nicht
geänderten Files erscheinen bei jedem Build.
Vlad Tepesch schrieb:> eine Warnung geht weg, aber es kommt eine neue dazu.> ../sndrx-bootloader.c:172: warning: return type of 'main' is not 'int'> ../sndrx-bootloader.c: In function 'main':> ../sndrx-bootloader.c:209: warning: 'noreturn' function does return
Ohne Warungen geht es so:
Neue Version 1.4.0 ist online.
Änderungen:
- Optimierung der Schaltung:
Pullup-Widerstand am Transistor wegoptimiert, übernimmt nun der
ATmega.
- Optimierung des Bootloaders:
Der Bootloader wurde von der Größe her auf unter 1KB reduziert.
- Bugfix Bootloader:
Timer wird nun vor Applikationsstart wieder deaktiviert
- Bugfix Bootloader:
Wenn die Übertragung fehlerhaft war, wird nun auf eine neue
Übertragung gewartet
Der Artikel, der Schaltplan und die Download-Dateien wurden
aktualisiert, siehe:
http://www.mikrocontroller.net/articles/SOUNDRX
Gruß,
Frank
Hallo Frank,
vielleicht lässt sich Deine Rechnung oben vereinfachen. Bei der
Flankecodierung werden die Bits in zwei unterschiedliche Abstände
zwischen den Flanken codiert. Hat man z.B. die Abstände 2T für low und
3T für High ergibt sich für die Übertragung von 8000-low und 8000-high
Bits folgende Rechnung:
8000*2T+8000*3T=40000T
mit T = 1/441000Hz
ergibt sich also eine Übertragungszeit von 40000*(1/44100)=0.9 Sekunden
Die 2 Kilobyte Daten werden also in unter einer Sekunde übertragen.
>SOUNDRX arbeitet also mit einer Toleranz von +/- 1T.>Wenn man diese Toleranz auch bei der Flanken-Kodierung beibehalten will,>sieht das nicht mehr ganz so gut aus mit der Geschwindigkeitssteigerung.
Die Flankencodierung hat eine Toleranz von +/-1T, wenn man die Zeiten
low=2T und high=4 T wählt.
Man erhält folgende Geschwindigkeiten für die verschiedenen
Codieungsarten:
2T/3T: 44100/((2+3)/2)=17640 Bit/Sekunde= 2205 Byte/Sekunde
2T/4T: 44100/((2+4)/2)=14700 Bit/Sekunde= 1837 Byte/Sekunde
Die Flankencodierung hat ähnliche Eigenschaften wie die
Manchestercodierung: man kann bei jedem Flankenwechsel neu
synchronisieren, dadurch wird das ganze sehr stabil gegenüber
Taktschwankungen.
Grüße,
chris
Peter Dannegger schrieb:> Compilerschalter:
Habe mal alle durchprobiert, sowohl einzeln als auch alle auf einmal.
> -fno-split-wide-types
Verschlechterung: 20 Bytes
> -fno-tree-loop-optimize
Gewinn: 4 Bytes
> -fno-move-loop-invariants
Gewinn: 16 Bytes
> -Wl,--relax
Gewinn: 0 Bytes
> --combine -fwhole-program
Verschlechterung: 28 Bytes
Alle auf einmal zusätzlich gesetzt:
> -fno-split-wide-types> -fno-tree-loop-optimize> -fno-move-loop-invariants> -Wl,--relax> --combine -fwhole-program
Gewinn: 14 Bytes
Ich habe noch andere Kombinationen durchgetestet, den besten Gewinn habe
ich mit
> -fno-tree-loop-optimize> -fno-move-loop-invariants
Dann ist der Gewinn 26 Bytes. Alle anderen Optionen sind hier
kontraproduktiv bzw. bringen nichts.
> Ist wohl noch die alte Version gewesen:> 19.07.2011 V. 1.3.1
Ja, ich habs mit der 1.4.0 getestet: Der Bootloader ist von 1014 Bytes
auf 988 Bytes geschrumpft - sehr schön!
Danke für die Tipps,
Frank
Hallo Chris,
chris schrieb:> Die Flankencodierung hat eine Toleranz von +/-1T, wenn man die Zeiten> low=2T und high=4 T wählt.
Nö :-)
Was ist mit einem gemessenen Signal von 3T? Ist das High oder Low?
Erst bei low=2T und high=5T passt es, dann ist:
1-3: low
4-6: high
> Man erhält folgende Geschwindigkeiten für die verschiedenen> Codieungsarten:>> 2T/3T: 44100/((2+3)/2)=17640 Bit/Sekunde= 2205 Byte/Sekunde> 2T/4T: 44100/((2+4)/2)=14700 Bit/Sekunde= 1837 Byte/Sekunde
Ich vervollständige das mal:
2T/5T: 44100/((2+5)/2)=12600 Bit/Sekunde= 1557 Byte/Sekunde
> Die Flankencodierung hat ähnliche Eigenschaften wie die> Manchestercodierung: man kann bei jedem Flankenwechsel neu> synchronisieren, dadurch wird das ganze sehr stabil gegenüber> Taktschwankungen.
Ja, das ist mir klar. Ich habe leider bei Flankencodierungen ähnlich wie
bei Manchester so meine Abneigungen. Diese sind aber rein psychologisch
bedingt. Es liegt wohl daran, dass ich damals bei der
RC6-Implementierung in IRMP Blut und Wasser schwitzte, um das vernünftig
erkennen zu können. Grund war ein Taktratenwechsel mitten im Frame: das
Toggle-Bit wird dort lediglich mit der halben Rate gesandt als die
anderen Bits. Dann bekommst Du 1-, 1,5-, 2- und 2,5-fache Taktlängen...
äußerst unschön zu handlen. Das war für mich ein Kampf bis aufs Messer,
bis es korrekt lief...
Aber ich werde meinen inneren Schweinehund überwinden und das auf jeden
Fall testen... ist so ähnlich wie für andere Leute mit Höhenangst, die
an den Rand eines Wolkenkratzer-Dachs gehen... ;-)
Gruß,
Frank
Frank M. schrieb:> Ja, ich habs mit der 1.4.0 getestet: Der Bootloader ist von 1014 Bytes> auf 988 Bytes geschrumpft - sehr schön!
Füg mal ins *.c noch das ein:
Für main: Gewinn = 0.
Für main_loop: Gewinn = 4 Bytes.
Gruß, Frank
EDIT:
Folgende Befehle sind dadurch in main_loop entfallen:
3a74: df 93 push r29
3a76: cf 93 push r28
Ich muss dazu anmerken, dass ich vorher drin hatte:
1
voidmain_loop(void)__attribute__((noreturn));
Das ist dann aber nicht mehr notwendig, wenn man das OS_main-Attribut
verwendet - wie ich gerade sehe. Also habe ich nun main_loop() wieder in
main() umbenannt und lediglich mit dem Attribut OS_main versehen. Das
bringt 8 Bytes Gewinn gegenüber dem noreturn-Attribut. Insgesamt sind es
22 Bytes Gewinn.
Peter Dannegger schrieb:> Vlad Tepesch schrieb:>> eine Warnung geht weg, aber es kommt eine neue dazu.>> ../sndrx-bootloader.c:172: warning: return type of 'main' is not 'int'>> ../sndrx-bootloader.c: In function 'main':>> ../sndrx-bootloader.c:209: warning: 'noreturn' function does return>> Ohne Warungen geht es so:>
1
>
2
>// avoid push in main
3
>intmain(void)__attribute__((OS_main));
4
>
Richtig!
Für einen sauberen Reset kann man den Watchdog missbrauchen.
Simon K. schrieb:> Richtig!>> Für einen sauberen Reset kann man den Watchdog missbrauchen.
?
Was hat denn ein Reset damit zu tun, daß im Main eine Endlosschleife
läuft?
Man kann theoretisch das Main verlassen, landet dann aber auch in einer
Endlossschleife. Es gibt auf dem AVR kein OS, wohin das Main zurück
kann.
Daher kann sich das Main sparen, benutzte Register zu pushen. Und genau
das sagt dem Compiler dieses obige Attribut.
Peter
Peter Dannegger schrieb:> Simon K. schrieb:>> Richtig!>>>> Für einen sauberen Reset kann man den Watchdog missbrauchen.>> ?>> Was hat denn ein Reset damit zu tun, daß im Main eine Endlosschleife> läuft?
Nichts. Sind doch auch zwei Absätze dazwischen! ;-) Das war als Anregung
zu der Reset-Problematik mit dem jmp 0 gedacht.
Simon K. schrieb:> Nichts. Sind doch auch zwei Absätze dazwischen! ;-) Das war als Anregung> zu der Reset-Problematik mit dem jmp 0 gedacht.
Ein Reset über den Watchdog hat den Nachteil, dass dann wieder erst der
Bootloader gestartet wird... und man erneut 3 Sekunden warten muss,
bevor zur Applikation durchgestartet wird. Das brauche ich aber nicht
nach erfolgreichem Flashen...
Ausserdem kosten die wdt-Aufrufe einige Bytes an Code, den ich lieber
sparen möchte ;-)
Gruß,
Frank
EDIT: Deine Idee ist sogar noch viel schlechter: Der Watchdog würde ja
immer wieder dafür sorgen, dass der Bootloader hochkommt.... in die
eigentliche Applikation komme ich dann nie wieder!
>chris schrieb:>> Die Flankencodierung hat eine Toleranz von +/-1T, wenn man die Zeiten>> low=2T und high=4 T wählt.
Frank schrieb
>Nö :-)
doch ;-)
>Was ist mit einem gemessenen Signal von 3T?
Eigentlich sollte man ja 2T oder 4T messen. Mit Messfehler wäre das dann
z.B.
low: 1..2.9999999T
high: 3T-5T
chris schrieb:> Eigentlich sollte man ja 2T oder 4T messen. Mit Messfehler wäre das dann> z.B.>> low: 1..2.9999999T> high: 3T-5T
Das geht nicht. Ich polle. Nämlich 44100 mal pro Sekunde. Der PC schickt
Signale, die mindestens 2T lang sind, also nominell ändern sich die
Werte bis zu 22050 mal in der Sekunde.
Das heisst, ich kann genau 2 identische Werte pollen, bevor die Flanke
kommt.
Wenn ich zufällig ganz nahe an der Flanke des Signals polle, kann das
mal kurz vor oder auch kurz danach passieren, wenn ich nicht so genau
synchron bin - zum Beispiel, weil ich den internen RC-Oszillator
verwende. Das Konzept hat also schon prinzipbedingt einen Fehler von +/-
1. Da dieser Fehler in beide Richtungen ausschlagen kann und mir nur
ganzzahlige Messwerte 1,2,3,4,5,... vorliegen (2.99999T ist also
unrealistisch), muss ich, um auf der sicheren Seite zu sein, zumindest
einen Meßfehler von +/- 1 zulassen. Und das geht nur, wenn ich den
Abstand beider Längen auf 3 setze.
chris schrieb:>>Das geht nicht. Ich polle. Nämlich 44100 mal pro Sekunde.>> Du kannst ja auch einen Timer verwenden. Dann geht's schneller und> genauer.
Ich benutze einen Timer. Quarzgenau mit 44100 / sec. :-)
Um einen Abtastfehler von +/- 1 zuverlässig zu kompensieren, muss ich
auf das 3fache von 22050 hoch, also auf 66150 / sec. Dann genüge ich
auch dem Abtasttheorem, dass die Abtastfrequenz mehr als das Doppelte
der maximalen Signalfrequenz sein muss.
Literatur dazu:
http://de.wikipedia.org/wiki/Nyquist-Shannon-Abtasttheorem
Ich muss mal ausrechnen, ob die 66150 / sec den µC nicht soweit
dichtmachen, dass gar nichts mehr anderes läuft. Bei 120 Prozessortakten
in der ISR wäre der µC jedenfalls zu 100% mit dem Abtasten beschäftigt.
Das wäre nicht Sinn der Sache.
Eine andere Möglichkeit wäre die Umstellung auf einen
Pin-Change-Interrupt. Das würde den Prozessor wesentlich entlasten,
jedoch verzichtet man dann auf einige Freiheiten, z.B. auf freie
Pin-Wahl. Nicht jeder Pin eines ATmegas ist PCINT-fähig.
Es geht auch anders. Den Timer musst Du "Full Speed" laufen lassen, dann
hast Du die beste Zeiauflösungg. Hier mal Pseudo-Code
warten bis flanke
loop:
timer starten
warten bis flanke
timer lesen
if time>Grenzwert bit = 1 ansonsten 0
jump loop
chris schrieb:> Es geht auch anders. Den Timer musst Du "Full Speed" laufen lassen, dann> hast Du die beste Zeiauflösungg. Hier mal Pseudo-Code
Den Timer benutze ich, um 44100 mal pro Sekunde den Input-Portpin per
Timer-Interrupt-Handler auszulesen. Das nenne ich "Pollen". Das passiert
also im Hintergrund, während das Hauptprogramm was anderes machen kann.
> warten bis flanke> loop:> timer starten> warten bis flanke> timer lesen> if time>Grenzwert bit = 1 ansonsten 0> jump loop
Ja klar, dann muss ich für "warten bis flanke" einen PCINT
(Pin-Change-Interrupt) benutzen, genau das habe ich doch oben
geschrieben. :-)
Ich werde bestimmt nicht den Rückschritt machen, die Flanke in der
Hauptfunktion zu pollen, also Deinen Pseudo-Code zu implementieren. Dann
macht der µC ja nichts mehr anderes ;-)
Ich denke mir das so:
PCINT-Interrupt: // es kam eine Flanke
Timer lesen und abspeichern
if time>Grenzwert bit = 1 ansonsten 0 im Ringbuffer speichern
Timer zurücksetzen
Return from Interrupt // nix jump loop
Fazit:
Im Moment arbeitet das Pollen interruptgesteuert über Timer-Interrupt.
Eine CPU-Entlastung und eine höhere Abtastauflösung lässt sich durch
einen zusätzlichen Pin-Change-Interrupt erreichen.
Ich werde mir mal anschauen, wieviel Arbeit das ist.
Gruß,
Frank
>Ich werde bestimmt nicht den Rückschritt machen, die Flanke in der>Hauptfunktion zu pollen, also Deinen Pseudo-Code zu implementieren. Dann>macht der µC ja nichts mehr anderes ;-)
Was muss Dein Code machen, außer das Flash zu programmieren?
chris schrieb:>>Ich werde bestimmt nicht den Rückschritt machen, die Flanke in der>>Hauptfunktion zu pollen, also Deinen Pseudo-Code zu implementieren. Dann>>macht der µC ja nichts mehr anderes ;-)>> Was muss Dein Code machen, außer das Flash zu programmieren?
Beim Bootloader muss ich Blöcke zu je 128 Bytes (SPM_PAGESIZE)
zusammensammeln und dann:
128 Bytes Flash löschen
128 Bytes wortweise in den internen PageBuffer schreiben
128 Bytes vom Pagebuffer ins Flash schreiben
Der Grund ist einfach: das Flash lässt sich nicht byteweise beschreiben,
sondern nur in Blöcken zu je 128 Bytes.
Das kann ich nicht mal eben zwischen 2 Flanken erledigen, dafür ist die
Zeit zu kurz. Es geht noch nichtmals zwischen 2 empfangenen Bytes,
deshalb musste ich auch den Ringbuffer in SOUNDRX einbauen. Das habe ich
alles schon getestet. Das Flashen muss "quasi-parallel" ablaufen:
während ich die nächsten 128 Bytes einsammele, flasht das Ding noch im
Hintergrund die letzten 128 Bytes.
SOUNDRX ist aber nicht nur als Bootloader gedacht, sondern kann auch zur
Informationsübertragung genutzt werden. Und auch da hat der µC noch
andere Aufgaben zu erledigen als nur Bytes zu "fressen". Irgendwas muss
er ja auch noch damit machen - sprich er muss die Informationen auch
verarbeiten. Egal wofür... ob er empfangene Mitteilungen auf einem LCD
ausgeben soll oder was weiß ich. Wenn er nichts anderes macht als Bytes
über die Soundkarte empfangen, ist er ja nichts weiter als ein
"schwarzes Loch"... eine Art Byte-Mülleimer. ;-) Das kanns nicht sein,
oder?
Du hast recht. Beim programmieren des Flash könnte es ein Problem geben.
Ich bin mir nicht mehr sicher: muss man nicht ein Register im Flash
pollen, um zu sehen, dass die Daten geflasht wurden? Wie ist das mit dem
Timing, reicht die Zeit immer aus, bevor neue Daten von der
Schnittstelle kommen?
Was mir noch eingefallen ist: Eventuell verhalten sich die
Audio-Interfaces der verschiedenen Rechner unterschiedlich. Vielleicht
können alle, die es ausprobiert haben, einen Erfahrungsbericht posten,
um zu sehen, ob das Verfahren bei allen Rechnern funktioniert.
Die Datenübertragung vom PC zum Mikrocontroller habe ich schon mal
irgendwo gesehen, aber leider finde ich die Stelle nicht mehr.
Nach einiger Suche ist mir dass hier über den Weg gelaufen:
http://www.eecs.umich.edu/~prabal/projects/hijack/
Wäre mal interessant zu sehen, welche Datenraten andere Leute erreichen.
Hört sich toll an.
Ich hätte da auch noch ne tolle Idee.
Mann könnte dich einen SPI oder Jtag Programmer bauen der anstatt mit
USB oder RS232 dann über die Soundkarte des Rechners läuft.
Ich bekommme Die USB to RS232 dinger auch nicht wirklich zum laufen und
das Notebook hatt leider keinen RS232 mehr.
ATmega user schrieb:> Mann könnte dich einen SPI oder Jtag Programmer bauen der anstatt mit> USB oder RS232 dann über die Soundkarte des Rechners läuft.
Wird nicht gehen, denn Du hast keinen Rückkanal. Die Informationen
fließen nur in eine Richtung.
> Ich bekommme Die USB to RS232 dinger auch nicht wirklich zum laufen und> das Notebook hatt leider keinen RS232 mehr.
Ich benutze meist diesen hier:
http://shop.myavr.de/Bauelemente%20und%20Controller/myUSBtoUART.htm?sp=article.sp.php&artID=200024
Ist billig und braucht keinen MAX232 auf der µC-Seite, da dort direkt
TTL-Pegel ankommen. Nachteil: Du kannst die Übertragungsstrecke nicht so
lang machen wie bei einer RS232.
Frank M. schrieb:
>> Mann könnte dich einen SPI oder Jtag Programmer bauen der anstatt mit>> USB oder RS232 dann über die Soundkarte des Rechners läuft.>> Wird nicht gehen, denn Du hast keinen Rückkanal. Die Informationen> fließen nur in eine Richtung.
Normalerweise besitzt doch fast Jede Soundkarte einen Aux-In oder einen
Microphon-in. Die könnte man doch als Rückleitung benutzen.
ATmega user schrieb:> Normalerweise besitzt doch fast Jede Soundkarte einen Aux-In oder einen> Microphon-in. Die könnte man doch als Rückleitung benutzen.
Ja, daran habe ich auch schon mal gedacht. Ich hab mich damit aber noch
nicht beschäftigt. Soviel ich weiß, kann ich da nur ein Recording drauf
absetzen, irgendwann(?) die Aufnahme stoppen und dann auswerten. das
wäre ein wenig träge, oder? ;-)
Wenn das auch während der Aufnahme geht, dann könnte es klappen. Muss
ich mich mal mit auseinandersetzen...
was sollte dich darin hindern, ein Programm zu schreiben, was den
AudioIn einliest und direkt verarbeitet?
Nicht, dass ich das schonmal gemacht hätte, aber oich bin sicher, so
schwer ist das nicht ;)
Wir haben heute mal testweise SOUNDRX auf Flankencodierung umgestellt
und das Signal per Pin-Change-Interrupt ausgewertet.
Wir kommen nun auf eine mittlere Transferrate von 2205 Byte/sec.
Wir werden noch diverse Tests mit dem Bootloader machen, bevor wir ein
Update zur Verfügung stellen. Außerdem haben wir bemerkt, dass die
Zeiten für Low und High-Signal bei diesen Geschwindigkeiten um eine
Konstante differieren, die wohl aus der Schaltung oder von der
Unzulänglichkeit der Soundkarte herrührt. Wir werden deshalb noch eine
Art "Training" in die Sendefunktion einbauen, damit die
Korrektur-Konstante vom µC dynamisch ermittelt werden kann. Im Moment
steht sie noch fest im Source.
Viele Grüße,
Robert und Frank
Hallo Frank,
>Wir kommen nun auf eine mittlere Transferrate von 2205 Byte/sec.
herzlichen Glückwunsch zur Geschwindigkeitssteigerung ;-)
> Außerdem haben wir bemerkt, dass die>Zeiten für Low und High-Signal bei diesen Geschwindigkeiten um eine>Konstante differieren, die wohl aus der Schaltung oder von der>Unzulänglichkeit der Soundkarte herrührt.
Ihr könnt die Schaltung auf 3 Bauteile reduzieren. Ein 10K Widerstand
nach Masse, ein 10K Widerstand zur positiven Versorgung und ein 10nF als
Kopplung zum Audio-Jack. Die Zeiten sind dann symmetrisch. Bei mir
funktionierte es augezeichnet.
Gruß,
chris
Hallo Chris,
chris schrieb:> Ihr könnt die Schaltung auf 3 Bauteile reduzieren. Ein 10K Widerstand> nach Masse, ein 10K Widerstand zur positiven Versorgung und ein 10nF als> Kopplung zum Audio-Jack. Die Zeiten sind dann symmetrisch. Bei mir> funktionierte es augezeichnet.
Hm. Soviel ich weiß, kommt da am Audio-Ausgang irgendwas um die 1Vss
raus. Den ziehst Du nun in "die Mitte" mit den beiden Widerständen und
fertig? Ich halte das ja für etwas gewagt, da Du damit eigentlich keine
definierten High-/Low-Werte erzeugst. Hast Du ein Oszilloskop und kannst
mal sagen, was da für Spannungen am µC-Eingang anliegen?
Aber das mit dem 10nF-Kondensator (statt 100nF) werde ich mal
ausprobieren. Ich vermute, dass bei der hohen Geschwindigkeit der
100nF-Kondensator zu groß (und damit zu träge) ist.
Gruß,
Frank
chris schrieb:> Ihr könnt die Schaltung auf 3 Bauteile reduzieren. Ein 10K Widerstand> nach Masse, ein 10K Widerstand zur positiven Versorgung und ein 10nF als> Kopplung zum Audio-Jack. Die Zeiten sind dann symmetrisch. Bei mir> funktionierte es augezeichnet.>> Gruß,> chris
wenn man dann noch den Widerstand des internen Pullups ermittelt und den
Pulldown in entsprechender Größe wählt, kann man den externen Pullup
auch noch weglassen ;)
>Hm. Soviel ich weiß, kommt da am Audio-Ausgang irgendwas um die 1Vss>raus. Den ziehst Du nun in "die Mitte" mit den beiden Widerständen und>fertig? Ich halte das ja für etwas gewagt, da Du damit eigentlich keine>definierten High-/Low-Werte erzeugst.
Soweit ich im Atmega8 Datenblatt gelesen habe, sind die digitalen
Eingänge Schmitt-Trigger-Eingänge mit 0.5V Hysterese. Das passt ideal zu
den 1Vss des Audiosignals, wenn man den Level richtig einstellt. In den
Diagramme zu dne High-Low-Werten hatte ich eine Schwelle von ca. 1.7V
bei VCC=5V abgelesen. Allerdings habe ich dann beim realen Versuch
festgestellt, dass die Schaltschwellen doch bei der halben Versorgung
liegen ( ok, ich hatte einen Arduino mit Atmega168 verwendet, man müsste
die Datenblätter mal vergleichen ).
Mittlerweile ist mir auch der Vorteil des Manchestercodes gegenüber der
Flankenkodierung klarer geworden. Der Hamming-Code ist Mittelwertfrei.
Bei der Flankencodierung kann es passieren, dass der mittlere Pegel
abhängig vom Code ein wenig den Mittelwert wandern lässt, Das gäbe dann
vermutlich leichte Zeitfehler, da die Flanken nicht wirklich rechteckig
sind.
>wenn man dann noch den Widerstand des internen Pullups ermittelt und den>Pulldown in entsprechender Größe wählt, kann man den externen Pullup>auch noch weglassen ;)
vielleicht geht das, aber die Pullups sind vermutlich nicht so exakt.
Wenn die Schwelle nicht stimmt, könnte die Signaldetektion ein Problem
haben.
chris schrieb:> Soweit ich im Atmega8 Datenblatt gelesen habe, sind die digitalen> Eingänge Schmitt-Trigger-Eingänge mit 0.5V Hysterese.
Ich lese das hier im Datenblatt vom ATmega168:
Input Low Voltage,Except XTAL1 and Reset pin: max. 0.3Vcc
Input High Voltage, Except XTAL1 and RESET pins: min. 0.6Vcc
Wenn ich das auf Vcc = 5V umrechne, erhalte ich:
Low: max. 1,5V
High: min. 3,0V
Zwischen 1,5 und 3V ist eine nicht definierte Lücke.
> Mittlerweile ist mir auch der Vorteil des Manchestercodes gegenüber der> Flankenkodierung klarer geworden. Der Hamming-Code ist Mittelwertfrei.> Bei der Flankencodierung kann es passieren, dass der mittlere Pegel> abhängig vom Code ein wenig den Mittelwert wandern lässt, Das gäbe dann> vermutlich leichte Zeitfehler, da die Flanken nicht wirklich rechteckig> sind.
Ja, ich hatte auch den Verdacht, dass die Flanken nicht steil genug sind
(aufgrund des Kondensators?) und deshalb der µC eine Asymmetrie sieht:
Die Länge der Low-Signale unterscheidet sich um ein gutes Stück von der
Länge der High-Signale. In der Summe stimmts natürlich, deshalb hatte
ich bei der Pulse-Distance-Codierung bisher auch keine Probleme, weil
ich immer die Summe Puls + Pause betrachtet habe.
Diese Asymmetrie schwächt sich übrigens nach einem Dutzend Bits
(Flanken) ab, d.h. das Ergebnis wird besser. Aber es bleibt bei einer
Differenz von 10% Abweichung (anfangs sind es sogar 20%). Damit
"verschmelzen" die gewählten Längen von 2T,3T,4T,5T innerhalb der
Fehlergrenzen teilweise ineinander. Erst das Korrigieren von Low- und
High-Zeiten um eine empirisch ermittelte Konstante brachte das Ergebnis
wieder in Ordnung.
Verstehe ich zwar nicht, aber das Senden von zwei Dummy-Bytes (0x00
0x00) vor dem eigentlichen Start-Bit des Blocks verbessert die
Signal-Sicherheit signifikant. Diese beiden "Training-Bytes" werde ich
dann wohl zukünftig zum Bestimmen der
Low-/High-Längendifferenz-Korrekturwerte verwenden. Vielleicht wäre
damit sogar eine Art "Autobaud-Detection" möglich - mal sehen. Aber
vielleicht reicht es ja auch, den Kondensator durch 10nF zu ersetzen.
Wenn dann die Asymmetrie weg ist, kann ich mir den Zinnober sparen.
Zur Manchester-Codierung: Die Bits "0" und "1" wären dann zwar gleich
lang, aber ich bräuchte immer 4T für ein Bit. Max. Geschwindigkeit wäre
dann 1378 Bytes/sec - also ein fetter Rückschritt.
> vielleicht geht das, aber die Pullups sind vermutlich nicht so exakt.> Wenn die Schwelle nicht stimmt, könnte die Signaldetektion ein Problem> haben.
Ehrlich gesagt, bevorzuge ich die Transistor-Lösung. Kostet 5 Cent mehr,
dafür ist man aber auf der sichereren Seite. Die Widerstand-Only-Lösung
ist mir zu "abenteuerlich" ;-)
>Ja, ich hatte auch den Verdacht, dass die Flanken nicht steil genug sind>(aufgrund des Kondensators?)
Der Audioausgang ist kein Digitalausgang. Der Ausgang des DA-Wandlers
dürfte mit einem Antialiasing-Filter versehen sein, dessen Grenzfrequenz
je nach Filterordnung einiges unter 20kHz liegt. Das Signal bei den für
diese Projekt verwendeten Pulslängen kann also gar nicht rechteckförmig
sein. Die Transistorschaltung legt die Schaltschwelle irgendwo in den
Sinus, deshalb erhälst Du so unterschiedliche Pulslängen.
>Ja, ich hatte auch den Verdacht, dass die Flanken nicht steil genug sind>(aufgrund des Kondensators?)
Der Kondensator macht hier das glatte Gegenteil: er verstärkt die
Flanken eher, da das Ganze einen Hochpass darstellt.
>Verstehe ich zwar nicht, aber das Senden von zwei Dummy-Bytes (0x00>0x00) vor dem eigentlichen Start-Bit des Blocks verbessert die>Signal-Sicherheit signifikant
Der Kondensator trennt den Gleichspannungsanteil des Signals ab. Da Du
von einer Gleichspannung aus startest, dauert es eine Weile, bis sich
die Ladung des Kondensators auf den Mittelwert des Signals einstellt.
Wenn Du einen größeren Kondensator einsetzt, wird dieser Vorgang länger.
Bei einem kleineren Kondensator geht das Einschwingen entsprechen
schneller.
Bei einem Nicht-Mittelwertfreien Signal gibt es auch während der
Datenübertragung eine Veränderung der Schwelle. Du kannst es
ausprobieren: Übertrage 100x den Wert 0xAA und danach 0xFF. Bei den 0xFF
wirst Du eine Zeit lang eine Veränderung der Zeiten feststellen.
chris schrieb:> Der Audioausgang ist kein Digitalausgang. Der Ausgang des DA-Wandlers> dürfte mit einem Antialiasing-Filter versehen sein, dessen Grenzfrequenz> je nach Filterordnung einiges unter 20kHz liegt. Das Signal bei den für> diese Projekt verwendeten Pulslängen kann also gar nicht rechteckförmig> sein. Die Transistorschaltung legt die Schaltschwelle irgendwo in den> Sinus, deshalb erhälst Du so unterschiedliche Pulslängen.
Stimmt auffallend. Ich habe die Schaltung mal in LT-Spice ausprobiert.
Je flacher die Rechtecke ansteigen/abfallen, desto asymmetrischer wird
die Geschichte.
Gruß und Dank,
Frank
chris schrieb:> Der Kondensator trennt den Gleichspannungsanteil des Signals ab. Da Du> von einer Gleichspannung aus startest, dauert es eine Weile, bis sich> die Ladung des Kondensators auf den Mittelwert des Signals einstellt.> Wenn Du einen größeren Kondensator einsetzt, wird dieser Vorgang länger.> Bei einem kleineren Kondensator geht das Einschwingen entsprechen> schneller.
Stimmt, auch das habe ich mit LT-Spice getestet und verifiziert. 10nF
und 100nF unterscheiden sich dabei aber bei den hier verwendeten
Taktzeiten fast unmerklich.
> Bei einem Nicht-Mittelwertfreien Signal gibt es auch während der> Datenübertragung eine Veränderung der Schwelle. Du kannst es> ausprobieren: Übertrage 100x den Wert 0xAA und danach 0xFF. Bei den 0xFF> wirst Du eine Zeit lang eine Veränderung der Zeiten feststellen.
Das stimmt mich nicht gerade optimistisch. Bei der
Puls-Distance-Codierung spielt die Asymmetrie der Signale überhaupt
keine Rolle, da ich ja immer die Summe von Low+High betrachte. Bei der
Flankencodierung habe ich da echt zu kämpfen. Die Signalzeiten für Bit0
= 2T und Bit1 = 3T kommen sich trotz "Training" und anschließender
Korrektur um einen konstanten Wert verdächtig "nahe", weil die
gemessenen Zeiten trotz Korrektur um ca. 5-10 Prozent schwanken :-(
Ich werde die ganze Geschichte mal mit Bit0 = 2T und Bit1 = 4T testen.
Dann sinkt zwar die mittlere Geschwindigkeit von 2205 Bytes/sec auf 1837
Bytes/sec, aber insgesamt ist mir die Übertragungssicherheit wichtiger
als die Geschwindigkeit.
Gruß,
Frank
>Das stimmt mich nicht gerade optimistisch.
Ja, am störsichersten wäre tatsächlich der Manchestercode, weil dort der
Mittelwert 0 ist. Allerdings verliert man dann natürlich den
Geschwindigkeitsvorteil, weil man für jedes Bit 4T benötigt.
Bei meiner Schaltung und meinem Rechner konnte ich aber auch mit
kürzeren Zeiten senden.
>Bei der>Puls-Distance-Codierung spielt die Asymmetrie der Signale überhaupt>keine Rolle, da ich ja immer die Summe von Low+High betrachte.
Wenn Du z.B. immer die steigenden Flanke betrachtest, sollten die Zeiten
tatsächlich konstant bleiben. Bei der Pulscodierung ist der Mittelwert
aber auch von den übertragenen Daten abhängig, da das
Puls/Pausenverhältnis ja für High- und Low-Daten unterschiedlich ist.
Zumindest der Störabstand verändert sich dann ein wenig in Abhängigkeit
der übertragenen Daten.
> Bei der> Flankencodierung habe ich da echt zu kämpfen. Die Signalzeiten für Bit0> = 2T und Bit1 = 3T kommen sich trotz "Training" und anschließender> Korrektur um einen konstanten Wert verdächtig "nahe", weil die> gemessenen Zeiten trotz Korrektur um ca. 5-10 Prozent schwanken :-(
Versuche es doch mal mit der Spannungsteilerschaltung. Eventuell wäre
ein Poti ganz gut, um die Schwelle genau auf den Mikrocontroller
einzustellen. Bei der Transistorschaltung liegt die Schaltschwelle zu
unsymmetrisch, so dass ungünstige Zeiten herauskommen können. Wenn Dir
die Spannungsteilerschaltung nicht vertrauenswürdig erscheint, kannst
Du auch den eingebauten Komparator in den Atmels verwenden.
>Ich werde die ganze Geschichte mal mit Bit0 = 2T und Bit1 = 4T testen.>Dann sinkt zwar die mittlere Geschwindigkeit von 2205 Bytes/sec auf 1837>Bytes/sec, aber insgesamt ist mir die Übertragungssicherheit wichtiger>als die Geschwindigkeit.
Es könnte sein dass es bei 2T 4T noch schlechter wird, da sich der
Mittelwert statt von 2/3 auf 2/4 ändert.
Gruß,
chris
Guten Morgen Chris,
chris schrieb:> Versuche es doch mal mit der Spannungsteilerschaltung.
Ich werde es am Wochenende mal ausprobieren - mit Poti. Versprochen :-)
> Es könnte sein dass es bei 2T 4T noch schlechter wird, da sich der> Mittelwert statt von 2/3 auf 2/4 ändert.
Ich habe mit den folgenden Zeiten neu getestet:
0-Bit: 2T
1-Bit: 4T
Stop: 6T
Start: 8T
Ich habe dabei nicht feststellen können, dass die Asymmetrie schlechter
wird. Testübertragungen verliefen auch ohne Fehler. Ich werde mir bis
zum Wochenende die gemessenen Werte nochmals anschauen und die
Abweichungen vom Idealwert begutachten.
Gruß,
Frank
Frank M. schrieb:> Der Watchdog würde ja immer wieder dafür sorgen, dass der Bootloader> hochkommt.... in die eigentliche Applikation komme ich dann nie wieder!
Der Bootloader könnte als erstes die Reset-Ursache prüfen:
Reset-Pin oder Power On -> Bootloader starten
Watchdog -> Applikation anspringen
Das kostet ein paar Bytes für den Test, spart aber auf der anderen Seite
den Code, der nach erfolgreichem Flashen den Timer, Interrupt, etc.
wieder abschaltet.
R. Max schrieb:> Der Bootloader könnte als erstes die Reset-Ursache prüfen:>> Reset-Pin oder Power On -> Bootloader starten> Watchdog -> Applikation anspringen>> Das kostet ein paar Bytes für den Test, spart aber auf der anderen Seite> den Code, der nach erfolgreichem Flashen den Timer, Interrupt, etc.> wieder abschaltet.
Danke, Du hast vollkommen recht. Ich werde das so einbauen.
R. Max schrieb:> Reset-Pin oder Power On -> Bootloader starten> Watchdog -> Applikation anspringen>> Das kostet ein paar Bytes für den Test, spart aber auf der anderen Seite> den Code, der nach erfolgreichem Flashen den Timer, Interrupt, etc.> wieder abschaltet.
würde ich nicht machen.
Was ist wenn man in der Applikation einen weg einbauen will, den
bootloader zu starten?
der einzige saubere Weg ist, einen WD-Reset auszulösen (@frank: so wird
es ja auch in der WC gemacht).
genau damit würdest du aber nicht mehr in den Bootloader kommen.
Vlad Tepesch schrieb:> Was ist wenn man in der Applikation einen weg einbauen will, den> bootloader zu starten?
Jump auf 0x3800 sollte doch gehen, oder?
> der einzige saubere Weg ist, einen WD-Reset auszulösen (@frank: so wird> es ja auch in der WC gemacht).> genau damit würdest du aber nicht mehr in den Bootloader kommen.
Wieso nicht? Ein Reset über den WD würde doch genau den Bootloader
wieder starten... oder hab ich da jetzt etwas verpeilt?
Frank M. schrieb:> Ein Reset über den WD würde doch genau den Bootloader wieder starten...
Ja, der würde aber nach meinem Vorschlag die Tatsache, daß der Reset vom
WD ausgelöst wurde als Kriterium nehmen, direkt in die Applikation zu
springen.
Will man per WD in beide Richtungen springen können, braucht man
zusätzlich eine Variable, die nur nach einem Power-On-Reset
initialisiert wird, und dem Bootloader nach einem Watchdog-Reset
mitteilt, welche Seite den Watchdog gestartet hat.
Servus!
Schade, dass da jetzt nimma weiterentwickelt wird an dem Bootloader. -
Wär ne coole Sache.
Wenn man den obendrein noch mit (den Funktionen von) Hagen Re's
AVRootloader kombinieren könnt wär das absolut genial. ;)
Leider kann ich nicht so gut programmieren, dass ich das bewerkstelligen
könnte.
Vielleicht schaffts von euch jemand?
Wenn auch nur zumindest, dass man sich den Transistor sparen könnte und
per ADC oder so das machen könnte, weils ja mim Transistor völlig
asymmetrisch ist.
Gruß,
Stefan
Stefan schrieb:> Schade, dass da jetzt nimma weiterentwickelt wird an dem Bootloader. -
Keine Bange, es wird weiterentwickelt. Robert und ich haben uns bei der
Erhöhung der Geschwindigkeit auf 2000 Zeichen/sec die letzten 2 Sonntage
um die Ohren geschlagen, weil es mit bestimmten Timing-Kombinationen von
0/1-Bits partout nicht funktionieren wollte. Mittlerweile läuft es aber
stabil. Jetzt muss die Dokumentation aber noch geändert werden, ca. 80%
des Artikels muss neu geschrieben werden.
Die neue Geschwindigkeitserhöhung auf 2000 Z/sec braucht nämlich eine
neue Schaltung, nämlich entweder:
- Nur ein Trimmpoti, welches durch ein extra geschriebenes
Kalibrierungsprogramm exakt auf die Schaltschwelle des
verwendeten ATmega eingestellt wird
oder
- eine neue OPV-Schaltung, die ohne Kalibrierung funktioniert.
> Wenn man den obendrein noch mit (den Funktionen von) Hagen Re's> AVRootloader kombinieren könnt wär das absolut genial. ;)
Ein Schritt nach dem andern, bitte. :-)
> Vielleicht schaffts von euch jemand?> Wenn auch nur zumindest, dass man sich den Transistor sparen könnte und> per ADC oder so das machen könnte, weils ja mim Transistor völlig> asymmetrisch ist.
Geht mittlerweile mit einem einzigen Poti - so wie es chris
vorgeschlagen hatte. Wie gesagt, das Poti muss einmalig justiert werden.
Update mit der neuen Software kommt spätestens am Wochenende.
Gruß,
Frank
Neue Version 1.5.0 ist online.
Änderungen:
- Sendeformat auf flankencodiertes Protokoll umgestellt
- Erhöhung der Übertragungsgeschwindigkeit auf ca. 2000 Zeichen/sec
- Vereinfachung der Empfangsschaltung auf einen Kondensator + ein Poti
- Kalibrierungsprogramm für die Einfachst-Schaltung
- Checksums für Bootloaderübertragung eingebaut
Der Artikel, der Schaltplan und die Download-Dateien wurden
aktualisiert, siehe:
http://www.mikrocontroller.net/articles/SOUNDRX
Gruß,
Frank
Ich versteh noch nicht so ganz, wofür man das Poti benötigt. Im
Datenblatt sind die beiden Schaltschwellen für den
Schmitt-Trigger-Eingang doch angegeben.
Der AVR hat außerdem üblicherweise einen Analog Komparator eingebaut,
der mit einem Eingang intern an die interne Referenz geschaltet werden
kann. Dadurch könnte man sich die externe Beschaltung mit dem LM358
sparen.
Simon K. schrieb:> Ich versteh noch nicht so ganz, wofür man das Poti benötigt. Im> Datenblatt sind die beiden Schaltschwellen für den> Schmitt-Trigger-Eingang doch angegeben.
SOUNDRX braucht bei Flankencodierung ein symmetrisches Signal, siehe
Diskussion oben und in
Beitrag "Schaltschwelle bei Atmega digital Eingang"
Die Schaltschwellen der digitalen(!) Eingänge sind je nach
ATmega-Generation (z.B. ATmega8 vs. ATmega88) verschieden.
> Der AVR hat außerdem üblicherweise einen Analog Komparator eingebaut,> der mit einem Eingang intern an die interne Referenz geschaltet werden> kann. Dadurch könnte man sich die externe Beschaltung mit dem LM358> sparen.
Der Witz ist, dass SOUNDRX keinen Analog-Eingang benötigt, sondern mit
jedem digitalen Eingang zurechtkommt. Erzähl mir mal, wie Du das mit
einem ATmega162 sonst machen willst ;-)
Frank M. schrieb:> Beitrag "Schaltschwelle bei Atmega digital Eingang">> Die Schaltschwellen der digitalen(!) Eingänge sind je nach> ATmega-Generation (z.B. ATmega8 vs. ATmega88) verschieden.
OK. Aber trotzdem braucht man ja nicht zwangsläufig eine
Kalibrier-Routine, wenn man einen bestimmten AVR benutzen will. Dann
kann man ja die nötigen Spannungen aus dem Datenblatt entnehmen.
> Der Witz ist, dass SOUNDRX keinen Analog-Eingang benötigt, sondern mit> jedem digitalen Eingang zurechtkommt. Erzähl mir mal, wie Du das mit> einem ATmega162 sonst machen willst ;-)
Der Witz am Komparator wäre dann, dass man keine Kalibrier-Routine und
auch keine große externe Beschaltung braucht ;-)
Ansonsten eine nette Spielerei.
Simon K. schrieb:> OK. Aber trotzdem braucht man ja nicht zwangsläufig eine> Kalibrier-Routine, wenn man einen bestimmten AVR benutzen will. Dann> kann man ja die nötigen Spannungen aus dem Datenblatt entnehmen.
Ich messe keine Spannungen. Das Signal muss vom Offset her genau in der
Mitte liegen, damit die High-Pegel- und die Low-Pegel-Zeiten gleich lang
sind. Da nützen mir die Spannungen aus dem Datenblatt gar nichts.
Ausserdem stehen dort für digitale Eingänge lediglich garantierte
Mindest-Spannungen für High-Pegel und Höchst-Spannungen für Low-Pegel
drin. Gerade die Grauzone dazwischen lässt Atmel bewusst offen,
garantiert also für nichts. Aber genau diese Zone ist für die Lösung mit
lediglich einem Poti und einem Kondensator spannend. Dass da ein
Schmitt-Trigger am Digital-Eingang liegt, wird zwar im Datenblatt kurz
erwähnt, aber über die eigentlichen Schwellwerte wird keine Aussage
getroffen.
> Der Witz am Komparator wäre dann, dass man keine Kalibrier-Routine und> auch keine große externe Beschaltung braucht ;-)
Das ist richtig. Vielleicht mache ich noch eine spezielle Variante für
ADC-Komparator-Eingänge, die man dann über soundrxconfig.h einstellen
kann.
> Ansonsten eine nette Spielerei.
Jepp :-)
Frank M. schrieb:> Gerade die Grauzone dazwischen lässt Atmel bewusst offen,> garantiert also für nichts. Aber genau diese Zone ist für die Lösung mit> lediglich einem Poti und einem Kondensator spannend. Dass da ein> Schmitt-Trigger am Digital-Eingang liegt, wird zwar im Datenblatt kurz> erwähnt, aber über die eigentlichen Schwellwerte wird keine Aussage> getroffen.
Möglicherweise, weil die Schwelle in dem Bereich temperatur-, spannungs-
und/oder launeabhängig ist. Die Schaltung könnte also unter bestimmten
Umständen überhaupt nicht mehr funktionieren, obwohl sie mal kalibriert
wurde.
Sehr wackelig die Angelegenheit, finde ich. Reicht die Amplitude der
Audiodatei nicht um die im Datenblatt angegebenen Grenzen zu
überschreiten? Sodass man einfach (Vh+Vl)/2 als Offset einstellen kann?
Simon K. schrieb:> Sehr wackelig die Angelegenheit, finde ich.
Wenn es Dir zu wackelig ist, nimm die OpAmp-Schaltung. Die arbeitet in
jedem Fall ohne Kalibrierung.
> Reicht die Amplitude der Audiodatei nicht um die im Datenblatt angegebenen> Grenzen zu überschreiten?
Sie sollte ausreichen, da der Kopfhörer-Ausgang einer Soundkarte in der
Regel 2Vss bringt - meistens sogar mehr. Wenn Du ein Oszi hast, kannst
Du das ja mal verifizieren Ich habe nämlich keins ;-)
Rufe dazu
http://www.kammerton.de/
auf und drehe den Lautstärkeregler Deiner Soundkarte auf mehr als 75%.
> Sodass man einfach (Vh+Vl)/2 als Offset einstellen kann?
Die Schaltschwelle liegt bei modernen ATmegas ziemlich genau bei Vcc/2.
Eigentlich reicht es, das Poti auf die Mittenstellung einzustellen oder
einfach 2 Widerstände mit gleichen Werten zu nehmen. Mit den in SOUNDRX
eingebauten Toleranzen reicht das aus. Ich wollte nur auf Nummer Sicher
gehen und habe mit dem Kalibrierungsprogramm bei meinem ATmega168P die
Schaltschwelle bei 2,45V statt 2,5V gefunden. Auf jeden Fall sind dann
die (digitalen) High-Pegel genauso lang wie die Low-Pegel bei
symmetrischem Ausgangssignal der Soundkarte. Die 0,05V entsprechen aber
gerade mal einer Abweichung von 2 Prozent. Das verkraftet SOUNDRX
allemal. Von "wackelig" kann daher keine Rede sein.
Aber wie gesagt: wem es so nicht gefällt, kann einfach die
OpAmp-Schaltung nehmen.
Frank M. schrieb:> Aber wie gesagt: wem es so nicht gefällt, kann einfach die> OpAmp-Schaltung nehmen.
Alternativ könnte man einen npn- und einen pnp-Transistor in
Gegentakt-Schaltung anordnen und die beiden Basen über je 10kOhm
verbinden. Über einen Keramik- oder Folie-C 100nF koppelt man das Signal
an den gemeinsamen Verbindungspunkt der Basiswiderstände. Die Emitter
der Transistoren liefern das Signal für den µC. Auch nicht viel größer
als die Einfachst-Schaltung, aber vertägt mehr Toleranzen bei den
Signalpegeln.
Hallo Frank,
gerade eben habe ich mal den Bootloader für einen Atmega168 compiliert.
In meinem AVR-Studio ( -OS ) erhalte ich eine Programmgröße von 1200
Bytes. Soweit ich weiß, hat der Atemga168 nur 1024 Byte für den
Bootloader zur Verfügung.
Hattet Ihr eine kleiner Codegröße?
Gruß,
chris
chris schrieb:> gerade eben habe ich mal den Bootloader für einen Atmega168 compiliert.> In meinem AVR-Studio ( -OS ) erhalte ich eine Programmgröße von 1200> Bytes. Soweit ich weiß, hat der Atemga168 nur 1024 Byte für den> Bootloader zur Verfügung.
Das stimmt nicht. Die Bootloadergröße kann beim ATmega168 bis 2KB
betragen. Eingestellt wird die Bootloadergröße über die E-Fuse. Bei der
größtmöglichen Einstellung sind 1024 Words(!) möglich, das entspricht
2048 Bytes. Das Projekt ist auch so eingestellt, dass der Bootloader auf
die Adresse 0x3800 compiliert wird, siehe Linker-Options:
"--section-start=.text=0x3800". Somit werden die letzten 2KB für den
Bootloader genutzt.
Für die richtige Einstellung der Fuses ist die Seite
http://www.engbedded.com/fusecalc/
sehr gut geeignet.
> Hattet Ihr eine kleiner Codegröße?
Ja, wir waren schon mal knapp unter 1KB, aber durch die Umstellung auf
Flankencodierung und Einführung von Checksum-Übertragungen, um das ganze
sicherer zu gestalten, sind wir wieder über die 1KB-Grenze gerutscht.
Ich hoffe, wir können das wieder unter 1KB drücken. Im Moment muss man
daher noch 2KB (= 1024 Words) einstellen.
Gruß,
Frank
Knut Ballhause schrieb:> Alternativ könnte man einen npn- und einen pnp-Transistor in> Gegentakt-Schaltung anordnen und die beiden Basen über je 10kOhm> verbinden.
Das klingt nicht schlecht, ich werde das mal bei Gelegenheit
ausprobieren. Vielleicht malst Du mal dazu eine kleine Skizze, damit ich
nichts falsch mache? ;-)
Dank und Gruß,
Frank
Hi Frank,
hier habe ich mich auch mal an einem Audio-Bootloader versucht:
http://www.hobby-roboter.de/forum/viewtopic.php?f=4&t=127&p=530
Ich habe die Manchestercodierung verwendet. Die läuft zwar nur halb so
schnell, aber dafür muss man den Spannungsteiler nicht kalibrieren. Eine
Auto-Detektion der Baurate ist auch drin.
Das Java-Download-Programm ist etwas schlampig programmiert. Aber es
funktioniert. Vielleicht gibt es hier einen Java-Experten für
Oberflächen, der das Ganze etwas aufpolieren hilft.
Gruß,
chris
Mit ist noch eine Idee für den minimalistischen Spannungsteilereingang
gekommen:
Wenn man die Portrichtung des MCs auf Ausgang schaltet und den
Audiostecker an den Mikrofoneingang steckt, könnte man Daten zum PC
übertragen.
Wenn man den Audio PC-Ausgang an den Mikrofon Eingang des PCs anschließt
und das Signal für den Mikrocontroller-Pin abzweigt, könnte man eine
halbdublex Verbindung realiseren.
Weiß jemand, ob der MC-Eingang des PC das 5V Signal des MCs aushält?
Frank M. schrieb:> Das klingt nicht schlecht, ich werde das mal bei Gelegenheit> ausprobieren. Vielleicht malst Du mal dazu eine kleine Skizze, damit ich> nichts falsch mache? ;-)
Folgendes habe ich probiert, entspricht zwar nicht ganz obiger
Beschreibung, funktioniert aber hervorragend:
1
o Vcc
2
|
3
|
4
220k |
5
___ |<
6
.---|___|----| BC857C
7
| |\
8
| |
9
| |
10
Eingang ----o o--- zum µC Pin
11
| 220k |
12
| ___ |/
13
'---|___|----| BC847C
14
|>
15
|
16
|
17
-
18
(created by AACircuit v1.28.6 beta 04/19/05 www.tech-chat.de)
Am Controllerpin ist bei einem rechteckförmigen Eingangssignal von der
Soundkarte von -20db bereits ein schönes Rechteck mit vollem
Spannungshub zu sehen. Siehe Screenshot.
chris schrieb:> Weiß jemand, ob der MC-Eingang des PC das 5V Signal des MCs aushält?
Würde ich nicht drauf anlegen. Auf jeden Fall geht er stark in
Sättigung, was sich auf die Signalqualität auswirkt. Besser einen
Spannungsteiler 100:1 vorschalten.
Zu oben genannter Schaltung bleibt noch anzumerken, dass man sie bei
Nichtgebrauch besser abschaltbar gestaltet, da sonst etwa 3mA Ruhestrom
über die Transistoren fließen.
>Würde ich nicht drauf anlegen.
Ich habe es doch mal ausprobiert, der Computer lebt und die Signale
sahen gut aus.
Die Manchestercodierung funktioniert ziemlich gut mit meiner
Spannungsteilerversion:
http://www.mikrocontroller.net/attachment/preview/120273.jpg
Als Line in wird der TxD-Pin des Controllers verwendet. Wenn man ein
gewöhnliches Programm hochlädt hat das den Vorteil, dass die "printf"
direkt auf Line-In des PC gehen, wenn man den Anschluss dort ansteckt.
Um das Signal kleiner zu machen, könnte man noch einen Widerstand vor
den TxD Eingang setzen.
Beim Programmieren hatte ich allerdings Schwierigkeiten einen
vernünftigen Java-Treiber für Line-IN zu finden, deshalb gibt's PC
seitig für den Rückkanal erst mal noch keine Software.
Frank M. schrieb:> Übrigens: die Idee ist eigentlich 30 Jahre alt. Damals hat man in den>> C64/ZX-Spectrum die Programme/Daten mit einem Kassettenrecorder geladen.>> Hier passiert eigentlich genau dasselbe:
Die gute alte Zeit! :-)