Forum: Mikrocontroller und Digitale Elektronik Wie Assembler ISR in C integrieren ?


von Veit D. (devil-elec)


Lesenswert?

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
7
#define OUTPORT PORTC
8
9
void set_Timer1(void);  // Funktion deklarieren
10
void set_Timer1()       // CTC Mode
11
{   
12
  cli();  //stop interrupts
13
14
  // set Timer-1 Register
15
  TCCR1A = 0;      // Reset TCCR1A Register 
16
  TCCR1B = 0;      // Reset TCCR1B Register
17
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
18
  TCNT1  = 0;      // initialize counter value to 0
19
      
20
  OCR1A =  150;    // Compare Match Register A, ca. 53kHz
21
  
22
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
23
  
24
  TCCR1B |= (1 << CS10);    // set Prescaler 1
25
  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_t tmp = OUTPORT;
33
  tmp += INCRMNT;
34
  OUTPORT = tmp;
35
}
36
37
38
int main(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
1
.org OC1Aaddr
2
; c-haters optimierte Routine
3
  in     isr_SREG,SREG
4
5
  in     isr_tmp,outPORT
6
  subi   isr_tmp,incrmnt       ; synchr. pos. Flanke
7
  out    outPORT,isr_tmp
8
9
  out    SREG,isr_SREG
10
  reti

von Ralf G. (ralg)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> der Assembler Einbau an sich hat im anderen Thread funktioniert
Dann zeigen Sie uns bitte das so entstandene Programm.

von Georg G. (df2au)


Lesenswert?

Veit D. schrieb:
> .org OC1Aaddr

Wenn das die Adresse des Interrupt Vektors in der Vektor-Tabelle ist, 
solltest du Risiken und Nebenwirkungen bedenken.

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Ich würde trotzdem gerne das Programm sehen.

von S. Landolt (Gast)


Lesenswert?

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.

von Pandur S. (jetztnicht)


Lesenswert?

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.

: Bearbeitet durch User
von Georg G. (df2au)


Lesenswert?

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.

von Amateur (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
7
#define OUTPORT PORTC
8
9
 
10
void set_Timer1(void);  // Funktion deklarieren
11
void set_Timer1()       // CTC Mode
12
{
13
  cli();  //stop interrupts
14
15
  // set Timer-1 Register
16
  TCCR1A = 0;      // Reset TCCR1A Register
17
  TCCR1B = 0;      // Reset TCCR1B Register
18
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
19
  TCNT1  = 0;      // initialize counter value to 0
20
  
21
  OCR1A =  50;     // Compare Match Register A
22
  
23
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
24
  
25
  TCCR1B |= (1 << CS10);    // set Prescaler 1
26
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
27
  
28
  sei();   //allow interrupts
29
  
30
}  // end Funktion
31
32
33
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
34
  asm volatile(
35
  "push   r16"    "\n\t"
36
  "in     r16,%[sreg]"  "\n\t"
37
  "push   r16"    "\n\t"
38
  "in     r16,%[port]"  "\n\t"
39
  "subi   r16,0x20"  "\n\t"
40
  "out    %[port],r16"  "\n\t"
41
  "pop    r16"    "\n\t"
42
  "out    %[sreg],r16"  "\n\t"
43
  "pop    r16"    "\n\t"
44
  "reti"    "\n\t"
45
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
46
  [port] "I" (_SFR_IO_ADDR(PORTC)));
47
}
48
49
50
int main(void)  {
51
  DDRC = 0xFF;   // alles Ausgänge
52
  PORTC = 0;     // alles aus
53
  
54
  set_Timer1();
55
  
56
  while(1)  {
57
58
  }
59
  
60
}   // Ende main()

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Bei Inline Assembler krieg ich immer Augenkrebs.

Hier mal ein Beispiel für C + Assembler:

Beitrag "AVR: Fast-PWM (BAM) 12 Bit für 8 Kanäle"

von Stefan K. (stefan64)


Lesenswert?

>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

von Ralf G. (ralg)


Lesenswert?

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
  asm volatile(
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.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von Veit D. (devil-elec)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Matthias L. (Gast)


Lesenswert?

>aber genügend Derivate.

ebenso fertige DDS-Chips...

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.)

von Stefan K. (stefan64)


Lesenswert?

>ohne daß es Konflikte mit LIBs oder anderen Routinen gibt.

Genauer: ohne daß es Konflikte mit IAR-Libs gibt.

Gruß, Stefan

von m.n. (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.)

von m.n. (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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?

von Steffen R. (steffen_rose)


Lesenswert?

Es wurde zumindest gesagt, das das Retten der Register bei Nutzung von 
C+ASM einen Großteil der Takte in Anspruch nimmt.

von S. Landolt (Gast)


Lesenswert?

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:
1
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
2
  asm volatile(
3
  "out    %[gp1],r16"    "\n\t"
4
  "in     r16,%[sreg]"   "\n\t"
5
  "out    %[gp2],r16"    "\n\t"
6
  "in     r16,%[port]"   "\n\t"
7
  "subi   r16,0x20"      "\n\t"
8
  "out    %[port],r16"   "\n\t"
9
  "in     r16,%[gp2]"    "\n\t"
10
  "out    %[sreg],r16"   "\n\t"
11
  "in     r16,%[gp1]"    "\n\t"
12
  "reti"    "\n\t"
13
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
14
    [gp1]  "I" (_SFR_IO_ADDR(GPIOR1),
15
    [gp2]  "I" (_SFR_IO_ADDR(GPIOR2),
16
    [port] "I" (_SFR_IO_ADDR(PORTC)));

von m.n. (Gast)


Lesenswert?

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"

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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).

von Steffen R. (steffen_rose)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
11
    served. […]

von S. Landolt (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
7
#define OUTPORT PORTC
8
9
void set_Timer1(void);  // Funktion deklarieren
10
void set_Timer1()       // CTC Mode
11
{
12
  cli();  //stop interrupts
13
14
  // set Timer-1 Register
15
  TCCR1A = 0;      // Reset TCCR1A Register
16
  TCCR1B = 0;      // Reset TCCR1B Register
17
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
18
  TCNT1  = 0;      // initialize counter value to 0
19
  
20
  OCR1A =  25;     // Compare Match Register A (min. 25 = 308kHz)
21
  
22
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
23
  
24
  TCCR1B |= (1 << CS10);    // set Prescaler 1
25
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
26
  
27
  sei();   //allow interrupts
28
  
29
}  // end Funktion
30
31
32
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
33
  asm volatile(
34
  "push   r16"    "\n\t"
35
  "in     r16,%[sreg]"  "\n\t"
36
  "push   r16"    "\n\t"
37
  "in     r16,%[port]"  "\n\t"
38
  "subi   r16,0x20"  "\n\t"
39
  "out    %[port],r16"  "\n\t"
40
  "pop    r16"    "\n\t"
41
  "out    %[sreg],r16"  "\n\t"
42
  "pop    r16"    "\n\t"
43
  "reti"    "\n\t"
44
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
45
  [port] "I" (_SFR_IO_ADDR(PORTC)));
46
}
47
48
49
int main(void)  {
50
  DDRC = 0xFF;   // alles Ausgänge
51
  PORTC = 0;     // alles aus
52
  
53
  set_Timer1();
54
  
55
  while(1)  {
56
57
  }
58
  
59
}   // Ende main()


und mit dem neuen Code komme ich auf 364kHz.
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
void set_Timer1(void);  // Funktion deklarieren
7
void set_Timer1()       // CTC Mode
8
{
9
  cli();  //stop interrupts
10
11
  // set Timer-1 Register
12
  TCCR1A = 0;      // Reset TCCR1A Register
13
  TCCR1B = 0;      // Reset TCCR1B Register
14
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
15
  TCNT1  = 0;      // initialize counter value to 0
16
  
17
  OCR1A =  21;     // Compare Match Register A (min. 21 = 364kHz)
18
  
19
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
20
  
21
  TCCR1B |= (1 << CS10);    // set Prescaler 1
22
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
23
  
24
  sei();   //allow interrupts
25
  
26
}  // end Funktion
27
28
29
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
30
  asm volatile(
31
  "out    %[gp1],r16"    "\n\t"
32
  "in     r16,%[sreg]"   "\n\t"
33
  "out    %[gp2],r16"    "\n\t"
34
  "in     r16,%[port]"   "\n\t"
35
  "subi   r16,0x20"      "\n\t"
36
  "out    %[port],r16"   "\n\t"
37
  "in     r16,%[gp2]"    "\n\t"
38
  "out    %[sreg],r16"   "\n\t"
39
  "in     r16,%[gp1]"    "\n\t"
40
  "reti"    "\n\t"
41
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
42
  [gp1]  "I" (_SFR_IO_ADDR(GPIOR1)),
43
  [gp2]  "I" (_SFR_IO_ADDR(GPIOR2)),
44
  [port] "I" (_SFR_IO_ADDR(PORTC)));
45
}
46
47
48
int main(void)  {
49
  DDRC = 0xFF;   // alles Ausgänge
50
  PORTC = 0;     // alles aus
51
  
52
  set_Timer1();
53
  
54
  while(1)  {
55
56
  }
57
  
58
}   // Ende main()

von Veit D. (devil-elec)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.)

von Ralf G. (ralg)


Lesenswert?

Veit D. schrieb:
> Das muß also rein,

Genau. [ Wie man leicht am ASM-Listing sieht ;-) ]

