Hallo,
ich habe/möchte mir einen Taktgenerator programmieren. Dazu wurde mein
damaliges Grundgrüst optimiert.
Beitrag "AVR Timer 1 Normal/CTC Mode ?"
Später kam dann noch Assembler hinzu um den maximal möglichen Takt zu
erhöhen. Ich selbst habe jedoch von Assembler keine Ahnung.
Jetzt besteht mein Wunsch darin, wenigstens die Assembler Routine für
den Interrupt in C einzubauen. Damit ich weiterhin in C programmieren
kann aber dennoch der schnellste Takt machbar ist.
C Code:
1
#define F_CPU 16000000UL
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#define NOP __asm__ __volatile__ ("nop\n\t")
5
6
#define INCRMNT 0x01 // Toggle Bits 0-7 im ISR Handler
TIMSK1|=(1<<OCIE1A);// enable Timer Compare Interrupt A
26
27
sei();//allow interrupts
28
29
}// end Funktion
30
31
ISR(TIMER1_COMPA_vect){// Timer 1.A Interrupt
32
uint8_ttmp=OUTPORT;
33
tmp+=INCRMNT;
34
OUTPORT=tmp;
35
}
36
37
38
intmain(void){
39
DDRC=0xFF;// alles Ausgänge
40
PORTC=0;// alles aus
41
42
set_Timer1();
43
44
while(1){
45
46
}
47
48
}// Ende main()
Jetzt müßte der Assembler ISR optimal eingebaut werden. Sodass ich auf
ca. 500kHz komme statt ca. 220kHz mit reinen C. Wäre jemand so nett mir
dabei zu helfen?
das wäre die Assembler ISR
Den Assembler-Code baust du genau so ein, wie in dem anderen Thread
schon mal beschrieben. Nur: von den 21 Takten Minimum kommst du nur weg,
wenn du für die ISR zwei Register reservierst, um 'push' und 'pop'
einzusparen(*). 8Takte gespart, mehr nicht!
(*) Ob man da sich allerdings an gewisse Regeln halten muss oder der gcc
da generell nicht so amused ist, entzieht sich meiner Kenntnis.
Ralf G. schrieb:> (*) Ob man da sich allerdings an gewisse Regeln halten muss oder der gcc> da generell nicht so amused ist, entzieht sich meiner Kenntnis.
Man sollte dann lieber keinen fremden Code wie die avr-libc verwenden,
ohne gelgentlich im resultierenden Code nachzusehen, ob die reservierten
Register davon verwendet werden.
Hallo,
der Assembler Einbau an sich hat im anderen Thread funktioniert. Kommt
aber nicht an den max. Takt vom reinen Assembler ran. Deshalb wurde mir
geraten nochmal einen extra Thread aufzumachen für die C Experten. Um
eventuell notwenige Anpassungen einzubauen und Overheads zuvermeiden.
Hallo,
ich hatte doch deinen (darfst mich ruhig dutzen) letzten Assembler Code
eingefügt und kam auf 235kHz. Die Hälfte vom reinen Assembler.
Beitrag "Re: AVR Timer 1 Normal/CTC Mode ?"
Jetzt geht es darum ob man das noch optimieren kann mit dem Inline
Assembler.
Warum soll der Vektor ein Risiko sein? Das ist doch der den die ISR
anspringen muß. Verstehe die Antwort / den Hinweis nicht wirklich.
Pardon, ich versteh's nicht. Sie haben sich jetzt zwei Monate lang über
dieses unbefriedigende Programm mit seinen mageren 235 kHz geärgert, da
haben Sie es doch griffbereit und es kostet Sie nur ein paar
Tastendrücke, es uns hier vorzustellen.
Dann sehen auch die Spezialisten, die sich sicher bald einstellen, Ihren
aktuellen Stand und müssen sich die Informationen nicht an zwei Stellen
zusammensuchen.
Und weshalb nimmt man anstelle eines ausgelasteten Controllers nicht
einen normalen Hardware Ansatz ? zB ein kleines CPLD ? Dem macht man ein
kleines SPI interface zum Setzen der zu venwendenden Register.
Veit D. schrieb:> Warum soll der Vektor ein Risiko sein? Das ist doch der den die ISR> anspringen muß. Verstehe die Antwort / den Hinweis nicht wirklich.
Dein Code überlagert dann andere Vektoren. Sobald du die (am besten aus
dem C-teil heraus, dann wird es unübersichtlicher) nutzen willst, knallt
es. Im besten Fall bekommst du eine Warnung vom Linker.
In die Tabelle gehört nur ein Sprung zur ausführenden Routine, die
irgendwo im freien Speicher steht. Das kostet natürlich leider Zeit.
Schreib die ISR in C, sieh dir das .lss File an, wie es gemacht wird.
Zu Zeiten von Studio 4 gab es zu den Bibliotheken Hilfen, in denen
beschrieben wurde, welche Register die Bibliotheken selber verwenden.
Ich glaube in den FAQ.
Wenn ich mich recht erinnere, war im unteren Bereich, nicht R0 und R1,
noch so einiges frei.
Da waren auch drei Beispiele, wie man Assembler in C einbettet dabei.
Hallo,
bin jetzt mit dem Antworten überfordert. Das damalige Programm bekomme
ich nicht mehr so zusammengebaut bis 235kHz. Hatte es nicht gespeichert
und aufgehoben. Hab das jetzt den ganzen Vormittag probiert und neu
zusammengesetzt, sodass es fehlerfrei kompiliert, komme aber nicht über
200kHz.
@ Georg: In C läuft es ja. Wäre mir jedoch zu langsam. Deswegen bräuchte
ich Hilfe beim umbauen das der Interrupt in Assembler läuft. Wo finde
ich das .lss File und womit schaue ich das an?
@ oder doch: ich möchte erstmal probieren wie weit man das mit einem µC
machen kann. Einen Special IC kann ich immer noch nehmen, wenn das alles
nichts bringt.
Hier der aktuelle Code.
1
#define F_CPU 16000000UL
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#define NOP __asm__ __volatile__ ("nop\n\t")
5
6
#define INCRMNT 0x20 // Toggle Bit 7,6,5 im ISR Handler
>Einen Special IC kann ich immer noch nehmen, wenn das alles>nichts bringt.
Bevor Du einen Special-IC nimmst: wenn Du z.B. auf den STM32 (oder einen
anderen uc mit DMA) umsteigst, dann kannst Du diese Aufgabe komplett von
der DMA erledigen lassen. Damit erreichst Du Frequenzen über 5 Mhz.
Gruß, Stefan
Wenn das das vollständige und niemals zu erweiternde Programm ist, lässt
du 'push'/ 'pop' und das Sichern/ Wiederherstellen von SREG in der ISR
einfach weg. Ich würde das Einlesen des Ports auch noch weglassen. (Der
ist bei dieser Verwendung sowieso zu nichts anderem zu gebrauchen.)
Spart 11Zyklen.
1
ISR(TIMER1_COMPA_vect,ISR_NAKED)
2
{
3
asmvolatile(
4
"inc r8 ""\n\t"
5
"out %[port],r8""\n\t"
6
"reti""\n\t"
7
::[sreg]"I"(_SFR_IO_ADDR(SREG)),
8
[port]"I"(_SFR_IO_ADDR(PORTC)));
9
}
Wenn in der 'while(1)' mal noch was rein soll, was selbstverständlich
wieder auf die Geschwindigkeit geht, reserviert man für das Sichern von
SREG und für die Berechnung jeweils ein Register.
Achso:
Da es mir ähnlich wie Peter geht: Die Syntax braucht noch ein paar
Schönheitskorrekturen. Stichwort: SREG (aber ehe ich was total
versaue...)
Und: 'r8' würde ich auch irgendwie durch eine Variable ersetzen wollen.
Was du im vorigen Thread bereits gefragt worden bist, aber nicht
beantwortet hast:
Welche Waveform willst du denn überhaupt haben?
Du hast ja offenbar einen fetten ATmega2560, da kannst du ja auch
alles als Tabelle ablegen (RAM oder Flash, je nach Anforderung).
Hallo,
in die while(1) soll nochwas rein. Displayausgabe, Taster- und
Potiabfrage und Berechnung zum Timer neu einstellen für die
Wunschfrequenz und Form. Da der Takt aber von einem ISR gemacht wird,
spielt doch der Inhalt der while keine Rolle in meinen Augen. Wird eben
öfters unterbrochen.
Damit zur Frage. Ja ich möchte eine Frequenztabelle hinterlegen. Ich
möchte Rechteck, Dreieck/Rampe und Sinus erzeugen. Für den Sinus brauche
ich einen hohen Grundtakt, damit der durch die "Stützstellen" nicht
gnadenlos einbricht. Ich denke zur Zeit an ein 8Bit R2R Netzwerk als
DAC. Wie gesagt, ich möchte erstmal versuchen was mit nur einem µC so
möglich ist. Vielleicht reicht es mir aus, vielleicht auch nicht. Werde
ich dann sehen.
Veit D. schrieb:> Für den Sinus brauche> ich einen hohen Grundtakt, damit der durch die "Stützstellen" nicht> gnadenlos einbricht.
Für Dreieck brauchst du mehr als für Sinus, denn du hast theoretisch
unendlich „pieksige“ Spitzen. Da musst du sowieso einen Kompromiss
finden, wie spitz das Dreieck tatsächlich werden soll.
Für Sinus kommst du (mit passendem Rekonstruktionsfilter) bei einer
DDS bis 40 % des Grundtaktes, also etwas mehr als 2 Stützstellen pro
Periode. Den Rest „pumpt“ das Rekonstruktionsfilter zurecht (darum
heißt es ja auch so).
Ohne Rekonstruktionsfilter isses sowieso Humbug.
Ansonsten solltest du dir ansehen, was Jesper Hansen da schon vor
mehr als einem Jahrzehnt mit einem AT90S1200 bzw. später ATtiny2313
in seinem Mini-DDS fabriziert hat. Das Original gibt's leider
inzwischen nicht mehr im Netz, aber genügend Derivate.
Veit D. schrieb:> Hallo,>> in die while(1) soll nochwas rein. Displayausgabe, Taster- und> Potiabfrage und Berechnung zum Timer neu einstellen für die> Wunschfrequenz und Form. Da der Takt aber von einem ISR gemacht wird,> spielt doch der Inhalt der while keine Rolle in meinen Augen. Wird eben> öfters unterbrochen.
und genau da liegt der Hund begraben.
Der Compiler benutzt für den Rest in der while schleife irgendwelche
Register. Welche genau, das müsste man ergründen. Der springende Punkt
ist aber, dass du dann in der ISR nicht einfach eines dieser benutzen
Register benutzen kannst, sondern du musst es fein säuberlich sichern
und wieder herstellen. Nachdem die ISR durchgelaufen ist, darf es für
den unterbrochenen Code keine sichtbaren 'Schäden' geben. Die ISR muss
alles wieder genau so hinterlassen, wie sie es beim Einsprung
vorgefunden hat.
Genau darum geht es und genau das benötigt die Taktzyklen.
Allenfalls könnte man noch versuchen, 1 spezielles Register mit dem C
Modifier "register" an eine Variable zu binden, so dass dieses Register
ausschliesslich nur für diese Variable reserviert ist. Der Compiler
berücksichtigt das dann und benutzt dieses Register im Code in der while
SChleife nicht mehr.
Aber: Du hast auch Code der bereits vorkompiliert vorliegt, das sind zb
alle Funktionen die du aus der Standard-Library benutzt. Und dieser Code
wurde ohne dieses Wissen der Registerreservierung compiliert. D.h. dort
kann es dir passieren, dass einzelne Funktionen genau dieses CPU
Register benutzen. Dann ist es Essig mit der ausschliesslichen
Reservierung eines Registers nur für deine Variable.
langer Rede kurzer Sinn: man kann in der ISR die ganze Sicherungsarbeit
von Registern bleiben lassen. Aber man muss dann schon sehr genau wissen
was man tut und welche Funktionen man aus der Standardlibrary benutzen
darf und welche bei welcher Registerreservierung tabu sind.
Wenn das Programm mit <= 4 KB Code auskommt, kann man die kostenfreie
IAR-Kickstarter IDE benutzen, wobei man die Register R4-R15 reservieren
kann, ohne daß es Konflikte mit LIBs oder anderen Routinen gibt.
Ein vorsorgliches NEIN: das Programm wird weder langsamer noch größer
durch diesen Komfort.
m.n. schrieb:> Wenn das Programm mit <= 4 KB Code auskommt,
Für einen ATmega2560?
> kann man die kostenfreie> IAR-Kickstarter IDE benutzen, wobei man die Register R4-R15 reservieren> kann, ohne daß es Konflikte mit LIBs oder anderen Routinen gibt.
D.h. sie garantieren, dass in ihren Bibliotheksroutinen der Compiler
niemals diese Register benutzen wird? Das könnte (in Routinen mit
einem großen Bedarf für lokale Register) zu einer generellen
„Pessimierung“ der Bibliotheksroutinen führen, da der Compiler dann
mehr auf den Stack verlagern müsste. Das wäre ein ziemlich hoher
Preis für diesen (nur selten genutzten) Komfort.
Genau das ist auch der Grund, warum es bei AVR-GCC/avr-libc eben keine
derartige Garantie gibt: der Mechanismus, sich eine Variable auf ein
Register zu binden, existiert dort genauso, aber man muss natürlich
den kompletten Code so compilieren, dass in allen Übersetzungseinheiten
diese feste Reservierung sichtbar ist, damit der Compiler nicht auf die
Idee kommt, das Register anderweitig zu nutzen. Da in der normalen
Standarbibliothek diese Bindung beim Compilieren nicht da war, ist es
eben nicht garantiert. (Man könnte sich allerdings die Bibliothek
selbst mit einer entsprechend sichtbaren Registerreservierung neu
compilieren, ist ja Opensource.)
Jörg W. schrieb:> m.n. schrieb:>> Wenn das Programm mit <= 4 KB Code auskommt,>> Für einen ATmega2560?
Warum denn nicht?
Nur weil dieser mehr IO-Pins und mehr Peripherie auf dem Chip hat, ist
das doch kein Hinderungsgrund.
>> kann man die kostenfreie>> IAR-Kickstarter IDE benutzen, wobei man die Register R4-R15 reservieren>> kann, ohne daß es Konflikte mit LIBs oder anderen Routinen gibt.>> D.h. sie garantieren, dass in ihren Bibliotheksroutinen der Compiler> niemals diese Register benutzen wird? Das könnte (in Routinen mit> einem großen Bedarf für lokale Register) zu einer generellen> „Pessimierung“ der Bibliotheksroutinen führen, da der Compiler dann> mehr auf den Stack verlagern müsste. Das wäre ein ziemlich hoher> Preis für diesen (nur selten genutzten) Komfort.
Die LIBs sind offensichtlich so übersetzt, daß sie die reservierten
Register durchgehend nicht anfassen. Das bedeutet, daß auch ohne
explizite Reservierung die LIBs wohl nicht auf den vollen Registersatz
zugreifen. Soweit ich mit oder ohne reservierten Registern gearbeitet
hatte, konnte ich keine Einschränkung wahrnehmen.
Nebenbei kann man sich auch den Luxus von 64-Bit double-Berechnungen
leisten, sofern es notwendig ist.
m.n. schrieb:>>> Wenn das Programm mit <= 4 KB Code auskommt,>> Für einen ATmega2560?>> Warum denn nicht?
Warum sollte ich einen Controller, der 256 KiB Flash hat, mit einer
Krücke von Compiler bedienen wollen, der davon nur 1/64 ausnutzen
will?
Ich habe gerade mal nachgesehen: es gibt derzeit ganze drei Funktionen
in der avr-libc, für die der Compiler beispielsweise R2 benutzt:
qsort(), strftime() und vfprintf() (welche das Herzstück der gesamten
printf()-Familie ist). Solange er keine dieser Funktionen nutzt,
kann er R2 also auch für eigenen Code nehmen. Es garantiert halt
nur keiner, aber die Methoden wurden ja schon genannt (Audit des
Compilats, oder Selbstcompilieren der avr-libc mit reservierten
Registern).
Wenn ich mir allerdings ansehe, in wie vielen Funktionen der Compiler
auf R15 zurückgreift, dann heißt das schon, dass in der IAR-Lib einiges
verschenkt wird, wenn sie diesen kompletten Registerblock als reserviert
ansieht. OK, die Funktionen, die es betrifft, sind in der Regel
natürlich eher „Langläufer“.
> Nebenbei kann man sich auch den Luxus von 64-Bit double-Berechnungen> leisten, sofern es notwendig ist.
Wäre ein anderer Punkt, den vermutlich beim AVR-GCC keiner mehr
angehen wird, ja. Sich aber dafür auf 4 KiB einschränken (oder
Unsummen an Geld bezahlen)?
Ehrlich: wenn ich sowas wirklich bräuchte, würde ich heute nicht
weiter drüber nachdenken und gleich einen ARM stattdessen nehmen.
Die bekomme ich wahlweise auch mit FPU. (Das ist auch der Grund,
warum ich nicht annehme, dass jemand noch genügend Motivation hätte,
dem AVR-GCC heutzutage 64-bit double beizubringen.)
Jörg W. schrieb:> Warum sollte ich einen Controller, der 256 KiB Flash hat, mit einer> Krücke von Compiler bedienen wollen, der davon nur 1/64 ausnutzen> will?
Weil Flash auch leer bleiben kann und man viel IO und z.B. Timer braucht
und einen AVR verwenden möchte. Ist das denn so schwer zu verstehen?
Jörg W. schrieb:> Ehrlich: wenn ich sowas wirklich bräuchte, würde ich heute nicht> weiter drüber nachdenken und gleich einen ARM stattdessen nehmen.
Mach doch, das war aber nicht das Problem des TO.
Hallo,
ich muß mich erstmal für Eure Hilfe und Hinweise bedanken. Leider
übersteigt das meine derzeitigen Programmierfähigkeiten. Ich möchte
jetzt auch nicht noch mit anderen IDEs anfangen. Ich hatte gedacht,
jemand guckt über den Code, ändert paar Zeilen und das Ding läuft.
Scheinbar ist es nicht so einfach mit dem Assembler. Mal sehen was mit
200kHz so geht. Ansonsten muß ich zu einem DDS von Analog Devices
greifen.
m.n. schrieb:> Weil Flash auch leer bleiben kann und man viel IO und z.B. Timer braucht> und einen AVR verwenden möchte.
Dann hätte es auch ein ATmega640 oder 1280 getan. (Die sind sogar
besser bezüglich Taktfrequenz vs. Versorgungsspannung, da sie in einer
neueren Technologie als der alte ATmega2560 gefertigt werden.)
Klar kann man sich die Schnupper-Krücke von IAR immer passend
zurechtreden. (Nichts gegen IAR, ist auf jeden Fall ein guter
Compiler, aber der Preis ist einfach jenseits von gut und böse.)
Wenn er DDS machen will, dann soll er doch gottverdammich die Tabellen
dafür in seinen riesigen Flash packen.
> Mach doch, das war aber nicht das Problem des TO.
Richtig, 64-bit double auch nicht, trotzdem hast du's ins Feld geführt.
Veit D. schrieb:> Ich möchte jetzt auch nicht noch mit anderen IDEs anfangen.
Eine andere IDE bräuchtest du für die zahlreichen ARMs von Atmel
nicht einmal. ;-)
Ist dein Hardwaredesign schon auf ATmega2560 fixiert? Ansonsten
könntest du, wenn du bei AVR bleiben willst, auch noch einen Blick
auf die Xmegas riskieren. Die haben DMA und Eventsystem, die dir
hier hilfreich sein könnten.
Nach wie vor ist doch völlig unklar, warum Assembler rund doppelt so
schnell ist wie C&Assembler, und das nach 30 Antworten. Oder habe ich
etwas überlesen?
Wir hatten doch diesen Parameter 'ISR_NAKED', wieso passiert da noch
immer etwas vollautomatisch?
Hier der Vorschlag von vor zwei Monaten, auch wenn's schlimm aussieht
und Peter Dannegger die Motten kriegt:
> Da der ATmega2560 über GPIORs verfügt, lassen sich die push/pop> durch out/in ersetzen und somit noch 4 Takte einsparen:
Jörg W. schrieb:> Klar kann man sich die Schnupper-Krücke von IAR immer passend> zurechtreden.
Das ist doch billige Polemik, die immer wieder bei den drei Buchstaben
IAR auftaucht. Oder anders formuliert, man kann sich eine Lösung auch
immer passend schlechtreden. Siehe: "generelle Pessimierung"
m.n. schrieb:> Das ist doch billige Polemik, die immer wieder bei den drei Buchstaben> IAR auftaucht.
Ob des Preises. Also teure Polemik. :-)
Ich habe klipp und klar geschrieben, dass es ein sehr guter Compiler
ist, und da würde ich auch keine Luft dran lassen.
> Oder anders formuliert, man kann sich eine Lösung auch> immer passend schlechtreden. Siehe: "generelle Pessimierung"
Wo ist dafür deine technische Widerlegung?
Ich habe diese Aussage von mir zumindest begründet (die zugleich
die Begründung dafür ist, warum wir diesen „Komfort“, wie du es
nennst, eben nicht by default in der avr-libc anbieten möchten).
S. Landolt schrieb:> Wir hatten doch diesen Parameter 'ISR_NAKED', wieso passiert da noch> immer etwas vollautomatisch?
Da passiert nichts vollautomatisch, außer dem RETI (das
überflüssigerweise trotzdem noch im inline asm steht).
Allerdings fände ich an Stelle des TEs es auch sinnvoller, die
ISR dann gleich als separate Assemblerquelle ins Projekt zu nehmen.
Warum sich bei ihm die Varianten so sehr in der erreichbaren Frequenz
unterscheiden, kann er nur selbst analysieren, denn dafür fehlt uns
einiges an Code (so wie ich das sehe).
S. Landolt schrieb:> Wir hatten doch diesen Parameter 'ISR_NAKED', wieso passiert da noch> immer etwas vollautomatisch?
Nein, hier wurde diskutiert, was bei den hier veröffentlichten
Codeschnipseln nötig ist.
Um wieder zum Thema zu kommen. Würde man r16 in deinem zitierten
Vorschlag reservieren können, bräucht dieser nicht an eine geschickte
Stelle gesichert werden.
S. Landolt schrieb:> Hier der Vorschlag von vor zwei Monaten,Veit D. schrieb:> der Assembler Einbau an sich hat im anderen Thread funktioniert. Kommt> aber nicht an den max. Takt vom reinen Assembler ran. Deshalb wurde mir> geraten nochmal einen extra Thread aufzumachen für die C Experten. Um> eventuell notwenige Anpassungen einzubauen und Overheads zuvermeiden.
Bei der vorgeschlagenen Routine komme ich auf 20, vielleicht 22 Takte,
die angegebenen 235 kHz bei C&Asm aber ergeben 34 - wo verschwindet die
Differenz?
S. Landolt schrieb:> und Peter Dannegger die Motten kriegt:
Dem GCC ist es vollkommen schnurz, ob Du inlinest oder es lesbar in eine
*.S schreibst.
Und wenn es auf den schnellen Interrupt ankommt, kann man den ATmega2560
eh nicht vollschreiben. Jede andere Task mit Interrupts wird Dir das
Timing zunichte machen.
S. Landolt schrieb:> Bei der vorgeschlagenen Routine komme ich auf 20, vielleicht 22 Takte,> die angegebenen 235 kHz bei C&Asm aber ergeben 34 - wo verschwindet die> Differenz?
Hast du daran gedacht?
1
Interrupt Response Time
2
3
The interrupt execution response for all the enabled AVR
4
interrupts is five clock cycles minimum. After five clock cycles
5
the program vector address for the actual interrupt han- dling
6
routine is executed. During these five clock cycle period, the
7
Program Counter is pushed onto the Stack. The vector is normally a
8
jump to the interrupt routine, and this jump takes three clock
9
cycles. If an interrupt occurs during execution of a multi-cycle
10
instruction, this instruction is completed before the interrupt is
Jörg Wunsch schrieb:
> Hast du daran gedacht? ...
Rechnen Sie selbst: die Routine incl. reti benötigt 14 Takte; plus die
von Ihnen zitierten 8 (obwohl ein rjmp reichen würde, also 7) macht
22. Meinetwegen plus 2 für 'interrupt of a multi-cycle instruction',
sind aber keine 34.
Peter Dannegger schrieb:> S. Landolt schrieb:>> und Peter Dannegger die Motten kriegt:>> Dem GCC ist es vollkommen schnurz, ob Du inlinest oder es lesbar in eine> *.S schreibst.
Ich find's ja selbst potthässlich, habe aber keine Ahnung von c und weiß
folglich nichts besseres.
Eigentlich wäre es ja im ureigensten Interesse von Veit Devil, seine
Sache voranzutreiben, was habe ich als Assemblerprogrammierer damit zu
tun.
Mich ärgert nur etwas, dass in diesem geschätzten Forum auf eine simple
Softwarefrage Vorschläge kommen wie: andere IDE, anderer Prozessor oder
gleich ganz andere Hardware.
S. Landolt schrieb:> habe aber keine Ahnung von c und weiß> folglich nichts besseres.
Der GCC weiß automatisch, daß *.c C ist und *.S Assembler.
Einfach nur zum Build hinzufügen oder ins Makefile, dann machts der GCC
schon richtig.
S. Landolt schrieb:> Mich ärgert nur etwas, dass in diesem geschätzten Forum auf eine simple> Softwarefrage Vorschläge kommen wie: andere IDE, anderer Prozessor oder> gleich ganz andere Hardware.
Naja, die Softwarefrage ist ja lange geklärt. Nur er glaubt das nicht.
Das ist ihm ja nicht schnell genug.
Hallo,
die Softwarefrage ist aus meiner Sicht noch nicht abschließend geklärt.
Da sich jeder mit anderen Dingen in dem Thread beschäftigt.
Ich möchte doch erstmal nichts weiter wissen, ob eine weitere
Taktsteigerung möglich ist. Ob mir das dann reicht ist eine ganz andere
Frage die hier nichts zu suchen hat. Anders formuliert. Ich möchte
nichts weiter wie das Maximum mit dem Inline Assembler rausholen. Auf
das push und pop werde ich nicht verzichten können, weil ich jetzt noch
nicht weis was ich später in das C Programm noch so einbaue.
Das andere Interrupts das Timing stören ist mir bewußt. Nur habe ich
nicht gesagt das ich andere ISRs verwenden möchte.
Ich habe das heute nochmal in aller Ruhe durchgemessen. Bitte jetzt
keinen Aufschrei. Mir lies das keine Ruhe was S.Landolt mit dem Takten
vorrechnete. Ich kann jetzt nicht mehr sagen was mich auf ca. 235kHz
limitierte. Scheinbar beim rumtesten Mist gemacht. Entschuldigung.
Jedenfalls sind es jetzt 308kHz. Mit dem neuen Code im Interrupt Handler
sind es 364kHz.
S.Landolt, erstmal Tausend Dank dafür.
Sind wir damit mit OCR1A 21 beim Minimum angekommen? Wenn das mit dem
Takten gleichzusetzen ist.
Falls ja, kann ich jetzt den Code so verwenden und in C weiter machen
ohne auf Registersicherungen Rücksicht zu nehmen?
@ Jörg. Das mit dem Interrupt Response Time gilt doch aber für C und
Assembler gleichermaßen?
1
#define F_CPU 16000000UL
2
#include<avr/io.h>
3
#include<avr/interrupt.h>
4
#define NOP __asm__ __volatile__ ("nop\n\t")
5
6
#define INCRMNT 0x20 // Toggle Bit 7,6,5 im ISR Handler
Hallo,
wenn ich auf das vermeintliche
"reti" "\n\t"
verzichte, dann taktet es nicht mehr sauber. Dann takten alle Pins
gleich und mit einem kürzerem Taktverhältnis. Das muß also rein, laut
meiner Meinung.
Veit D. schrieb:> Sind wir damit mit OCR1A 21 beim Minimum angekommen? Wenn das mit dem> Takten gleichzusetzen ist.
So ziemlich. Das ist die Zeit in der ISR. Plus... irgendwas.
Ablauf:
Start ISR -> Abarbeitung ISR -> Rückkehr aus ISR -> Versuch!!! der
Abarbeitung des (im Moment noch nicht erstellten) Hauptprogrammes, denn
-> der nächste Interrupt schlägt sofort zu.
(Deshalb die Hinweise auf andere Hardware.)
Okay, die 364 kHz stimmen, das sind 22 Takte. Schneller geht es mit dem
ATmega2560 nicht (ein 1284 benötigt 2 Takte weniger, er könnte auch 20
statt 16 MHz).
Wenn im GCC eine Arbeitsregisterreservierung möglich wäre, ließen sich
noch 4 Takte sparen. Im aktuellen Stand müssen Sie daran denken, dass
GPIOR1 und GPIOR2 reserviert sind.
Veit D. schrieb:> wenn ich auf das vermeintliche> "reti" "\n\t"> verzichte, dann taktet es nicht mehr sauber.
ISR_NAKED verlangt, dass Du es angeben mußt.
S. Landolt schrieb:> Wenn im GCC eine Arbeitsregisterreservierung möglich wäre, ließen sich> noch 4 Takte sparen.
Ist möglich, Randbedingungen s. o.
Aber wie schon geschrieben, selbst beim AVR würde man sich einen
Gefallen tun mit einem Xmega, der den ganzen Salat in DMA abwickeln
kann (so man die Wertetabelle im RAM ablegt, aber das sollte kein
großes Problem sein). Damit braucht man den ganzen blöden Overhead
für die Interruptbehandlung nicht mehr.
Jörg W. schrieb:> S. Landolt schrieb:>> Wenn im GCC eine Arbeitsregisterreservierung möglich wäre, ließen sich>> noch 4 Takte sparen.>> Ist möglich, Randbedingungen s. o.>> Aber wie schon geschrieben, selbst beim AVR würde man sich einen> Gefallen tun mit einem Xmega,
Einen Gefallen tun?
Das geht gar nicht anders. Der µC ist jetzt den ganzen Tag nur im
Interrupt.
Ich bin mir nicht sicher, ob ich jetzt auf dem Schlauch stehe, aber nach
meiner Erinnerung macht der Atmega 16 Mips. Und damit rechne ich :
16Mhz / 364khz = 43,95 Zyklen.
Verrechne ich mich oder läuft der Atmega des TO auf 8Mhz?
Gruß, Stefan
P.S.:
Um zu sehen, wieviel Zeit main neben der ISR noch bleibt, kann man in
main in einer Endlosschleife einen Pin toggeln lassen. Dann sieht man im
Oszi relativ genau, wieviel Zeit die ISR braucht und wieviel der main
noch übrig bleibt.
Stefan K. schrieb:> Atmega 16 Mips
Wenn er nur zwei Zahlen in Registern addieren soll: ja. ;-)
In der Realität hat er aber genügend Befehle, die länger als einen Takt
brauchen.
Hallo,
ich habe einen Logic Analyzer dranhängen. Kann damit sofort nach dem
flashen schauen ob sich der Takt ändert.
Mit ASM Listung meinst du sicherlich das .lss File. Das finde ich leider
nirgends. Im Debug/Release Ordner sehe keine .lss
In welchen Fällen könnte ich in C über GPIOR1/2 stolpern? Wie ich
herausgefunden habe sind die GPIORx freie schnellere Register. Wenn ich
in C weiter programmiere, mit Variablennamen usw., kümmert sich dann der
Compiler das nichts durcheinander kommt?
@Ralf.G. habe es verstanden und weis jetzt welches Problem auf mich
zukommt. Die while könnte extrem langsam werden je näher ich den
maximalen Takt einstelle. Theoretisch, vielleicht auch praktisch, käme
die while zum erliegen, wenn ich OCR1A auf minimum oder drunter setze.
Das testen und lernen und den Umgang mit dem Timer bringt mir jedoch
sehr viel. Hatte bisher immer Angst vor Timern, weil ich es nicht
verstanden hatte.
@Jörg:
Schon klar, was ich meine, bei 22 Takten und 16Mhz komme ich auf über
700khz mögliche ISR-Rate. Und das ist auch der Wert, an den ich mich für
Handmade-ISRs erinnern kann - wenn ich auch zugeben muß (nein, darf),
daß meine Atmega-Programmierzeit (zugunsten von ARM) schon eine Weile
her ist.
Viele Grüße, Stefan
@Veit:
Das kann nicht passieren, da der mc nach einer ISR Minimum einen Befehl
ausführt, siehe Atmega48A .... Manual Seite 15:
"When the AVR exits from an interrupt, it will always return to the main
program and execute one more instruction before any pending interrupt is
served."
Im Extremfall läuft also Dein main mit einer Frequenz die der ISR
entspricht.
Gruß, Stefan
Veit D. schrieb:> Hallo,>> in die while(1) soll nochwas rein. Displayausgabe, Taster- und> Potiabfrage und Berechnung zum Timer neu einstellen für die> Wunschfrequenz und Form. Da der Takt aber von einem ISR gemacht wird,> spielt doch der Inhalt der while keine Rolle in meinen Augen. Wird eben> öfters unterbrochen.>> Damit zur Frage. Ja ich möchte eine Frequenztabelle hinterlegen. Ich> möchte Rechteck, Dreieck/Rampe und Sinus erzeugen. Für den Sinus brauche> ich einen hohen Grundtakt, damit der durch die "Stützstellen" nicht> gnadenlos einbricht. Ich denke zur Zeit an ein 8Bit R2R Netzwerk als> DAC. Wie gesagt, ich möchte erstmal versuchen was mit nur einem µC so> möglich ist. Vielleicht reicht es mir aus, vielleicht auch nicht. Werde> ich dann sehen.
ich lese seit einer Weile mit einem halben Auge mit und frage mich die
ganze Zeit wie man dem Manne helfen könnte.
Zunächst: wenn's ein kommerzieller Job gewesen wäre, hätte ich einen
STM32 genommen. Billigere Entwicklung, billigere Stückkosten.
Wäre die Originalaufgabe auf dem AVR zu lösen, würde ich das komplett in
Maschinensprache lösen.
Aber auch eine Mischlösung C/ASM ist denkbar, wenn man die
Aufgabenstellung entsprechend anpasst:
Angenommen, das Benutzerinterface ist nicht voll verfügbar, während der
Generator läuft. Dann gehört im Stillstand die Kiste dem GCC und während
der Ausgabe dem ASM. Nur im Betriebszustandswechsel sind dann
Sicherungsarbeiten notwendig.
Will man nun bei ISR bleiben, könnten wir schon die 500kHz schaffen.
Richtig interessant wird das Ganze aber, wenn der Generator als Schleife
mit abgezählten Taktzyklen läuft. Damit knacken wir locker 1
Megasample/Sekunde.
Vor zwölf Jahren war das das Konzept hinter der Videoausgabe im MAHPONG.
A. K. schrieb:> Jörg W. schrieb:>> Wenn er nur zwei Zahlen in Registern addieren soll: ja. ;-)>> Einen Pin ein Weilchen auf f/2 toggeln geht auch.
Aber nur ein kleines Weilchen ;-) Bis zum Ende des Flashs.
@S. Landolt:
Der TO möchte aber kein Rechteck ausgeben, sondern eine variable
Wellenform:
>Damit zur Frage. Ja ich möchte eine Frequenztabelle hinterlegen. Ich>möchte Rechteck, Dreieck/Rampe und Sinus erzeugen. Für den Sinus brauche>ich einen hohen Grundtakt, damit der durch die "Stützstellen" nicht>gnadenlos einbricht. Ich denke zur Zeit an ein 8Bit R2R Netzwerk als>DAC.
Und dafür gibt man i.d.R. die Samplefrequenz an.
Was mich auch schon die ganze Zeit verwundert. Denn eigendlich legt man
für diese Aufgabe die Kurvenform im RAM oder Flash ab, und greift in der
ISR über einen Ptr auf diese zu. Davon sehe ich im Programm bisher aber
noch nichts. Sollen die Kurven in einem externen Eeprom gehalten werden
mit PORTC als Adresseingang?
Im Augenblick liefert ein DAC an PORTC nur eine Sägezahnkurve.
Gruß, Stefan
Hallo,
so ähnlich hat sich das bei mir auch durch den Thread
herauskristallisiert. Die Displayausgabe, die Potiabfrage usw. muß ja
nur aktiv sein, wenn ich irgendwas an der Frequenz ändern möchte. Wenn
die eingestellt ist, gibt es keinen Grund das Display zu aktualisieren
usw.
Ich würde also vielleicht nur noch einen weiteren ISR für einen Taster
verwenden um damit dem Programm zu sagen, stopp, jetzt wird was
geändert, stell das neu ein und mache dann erst weiter. Und wenn ich den
"Set-Taster" nicht drücke, dann dürfte der 2. ISR auch nicht stören.
Bevor ich hier jedoch weitermache, wollte ich das Grundgerüst mit dem
Takt abgearbeitet haben.
Jetzt frage ich mich natürlich, wir du auf 500kHz geschweige denn mehr
kommen möchtest. Ich denke der Taktgen. mit dem Timer1 und vorbelegten
OCR1A läuft schon als eigenständige Schleife.
Stefan K. schrieb:> Im Augenblick liefert ein DAC an PORTC nur eine Sägezahnkurve.
… die noch dazu im Flash in Form vorgegebener Additionsbefehle
„hinterlegt“ ist.
Eine benutzbare DDS würde ich mir irgendwie anders aufgebaut
vorstellen. ;-)
Hallo,
an den 16MHz hatte ich nicht gezweifelt. Nur wurde nach dem Takt
gefragt. :-)
Von der Tabelle siehst du auch noch nichts. Es kommt auch darauf an
wieviel Stützstellen ich verwende. Je mehr umso geringer mein
Ausgangstakt vom Sinus meinetwegen. Das ist aber ein ganz anderes
Problem und läßt uns abschweifen.
Hallo,
@ Jörg und andere. Ihr müßt nicht immer darauf hinweisen wie sinnlos das
ist und das es andere Hardware dafür gibt usw. Ich möchte basteln und
probieren was so möglich ist. Könnt ihr das nicht verstehen???
Veit D. schrieb:> Das ist aber ein ganz anderes Problem und läßt uns abschweifen.
Es ändert allerdings das Timing deiner ISR, insofern ist es eben
kein anderes Problem.
Hallo,
das ist mir doch schon längst bewußt, deswegen möchte ich ja den max.
Takt so hoch wie möglich treiben damit am Ende soviel wie möglich davon
übrig bleibt.
@Veit:
Du musst unterscheiden zwischen:
Samplefrequenz
--------------
das ist die Frequenz, mit der Dein ISR im Moment läuft. Und wenn ich das
richtig sehe, dann sind das 2 * 364khz = 728khz. Deine ISR wird also
728.000 mal in der s aufgerufen,
und der
Rechteckfrequenz
----------------
die Du maximal an PORTC, lt. Deinem Programm oben an PORTC.5 messen
kannst. Die ist die Hälfte der Samplefrequenz.
>Ich würde also vielleicht nur noch einen weiteren ISR für einen Taster>verwenden um damit dem Programm zu sagen, stopp, jetzt wird was>geändert, stell das neu ein und mache dann erst weiter. Und wenn ich den>"Set-Taster" nicht drücke, dann dürfte der 2. ISR auch nicht stören.>Bevor ich hier jedoch weitermache, wollte ich das Grundgerüst mit dem>Takt abgearbeitet haben.
Damit wirst Du Probleme bekommen. Ein 2.ISR neben dem besprochenen
zerstört Dein Timing. Tasten fragt man laut Lehrbuch (siehe Danegger:
Tastenentprellung) auch in einem Timer-ISR ab und nicht direkt
angeschlossen an einen Interrupt-Pin.
Was ggf. geht: main() als Endlosschleife und darin -ohne ISR- die Tasten
abfragen.
Das geht alles, insgesamt ist das aber längst nicht mehr
anfängerkompatibel.
Gruß, Stefan
>Von der Tabelle siehst du auch noch nichts. ... Das ist aber ein ganz >anderes
Problem und läßt uns abschweifen.
Ich hab mich auch schon gefragt. In der asm-ISR wird doch nur ein Port
getoggelt und krampfhaft versucht, so schnell wie möglich zu sein.
>>...235kHz>>...308kHz>>...364kHz
Oh... Warte mal ab, wenn die Tabellenindizierung dazukommt...
>Von der Tabelle siehst du auch noch nichts. Es kommt auch darauf an>wieviel Stützstellen ich verwende. Je mehr umso geringer mein>Ausgangstakt vom Sinus meinetwegen. Das ist aber ein ganz anderes>Problem und läßt uns abschweifen.
Möchtest Du die Tabelle im Flash oder im RAM ablegen?
Veit D. schrieb:> das ist mir doch schon längst bewußt, deswegen möchte ich ja den max.> Takt so hoch wie möglich treiben damit am Ende soviel wie möglich davon> übrig bleibt.
Wie Du noch einmal vier Takte sparen kannst, wurde ja schon beschrieben.
Auf der anderen Seite kann man einen AVR auch übertakten. Der 1. Versuch
mit 20 MHz brächte 25 % und, da dies funktionieren wird, kann man den 2.
Versuch mit 24 MHz angehen.
Nur Mut ;-)
Ach so. Wegen des kürzen Sprungbefehls, wäre ein ATmega48 noch einen
Tick schneller. Dafür würde sogar eine Schnupper-Krücke voll ausreichen
;-)
Statt immer weiter an einem unfertigen Programm zu optimieren, sollte
das Projekt erstmal auf die Füße gestellt werden, in dieser Reihenfolge:
* Spezifikation
* Implementation
* Optimierung
Spezifikation
-------------
* Welche Kurven sollen ausgegeben werden?
* Welche Samplingfrequenz ist minimal gefordert?
* Welche Samplingfrequenz ist gewünscht?
* Welcher Jitter ist zulässig?
* Welche Frequenzauflösung ist gefordert / gewünscht?
* Soll das Gerät gleichzeitig bedient werden,
während Kurven ausgegeben werden?
* Wie soll das Bedienkonzept aussehen?
Implementation
--------------
Programmieren des Gesamtkonzepts
Optimierung
-----------
Erst wenn das Gesamtkonzept steht, wird mit der Optimierung begonnen und
solange durchgezogen, bis mindestens die Minimalanforderungen oder
besser die "Möchte"-Anforderungen erfüllt sind.
Das hat folgende Vorteile:
Das Projekt wird immer besser. Auch wenn ohne Optimierung am Anfang nur
50khz Samplerate erreicht wird: es werden bereits reale Kurven
ausgegeben. Später in der Optimierungsphase können die erreichten Werte
nur besser werden.
Die bisherige Methode, einen Teilaspekt zu optimieren, ist für den
Programmierer nur frustrierend:
* Durch die fehlende Spec werden viele Aspekte nicht berücksichtigt,
die für das Projekt aber wichtig sind.
* Die anfängliche Optimierung wird durch "vergessene"
Implementierungsdetails zwangsläufig schlechter: der Einbau
eines Pointers auf die Kurvensamples und desssen
Incrementierung / Überlauferkennung wird das Ergebnis zwangsläufig
verschlechtern.
Gruß, Stefan
Hallo,
ich bin kein beruflicher Programmierer wie vielleicht viele von hier.
Ich habe den µC als reines Hobby entdeckt und gehe hier und da
vielleicht etwas planlos ran. Okay. Meine letztes Projekt, ein
Datalogger hat 2 Jahre gedauert. Macht ihr vielleicht an einem
Wochenende.
Bevor ich aber so richtig mit programmieren anfange für das Projekt,
wollte ich die Möglichkeiten abstecken. Aus meiner Sichtweise. Ich
beschäftige mich vorher gern mit tausend Teilaufgaben zum Verständnis um
danach an das große Ganze heranzugehen. Hatte mich schon beim Datalogger
fast verstrickt.
Die genauen Spezifikationen muß ich mir noch überlegen. Bei Sinus dachte
ich an so 50kHz. Falls das überhaupt noch machbar ist. Dann hoffentlich
wenigstens 25kHz. Was ein Jitter ist weis ich zwar. Einen für mich
festzulegen kann ich jedoch nicht. Hauptsache es funktioniert und die
Kurven sehen gut aus.
Nochmal wegen dem 2. ISR und Timing Zerstörung. Vielleicht reden wir
aneinander vorbei?
Ich habe das jetzt so verstanden. Beim Rücksprung aus dem ISR, macht der
µC noch paar Takte lang was im Hautpropgramm bis er wieder in den ISR
gezwungen wird. Um das zu reduzieren, wollte ich eine Art Flag im 2. ISR
setzen, sodass er nur im Hauptprogramm was machen muß wenn ich eine
andere Frequenz einstellen möchte usw. Ansonsten soll der ja nur die
gewünschte Frequenz ausgeben. Solange ich den Taster am ISR nicht
drücke, solange stört er den Timer ISR ja nicht.
Wenn ich die Tasterabfrage ohne ISR in while mache, dann könnte ich zwar
live Einstellungen ändern, was mich aber Takt kosten wird. Denke ich.
Deswegen würde ich schon in die Richtung gehen, dass ich in einer Art 2
Programme hin und her wechsel mit dem Taster. Entweder ich stelle was
ein oder der µC gibt mir ein Signal aus.
Stefan K. schrieb:>> Möchtest Du die Tabelle im Flash oder im RAM ablegen?
Wenn es schnell sein soll muß die Tabelle ins RAM. Ob die 8kB für Code
und Tabelle ausreichen werden weis ich noch nicht. Woher auch.
Veit D. schrieb:> Wenn ich die Tasterabfrage ohne ISR in while mache, dann könnte ich zwar> live Einstellungen ändern, was mich aber Takt kosten wird. Denke ich.
Eine 'Stopp-Taste' für die Frequenzausgabe. Die schaltet den Interrupt
ab und verzweigt in einen anderen Teil der 'while(1)'-Schleife. Jetzt
können bequem Einstellungen vorgenommen werden, gerne auch über
Interrupts (die natürlich seperat eingeschaltet werden müssen). 'Bei
fertig': Stopp-Taste' drücken und Anfangszustand für Frequenzausgabe
wieder einstellen.
Veit D. schrieb:> Bevor ich aber so richtig mit programmieren anfange für das Projekt,> wollte ich die Möglichkeiten abstecken.
Das ist als Designstudie sicher OK, aber man sollte dann schon ein
wenig an das rankommen, was man später tatsächlich machen will,
in diesem Fall also wenigstens eine (feste, erst einmal) Tabelle
abklappern.
> Die genauen Spezifikationen muß ich mir noch überlegen. Bei Sinus dachte> ich an so 50kHz. Falls das überhaupt noch machbar ist.
Jesper Hansen hat's wohl auf maximal 200 … 300 kHz gebracht in seinem
Mini-DDS. Die originale Seite existiert nicht mehr, aber hier habe
ich eine Kopie davon gefunden:
http://www.radanpro.com/Radan2400/mikrokontroleri/Jesper%27s%20AVR%20pages%20-%20MiniDDS.htm
(Leider zeigen die Links darin auf die damalige Originalseite, gehen
also jetzt auch nicht mehr.)
Das war mit einem ATtiny2313, getaktet von einem 11,06-MHz-Quarz,
halt komplett in Assembler gezimmert (war ja original noch für einen
AT90S1200 mit gerade 1 KiB Flash).
Dann solltest du deine 50 kHz allemal schaffen. ;-)
> Hauptsache es funktioniert und die> Kurven sehen gut aus.
Geringfügig solltest du dich noch mit der Theorie eines DDS und mit
der Aufgabe des Rekonstruktionsfilters beschäftigen. (Ja, Jesper hat
da auch keins dran.) Das ist nämlich das, was bei höheren Frequenzen
(und damit nur noch wenigen Stützstellen) dafür sorgt, dass die Kurven
gut aussehen.
> Ich habe das jetzt so verstanden. Beim Rücksprung aus dem ISR, macht der> µC noch paar Takte lang was im Hautpropgramm
Er macht mindestens einen Befehl aus dem unterbrochenen
(Haupt-)Programm.
> Wenn ich die Tasterabfrage ohne ISR in while mache, dann könnte ich zwar> live Einstellungen ändern, was mich aber Takt kosten wird. Denke ich.
Nein. Du sollst im Hauptprogramm nur abfragen, ob der Taster überhaupt
gedrückt ist, statt dass du diesem noch einen weiteren Interrupt
aufdrängelst. (Prellende Taster an einem Interrupt sind ohnehin Mist.)
Wenn er gedrückt ist, klemmst du die Erzeugung der Waveform ab, und
dann hast du die CPU-Leistung für die Bedienung. Mit dem Einschalten
der Waveform-Erzeugung wieder rennst du nur noch in die (enge) Schleife,
die den Taster abfragt.
Wenn du das strikt durchziehst, kannst du übrigens auch problemlos
in deiner Timer-ISR die Register R2 bis R15 vollmüllen. Die einfache
Tasterabfrage im main() wird diese Register nicht benötigen, die
werden erst dann gebraucht, wenn du komplizierter gestrickte
Bibliotheksfunktionen (wie eben printf()) aufrufst.
Veit D. schrieb:> Bevor ich aber so richtig mit programmieren anfange für das Projekt,> wollte ich die Möglichkeiten abstecken. Aus meiner Sichtweise. Ich> beschäftige mich vorher gern mit tausend Teilaufgaben zum Verständnis um> danach an das große Ganze heranzugehen.
Machen große Firmen ähnlich - in der Vorentwicklung.
Aber irgendwann lohnt es dann nicht mehr, noch tiefer einzusteigen. Wie
hier schon geschrieben wurde, sind weitere Schritte erst möglich, wenn
das große Ganze bekannt ist.
Eine zweite ISR aufzumachen, wird das gesamte Timing stören, das Du bis
dahin heraus gekitzelt hast.
Du solltest Deine Aufgabe dahin gehend betrachten, dass Deine 3xx kHz
ISR die zentrale "Taktquelle" für Dein gesamtes Projekt darstellt.
Die Tastenentprellung kannst Du locker in der while(1) machen, wenn
Du interne Register des AVR zur Signalisierung benutzt.
Als Beispiel: Gesetzt der Fall, Du würdest den UART nicht benötigen,
dann kannst Du eines der UART-Register, (bin jetzt zu faul zum
nachschauen) das R/W-fähig ist, in der ISR setzen und in der main
auswerten/löschen. Das kostet Dich nur einen Takt in der ISR. Hat aber
den Charme, dass Du für die main einen "Timer" hast.
Diese Vorgehensweise, d.h. die Verwendung von Registern ungenutzter
Peripherie, empfiehlt Atmel ausdrücklich, um die Performance zu
steigern.
Ich nutze diese Vorgehensweise immer wenn es möglich.
Habe vor kurzer Zeit einen Drehzahlmesser mit 4x 7-Segment mit einem
2313 in C realisiert, bei dem ich nur I/O Register als "Variablen"
verwendet habe.
Betrachte Dein Projekt einmal unter diesem Aspekt.
Viel Erfolg!
Hallo Veit,
>ich bin kein beruflicher Programmierer wie vielleicht viele von hier.>Ich habe den µC als reines Hobby entdeckt und gehe hier und da>vielleicht etwas planlos ran.
Das ist ja kein Problem ;-)
Was ich rüberbringen wollte: auch als Hobby-Programmierer ist es
sinnvoll, sich vorab über ein paar Eckdaten Gedanken zu machen. Wenn man
später bessere Werte hinkriegt: Super, das ist ein Erfolgserlebnis.
Nichts ist schlimmer, als wenn der Chef sagt: "Es soll halt möglichst
genau messen". Das kannst Du nämlich nie erreichen. Und das gilt auch,
wenn Du Dein eigener Chef bist.
Warum ich so genau nach den o.g. Werten frage: je nach diesen Vorgaben
ändert sich Deine ISR-Routine:
Du kannst z.B. die aktuelle Kurve in ein Array fester Länge kopieren und
vom ISR ausgeben. Das hat den Vorteil, daß die ISR einfach bleibt (wegen
der festen Array-Länge). Hier gibt es auch einige
Optimierungsmöglichkeiten: z.B. Arraygröße 2 hoch n. Aber den Nachteil,
daß Du die Frequenz des Signals nur schlecht ändern kannst.
Oder Du benutzt ein Array mit variabler Länge. Dann wird Deine ISR
komplizierter (Du musst den Array-Pointer mit einem variablen Wert
vergleichen, das dauert länger + braucht ev. mehr Register). Dafür ist
die Frequenz viel variabler.
>Nochmal wegen dem 2. ISR und Timing Zerstörung. Vielleicht reden wir>aneinander vorbei?>....>Um das zu reduzieren, wollte ich eine Art Flag im 2. ISR>setzen, sodass er nur im Hauptprogramm was machen muß wenn ich eine>andere Frequenz einstellen möchte usw.
Dafür brauchst Du gar keinen ISR. Du benutzt einen Timer, der Dir alle
10ms das Timer-Interrupt-Flag setzt. Statt damit eine ISR zu triggern,
kannst Du dieses Flag auch in main abfragen, darauf reagieren und es
wieder zurücksetzen. Du machst dann praktisch das, was andere im ISR
machen, in einem Teil der mainloop.
>Wenn ich die Tasterabfrage ohne ISR in while mache, dann könnte ich zwar>live Einstellungen ändern, was mich aber Takt kosten wird. Denke ich.
Nein, das kostet Dich nichts. Selbst wenn die main wesentlich langsamer
läuft: der Anwender wird das kaum merken, wenn es einigermassen
effizient programmiert ist.
Happy programming , Stefan
Hallo,
okay, Danke für die wertvollen Hinweise. Dann werde ich mich mal weiter
mit DDS und Tabelle erstellen beschäftigen und meine Wünsche abstecken
und ein Grundgerüst erstellen. Ich weis ja wo ich Euche finde. :-)
S. Landolt schrieb:
> Wenn im GCC eine Arbeitsregisterreservierung möglich wäre, ließen sich> noch 4 Takte sparen.
Die ist doch möglich. Und um Konflikte zu entdecken, würde ich einfach:
- Übersetzen ohne Bindung von Variablen an Register.
- Im .lss File nach den fraglichen Registern suchen.
- Falls nicht gefunden -> Juhu! V. an R. binden und (vorerst) gut.
- Sonst Assembler-Kenntnisse auspacken, denn es kann ja sein, daß Regs
verwendet werden, aber die Lib diese sichert.
Laut Jörg, und der sollte es wissen, werden die niedrigen Regs ab R2
von fast keiner Lib benutzt.
Das ist natürlich nicht vollautomatisch, aber eingestreuter
Assembler-Code muß auch nicht jede Änderung mitmachen.
Ich würde das aber erst gegen Ende machen, wenn eigentlich alles geht,
aber die ISR noch beschleunigt werden muß, und damit muß man nicht
ständig .lss Files durchsuchen.
BTW, wenn man Lib's als Templates in .h Files hat, in "mehr C", dann
wird alles mit den gebundenen Registern übersetzt und macht automatisch
um diese einen großen Bogen. Falls mal jemand konkrete Vorteile der
"noch höheren" Sprache braucht.
Hallo,
hab jetzt die .lss :-)
Kann ich den ersten Teil mit den bad interrupts weglassen?
Zur Vollständigkeit. Für alle die den Code sehen wollten. Das ist die
Variante mit den 308kHz.
1
Sections:
2
IdxNameSizeVMALMAFileoffAlgn
3
0.text000001580000000000000000000000542**1
4
CONTENTS,ALLOC,LOAD,READONLY,CODE
5
1.comment000000300000000000000000000001ac2**0
6
CONTENTS,READONLY
7
8
Disassemblyofsection.text:
9
10
00000000<__vectors>:
11
0:71c0rjmp.+226;0xe4<__ctors_end>
12
2:0000nop
13
…
14
42:0000nop
15
44:78c0rjmp.+240;0x136<__vector_17>
16
46:0000nop
17
…
18
19
000000fa<_Z10set_Timer1v>:
20
fa:f894cli
21
fc:10928000sts0x0080,r1
22
100:e1e8ldir30,0x81;129
23
102:f0e0ldir31,0x00;0
24
104:1082stZ,r1
25
106:afe6ldir26,0x6F;111
26
108:b0e0ldir27,0x00;0
27
10a:1c92stX,r1
28
10c:10928500sts0x0085,r1
29
110:10928400sts0x0084,r1
30
114:89e1ldir24,0x19;25
31
116:90e0ldir25,0x00;0
32
118:90938900sts0x0089,r25
33
11c:80938800sts0x0088,r24
34
120:8081ldr24,Z
35
122:8860orir24,0x08;8
36
124:8083stZ,r24
37
126:8081ldr24,Z
38
128:8160orir24,0x01;1
39
12a:8083stZ,r24
40
12c:8c91ldr24,X
41
12e:8260orir24,0x02;2
42
130:8c93stX,r24
43
132:7894sei
44
134:0895ret
45
46
00000136<__vector_17>:
47
136:0f93pushr16
48
138:0fb7inr16,0x3f;63
49
13a:0f93pushr16
50
13c:08b1inr16,0x08;8
51
13e:0052subir16,0x20;32
52
140:08b9out0x08,r16;8
53
142:0f91popr16
54
144:0fbfout0x3f,r16;63
55
146:0f91popr16
56
148:1895reti
57
58
0000014a<main>:
59
14a:8fefldir24,0xFF;255
60
14c:87b9out0x07,r24;7
61
14e:18b8out0x08,r1;8
62
150:d4dfrcall.-88;0xfa<_Z10set_Timer1v>
63
152:ffcfrjmp.-2;0x152<main+0x8>
64
65
00000154<_exit>:
66
154:f894cli
67
68
00000156<__stop_program>:
69
156:ffcfrjmp.-2;0x156<__stop_program>
und hier die mit 364kHz
1
Sections:
2
IdxNameSizeVMALMAFileoffAlgn
3
0.text000001580000000000000000000000542**1
4
CONTENTS,ALLOC,LOAD,READONLY,CODE
5
1.comment000000300000000000000000000001ac2**0
6
CONTENTS,READONLY
7
8
Disassemblyofsection.text:
9
10
00000000<__vectors>:
11
0:71c0rjmp.+226;0xe4<__ctors_end>
12
2:0000nop
13
…
14
44:78c0rjmp.+240;0x136<__vector_17>
15
46:0000nop
16
…
17
000000fa<_Z10set_Timer1v>:
18
fa:f894cli
19
fc:10928000sts0x0080,r1
20
100:e1e8ldir30,0x81;129
21
102:f0e0ldir31,0x00;0
22
104:1082stZ,r1
23
106:afe6ldir26,0x6F;111
24
108:b0e0ldir27,0x00;0
25
10a:1c92stX,r1
26
10c:10928500sts0x0085,r1
27
110:10928400sts0x0084,r1
28
114:85e1ldir24,0x15;21
29
116:90e0ldir25,0x00;0
30
118:90938900sts0x0089,r25
31
11c:80938800sts0x0088,r24
32
120:8081ldr24,Z
33
122:8860orir24,0x08;8
34
124:8083stZ,r24
35
126:8081ldr24,Z
36
128:8160orir24,0x01;1
37
12a:8083stZ,r24
38
12c:8c91ldr24,X
39
12e:8260orir24,0x02;2
40
130:8c93stX,r24
41
132:7894sei
42
134:0895ret
43
44
00000136<__vector_17>:
45
136:0abdout0x2a,r16;42
46
138:0fb7inr16,0x3f;63
47
13a:0bbdout0x2b,r16;43
48
13c:08b1inr16,0x08;8
49
13e:0052subir16,0x20;32
50
140:08b9out0x08,r16;8
51
142:0bb5inr16,0x2b;43
52
144:0fbfout0x3f,r16;63
53
146:0ab5inr16,0x2a;42
54
148:1895reti
55
56
0000014a<main>:
57
14a:8fefldir24,0xFF;255
58
14c:87b9out0x07,r24;7
59
14e:18b8out0x08,r1;8
60
150:d4dfrcall.-88;0xfa<_Z10set_Timer1v>
61
152:ffcfrjmp.-2;0x152<main+0x8>
62
63
00000154<_exit>:
64
154:f894cli
65
66
00000156<__stop_program>:
67
156:ffcfrjmp.-2;0x156<__stop_program>
und was der Compiler aus reinem C macht, der Code von ganz oben, wobei
hier eine Variablenübergabe drin. Kann man jetzt nicht ganz 1:1
vergleichen, nehme ich an.
Veit D. schrieb:> Kann ich den ersten Teil mit den bad interrupts weglassen?
Können kann man alles, aber es bringt dir nichts, wenn du nun
ausgerechnet an der Vektortabelle was sparen willst. Du hast
schließlich endlos Platz in deinem ATmega2560, und die nicht
benutzten ISRs (die alle auf den vordefinierten Handler
__bad_interrupt springen) kosten dich keinen müden CPU-Takt.
Hallo,
Missverständnis!
Ich wollte nicht den Code im Code weglassen, sondern den Teil hier im
Forumsbeitrag Zwecks besserer Übersicht, wenn das niemanden stört.
Jörg W. schrieb:> (Leider zeigen die Links darin auf die damalige Originalseite, gehen> also jetzt auch nicht mehr.)
Na, dann geh doch auf https://archive.org/ und wirf die URL in den
Suchschlitz. Ist doch alles noch da!
Mal 'ne Frage am Rande: Wäre es ohne Interrupt nicht schneller? In einer
Schleife auf das Interrupt-Flag warten, Abbruch-Flag prüfen.
Bedienvorgang löst Interrupt aus und setzt Abbruch-Flag.
Veit D. schrieb:> Ich wollte nicht den Code im Code weglassen, sondern den Teil hier im> Forumsbeitrag
Dann solltest du einen Texteditor deiner Wahl benutzen oder einfach
nur ein paar relevante Dinge copy&pasten. ;-)
Konrad S. schrieb:> Na, dann geh doch auf https://archive.org/ und wirf die URL in den> Suchschlitz. Ist doch alles noch da!
Nein, das Internet-Archiv speichert leider auch keine Zip-Archive
oder Assembler-Quelltexte etc.
> Mal 'ne Frage am Rande: Wäre es ohne Interrupt nicht schneller?
Hätte zumindest mehr Jitter.
Jörg W. schrieb:> Veit D. schrieb:>> Ich wollte nicht den Code im Code weglassen, sondern den Teil hier im>> Forumsbeitrag>> Dann solltest du einen Texteditor deiner Wahl benutzen oder einfach> nur ein paar relevante Dinge copy&pasten. ;-)
ach Jörg, Du nimmst dich aber auch gänz schön wichtig. Ich weis doch auf
den ersten Blick nicht was vom Code für Euch wichtig ist und was nicht.
Deswegen habe ich erstmal alles kopiert. Mit dem Gedanken es könnte
vielleicht uninteressant sein. Ist das jetzt so schlimm? Ich denke
nicht. Nach 1h kann ich nun nichts mehr löschen.
Hallo,
okay, alles klar. Könntest Du die bad interrupt Zeilen als Mod löschen?
Wegen der C++ Frage. Ehrlich gesagt bin ich mir immer unsicher was ich
auswählen muß wenn ich ein neues Projket in AS anlege.
GCC C Executable
GCC C Static Library
GCC C++ Executable
GCC C++ Static Library
Eine gute Erklärung auf deutsch hatte ich noch nicht gefunden.
Vielleicht kann das jemand kurz erklären? Danke.
Veit D. schrieb:> okay, alles klar. Könntest Du die bad interrupt Zeilen als Mod löschen?
Erledigt.
> Wegen der C++ Frage. Ehrlich gesagt bin ich mir immer unsicher was ich> auswählen muß wenn ich ein neues Projket in AS anlege.>> GCC C Executable> GCC C Static Library> GCC C++ Executable> GCC C++ Static Library
Nun, wenn du ein "C++ Executable" anlegst, dann heißt das, dass du in
C++ programmieren willst.
Kann man natürlich machen, hat aber eigentlich keinen großen Sinn,
wenn man dann doch bloß reines C schreibt.
Wenn diese .lss jeweils (ohne Startupcode) den gesamten Code-Umfang
darstellen, dann kannst du problemlos R2..R15 an globale
Register-Variablen binden (oder umgekehrt je nach Sichtweise).
Damit kann man in der ISR SREG in ein Register sichern und alle Daten,
die ausgegeben werden sollen in Andere.