von Stefan K. (stefan64)


Lesenswert?

Wie misst Du die Zeit, die Du über
OCR1A =  21;
minimal einstellen kannst?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Veit D. schrieb:
> wenn ich auf das vermeintliche "reti"    "\n\t"
> verzichte, dann taktet es nicht mehr sauber.

Ja, hast recht, da hatte ich mich geirrt.

von S. Landolt (Gast)


Lesenswert?

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.

von Steffen R. (steffen_rose)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Stefan K. (stefan64)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Stefan K. (stefan64)


Lesenswert?

@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

von Stefan K. (stefan64)


Lesenswert?

@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

von S. Landolt (Gast)


Lesenswert?

Für eine Frequenz müssen Sie hin- und herschalten, benötigen also für 
einen Wellenzug zwei ISR-Durchläufe.

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

der µC läuft mit 16MHz.

von (prx) A. K. (prx)


Lesenswert?

Jörg W. schrieb:
> Wenn er nur zwei Zahlen in Registern addieren soll: ja. ;-)

Einen Pin ein Weilchen auf f/2 toggeln geht auch.

von Cyblord -. (cyblord)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

Veit Devil schrieb:
> der µC läuft mit 16MHz.
Haben Sie je daran gezweifelt?

von Stefan K. (stefan64)


Lesenswert?

@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

von Veit D. (devil-elec)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. ;-)

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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???

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Stefan K. (stefan64)


Lesenswert?

@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 Matthias L. (Gast)


Lesenswert?

>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 Stefan K. (stefan64)


Lesenswert?

>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?

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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 
;-)

von Stefan K. (stefan64)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Steffen R. (steffen_rose)


Lesenswert?

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.

von OldMan (Gast)


Lesenswert?

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!

von Stefan K. (stefan64)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.  :-)

von Carl D. (jcw2)


Lesenswert?

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.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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
Idx Name          Size      VMA       LMA       File off  Algn
3
  0 .text         00000158  00000000  00000000  00000054  2**1
4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
5
  1 .comment      00000030  00000000  00000000  000001ac  2**0
6
                  CONTENTS, READONLY
7
8
Disassembly of section .text:
9
10
00000000 <__vectors>:
11
   0:  71 c0         rjmp  .+226      ; 0xe4 <__ctors_end>
12
   2:  00 00         nop
13

14
  42:  00 00         nop
15
  44:  78 c0         rjmp  .+240      ; 0x136 <__vector_17>
16
  46:  00 00         nop
17

18
19
000000fa <_Z10set_Timer1v>:
20
  fa:  f8 94         cli
21
  fc:  10 92 80 00   sts  0x0080, r1
22
 100:  e1 e8         ldi  r30, 0x81  ; 129
23
 102:  f0 e0         ldi  r31, 0x00  ; 0
24
 104:  10 82         st  Z, r1
25
 106:  af e6         ldi  r26, 0x6F  ; 111
26
 108:  b0 e0         ldi  r27, 0x00  ; 0
27
 10a:  1c 92         st  X, r1
28
 10c:  10 92 85 00   sts  0x0085, r1
29
 110:  10 92 84 00   sts  0x0084, r1
30
 114:  89 e1         ldi  r24, 0x19  ; 25
31
 116:  90 e0         ldi  r25, 0x00  ; 0
32
 118:  90 93 89 00   sts  0x0089, r25
33
 11c:  80 93 88 00   sts  0x0088, r24
34
 120:  80 81         ld  r24, Z
35
 122:  88 60         ori  r24, 0x08  ; 8
36
 124:  80 83         st  Z, r24
37
 126:  80 81         ld  r24, Z
38
 128:  81 60         ori  r24, 0x01  ; 1
39
 12a:  80 83         st  Z, r24
40
 12c:  8c 91         ld  r24, X
41
 12e:  82 60         ori  r24, 0x02  ; 2
42
 130:  8c 93         st  X, r24
43
 132:  78 94         sei
44
 134:  08 95         ret
45
46
00000136 <__vector_17>:
47
 136:  0f 93         push  r16
48
 138:  0f b7         in  r16, 0x3f  ; 63
49
 13a:  0f 93         push  r16
50
 13c:  08 b1         in  r16, 0x08  ; 8
51
 13e:  00 52         subi  r16, 0x20  ; 32
52
 140:  08 b9         out  0x08, r16  ; 8
53
 142:  0f 91         pop  r16
54
 144:  0f bf         out  0x3f, r16  ; 63
55
 146:  0f 91         pop  r16
56
 148:  18 95         reti
57
58
0000014a <main>:
59
 14a:  8f ef         ldi  r24, 0xFF  ; 255
60
 14c:  87 b9         out  0x07, r24  ; 7
61
 14e:  18 b8         out  0x08, r1  ; 8
62
 150:  d4 df         rcall  .-88       ; 0xfa <_Z10set_Timer1v>
63
 152:  ff cf         rjmp  .-2        ; 0x152 <main+0x8>
64
65
00000154 <_exit>:
66
 154:  f8 94         cli
67
68
00000156 <__stop_program>:
69
 156:  ff cf         rjmp  .-2        ; 0x156 <__stop_program>


und hier die mit 364kHz
1
Sections:
2
Idx Name          Size      VMA       LMA       File off  Algn
3
  0 .text         00000158  00000000  00000000  00000054  2**1
4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
5
  1 .comment      00000030  00000000  00000000  000001ac  2**0
6
                  CONTENTS, READONLY
7
8
Disassembly of section .text:
9
10
00000000 <__vectors>:
11
   0:  71 c0         rjmp  .+226      ; 0xe4 <__ctors_end>
12
   2:  00 00         nop
13

14
  44:  78 c0         rjmp  .+240      ; 0x136 <__vector_17>
15
  46:  00 00         nop
16

17
000000fa <_Z10set_Timer1v>:
18
  fa:  f8 94         cli
19
  fc:  10 92 80 00   sts  0x0080, r1
20
 100:  e1 e8         ldi  r30, 0x81  ; 129
21
 102:  f0 e0         ldi  r31, 0x00  ; 0
22
 104:  10 82         st  Z, r1
23
 106:  af e6         ldi  r26, 0x6F  ; 111
24
 108:  b0 e0         ldi  r27, 0x00  ; 0
25
 10a:  1c 92         st  X, r1
26
 10c:  10 92 85 00   sts  0x0085, r1
27
 110:  10 92 84 00   sts  0x0084, r1
28
 114:  85 e1         ldi  r24, 0x15  ; 21
29
 116:  90 e0         ldi  r25, 0x00  ; 0
30
 118:  90 93 89 00   sts  0x0089, r25
31
 11c:  80 93 88 00   sts  0x0088, r24
32
 120:  80 81         ld  r24, Z
33
 122:  88 60         ori  r24, 0x08  ; 8
34
 124:  80 83         st  Z, r24
35
 126:  80 81         ld  r24, Z
36
 128:  81 60         ori  r24, 0x01  ; 1
37
 12a:  80 83         st  Z, r24
38
 12c:  8c 91         ld  r24, X
39
 12e:  82 60         ori  r24, 0x02  ; 2
40
 130:  8c 93         st  X, r24
41
 132:  78 94         sei
42
 134:  08 95         ret
43
44
00000136 <__vector_17>:
45
 136:  0a bd         out  0x2a, r16  ; 42
46
 138:  0f b7         in  r16, 0x3f  ; 63
47
 13a:  0b bd         out  0x2b, r16  ; 43
48
 13c:  08 b1         in  r16, 0x08  ; 8
49
 13e:  00 52         subi  r16, 0x20  ; 32
50
 140:  08 b9         out  0x08, r16  ; 8
51
 142:  0b b5         in  r16, 0x2b  ; 43
52
 144:  0f bf         out  0x3f, r16  ; 63
53
 146:  0a b5         in  r16, 0x2a  ; 42
54
 148:  18 95         reti
55
56
0000014a <main>:
57
 14a:  8f ef         ldi  r24, 0xFF  ; 255
58
 14c:  87 b9         out  0x07, r24  ; 7
59
 14e:  18 b8         out  0x08, r1  ; 8
60
 150:  d4 df         rcall  .-88       ; 0xfa <_Z10set_Timer1v>
61
 152:  ff cf         rjmp  .-2        ; 0x152 <main+0x8>
62
63
00000154 <_exit>:
64
 154:  f8 94         cli
65
66
00000156 <__stop_program>:
67
 156:  ff cf         rjmp  .-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.
1
Sections:
2
Idx Name          Size      VMA       LMA       File off  Algn
3
  0 .text         00000162  00000000  00000000  00000054  2**1
4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
5
  1 .comment      00000030  00000000  00000000  000001b6  2**0
6
                  CONTENTS, READONLY
7
8
Disassembly of section .text:
9
10
00000000 <__vectors>:
11
   0:  71 c0         rjmp  .+226      ; 0xe4 <__ctors_end>
12
   2:  00 00         nop
13

14
000000fa <_Z10set_Timer1v>:
15
  fa:  f8 94         cli
16
  fc:  10 92 80 00   sts  0x0080, r1
17
 100:  e1 e8         ldi  r30, 0x81  ; 129
18
 102:  f0 e0         ldi  r31, 0x00  ; 0
19
 104:  10 82         st  Z, r1
20
 106:  af e6         ldi  r26, 0x6F  ; 111
21
 108:  b0 e0         ldi  r27, 0x00  ; 0
22
 10a:  1c 92         st  X, r1
23
 10c:  10 92 85 00   sts  0x0085, r1
24
 110:  10 92 84 00   sts  0x0084, r1
25
 114:  86 e9         ldi  r24, 0x96  ; 150
26
 116:  90 e0         ldi  r25, 0x00  ; 0
27
 118:  90 93 89 00   sts  0x0089, r25
28
 11c:  80 93 88 00   sts  0x0088, r24
29
 120:  80 81         ld  r24, Z
30
 122:  88 60         ori  r24, 0x08  ; 8
31
 124:  80 83         st  Z, r24
32
 126:  80 81         ld  r24, Z
33
 128:  81 60         ori  r24, 0x01  ; 1
34
 12a:  80 83         st  Z, r24
35
 12c:  8c 91         ld  r24, X
36
 12e:  82 60         ori  r24, 0x02  ; 2
37
 130:  8c 93         st  X, r24
38
 132:  78 94         sei
39
 134:  08 95         ret
40
41
00000136 <__vector_17>:
42
 136:  1f 92         push  r1
43
 138:  0f 92         push  r0
44
 13a:  0f b6         in  r0, 0x3f  ; 63
45
 13c:  0f 92         push  r0
46
 13e:  11 24         eor  r1, r1
47
 140:  8f 93         push  r24
48
 142:  88 b1         in  r24, 0x08  ; 8
49
 144:  8f 5f         subi  r24, 0xFF  ; 255
50
 146:  88 b9         out  0x08, r24  ; 8
51
 148:  8f 91         pop  r24
52
 14a:  0f 90         pop  r0
53
 14c:  0f be         out  0x3f, r0  ; 63
54
 14e:  0f 90         pop  r0
55
 150:  1f 90         pop  r1
56
 152:  18 95         reti
57
58
00000154 <main>:
59
 154:  8f ef         ldi  r24, 0xFF  ; 255
60
 156:  87 b9         out  0x07, r24  ; 7
61
 158:  18 b8         out  0x08, r1  ; 8
62
 15a:  cf df         rcall  .-98       ; 0xfa <_Z10set_Timer1v>
63
 15c:  ff cf         rjmp  .-2        ; 0x15c <main+0x8>
64
65
0000015e <_exit>:
66
 15e:  f8 94         cli
67
68
00000160 <__stop_program>:
69
 160:  ff cf         rjmp  .-2        ; 0x160 <__stop_program>

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Konrad S. (maybee)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von Konrad S. (maybee)


Lesenswert?


von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Konrad S. schrieb:
> Jörg W. schrieb:
>> Nein, das Internet-Archiv speichert leider auch keine Zip-Archive
>> oder Assembler-Quelltexte etc.
>
> 
https://web.archive.org/web/20131019151406/http://www.myplace.nu/avr/minidds/minidds_schematic.gif
>
> 
https://web.archive.org/web/20150609051846/http://www.myplace.nu/avr/minidds/minidds.asm
>
> 
https://web.archive.org/web/20150609112448/http://www.myplace.nu/avr/minidds/ddscontrol.zip

Danke.  Irgendwie war es mir nicht gelungen, das aufzuspüren.

Veit D. schrieb:
> ach Jörg, Du nimmst dich aber auch gänz schön wichtig.

Nö, ich hatte den Eindruck, dass du eine Frage gestellt hast, und
wollte dir darauf antworten.

> Ich weis doch auf
> den ersten Blick nicht was vom Code für Euch wichtig ist und was nicht.

OK, wenn's wiedermal so passt: eine komplette .lss-Datei am besten
als Anhang posten.  Ansonsten sind Dinge, die sowieso aus der
Bibliothek kommen (Vektor-Tabelle, Startup-Code, Bibliotheksroutinen,
sofern vorhanden) eher weniger interessant.  Im wesentlichen wäre
die Implementierung der ISR von Interesse, und da siehst du selbst
mit einem ungeübten Blick, dass der Compiler halt aufgrund seiner
Formalismen einen Haufen Dinge macht, die du eigentlich gar nicht
brauchen würdest (bspw. R1 auf 0 setzen).

Btw.:

_Z10set_Timer1v

=> Du baust das alles als C++?

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.