Forum: Mikrocontroller und Digitale Elektronik AVR Timer 1 Normal/CTC Mode ?


von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

AVR Mega2560
ich verzweifel so langsam am Timer 1.
Er läuft im Normalmode und soll alle 3 Kanäle am Toggle Pin schalten.
Die takten eigentlich auch verschieden, nur das rücksetzen klappt nicht.
Und dann schalten die zwischendurch nochmal um. Warum weis ich nicht. 
Siehe Screenshot. Die Beschriftung ist das was ich denke, ob das stimmt 
weis ich nicht. Nur bei der ersten Taktung je Kanal bin ich mir sicher, 
weil das rechnerisch hinkommt. Was an den anderen Flanken passiert ... 
keine Ahnung.

Mein Verständnis nach, zählt TCNT1 munter hoch, wenn das mit einem OCR1x 
übereinstimmt toggelt der dazugehörige Pin. Wenn ich nun den Compare ISR 
vom langsamsten Takt nehme und darin das TCNT1 einfach erhöhe um 
zeitiger einen Overlow zu manipulieren, sollte sich das TCNT1 damit 
zeitiger auf 0 setzen und meine Toggle Pins wieder beim nächsten Compare 
toggeln.

Irgendwie toggelt es aber nicht wie gewünscht.
Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3 
verschiedene Takte zu erzeugen.

Wo liegt mein Fehler?
1
  cli();  //stop interrupts
2
3
  // set Timer-1 Register
4
  TCCR1A = 0;      // Reset TCCR1A Register 
5
  TCCR1B = 0;      // Reset TCCR1B Register
6
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
7
  TCNT1  = 0;      // initialize counter value to 0
8
  
9
  TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match 
10
  TCCR1A  |= (1 << COM1B0);  // set Toggle OCnB Pin on compare match    
11
  TCCR1A  |= (1 << COM1C0);  // set Toggle OCnC Pin on compare match 
12
  
13
  OCR1A =   99;    // Compare Match Register A >> 10kHz (Precaler 8)
14
  OCR1B =  199;    // Compare Match Register B
15
  OCR1C =  299;    // Compare Match Register C
16
  
17
  TCCR1B |= (1 << CS11);    // set Prescaler 8
18
  TIMSK1 |= (1 << TOIE1);   // enable Timer1 Overflow Interrupt
19
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
20
  TIMSK1 |= (1 << OCIE1B);  // enable Timer Compare Interrupt  B
21
  TIMSK1 |= (1 << OCIE1C);  // enable Timer Compare Interrupt  C
22
23
  sei();   //allow interrupts
24
  
25
}  // end Funktion
26
27
28
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
29
  cli();
30
  TCNT1H = 0xFF;
31
  TCNT1L = 0xFF;
32
  sei();
33
}

: Bearbeitet durch User
von foo (Gast)


Lesenswert?

Veit D. schrieb:
> Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3
> verschiedene Takte zu erzeugen.

Geht nicht, kannst nur verschiedene Tastverhältnisse über PWM erzeugen, 
der Takt bleibt aber immer gleich.

>   TIMSK1 |= (1 << TOIE1);   // enable Timer1 Overflow Interrupt
>   TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
>   TIMSK1 |= (1 << OCIE1B);  // enable Timer Compare Interrupt  B

Warum funktioniert dein Programm überhaupt, ohne einen Interrupt Handler 
für den Overflow vom Timer und für Compare A/B?

>   TCNT1H = 0xFF;
>   TCNT1L = 0xFF;

Kann auch durch TCNT1 = 0xFFFF ersetzt werden

>   sei();

Würde ich in der ISR gar nicht machen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe viele Seiten gelesen, auch das Datenblatt und auch hier das AVR 
Timer Tutorial. Das vorweg.

Wofür braucht man einen extra Interrupt Handler? Ich denke das braucht 
man nur wenn man bei dem Ereignis noch irgendwas machen möchte außer der 
Reihe. Die Flags und Interrupts setzt der µC doch selbst wieder zurück 
im CTC Mode und hier möchte/muß ich das beim Compare.C. Oder nicht?

Und wegen dem TCNT1 Register. Laut dem Tutorial hier muß man das High 
und Low Byte getrennt beschreiben und dazu die Interrupts auschalten. 
Stimmt das nicht?

Und wegen meinen Takten. Ich kann doch aber den Takt nochmal immer 
halbieren von A zu B und B zu C.

Nur was macht der µC beim ersten und zweiten Fragezeichen im Screen?

: Bearbeitet durch User
von foo (Gast)


Lesenswert?

Veit D. schrieb:
> Wofür braucht man einen extra Interrupt Handler?

Nun. du hast den Compare Interrupt ja aktiviert. Also muss dafür auch 
eine ISR definiert sein. Ist dem nicht so, resettet der AVR sich, weil 
an dem Interrupthandler keine Adresse einer gültigen Funktion steht.

Ich glaube aber eher dass du nicht das komplette C-File gepastet hast... 
der Anfang fehlt irgendwie.

> Und wegen dem TCNT1 Register. Laut dem Tutorial hier muß man das High
> und Low Byte getrennt beschreiben

Macht der Compiler

> und dazu die Interrupts auschalten.

(Macht der Compiler nicht, aber...)

> Stimmt das nicht?

Du befindest dich zu dem Zeitpunkt in einem Interrupt Handler. Andere 
Interrupts sind da gesperrt bis zum Ende der ISR.
(Ausnahme: man aktiviert sie manuell wieder. Braucht man selten und muss 
man aufpassen bei. Hier wird es nicht benötigt)

von foo (Gast)


Lesenswert?

Bei welcher Frequenz läuft das ganze eigentlich?

Zumindestens von 392.26ms bis 393.34ms sieht das ganze für mich halbwegs 
logisch aus. Du hast ja nur COM1x0 gesetzt, d.h. von sich aus toggelt 
der AVR bei nem Match nur. Zurückgesetzt bei nem Overflow wird da bei 
deinen Einstellungen nichts.

Bei ca. 392.81ms dürfte ein zweiter Overflow stattfinden. Den siehst du 
natürlich im Diagramm nicht, weil da nichts passiert (der Timer ist eben 
nicht so konfiguriert, dass er beim Overflow OC1x löscht...)

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

das läuft mit Prescaler 8 mit 16MHz CPU Takt.
Kanal A von Timer 1 mit Compare Einstellung 99 entpricht 50µs.

So, hab den Code gleich nochmal geändert.
Jetzt habe ich folgendes siehe Screenshot 1.
Ohne   ISR(TIMER1_OVF_vect) { }
Was macht der hier noch für einen Toggle bei ca. 0,8ms und kurz danach 
auf Kanal B und dann Kana C? Dann wieder 3 kurze LOW Pulse? Verstehe ich 
noch nicht.

sieht das ähnlich wie im alten Screenshot 2.
Mit   ISR(TIMER1_OVF_vect) { }
taktet die wie sie wollen.

Du meinst die Interrupts sind noch falsch gesetzt?
Der Overflow Interrupt ist jedoch zusätzlich gesetzt.
1
/*
2
 Arduino Mega 2560
3
*/
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
#define NOP __asm__ __volatile__ ("nop\n\t")
8
9
void set_Timer1(void);  // Funktion deklarieren
10
void set_Timer1()       // Normal Mode, kein CTC
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
  TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match > Pin 11 Mega2560
21
  TCCR1A  |= (1 << COM1B0);  // set Toggle OCnB Pin on compare match > Pin 12 Mega2560
22
  TCCR1A  |= (1 << COM1C0);  // set Toggle OCnC Pin on compare match > Pin 13 Mega2560
23
  
24
  OCR1A =   99;    // Compare Match Register A >> 10kHz (Precaler 8)
25
  OCR1B =  199;    // Compare Match Register B
26
  OCR1C =  299;    // Compare Match Register C
27
  
28
  TCCR1B |= (1 << CS11);    // set Prescaler 8
29
  TIMSK1 |= (1 << TOIE1);   // enable Timer1 Overflow Interrupt
30
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
31
  TIMSK1 |= (1 << OCIE1B);  // enable Timer Compare Interrupt  B
32
  TIMSK1 |= (1 << OCIE1C);  // enable Timer Compare Interrupt  C
33
  sei();   //allow interrupts
34
  
35
}  // end Funktion
36
37
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
38
}
39
40
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
41
}
42
43
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
44
  TCNT1 = 65535;
45
}
46
47
ISR(TIMER1_OVF_vect) {  // Timer 1 Interrupt 
48
}
49
50
int main(void)  {
51
  DDRB = 0xFF;   // alles Ausgänge
52
53
  set_Timer1();
54
  
55
while(1)  {         
56
57
}
58
  
59
}   // Ende main()

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:

> Jetzt habe ich folgendes siehe Screenshot 1.
> Ohne   ISR(TIMER1_OVF_vect) { }

Was heisst 'ohne'.

Noch mal für dich zum mitmeisseln:

Wenn du einen Interrupt freigibst, und das tust du hier
1
  TIMSK1 |= (1 << TOIE1);   // enable Timer1 Overflow Interrupt
dann musst du auch eine entsprechende ISR im System haben.

Keine entsprechende ISR zu haben, ist ein FEHLER. Denn das gcc System 
ist so aufgesetzt, dass dieser Fall zu einem Programmneustart führt!

ALso:
Wenn du einen Interrupt brauchst, dann
* gibst du ihn frei
* schreibst eine zugehörige ISR

Wenn du auf einen spezifischen Interrupt nicht reagieren willst, dann
* gibst du ihn nicht frei
* und brauchst auch keine ISR dafür schreiben.


Es gibt nur diese beiden Möglichkeiten. Die Kombination
* Interrupt freigeben
* aber keine ISR dafür schreiben
ist so erst mal nicht vorgesehen. Macht auch keinen Sinn.

> Was macht der hier noch für einen Toggle bei ca. 0,8ms und kurz danach
> auf Kanal B und dann Kana C? Dann wieder 3 kurze LOW Pulse?

Den Screenshot kannst du vergessen. Ohne die zum Overflow gehörende ISR, 
sind da Programmneustarts drinnen, die man jetzt in diesem Licht 
analysieren müsste. Ist aber vergebene Liebesmüh, denn im Grunde 
interessiert dieser Fall nicht, weil du einen schweren Programmfehler 
machst, wenn du keine passende ISR für einen freigegebenen Interrupt 
hast.

Das andere Bild passt perfekt zum geposteten Programm. Genau dieses 
Ergebnis würde man erwarten.

: Bearbeitet durch User
von foo (Gast)


Lesenswert?

Komplett ohne ISR(TIMER1_OVF_vect) { } resettet der AVR sich bei jedem 
Overflow, weil du den Overflow Interrupt aktiviert, aber nicht mit Code 
versehen hast.

Das siehst du im ersten Screenshot an dem kurzen Low-Puls (eigentlich: 
Pin auf Input ohne Pullup). Während der Zeit ist der im Reset.

Was du im ersten Screenshot nicht siehst, ist der zweite Reset, der z.B. 
bei +0.75ms stattfindet. Da die Pins da sowieso low sind, fällts da 
nicht auf, dass die durch den Reset kurz low werden.


Der zweite Screenshot sieht genau so aus wie ich ihn erwarte. Von 
"Taktet wie sie wollen" kann da keine Rede sein :) Compare C lässt den 
Timer überlaufen, dadurch läuft er von vorne los.


Verwechselst du eventuell den Normal Mode (den hast du eingestellt) mit 
dem CTC Mode? Nur letzterer löscht dir beim Overflow automatisch OC1x. 
Dann sieht das ganze nämlich auch nach PWM aus.

Der CTC Mode verwendet allerdings immer OCR1A, d.h. du müsstest 299 (den 
höchsten Wert) da reinschreiben, ansonsten werden OCR1B/C nie erreicht.

von Karl H. (kbuchegg)


Lesenswert?

> Meine Idee zum ganzen ist mit einem Timer und seinen 3 Kanälen 3 verschiedene 
Takte zu erzeugen.


Denk an eine Uhr. Der Sekundenzeiger braucht 60 Sekunden um das 
Ziffernblatt einmal zu umrunden. Bringst du Kontakte bei den Sekunden 
15, 30 und 38 an, dann schalten die zwar zu unterschiedlichen Zeiten, 
aber der Sekundenzeiger braucht nach wie vor 60 Sekunden um einmal 
rundum zu kommen.

Genauso hier:
Du kriegst keine unterschiedlichen Frequenzen. Auf allen 3 Pins wirst du 
immer die gleichen Frequenzen haben. Lediglich in der Phase sind sie 
verschoben (d.h. die Pins gehen zu unterschiedlichen Zeitpunkten von 0 
auf 1 bzw. umgekehrt, aber der zeitliche Abstand zwischen den Flanken 
ist bei allen 3 Kanälen identisch).
Wenn du das willst, dann kannst du bei der Systematik bleiben. WEnn du 
aber unterschiedliche Frequenzen willst, dann hast du mit Zitronen 
gehandelt.

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


Lesenswert?

Hallo,

teilweise verstehe ich Eure Erklärungen, teilweise aber auch nicht.
Das mit den ISR's habe ich jetzt verstanden. Danke.

Nur das das 2. Diagramm so normal sein soll verstehe ich nicht.

Die takten alle mit der gleichen Frequenz 3,2kHz und alle mit einem 
Tastverhältnis von 50/50%. Dürfte laut meiner Logik nicht sein.

Start.
TCNT1 läuft bis 99 > Compare.A löst aus, Pin toggelt von 1 auf 0.
TCNT1 läuft weiter bis 199 > Compare.B löst aus, Pin toggelt von 1 auf 
0.
TCNT1 läuft weiter bis 299 > Compare.C löst aus, Pin toggelt von 1 auf 
0.

Beim Comp.C erhöhe ich künstlich den TCNT Wert. Overflow tritt ein und 
setzt alles zurück. Alles beginnt von vorn mit gemeinsamer Startflanke 0 
auf 1.
Macht es aber nicht. Irgendwo muß ich wohl einen Denkfehler haben.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:

> Start.
> TCNT1 läuft bis 99 > Compare.A löst aus, Pin toggelt von 1 auf 0.
> TCNT1 läuft weiter bis 199 > Compare.B löst aus, Pin toggelt von 1 auf
> 0.
> TCNT1 läuft weiter bis 299 > Compare.C löst aus, Pin toggelt von 1 auf
> 0.
>
> Beim Comp.C erhöhe ich künstlich den TCNT Wert. Overflow tritt ein und
> setzt alles zurück.

Was genau verstehst du unter 'alles'?

Die Pins werden nicht zurück gesetzt. Die hast du mittels COM Bits so 
eingestellt, dass sie vom Compare Match getoggelt werden. Und genau das 
machen sie auch. VOn eiem 0-Setzen bei Timer Overflow steht da nichts.

Du liest Dinge aus dem Datenblatt heraus, die da definitiv nicht drinnen 
stehen!

Der Timer läuft weiter, bis er bei 99 den Compare A erreicht. Pin wird 
von 0 auf 1 getoggelt.
läuft weiter bis 199, Compare B erreicht, Pin wird von 0 auf 1 getoggelt
läuft weiter bis 299, Compare C erreicht, Pin wird von 0 auf 1 getoggelt
Overflow forciert. Timer beginnt bei 0
99, COmpare A erreicht, Pin toggelt von 1 auf 0
199, Compare B erreicht, Pin toggelt von 1 auf 0
299, .... naja, so geht das jetzt immer weiter.

: Bearbeitet durch User
von foo (Gast)


Lesenswert?

Veit D. schrieb:
> Die takten alle mit der gleichen Frequenz 3,2kHz und alle mit einem
> Tastverhältnis von 50/50%. Dürfte laut meiner Logik nicht sein.

Das ist das Signal was du siehst. Der Timer läuft aber mit der doppelten 
Frequenz über.

> Alles beginnt von vorn mit gemeinsamer Startflanke 0
> auf 1.
> Macht es aber nicht.

Warum sollte es? Hast du nirgendwo so eingestellt.

Der Normal Mode setzt die Pins beim Overflow nicht auf 1 zurück.

Entweder du nimmst den CTC Mode (der macht das automatisch) oder du 
machst es von Hand zusammen mit dem Überlaufenlassen vom Timer (PORTB = 
.... nach TCNT1 = 0xffff)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dann habe ich echt die ganze Zeit was völlig fehl interpretiert. Danke 
für die Erklärung. Durch den mir unbekannten Reset dachte ich genau so 
soll es werden, ich bin nah dran.

Ich muß also dann beim Compare Interrupt selbst die Pins zurücksetzen. 
Jetzt verstehe ich auch warum meine Idee/Vorhaben so nicht funktionieren 
kann.

Ich müßte beim Compare.B den Pin von A manuell umschalten. Sodass "A" 
mit der doppelten Frequenz von B taktet. Und das gleiche beim Compare.C 
mit Pin "B". Dann habe ich zwar Taktdifferenzen drin, weil der ISR 
Aufruf auch Zeit kostet. Aber theoretisch müßte das so funktionieren? 
Dann brauche ich keinen künstlichen Overflow?

Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen 
Overflow ISR Handler schreiben?

von foo (Gast)


Lesenswert?

Pins A und B umschalten? Ich bin mir gerade nicht sicher was du vorhast. 
Wie sollen die drei Kurven aussehen? Was ist dein Ziel? Mal die Kurven 
mal auf

Veit D. schrieb:
> Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen
> Overflow ISR Handler schreiben?

Du musst gar nix wenn du nicht willst. Im Normal Mode nicht und auch in 
sonst keinem Modus. Falls das, was du vorhast, sich irgendwie mit den 
Flags vom Timer bewerkstelligen lässt ("Toggle ... on ..." etc) brauchst 
du auch gar keine Interrupts und kannst den Timer auch so laufen lassen.

Das einzige was du brauchst ist. Wenn du einen Interrupt aktiviert hast 
(das entsprechende Bit in TIMSK/... gesetzt hast) dann muss dafür auch 
eine ISR definiert werden, sonst gibts einen Reset wenn der Interrupt 
auslöst.

von Veit D. (devil-elec)


Lesenswert?

> Der Timer läuft weiter, bis er bei 99 den Compare A erreicht. Pin wird
> von 0 auf 1 getoggelt.
> läuft weiter bis 199, Compare B erreicht, Pin wird von 0 auf 1 getoggelt
> läuft weiter bis 299, Compare C erreicht, Pin wird von 0 auf 1 getoggelt
> Overflow forciert. Timer beginnt bei 0
> 99, COmpare A erreicht, Pin toggelt von 1 auf 0
> 199, Compare B erreicht, Pin toggelt von 1 auf 0
> 299, .... naja, so geht das jetzt immer weiter.

Hallo,

Moment nochmal bitte. Entweder bin ich wieder im alten falschen 
Denkmuster oder ... ?

Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind 
dann die Perioden alle gleich lang? Die sind nur verschoben.

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> Wegen den aktivierten Interrupts nochmal. Im CTC Modus muß ich keinen
> Overflow ISR Handler schreiben?

Welche ISR's du schreiben mußt, hängt davon ab, was du eigentlich 
erreichen willst. Im Idealfall garkeine. Generell gültig ist aber: Wenn 
du keine ISR für eine beliebige Interruptquelle schreibst, dann darfst 
du diese Interruptquelle auch nicht enablen. Das brauchst du auch nicht, 
denn auf die Funktion des Timers selber hat es überhaupt keinen 
Einfluß, ob eine der durch den Timer gefütterten Interruptquellen 
enabled ist oder nicht. Diesen simplen Sachverhalt hast du ganz 
offensichtlich nicht begriffen...

Da du auch sonst die Möglichkeiten und Grenzen eines Timers 
offensichtlich nicht begreifst, wäre vielleicht eine Annäherung aus der 
anderen Richtung sinnvoll: Du versuchst einfach mal aufzuschreiben (am 
besten in Kombination mit einer kleinen Grafik des gewünschten 
Signalverlaufs), was du eigentlich erreichen willst...


Soweit ich das übersehen kann, hast du das im Verlauf des gesamten 
bisherigen Threads nicht zu Stande gebracht. Und wenn man schon nicht 
genau formulieren kann, was man eigentlich erreichen will, dann ist 
jeder Versuch einer Implementierung des nicht durchdachten Scheißdrecks 
von vornherein zum Scheitern verurteilt. So einfach ist das.

von foo (Gast)


Angehängte Dateien:

Lesenswert?

Veit D. schrieb:
> Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind
> dann die Perioden alle gleich lang? Die sind nur verschoben.

Immer an den roten Linien findet ein Overflow statt.

Die Perioden sind gleich lang, weil sich der Wert von A immer 99 Takte 
nach jedem Overflow ändert. Da die Overflows 300 Takte 
auseinanderliegen, ändert sich A also alle 300 Takte.
Genauso wie B, nur dass das immer 199 Takte nach jedem Overflow 
stattfindet. Also auch alle 300 Takte, nur eben zu A verschoben...

von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:

> Wenn A/B/C nach verschiedenen Takten comparen und umschalten, warum sind
> dann die Perioden alle gleich lang? Die sind nur verschoben.

Weil ein Sekundenzeiger immer 60 Sekunden zum umrunden eines 
Zifferblattes braucht.

Hast du einen Kontakt, der schaltet, wenn der Sekundenzeiger bei 15 
angelangt ist, und hast du einen Kontakt der bei 38 schaltet, dann 
schalten sie zu verschiedenen Zeitpunkten während einer Umrundung. Aber 
von einem Schaltvorgang an einem Kontakt bis zum nächsten Schaltvorgang 
an demselben Kontakt dauert es immer 60 Sekunden. Nämlich genau so lang, 
wie der Sekundenzeiger zum Umrunden des Zifferblattes braucht.

D.h. deine beiden Kontakte schalten zwar während einer Zeigerumrundung 
zu verschiedenen Zeitpunkten, aber egal wo du den Kontakt anbringst, es 
dauert für jeden Kontakt IMMER 60 Sekunden, bis der Sekundenzeiger 
wieder bei ihm ist. Denn auch für die Sekundenmarkierung 38 muss der 
Sekundenzeiger einmal komplett um das Zifferblatt rum, bis er wieder bei 
der 38 angelangt ist. Und eine derartige Umrundung dauert nun mal 60 
Sekunden)

Jetzt klarer?

(Sekundenzeiger entspricht dem Timer Register TCNT1. Die Schaltkontakte 
entsprechen den OCR Compare Registern)

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


Lesenswert?

Hallo,

@ c-hater: es ging die ganze Zeit friedlich und konstruktiv ab im 
Thread, trotz meiner fatalen Missverständnisse zum Timer. Da mußt du 
jetzt nicht noch nachträglich frech werden wo fast alles geklärt ist. 
Das habe ich foo und Karl Heinz zu verdanken.

Die eingemalten Overflow Linien machen das Bild klarer. Ich mach mal 
eine Diagramm was ich vorhabe.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

also. vorgestellt habe ich mir das so wie im Bild. Mit 2 Takten wäre es 
sicherlich einfacher. Mit 3 müßte es nach neuen Erkenntnissen und 
aktuellen  Überlegungen dennoch möglich sein.

Start, TCNT bei 0 und zählt los ...

Compare.A > Pin.A geht automatisch von 0 auf 1 (leerer ISR)

Compare.B > Pin.B geht automatisch von 0 auf 1 und im ISR
            setzte ich Pin A manuell zurück auf 0
            und setzte das Compare Register neu.

Compare.A > Pin.A geht automatisch von 0 auf 1 (leerer ISR)

Overflow tritt ein > Pin.C geht automatisch von 0 auf 1 und im ISR setze 
ich manuell die Pins A und B zurück auf 0.

Soweit meine Theorie. Sollte doch funktionieren?

Man könnte das eigentlich auch im CTC Modus machen. Da müßte ich 
allerdings die Reihenfolge A/B/C umdrehen auf C/B/A, weil A den TCNT 
nullt. ALso A muß der langsamste Takt sein.

Meinungen?

Edit:
Wobei ich für A sicherlich kein 50/50 Tastverhältnis hinbekommen werde.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Wie wichtig ist es dir, dass der Timer selbst die Pins schaltet?

Im Moment hast du einen Mischmasch aus manuellem Pin Toggeln und 
timergestütztem Pin Toggeln.

Die Antwort auf die Frage hängt hauptsächlich davon ab, was sonst noch 
so auf dem AVR läuft, vor allen Dingen welche sonstige Interrupts noch 
im Spiel sind. Wenn sonst nichts auf dem AVR läuft, dann hat man eine 
kleine Unsicherheit, weil es davon abhängt welchen Befehl der AVR gerade 
ausführt, wenn ein Interrupt auftritt. D.h. die Flanken sind dann unter 
Umständen nicht ganz Taktgenau, die Frequenz würde aber stimmen.

Langer Rede kurzer Sinn: wenn es von den restlichen Umständen vertretbar 
ist, dann würde ich das ganz simpel mit einem CTC Modus machen, in 
dessen Compare Interrupt ich ein 3 Bit 'Wort' an 3 Pins ausgebe und dann 
das 3 Bit Wort um 1 erhöhe.

Man kann die ganze Sache auch rein in Hardware machen, in dem man für 
jeden Compare Match ein ISR nachschiebt, die den jeweils korrekten OCR 
Wert berechnet, wann der Compare das nächste mal auftreten soll. Der 
Timer toggelt dann nach wie vor die Pins beim Compare Match

(wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich 
gar nicht)
1
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
2
  OCR1A += 99;
3
}
4
5
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
6
  OCR1B += 199;
7
}
8
9
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
10
  OCR1C += 299;
11
}

fettich.

wie ich immer sage: wenn man am TCNT Register rumpfuschen muss, dann 
stimmt meistens was nicht.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Machbar.
Möglich sind auch ganzzahlige Verhältnisse, also z.B. A/B = 3/2, das 
Tastverhältnis sollte dabei auch etwa 50 % betragen können:
Den Grundtakt des Interrupts auf das kleinste gemeinsame Vielfache 
einstellen, und in der ISR für jeden Kanal einen Zähler laufen lassen, 
welcher bei Erreichen des gewünschten Wertes den Kanal umschaltet 
(Pin-toggle).
c-hater kennt sicher eine einfachere Lösung.

von foo (Gast)


Lesenswert?

Mir fällt da gerade spontan keine Lösung ein wie das ein einzelner Timer 
eigenständig hinbekommen könnte. Ohne Interrupthandler geht das hier 
vermutlich nicht. Mir fallen da gerade nur mäßig schöne Lösungen ein, 
die ich mal trotzdem hier lasse.

Idee 1 Lass den Timer mit dem für A gewünschten Takt laufen (CTC Mode, 
OCR1A passend einstellen). Lass einen Interrupt auslösen wenn er von 
vorne anfängt (Compare A oder overflow? Bin mir gerade nicht sicher.)

Da drin toggelst du Pin A.
Merk dir wie Pin B gerade steht.
Wenn Pin A jetzt 0 ist, dann toggelst du auch Pin B.
War B vorher 1 und ist jetzt 0, toggelst du auch Pin C.

Idee 2 lass den Timer von 0-65536 laufen, setze OCR1A = 16384, OCR1B = 
32768, OCR1C = 49152, schalte alle Compare Interrupts und den Overflow 
Interrupt an.
Pin A toggelst du in allen vier Interrupthandlern
Pin B nur in B und Overflow
Pin C nur bei Overflow

von Karl H. (kbuchegg)


Lesenswert?

Karl H. schrieb:

> fettich.

Nicht ganz.
Ich denke die additiven Konstanten müssten 100, 200 und 300 sein.
Die Initialwerte sind 99, 199 und 299. Aber addiert werden immer ganze 
Hunderter.

von Dieter F. (Gast)


Lesenswert?

Karl H. schrieb:
> (wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich
> gar nicht)

Läuft das dann nicht so langsam "auseinander" - durch die Zuweisungen 
und Prolog/Epilog der ISR's, die ja unterschiedlich oft durchlaufen 
werden?

von Karl H. (kbuchegg)


Lesenswert?

Äh. 400 nicht 300. (und 399 anstatt 299). Soll ja immer das Doppelte 
sein

ALso
1
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
2
  OCR1A += 100;
3
}
4
5
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
6
  OCR1B += 200;
7
}
8
9
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
10
  OCR1C += 400;
11
}
12
13
int main()
14
{
15
.....
16
17
   OCR1A = 99;
18
   OCR1B = 199;
19
   OCR1C = 399;
20
21
   ... Timer Setup. Normaler Modus. alle 3 Compare Match Interrupts
22
   ... Pin Toggle bei Match
23
24
   sei();
25
26
   while( 1 )
27
     ;
28
}

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Dieter F. schrieb:
> Karl H. schrieb:
>> (wenn ich mir das jetzt so überleg, so kompliziert ist das eigentlich
>> gar nicht)
>
> Läuft das dann nicht so langsam "auseinander" - durch die Zuweisungen
> und Prolog/Epilog der ISR's, die ja unterschiedlich oft durchlaufen
> werden?

DIeter.
Das solltest du aber schon wissen, dass es dem Timer Register völlig 
wurscht ist, was ich nebenher im µC mache. Das Timer Register zählt 
unabhängig davon einfach weiter.

Selbst wenn mal ein Interrupt ein wenig zu spät kommt (d.h. die ISR 
etwas zu spät aufgerufen wird), dann wird der nächste Interrupt trotzdem 
zeitgerecht ausgelöst.

In seinem Fall hat er 8 * 99 Takte Zeit, bis die ISR für den Compare 
Match A den neuen OCR Wert ins Register geschrieben haben muss. Das wird 
ja wohl doch reichen! Bei den anderen Compare Matches hat er noch viel 
länger Zeit.

: Bearbeitet durch User
von Dieter F. (Gast)


Lesenswert?

Karl H. schrieb:
> Das solltest du aber schon wissen, dass es dem Timer Register völlig
> wurscht ist, was ich nebenher im µC mache. Das Timer Register zählt
> unabhängig davon einfach weiter.

Ja, weiß ich :-)

Und 8*99 Takte reichen auch für 3 ISR ...

Du hast Recht, ich war auf dem Holzweg ....

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich war immer überzeugt das es funktionieren muß, nur mein Ansatz und 
Verständnis zum Timer war total daneben.

Das ist mein Code den ich laut vorheriger Überlegung umgesetzt habe.
B taktet langsamer wie A, wie gewünscht. Nur C taktet genau wie B. ???
Und dann dachte ich die takten synchron. Nicht verschoben. Irgendwas 
wird noch nicht stimmen.

Soll ich Euren Code testen oder wie machen wir weiter?
1
/*
2
 Arduino Mega 2560
3
*/
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
#define NOP __asm__ __volatile__ ("nop\n\t")
8
9
void set_Timer1(void);  // Funktion deklarieren
10
void set_Timer1()       // Normal Mode, kein CTC
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
  TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match > Pin 11 Mega2560
21
  TCCR1A  |= (1 << COM1B0);  // set Toggle OCnB Pin on compare match > Pin 12 Mega2560
22
  TCCR1A  |= (1 << COM1C0);  // set Toggle OCnC Pin on compare match > Pin 13 Mega2560
23
  
24
  OCR1A =  16384;    // Compare Match Register A >> 10kHz (Precaler 8)
25
  OCR1B =  32768;    // Compare Match Register B
26
  OCR1C =  65535;    // Compare Match Register C
27
    
28
  TCCR1B |= (1 << CS11);    // set Prescaler 8
29
  TIMSK1 |= (1 << TOIE1);   // enable Timer1 Overflow Interrupt
30
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
31
  TIMSK1 |= (1 << OCIE1B);  // enable Timer Compare Interrupt  B
32
  TIMSK1 |= (1 << OCIE1C);  // enable Timer Compare Interrupt  C
33
  
34
  sei();   //allow interrupts
35
  
36
}  // end Funktion
37
38
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
39
}
40
41
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
42
  PORTB &= ~(1 << 5);     // Pin von A zurück auf 0 setzen
43
  OCR1A = 49152;          // Compare Register A anpassen
44
}
45
46
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
47
  // toggelt Port.B.Bit7 = OC1C automatisch
48
}  
49
50
ISR(TIMER1_OVF_vect) { // Timer 1 Overflow Interrupt 
51
  PORTB &= ~(1 << 5);  // Pin von A zurück auf 0 setzen, Port.B.Bit5 = OC1A
52
  PORTB &= ~(1 << 6);  // Pin von B zurück auf 0 setzen, Port.B.Bit6 = OC1B
53
                       // Pin von B zurück auf 0 setzen, Port.B.Bit7 = OC1C
54
  OCR1A = 16384;       // Compare Register A anpassen
55
}
56
57
int main(void)  {
58
  DDRB = 0xFF;   // alles Ausgänge
59
60
  set_Timer1();
61
  
62
while(1)  {         
63
64
}
65
  
66
}   // Ende main()

von S. Landolt (Gast)


Lesenswert?

In Assembler und für einen ATmega48, 2*fA = 3*fB:

Der Timer1 wird eingestellt auf den Modus 'CTC auf ICR1', Interrupt auf 
OCR1A; dann sieht die ISR so aus:
1
.org OC1Aaddr
2
  inc  KcntA
3
  cpi  KcntA,2
4
  brne  pc+3
5
   ldi  KcntA,0
6
   sbi  PIN_KA,KA
7
8
  inc  KcntB
9
  cpi  KcntB,3
10
  brne  pc+3
11
   ldi  KcntB,0
12
   sbi  PIN_KB,KB
13
14
  ldi  tmp0,256/6
15
  add  Kcnt,tmp0
16
  brcc  pc+2
17
   ldi  Kcnt,256/12
18
  sts  OCR1AH,Kcnt
19
  sts  OCR1AL,null
20
  reti

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Assembler blicke ich nicht durch. Würde das nur kopieren aber nicht 
verstehen. Tut mir leid. Können wir bei C bleiben?

von S. Landolt (Gast)


Lesenswert?

Gerne. Nur arbeite ich nicht mit C, bleibe ich also Mitleser.

von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:

> Soll ich Euren Code testen oder wie machen wir weiter?

Ja klar.
Darum hab ich ihn ja reingeschoben.

Das ganze ist viel einfacher, als du denkst.
Die Technik, die zum Ziel führt, ist den Timer einfach lufe zu lassen 
und die Matches anzupassen

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

das ist ja echt genial Karl Heinz. Das ist genau das was ich machen 
wollte. Alle 3 Takte absolut synchron und immer um die Hälte langsamer. 
Ich wußte das es machbar ist. :-)  Auf die Idee das so in die ISR 
Handler zu schreiben wäre ich nicht gekommen. Auch der OCR1x Register 
Überlauf wird sich zu nutze gemacht damit es synchron bleibt.

Vielen Dank.

Das unten ist der Code dazu - zur Vollständigkeit.

Wo ist an meinem Code eins drüber der Denkfehler?
1
/*
2
 Arduino Mega 2560
3
*/
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
#define NOP __asm__ __volatile__ ("nop\n\t")
8
9
void set_Timer1(void);  // Funktion deklarieren
10
void set_Timer1()       // Normal Mode, kein CTC
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
  TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match
21
  TCCR1A  |= (1 << COM1B0);  // set Toggle OCnB Pin on compare match
22
  TCCR1A  |= (1 << COM1C0);  // set Toggle OCnC Pin on compare match
23
  
24
  OCR1A =   99;    // Compare Match Register A >> 10kHz (Precaler 8)
25
  OCR1B =  199;    // Compare Match Register B
26
  OCR1C =  399;    // Compare Match Register C
27
    
28
  TCCR1B |= (1 << CS11);    // set Prescaler 8
29
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
30
  TIMSK1 |= (1 << OCIE1B);  // enable Timer Compare Interrupt  B
31
  TIMSK1 |= (1 << OCIE1C);  // enable Timer Compare Interrupt  C
32
  
33
  sei();   //allow interrupts
34
  
35
}  // end Funktion
36
37
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
38
  OCR1A += 100;
39
}
40
41
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
42
  OCR1B += 200;
43
}
44
45
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
46
  OCR1C += 400;
47
}  
48
49
50
int main(void)  {
51
  DDRB = 0xFF;   // alles Ausgänge
52
53
  set_Timer1();
54
  
55
while(1)  {         
56
57
}
58
  
59
}   // Ende main()

von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:

> Wo ist an meinem Code eins drüber der Denkfehler?

Hab jetzt nicht alles durchdacht.

Aber ein Fehler ist:

Wenn der Timer die Kontrolle über die Pins hat, dann hat er auch die 
volle Kontrolle über die Pins!

d.h. du kannst dann nicht einfach mit Portzugriffen den Pin umschalten.
Erst mal musst du mittels COM Bits dem Timer die Kontrolle wieder 
entziehen, dann kannst du den Portpin umschalten, dann gibst du die 
Kontrolle wieder mit den COM Bits an den Timer zurück

von Veit D. (devil-elec)


Lesenswert?

Karl H. schrieb:

> Wenn der Timer die Kontrolle über die Pins hat, dann hat er auch die
> volle Kontrolle über die Pins!
>
> d.h. du kannst dann nicht einfach mit Portzugriffen den Pin umschalten.
> Erst mal musst du mittels COM Bits dem Timer die Kontrolle wieder
> entziehen, dann kannst du den Portpin umschalten, dann gibst du die
> Kontrolle wieder mit den COM Bits an den Timer zurück

Hallo,

ach'so. Woher weis man das alles? Steht das im Datenblatt? Hab ich nicht 
rausgelesen bisher.

Wegen Deiner Frage oben. Ich habe angefangen mir einen Frequenzgenerator 
zu programmieren. Dabei dann umgesetzt wie man den Prescaler und den 
passenden Compare Match Wert automatisch berechnen lassen kann. Sodass 
man dem ganzen nur noch die Wunschfrequenz übergibt. Als das fertig war, 
dachte ich mir, warum nicht noch die beiden anderen Kanäle B/C vom Timer 
1 verwenden bevor die nutzlos sich langweilen. Dabei bin ich dann 
abgestorben.

In dem Sinne kommen nicht noch mehr Interrupts dazu. Außer ich baue 
einen Frequenzzähler.  :-)

Hier mein Code zur automatischen Berechnung. Mein ganzer Stolz.  :-)
1
void calculate_Timer1_(float Frequency)
2
{  // berechnet den best möglichen Compare Match Wert und Prescaler von Timer 1
3
   // Timer 1 im CTC Modus
4
  float CPU_Takt = 16000000;
5
  int usedPrescaler[]={1,8,64,256,1024};  // mögliche Prescaler
6
  int Prescaler = 0;
7
  long maxCMR = 65535;          // max. Compare Match Register Wert
8
  long new_CMR = 0;             // passender errechneter Compare Match Register Wert
9
  
10
  if (Frequency < 0.2 || Frequency > 8000000)  {   // Gültigkeitskontrolle
11
    Prescaler = 0;
12
    new_CMR = 0;
13
  }
14
  else {
15
    for ( byte i = 0; i<5; i++)  {
16
      new_CMR = (long) (CPU_Takt/(usedPrescaler[i]*Frequency*2)-1+0.5);  // CMR Berechnung und runden
17
      if (new_CMR <= maxCMR && new_CMR >= 0)  {
18
        Prescaler = usedPrescaler[i];
19
        break;
20
      }  
21
    } 
22
    if (new_CMR < 0 || new_CMR > maxCMR)  {  // Ergebnis Gültigkeitsprüfung
23
      Prescaler = 0;
24
      new_CMR = 0;
25
    }
26
  }  // end else
27
  
28
  cli();  //stop interrupts
29
30
  // set Timer-1 Register
31
  TCCR1A = 0;      // Reset TCCR1A Register 
32
  TCCR1B = 0;      // Reset TCCR1B Register
33
  TIMSK1 = 0;      // Reset TIMSK1 Register 
34
  TCNT1  = 0;      // initialize counter value to 0
35
  OCR1A = new_CMR;         // Compare Match Register <= 65535
36
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
37
  
38
  switch (Prescaler)  {    // set Prescaler Clock Select Bits
39
    case    1 : TCCR1B |= (1 << CS10);  
40
                TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt Bit
41
                break;
42
    case    8 : TCCR1B |= (1 << CS11);
43
                TIMSK1 |= (1 << OCIE1A);
44
                break; 
45
    case   64 : TCCR1B |= (1 << CS11) | (1 << CS10);
46
                TIMSK1 |= (1 << OCIE1A);  
47
                break;
48
    case  256 : TCCR1B |= (1 << CS12); 
49
                TIMSK1 |= (1 << OCIE1A);
50
                break;
51
    case 1024 : TCCR1B |= (1 << CS12) | (1 << CS10); 
52
                TIMSK1 |= (1 << OCIE1A); 
53
                break;
54
    default :   ; // sollte nicht passieren
55
  }  // Ende switch
56
  
57
  sei();  //allow interrupts
58
}

von Karl H. (kbuchegg)


Lesenswert?

Solange du bei 3 Frequenzen überall denselben Prescaler benutzen kannst 
UND die jeweiligen Compare Werte nicht den erlaubten Bereich verlassen, 
kannst du auch unterschiedliche Frequenzen mit den 3 OCR Registern 
realisieren.

du könntest zb immer dem A Kanal die höchste Priorität einräumen, so 
dass diese Frequenz erzeugt werden kann. Beim B Kanal bzw. C Kanal 
könnte es dann sein, dass
* es sich mit den Werten für deren zu realsierende Frequenz so ausgeht, 
dass die jeweilige Frequenz (im Rahmen der Timergenauigkeit) exakt 
machbar ist
* es eine kleine Abweichung gibt
* die nicht machbar ist

Aber: Option 1 ist der Idealfall. Mit Option 2 kann man oft leben. 
Option 3 wäre praktisch dasselbe wie jetzt: dein Funktionsgenerator kann 
1 Frequenz erzeugen und nicht mehr.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ja, der Prescaler muß für alle unverändert bzw. gleich sein. Sonst macht 
das auch alles keinen Sinn. Habe gerade mit Prescaler 1 probiert und den 
obigen Compare Werten, dass Timing ist dann zu kurz. Da stimmt nichts 
mehr und C ist völlig aus dem Takt. Ich vermute die ISR Aufrufe 
verbraten mehr Zeit als die Compare Match's. Irgendwo ist immer eine 
Grenze, ist klar.
Hab dann alle Compare Werte verdoppelt und es taktete wieder sauber. 
Hier sieht man dann eine leichte  Verschiebung der Flanken. Was man 
sonst nicht sah. Wegen der ISR Aufrufe und Addition, nehme ich an.

Das mit Kanal A ist doch jetzt der Fall sein. Oder meinst Du das extra 
manuell priorisieren wenn noch andere ISR dazu kommen sollten?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

damit taktet noch alles synchron und sauber. A hat dann 53kHz.
Irgendwo zwischen 100 und 150 ist die Schmerzgrenze.  :-)

Ich bedanke mich bei allen die geholfen haben.
Recht herzliches Danke!  :-)

1
  TCCR1B |= (1 << CS10);    // set Prescaler 1
2
  OCR1A =  149;    // Compare Match Register A 
3
  OCR1B =  299;    // Compare Match Register B
4
  OCR1C =  599;    // Compare Match Register C
5
    
6
......
7
8
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
9
  OCR1A += 150;
10
}
11
12
ISR(TIMER1_COMPB_vect) {  // Timer 1.B Interrupt 
13
  OCR1B += 300;
14
}
15
16
ISR(TIMER1_COMPC_vect) {  // Timer 1.C Interrupt 
17
  OCR1C += 600;
18
}

von Uwe (Gast)


Lesenswert?

Hi, mal so als Denkanstoss aus dem Datenblatt:

• Bit 7 – FOCnA: Force Output Compare for Channel A
• Bit 6 – FOCnB: Force Output Compare for Channel B
• Bit 5 – FOCnC: Force Output Compare for Channel C
The FOCnA/FOCnB/FOCnC bits are only active when the WGMn3:0 bits 
specifies a non-PWM
mode. When writing a logical one to the FOCnA/FOCnB/FOCnC bit, an 
immediate compare
match is forced on the waveform generation unit. The OCnA/OCnB/OCnC 
output is changed
according to its COMnx1:0 bits setting. Note that the FOCnA/FOCnB/FOCnC 
bits are implemented
as strobes. Therefore it is the value present in the COMnx1:0 bits that 
determine the
effect of the forced compare.
A FOCnA/FOCnB/FOCnC strobe will not generate any interrupt nor will it 
clear the timer in Clear
Timer on Compare Match (CTC) mode using OCRnA as TOP.

habe das zwar noch nicht benutzt aber sollte die Kontrolle über die 
Ausgänge erleichtern

Viel Erfolg, Uwe

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl H. schrieb:
> Wenn du auf einen spezifischen Interrupt nicht reagieren willst, dann
> * gibst du ihn nicht frei
> * und brauchst auch keine ISR dafür schreiben.
>
> Es gibt nur diese beiden Möglichkeiten. Die Kombination
> * Interrupt freigeben
> * aber keine ISR dafür schreiben
> ist so erst mal nicht vorgesehen. Macht auch keinen Sinn.

 Es mag Situationen geben wo das schon einen Sinn macht oder es eben
 keinen Sinn macht, uC gleich zu resetten.
 Deswegen legen normale Compiler eine Interrupt Tabelle mit RETI an.
 Falls eine ISR definiert ist, wird RETI mit entspr. Adresse ersetzt.
 Auch beim programmieren in Assembler macht man das meistens so, vor
 allem bei grösseren uC wo das überhaupt nicht ins Gewicht fällt.
 TINY13 und ähnliche sind eine andere Geschichte, aber hier geht es um
 MEGA2560. gcc bietet zwar ISR(BADISR_vect) aber das ist nur ein Behelf.

 c ist und bleibt eben unlogisch - anstatt nur eine Warnung auszugeben,
 versucht der Compiler klüger zu sein als der Programmierer...

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marc V. schrieb:

>  Es mag Situationen geben wo das schon einen Sinn macht oder es eben
>  keinen Sinn macht

So?
Dann nenn doch mal 1 Szenario, bei dem es Sinn macht einen Interrupt, 
den man offensichtlich gar nicht behandeln will, freizugeben. In 99% 
aller Fälle ist das ein Fehler, der nur Rechenzeit kostet. Es ist also 
sinnvoll, dass so ein Fehler nicht unentdeckt bleibt.

>  c ist und bleibt eben unlogisch

Das hat mit C an sich nicht das geringste zu tun. Das haben die 
Implementierer der Runtime Lib beim gcc so entschieden.

Bitte nicht schon wieder: ein Implementierungsdetail einer spezifischen 
Implementierung herauspicken (das man auch ganz anders hätte 
implementieren können), und das dann zum Anlass zu nehmen um auf C 
loszugehen. Das eine hat mit dem anderen nichts zu tun!
Eisenbahnen sind auch nicht generell unkomfortabel, nur weil beim ICE ab 
einer bestimmten Temperatur die Klimaanlage nicht mehr mitkommt.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> also. vorgestellt habe ich mir das so wie im Bild.

Das sieht für mich wie das Signalspiel eines dreistufigen Binärzählers 
aus.

Das würde man natürlich in einer(!) ISR in Software realisieren mit 
einem einzigen Timerkanal als Antrieb, denn das wäre die bei weitem 
effizienteste Lösung.

Die ISR müßte in etwa so aussehen (ohne Nutzung weiterführender 
Optimierungen):

Beispielwerte für Bits 0..2 von OUTPORT
.EQU OUTMASK=7
.EQU INCRMNT=1

ISR:                ;(4)
 push R16           ; 2
 push R17           ; 2
 push R18           ; 2
 in R18,SREG        ; 1
 in R16,OUTPORT     ; 1
 andi R16,~OUTMASK  ; 1
 lds R17,COUNTER    ; 2
 or R16,R17         ; 1
 out OUTPORT,R16    ; 1
 subi R17,-INCRMNT  ; 1
 andi R17,OUTMASK   ; 1
 sts COUNTER,R17    ; 2
 out SREG,R18       ; 1
 pop R18            ; 2
 pop R17            ; 2
 pop R16            ; 2
 reti               ; 4
                    ;--
                    ;32


Damit kannst du drei beliebige "benachbarte" Portpins in der gewünschten 
Weise toggeln lassen, wobei die Flanken der drei Signale zueinander 
perfekt synchron sind, als Jitter fällt allerdings natürlich die 
variable Interruptlatenz an, die vor allem davon abhängt, was sonst noch 
an ISRs im System läuft.

Die maximale Ausgabefrequenz für das schnellste der drei Signale beträgt 
1/64 des Systemtaktes, die beiden anderen dementsprechend 1/128 und 
1/256. Wie gesagt: ohne weitere Optimierungen. Mit Optimierungen sind 
noch deutlich höhere Frequenzen erreichbar. Wie oft bei kurzen und sehr 
häufig durchlaufenen ISRs ist das Reservieren von Registern die 
Optimierung, die mit Abstand am meisten bringt. Hier würden dadurch 12 
Takte für push/pop entfallen und damit die möglichen Maximalfrequenzen 
auf 1/40, 1/80, 1/160 des Systemtaktes steigen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

schon erstaunlich was so eine Frage zum Timer auslöst. Sehr schön.  :-)

@ Uwe:
das mit dem FOC Bit hatte ich mir auch schon überlegt, als ich noch 
nicht zu Rande kam mit dem Timer. Nur habe ich dann nicht den gleichen 
Effekt wie jetzt mit dem Compare Match Interrupt? Die FOC Bits muß ich 
auch irgendwie zu bestimmten Zeiten ändern.

@ c-hater:
aha, Assembler Freak, wie S. Landholt.  :-)
Ja, sieht aus wie ein Binärzähler, soll es aber nicht werden.
Ich wollte nur den einen Timer voll ausnutzen wenn der schon 3 Kanäle 
hat.
Falls man mal einen 2. Takt benötigt der synchron zum ersten laufen 
soll.

Da ich leider von Assembler überhaupt nichts verstehe, kann ich den Code 
leider nicht lesen. Wenn Du das nochmal in C schreiben würdest, kann ich 
mitlesen. Wie reserviert man Register? Die Register vom Timer sind doch 
schon da.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl H. schrieb:
> In 99% aller Fälle ist das ein Fehler, der nur Rechenzeit kostet.
 Eben. Also nur eine Warnung vom Compiler.

> Es ist also sinnvoll, dass so ein Fehler nicht unentdeckt bleibt.
 Naturlich, (falls es es ein Fehler ist) aber, wie gesagt, eine einfache
 Warnung reicht vollkommen. Es ist kein fataler Fehler, kann keinen
 Schaden anrichten, warum gleich uC resetten ?

> So?
> Dann nenn doch mal 1 Szenario, bei dem es Sinn macht einen Interrupt,
> den man offensichtlich gar nicht behandeln will, freizugeben.
 Hmmm:
 Angenommen, ich will mal sehen, ob schon der nackte Interrupt als
 solches meinen Programm irgendwie durcheinander bringt. Muss ja nicht
 gerade ATMEL sein, es kann sich um einen uC mit verschachtelten und
 priorisierten Interrupts handeln.
 Will aber nicht seitenlang auskommentieren, mache das also mit #ifdef
 in irgendeinem .h file ('debug.h' z.B.).
 Bleibt alles ubersichtlich und leicht zu debuggen.

> Bitte nicht schon wieder: ein Implementierungsdetail einer spezifischen
> Implementierung herauspicken (das man auch ganz anders hätte
> implementieren können), und das dann zum Anlass zu nehmen um auf C
> loszugehen. Das eine hat mit dem anderen nichts zu tun!
 Tu ich nicht, nur mag ich halt c nicht weil es einerseits ganz
 unsinnige Sprachkonstrukte zulässt, aber andererseits etwas, was der
 Programmierer vielleicht bewusst so gemacht hat, mit Reset bestraft.
 Für mich ist das eben unlogisch.
 Auf jeden vermeintlichen Fehler soll hingewiesen werden, aber KEIN
 Fehler soll in einem Reset (und das auch noch ohne Warnung) enden.

 Zugegeben, es ist nur gcc und nicht c als solches, aber...

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marc V. schrieb:

>  Zugegeben, es ist nur gcc und nicht c als solches, aber...

Es ist noch nicht einmal gcc.
Es ist die Art und Weise, wie die Designer der für einen AVR 
spezifischen Runtime Library gedacht haben, dass die Dinge laufen 
sollen.

Mit so einer Aussage wie der getätigten diskreditierst du dich nur 
selber.
Du schimpfst auf Autos generell, weil die Entscheidungsträger bei Audi 
entschieden haben, die Batterie unter dem Rücksitz einzubauen. Man kann 
diese Entscheidung natürlich kritisieren. Aber die Kritik muss sich an 
die Entscheidungsträger bei Audi richten. Aber aus dieser 
Detailentscheidung eine generelle Kritik an Autos abzuleiten, zeigt 
einfach nur mangelnde Sachkenntnis.

Genauso auch hier.
Natürlich kann man die Entscheidung kritisieren, dass eine fehlende ISR 
zu einem Reset führt. Aber du musst deine Kritik an den richtigen 
Adressaten richten. C kann hier nicht das Geringste dafür.

> Will aber nicht seitenlang auskommentieren, mache das also
> mit #ifdef in irgendeinem .h file ('debug.h' z.B.).
> Bleibt alles ubersichtlich und leicht zu debuggen.

Sorry. Aber das seh ich jetzt als ein eher schwaches Argument an. Denn 
wenn ich zu Performance Zwecken einen Interrupt freigebe, dann hab ich 
auch die 15 Sekunden Zeit um eine leere ISR dafür zu schreiben.
Und so oft kommt der Fall ja dann auch wieder nicht vor.
In meinen Augen muss man sich schon am Normalfall richten und Ausnahmen 
als das sehen was sie sind: Ausnahmen. Dein konstruiertes Szenario ist 
sicherlich nicht der Normalfall. Noch sehe ich nicht den Sinn darin, zu 
Testzwecken einen Interrupt zuzulassen, der im regulären Programm 
überhaupt nicht vorkommt. Testest du auch, ob eine UART-Fifo dir dein 
Programm lahmlegt, obwohl du bei einem Lauflicht gar keine UART hast?

> Tu ich nicht, nur mag ich halt c nicht

Ist dein gutes Recht.
Ist aber noch lange kein Grund unsachlich zu werden und mit falschen 
Argumenten zu kommen.
Sowas nennt man in Diskussionen 'einen Strohmann bauen'. Weil man keine 
Argumente vorbringen kann, die die eigene Position stützen, greift man 
eben etwas anderes in diesem Umfeld an und tut dann so, als ob die 
Argumente auch  auf das ursprüngliche Thema greifen würden. Esoteriker 
benutzen diese Technik gerne. Für Globuli hat man keine Argumente, also 
konstruiert man einen Strohmann der da lautet: in der Medizin ist auch 
nicht alles korrekt. Ist völlig richtig, in der Medizin gibt es auch 
schwarze Schafe. Aber das ist ja schliesslich nicht relevant, wenn die 
Fragestellung lautet: Funktionieren Globuli besser als Plazebo? Selbst 
wenn alle Krankenkassen korrupt sind, selbst wenn alle Pharmafirmen nur 
Geld scheffeln wollen, selbst dann hat Wasser immer noch kein 
Gedächtnis.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Karl H. schrieb:
> Sorry. Aber das seh ich jetzt als ein eher schwaches Argument an. Denn
> wenn ich zu Performance Zwecken einen Interrupt freigebe, dann hab ich
> auch die 15 Sekunden Zeit um eine leere ISR dafür zu schreiben.
 Die ISR ist schon geschrieben, nur soll die vom Compiler eben nicht
 compiliert werden wenn ich es nicht will. Will aber auch nicht, dass
 dadurch mein Programm zum Ringelspiel wird.

> Und so oft kommt der Fall ja dann auch wieder nicht vor.
 Natürlich.

> Dein konstruiertes Szenario ist
> sicherlich nicht der Normalfall. Noch sehe ich nicht den Sinn darin, zu
> Testzwecken einen Interrupt zuzulassen, der im regulären Programm
> überhaupt nicht vorkommt. Testest du auch, ob eine UART-Fifo dir dein
> Programm lahmlegt, obwohl du bei einem Lauflicht gar keine UART hast?
 Wenn ich 2 Versionen habe, eine mit DMX und eine ohne, schon.

> Ist aber noch lange kein Grund unsachlich zu werden und mit falschen
> Argumenten zu kommen.
> Sowas nennt man in Diskussionen 'einen Strohmann bauen'. Weil man keine
> Argumente vorbringen kann, die die eigene Position stützen, greift man
 Ich will hier bestimmt keine Diskussion über Vor- und Nachteile der C
 Sprache führen. Ich benutze c nicht (oder nur sehr selten), habe aber
 nichts gegen Leute die es tun. Auch Arduino oder jede andere Sprache
 ist für mich in Ordnung.
 Jedem das Seine.

von S. Landolt (Gast)


Lesenswert?

Ihn dünkt', er säh' ein Teufelchen,
  das dreht' am Timer rum.
Er guckt' noch mal und merkt', es war
  gar ein Politikum.
"Ob c, ob asm", sprach er,
  " die Streiterei ist dumm."

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> Ja, sieht aus wie ein Binärzähler, soll es aber nicht werden.

Was denn dann?

> Ich wollte nur den einen Timer voll ausnutzen wenn der schon 3 Kanäle
> hat.

Ist das ein Designziel? Oder geht es darum, den eigentlich 
angestrebten Zweck mit minimalem Aufwand zu erreichen?

> Da ich leider von Assembler überhaupt nichts verstehe, kann ich den Code
> leider nicht lesen.

Keine Arme, keine Kekse.

> Wenn Du das nochmal in C schreiben würdest, kann ich
> mitlesen.

Ausnahmsweise, weil es sowieso zu warm ist, um irgendwas wirklich 
Sinnvolles zu tun, schreibe ich auch mal C-Style, ohne dafür bezahlt zu 
werden.

Außerdem musste heute sowieso schon den ganzen, gefühlt endlos langen, 
Tag C# schreiben, da habe ich den Kotze-Geschmack des C-Style sowieso 
schon dermaßen brennend im Hals, da kommt's dann auf die paar Zeilen 
mehr wohl auch nicht mehr an...

#define OUTMASK 7
#define INCRMNT 1
#define OUTPORT PORTC

volatile uint8_t COUNTER;

ISR(TIMER1_OVF_vect)       //oder was gerade geeignet erscheint
{
   uint8_t tmp=OUTPORT;    //tmp entspricht R16 in meinem Asm-Code

   tmp &= ~OUTMASK;
   tmp |= COUNTER;

   OUTPORT = tmp;

   COUNTER += INCRMNT;
   COUNTER &= OUTMASK;
}

> Wie reserviert man Register?

Man benutzt sie einfach ausschließlich in der zeitkritischen ISR.

> Die Register vom Timer sind doch
> schon da.

Das sind IO-Register der Peripherie, das ist was anderes. Die Register, 
die ich meine, sind die Arbeitsregister der MCU, also das, was in meinem 
Code beispielhaft als R16, R17 und R18 erscheint. Kannst du dir ungefähr 
als sehr schnelle lokale Variablen vorstellen.

Der verschissene C-Compiler, der dich von allem abschirmt, was wirklich 
wichtig ist, muß intern auch mit diesen Dingern arbeiten. Und er macht 
das gerade bei ISRs oft nicht wirklich intelligent. Aber diese wirklich 
sehr primitive Routine sollte ein heutiger C-Compiler aus eigener Kraft 
einigermaßen brauchbar umgesetzt bekommen, d.h. sie sollte nicht 
wesentlich mehr als die 32 Takte brauchen, die meine 
straightforward-Routine braucht, vielleicht sogar wirklich exakt diese 
32. Müßte ich direkt mal probieren, ob der gcc das schafft...

Bloß die ultimative Optimierung, Register exklusiv zu reservieren, die 
nötig wäre, um die Sache nochmals merklich schneller zu machen, die 
kannst du in C halt nur sehr eingeschränkt bzw. auch garnicht 
realisieren.

von S. Landolt (Gast)


Lesenswert?

an c-hater

3 Takte ließen sich noch einsparen, denn der OUTPORT ist ja bereits der 
COUNTER.
1
;ISR:               ;(4)
2
 push r16           ; 2
3
 push r17           ; 2
4
 push r18           ; 2
5
 in r18,SREG        ; 1
6
 in r16,OUTPORT     ; 1
7
 mov r17,r16        ; 1
8
 andi r16,~OUTMASK  ; 1
9
 subi r17,-INCRMNT  ; 1
10
 andi r17,OUTMASK   ; 1
11
 or r16,r17         ; 1
12
 out OUTPORT,r16    ; 1
13
 out SREG,r18       ; 1
14
 pop r18            ; 2
15
 pop r17            ; 2
16
 pop r16            ; 2
17
 reti               ; 4
18
                    ;--
19
                    ;29

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> 3 Takte ließen sich noch einsparen, denn der OUTPORT ist ja bereits der
> COUNTER.

Da hast du natürlich absolut Recht. Große Peinlichkeit.

Verdammt, ich bin ganz offensichtlich bereits wesentlich mehr 
Hochsprachen-geschädigt, als ich es selber wahr haben will. Ich bin ja 
schon fast genau so doof wie irgendsoein hergelaufener verschissener 
Compiler, wenn ich solche überaus naheliegenden Optimierungen nicht mehr 
ad hoc erkenne.

Und noch 16 Jahre bis zur Rente, ich werde wohl in dieser Zeit noch 
völlig verblöden und am Ende doch noch vollständiger C-Legastheniker 
werden...

Finstere Aussichten...

von S. Landolt (Gast)


Lesenswert?

Entschuldigung, es lag nicht in meiner Absicht, eine solche Reaktion 
hervorzurufen; im Gegenteil dachte ich, es freue Sie, wenn jemand bei 
Ihnen mitdenkt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

irgendwie bist du ganz schön schräg drauf, trotz deines älteren 
Jahrgangs. Da sollte man doch ruhiger und gelassender sein.
Da du dich erbarmt hast in C zu schreiben, möchte ich Dir natürlich 
dafür danken.

Nochmal Danke an alle. Habe viel gelernt von Euch.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich glaube Du hast das mit dem ISR Handler doch irgendwie falsch 
verstanden. Mit Deinem Assembler schalten doch 3 Pins gleichzeitig, 
takten also im selben Takt. Oder nicht? Das war nicht mein Wunschziel. 
Ich möchte die anderen beiden jeweils mit halben Takt haben.
Siehe meinem Beitrag vom 2.7. 21:53 Uhr.
Das geht laut meiner Logik nicht in einem ISR.
Das war bestimmt ein Missverständnis.

Noch eine Frage wegen der benötigten Interruptzeit.
Wenn ich nur einen Compare ISR verwende zum ausloten wieviel Takte 
Minimum sind, dann komme ich auf 51 Takte. Darunter kommt der Takt außer 
Tritt.
Im ISR erfolgt aber nur eine einfache Addition. Die der Compiler ja 
optimal umsetzen können sollte, denke ich. Jetzt stellt sich mir die 
Frage, warum in Assembler mehr gemacht werden kann mit weniger Takten? 
Geht doch eigentlich gar nicht. Oder habt ihr in der Rechnung was 
unterschlagen? Da kommt sicherlich noch was an Takten dazu was nichts 
mit dem Code im ISR zu tun hat. Aber drum herum mit dem ISR aus µC 
Sicht. Register sichern usw.
1
TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match
2
    
3
  OCR1A =  50;    // Compare Match Register A   (Precaler 1)
4
      
5
  TCCR1B |= (1 << CS10);    // set Prescaler 1
6
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
7
   
8
}  // end Funktion
9
10
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
11
  OCR1A += 51;            //  51 minimum mit 1 Kanal
12
                          //  92 minimum mit 2 Kanälen
13
                          // 109 minimum mit 3 Kanälen
14
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

und das hier taktet mit nur 122Hz auf Kanal A.  ???
Die anderen beiden Pins takten außer Rhythmus.
1
/*
2
 Arduino Mega 2560
3
*/
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <avr/interrupt.h>
7
#define NOP __asm__ __volatile__ ("nop\n\t")
8
9
#define OUTMASK B11111111
10
#define INCRMNT 1
11
#define OUTPORT PORTB
12
volatile uint8_t COUNTER;
13
14
void set_Timer1(void);  // Funktion deklarieren
15
void set_Timer1()       // Normal Mode, kein CTC
16
{   
17
  cli();  //stop interrupts
18
19
  // set Timer-1 Register
20
  TCCR1A = 0;      // Reset TCCR1A Register 
21
  TCCR1B = 0;      // Reset TCCR1B Register
22
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
23
  TCNT1  = 0;      // initialize counter value to 0
24
  
25
  TCCR1A  |= (1 << COM1A0);  // set Toggle OCnA Pin on compare match
26
  
27
  OCR1A =  50;    // Compare Match Register A (Precaler 1)
28
  
29
  TCCR1B |= (1 << CS10);    // set Prescaler 1
30
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
31
   
32
  sei();   //allow interrupts
33
  
34
}  // end Funktion
35
36
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
37
 uint8_t tmp=OUTPORT;     
38
 tmp &= ~OUTMASK;
39
 tmp |= COUNTER;
40
 OUTPORT = tmp;
41
 COUNTER += INCRMNT;
42
 COUNTER &= OUTMASK;
43
}
44
45
46
int main(void)  {
47
  DDRB = 0xFF;   // alles Ausgänge
48
49
  set_Timer1();
50
  
51
while(1)  {         
52
53
}
54
  
55
}   // Ende main()

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>und das hier taktet mit nur 122Hz auf Kanal A.  ???

Passt doch. Der 16-Bit-Timer hat bei 16 MHz eine Overflowtime von 4,096 
ms. Das ergibt beim togglen von OCR1A eine Periode von 8,192 ms oder 
eine Frequenz von 122 Hz.

MfG Spess

von S. Landolt (Gast)


Lesenswert?

Veit Devil schrieb:
> Mit Deinem Assembler schalten doch 3 Pins gleichzeitig,
> takten also im selben Takt. Oder nicht? Das war nicht mein
> Wunschziel. Ich möchte die anderen beiden jeweils mit
> halben Takt haben.

Weder weiß ich, wer angesprochen ist, noch was eigentlich das Wunschziel 
ist. Wie auch immer:
B0 liefert die Frequenz f, B1 f/2, B2 f/4, synchron auf die fallende 
Flanke. Möchte man die steigende, dann muss
subi r17,-INCRMNT  durch  subi r17,INCRMNT  ersetzt werden.
Die Frequenz f selbst wird bestimmt durch den Prescaler des Timers und 
den verwendeten Interrupt; für ein möglichst großes f wird man '/1' 
einstellen und den Modus 'CTC' mit sehr klein besetztem OCR bzw. ICR 
wählen.

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> ich glaube Du hast das mit dem ISR Handler doch irgendwie falsch
> verstanden.

Ich? Eher: Du.

> Mit Deinem Assembler schalten doch 3 Pins gleichzeitig,
> takten also im selben Takt. Oder nicht?

Nein. Sie schalten zwar gleichzeitig, aber eben nur wenn sie überhaupt 
schalten. D.h.: Flanken erfolgen gleichzeitig, aber nur für den höchsten 
Takt erfolgt bei jedem Aufruf der ISR eine Flanke, bei dem zweithöchsten 
nur bei jedem zweiten usw.

> Im ISR erfolgt aber nur eine einfache Addition.

Hmm? Inzwischen hast du doch auch den C-Code und damit sollte selbst für 
dich sichtbar sein, das da neben der Addition durchaus noch einige 
(bit-)logische Operationen passieren...

S. Landolt hat korrekterweise darauf hingewiesen, daß die Verwendung 
einer zusätzlichen Variablen für den Zähler (der im Kern allein für das 
gewünschte Signalspiel sorgt) überflüssig war (was sowohl meinen Asm- 
als auch meinen C-Code betrifft), aber die binäre Logik ist natürlich 
weiterhin nötig, wenn man bei der Wahl der Ausgangsbits einigermaßen 
frei bleiben will.

Bei kluger Wahl der benutzten Portbits ergeben sich allerdings wieder 
zusätzlich Optimierungsmöglichkeiten. Wählt man nämlich die Bits 7..5 
eines Ports zur Ausgabe, entfällt die Bitlogik vollständig und dadurch 
wird auch noch ein Register weniger benötigt, was es erspart, eben 
dieses zu retten/wiederherzustellen...
1
.EQU INCRMNT=$20
2
.EQU OUTPORT=PORTC
3
4
ISR:                ;(4)
5
 push R16           ; 2
6
 push R17           ; 2
7
 in R17,SREG        ; 1
8
 in R16,OUTPORT     ; 1
9
 subi R16,-INCRMNT  ; 1
10
 out OUTPORT,R16    ; 1
11
 out SREG,R17       ; 1
12
 pop R17            ; 2
13
 pop R16            ; 2
14
 reti               ; 4
15
                    ;--
16
                    ;21

bzw.
1
#define INCRMNT 0x20
2
#define OUTPORT PORTC
3
4
volatile uint8_t COUNTER;
5
6
ISR(TIMER1_OVF_vect)       //oder was gerade geeignet erscheint
7
{
8
   uint8_t tmp = OUTPORT;
9
   tmp += INCRMNT;
10
   OUTPORT = tmp
11
}

Derselbe Vorteil ergibt sich übrigens, wenn du statt drei acht Takte 
haben willst, also einen Port komplett benutzt. Die Routine ändert sich 
dadurch nicht weiter, nur die Definition der einen Konstanten:

#define INCRMNT 1

Oder allgemein gesprochen kannst du mit der optimierten Routine jede 
beliebige Zahl von :2-Takten erzeugen, wenn du nur immer die benötigten 
Ausgabebits mit dem höchstwertigen Portbit beginnst.

> Die der Compiler ja
> optimal umsetzen können sollte, denke ich. Jetzt stellt sich mir die
> Frage, warum in Assembler mehr gemacht werden kann mit weniger Takten?

Weil man in Asm keinerlei Rücksicht auf Erfordernisse und Konventionen 
der Runtime-Umgebung eine Compilers nehmen muß.

> Da kommt sicherlich noch was an Takten dazu was nichts
> mit dem Code im ISR zu tun hat.

Ja, natürlich. Selbst mein Asm-Code enthält ja noch etliches für die 
eigentliche Operation unnötige Zeug, in C siehst du diesen Overhead nur 
nicht im Quelltext, er ist aber natürlich trotzdem da und das oft noch 
in "fetterer" Form.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@ Spess:
ich nutze keinen Overflow Interrupt sondern den Compare Match Interrupt.
Und an den 122 Hz ändert sich auch nichts, egal welchen Counter ich 
ändere.

@ Landholt:
ich habs eigentlich schon mehrfach geschrieben inkl. gemalten 
Taktdiagramm. Weis nicht was ich noch besser erklären soll. Echt nicht.

@ hater:
tut mir leid, ich raff nicht was Dein Code machen soll. Also ich weis 
schon was er machen soll mit dem Port laut Deiner Beschreibung, Art 
Binrzähler, aber der Code sagt mir nichts was dazu passen könnte. Das 
Ding taktet Kanal A immer mit 122Hz oder 3,8Hz und die anderen sind 
außer dem Takt. Oder Kanal A ist nicht 50/50 im Tastv.

Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder 
auf den OUTPORT. Die Logik dahinter raff ich nicht.

Wenn ich dem Compare Match ISR nutze muß der sich ja nach dem richten.
Also wenn Du noch Lust hast und gute Laune, schauste Dir bitte den 
ganzen Code an und korrigierst ihn bitte, viel ist das ja nicht oder wir 
brechen hier ab und gut ist. Oder schreibst meinetwegen alles in 
Assembler und sagst mir welcher Wert den Haupttakt ändert. Nur das es 
erstmal funktioniert.
1
/*
2
 Arduino Mega 2560
3
 Timer 1
4
 http://www.mikrocontroller.net/topic/370736?goto=4188200#4188200
5
*/
6
7
#define F_CPU 16000000UL
8
#include <avr/io.h>
9
#include <avr/interrupt.h>
10
#define NOP __asm__ __volatile__ ("nop\n\t")
11
12
#define INCRMNT 20
13
#define OUTPORT PORTB
14
volatile uint8_t COUNTER;
15
16
void set_Timer1(void);  // Funktion deklarieren
17
void set_Timer1()       // Normal Mode, kein CTC
18
{   
19
  cli();  //stop interrupts
20
21
  // set Timer-1 Register
22
  TCCR1A = 0;      // Reset TCCR1A Register 
23
  TCCR1B = 0;      // Reset TCCR1B Register
24
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
25
  TCNT1  = 0;      // initialize counter value to 0
26
      
27
  OCR1A =  50;    // Compare Match Register A (ca. 157kHz)
28
  
29
  TCCR1B |= (1 << CS10);    // set Prescaler 1
30
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt  A
31
  
32
  sei();   //allow interrupts
33
  
34
}  // end Funktion
35
36
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
37
  uint8_t tmp = OUTPORT;
38
  tmp += INCRMNT;
39
  OUTPORT = tmp;
40
}
41
42
43
int main(void)  {
44
  DDRB = 0xFF;   // alles Ausgänge
45
46
  set_Timer1();
47
  
48
while(1)  {         
49
50
}
51
  
52
}   // Ende main()

: Bearbeitet durch User
von Bastler (Gast)


Lesenswert?

> Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder
auf den OUTPORT. Die Logik dahinter raff ich nicht.

Naja, die oberen 3 Bit sollen durchgezählt werden:
000xxxxxx + 00100000 =
001xxxxxx + 00100000 =
010xxxxxx + 00100000 =
011xxxxxx + 00100000 =
100xxxxxx + 00100000 =
101xxxxxx + 00100000 =
110xxxxxx + 00100000 =
111xxxxxx + 00100000 =
000xxxxxx + 00100000 =
...
Die x-en bleiben dabei was auch immer sie sind.

von spess53 (Gast)


Lesenswert?

Hi

>@ Spess:
>ich nutze keinen Overflow Interrupt sondern den Compare Match Interrupt.
>Und an den 122 Hz ändert sich auch nichts, egal welchen Counter ich
>ändere.

Dein Timer läuft aber im Normal Mode. Da zählt er bis 65535 und fängt 
dann von 0 wieder an. Der Inhalt vom OC-Registers bestimmt nur den 
Zeitpunkt des Interrupts. Der Abstand bleibt aber 65536 Timertakte. Da 
kannst du in das Register reinschreiben was du willst.

MfG Spess

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> Das
> Ding taktet Kanal A immer mit 122Hz oder 3,8Hz und die anderen sind
> außer dem Takt. Oder Kanal A ist nicht 50/50 im Tastv.

Du mußt natürlich an PortB5..7 messen und nicht an den OCx-Ausgängen. 
Wenn du das nicht erkannt hast...

> Du liest den OUTPORT aus, addierst 32 (0x20) hinzu und gibts das wieder
> auf den OUTPORT. Die Logik dahinter raff ich nicht.

Dabei ist die Logik überaus trivial: Wenn man was haben will, was ein 
Signalspiel wie ein Binärzähler erzeugt, dann ist die einfachste 
Methode, tatsächlich einen Binärzahler zu benutzen...

Und genau das wird tmp in den oberen drei Bits, wenn man fortgesetzt 
0x20 addiert. Und wenn man vor dem addieren den Port nach tmp liest und 
nach dem addieren das Ergebnis wieder auf den Port ausgibt, hat man 
nicht nur die gewünschten Signale auf den drei Portbits, sondern dann 
dient das Portregister selber auch noch als Speicher der 
Zwischenergebnisse zwischen den einzelnen ISR-Aufrufen.

> und sagst mir welcher Wert den Haupttakt ändert.

Keiner bzw. höchstens die Wahl des Prescalers.

Du benutzt den Timer einfach im falschen Modus. Du nimmst den 
Normalmodus bei dem immer der volle Zählumfang durchlaufen wird, müßtest 
ihn aber z.B. im CTC-Modus benutzen, bei dem beim Erreichen von OCxA 
TCNTx auf Null resettet wird.

Und sofort funktioniert das wie gewünscht.

> volatile uint8_t COUNTER;

Kannst du übrigens ersatzlos streichen, ist bei der letzten Änderung 
überflüssig geworden (mir selber allerdings beim C-Code ebenfalls 
durchgerutscht, das kommt vom C&P-"Programmieren").

von S. Landolt (Gast)


Lesenswert?

S. Landolt schrieb:
> B0 liefert die Frequenz f, B1 f/2, B2 f/4
So war es für die erste Programmversion von c-hater. Was war daran nicht 
zu verstehen?

c-hater schrieb:
> Du mußt natürlich an PortB5..7 messen und nicht an den OCx-Ausgängen.
Das gilt nun für die zeitoptimierte Version.

c-hater schrieb:
> Wenn du das nicht erkannt hast...
Da kommen mir allerdings auch Zweifel...

von S. Landolt (Gast)


Lesenswert?

Veit Devil schrieb:
> ...meinetwegen alles in Assembler...
> Nur dass es erstmal funktioniert.

Dem Manne kann geholfen werden (wenn ihm denn zu helfen ist):
1
.include "m644def.inc"
2
3
.equ outDDR   = DDRC           ; Ausgabe auf PORT C  (nix OCn !!)
4
                               ; ================================
5
.equ outPORT  = PORTC
6
.equ outMASK  = 0b11100000     ; ==> incrmnt
7
.equ incrmnt  = 0b00100000     ; ==> C5: f, C6: f/2, C7: f/4
8
.equ OCR1     = 50             ; ==> f = f_CPU/100, Beispiel fuer c
9
                ; asm erlaubt 14, bei f_CPU= 16 MHz also f= 571429 Hz
10
11
.def isr_SREG = r2
12
.def isr_tmp  = r16
13
.def tmp0     = r17
14
15
.org 0
16
  rjmp   reset
17
18
.org OC1Aaddr
19
; c-haters optimierte Routine
20
  in     isr_SREG,SREG
21
22
  in     isr_tmp,outPORT
23
  subi   isr_tmp,incrmnt       ; synchr. pos. Flanke
24
  out    outPORT,isr_tmp
25
26
  out    SREG,isr_SREG
27
  reti
28
   
29
reset:
30
  ldi   tmp0,outMASK
31
  out   outDDR,tmp0
32
  ldi   tmp0,high(OCR1-1)
33
  sts   OCR1AH,tmp0
34
  ldi   tmp0,low(OCR1-1)
35
  sts   OCR1AL,tmp0
36
  ldi   tmp0,(1<<CS10)+(1<<WGM12) ; f_CPU/1, CTC auf OCR1A
37
  sts   TCCR1B,tmp0
38
  ldi   tmp0,(1<<OCIE1A)
39
  sts   TIMSK1,tmp0
40
  sei
41
main_loop:
42
  rjmp  main_loop

(Ich habe keinen ATmega2560)

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

so Leute, hab das wegen der Addition nicht erkannt das als Effekt eine 
Art Bit schieben herauskommt. Danke für die Info.

Jetzt habe ich den CTC Mode aktiviert und nur den Compare Match 
Interrupt aktiv. Damit man die Pins Port.B, welche auch gleich OC1x 
sind, selbst schalten kann. Deshalb stimmte das alles schon noch mit den 
Pins. Ganz blöd bin ich auch nicht.  :-)

Ich habe das an Port B und Port C probiert. Habe aber den Effekt, dass 
die Takte kein 50% Tastverhälnis haben. Jetzt wird doch der ISR aller 
150 CPU Takte aufgerufen und damit der Port.C geändert. Damit müßte aber 
alles im 50% Tastv. laufen. ???
1
/*
2
 Arduino Mega 2560
3
 
4
 mit Assembler
5
 
6
 http://www.mikrocontroller.net/topic/370736?goto=4188200#4188200
7
 
8
*/
9
10
#define F_CPU 16000000UL
11
#include <avr/io.h>
12
#include <avr/interrupt.h>
13
#define NOP __asm__ __volatile__ ("nop\n\t")
14
15
#define INCRMNT 20     // Toggle Bit 7,6,5 im ISR Handler
16
#define OUTPORT PORTC
17
18
void set_Timer1(void);  // Funktion deklarieren
19
void set_Timer1()       // CTC Mode
20
{   
21
  cli();  //stop interrupts
22
23
  // set Timer-1 Register
24
  TCCR1A = 0;      // Reset TCCR1A Register 
25
  TCCR1B = 0;      // Reset TCCR1B Register
26
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
27
  TCNT1  = 0;      // initialize counter value to 0
28
      
29
  OCR1A =  150;    // Compare Match Register A, sollten 53kHz sein
30
  
31
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
32
  
33
  TCCR1B |= (1 << CS10);    // set Prescaler 1
34
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
35
  
36
  sei();   //allow interrupts
37
  
38
}  // end Funktion
39
40
ISR(TIMER1_COMPA_vect) {  // Timer 1.A Interrupt 
41
  uint8_t tmp = OUTPORT;
42
  tmp += INCRMNT;
43
  OUTPORT = tmp;
44
}
45
46
47
int main(void)  {
48
  DDRC = 0xFF;   // alles Ausgänge
49
  PORTC = 0;     // alles aus 
50
  
51
  set_Timer1();
52
  
53
while(1)  {         
54
55
}
56
  
57
}   // Ende main()

von S. Landolt (Gast)


Lesenswert?

> #define INCRMNT 20     // Toggle Bit 7,6,5 im ISR Handler

Wie wäre es mit  0x20?

von S Landolt (Gast)


Lesenswert?

> so Leute, hab das wegen der Addition nicht erkannt das als Effekt
> eine Art Bit schieben herauskommt.

Wieso Bit schieben? Ich dachte, Sie wollen einen Binärzähler.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

oh man, 20 und 0x20 ist natürlich ein Unterschied. Das kommt vom wilden 
rumprobieren. Wollte dich gerade loben für dein Assembler, funktioniert 
nämlich 1a. Anbei ein Bild davon.

Ich hatte geschrieben eine Art Bit schieben! Oder eine Art 
Lauflichtmuster.
Nenn es wie du möchtest. Binärzähler ist natürlich treffend für das was 
es macht.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

jetzt klappt das auch mit dem C Code.  :-)

Man war das eine schwere Geburt, begleitet von Missverständnissen und 
eigenen Schusselfehlern.

Ich bedanke mich für Eure Geduld.  c-hater und Landholt.
Alle anderen natürlich auch.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Ihn dünkt', er sähe ein Problem,
  das war so zäh wie Brei.
Er guckt' noch mal und merkt', es war
  ein Fall für mehr als zwei.
"Potz Tsakalotos auch", sprach er,
  "jetzt klappt das einwandfrei!"

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> "Potz Tsakalotos auch", sprach er,
>   "jetzt klappt das einwandfrei!"

Who, the fuck, is "Tsakalotos"?

Die Leute, die Wikipedia ausspuckt, scheinen mir allesamt nicht ganz zum 
Thema zu passen und meine sowieso weitgehend vergessene klassische 
Bildung bezüglich der griechischen Antike hilft mir auch nicht weiter.

Also wen, zum Teufel, meinst du?

Vorläufig würde ich "Tantalus" als Ersatz vorschlagen. Der paßt in dein 
schräges Versmass und obendrein auch logisch in den Text. Und er hat den 
Vorteil, dass er halt eine der wenigen Gestalten der griechischen 
Klassik ist, die ich nicht vergessen habe...

von S. Landolt (Gast)


Lesenswert?

Ich finde nur den Namen des Neuen so witzig, klingt wie 'Donnerwetter', 
'Dunnerschlach', 'Potz Blitz', oder wie das 'Donizetti' von Danny Wilde 
aus 'Die 2', falls Sie sich noch an diese Fernsehserie Anfang der 
Siebziger erinnern können.

von S. Landolt (Gast)


Lesenswert?

Sehen Sie, das ist der Unterschied zwischen uns beiden: Sie denken an 
Tantalos und seine Qualen, ich an Danny Wildes schrägen Witze.
Versmaß und Schema sind übrigens geklaut aus 'Sylvie & Bruno' von 
Charles Lutwidge Dodgson alias Lewis Carroll (in der Übersetzung von 
Sabine Hübner).

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> Ich finde nur den Namen des Neuen so witzig

Nachdem ich jetzt die Nachrichten gesehen habe, weiß ich auch, wen du 
meinst.

> klingt wie 'Donnerwetter',
> 'Dunnerschlach', 'Potz Blitz'

Keine besonders clevere Idee, den "Klang" fremder Namen in der eigenen 
Sprache zu instrumentalisieren, für welchen Zweck auch immer. Findest du 
nicht auch?

von Uwe (Gast)


Lesenswert?

Hi,
das Thema ist nun zwar anders, besser geklärt aber ich will trotzdem 
noch mal auf die FOC-Bits zurückkommen. Wenn man Ausgänge via COMnx1:0 
bits
an Timer bindet, sind die Ausgänge nicht mehr über Out/SBI/CBI 
erreichbar es sei denn du setzt COMnx1:0 auf 0, beeinflusst den Ausgang 
und setzt COMnx1:0 erneut auf das gewünschte. Wenn wir mal bei deinen 3 
ISR's bleiben wollen wäre es möglich in jeder ISR die anderen Ausgänge 
über FOCx zu toggeln ohne die "COMnx1:0" rumschreiberei.

Viel Erfolg, Uwe

von S. Landolt (Gast)


Lesenswert?

an c-hater

Wieso clever? Spaßig allenfalls, aber selbst das ist zugegebenermaßen 
Geschmackssache. Ich hatte mich einfach gefreut, dass unser junger 
Mensch jetzt ans Ziel gekommen ist, und in dieser Laune musste ich eben 
über den Namen lachen, wie so allmählich über die ganze neugriechische 
Tragödie.
Suchen Sie sich heute Abend Ihren eigenen, ganz persönlichen Grund für 
gute Laune, und sei es nur, dass der Erfolg hier zu großen Teilen Ihnen 
zu verdanken ist.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das mit den FOC Bits guck ich mir später mal an, hab zur Zeit andere 
Dinge im Kopf.

Was ich noch fragen möchte ist, woher weis man wie Definitionsdatei für 
seinen µC heißt?

Du hattest für Deinen   .include "m644def.inc"  geschrieben.
Die Zeile hatte ich weggelassen und das hat für mich Atmel Studio 
passend erledigt. Beim Projekt anlegen hab ich ja den 2560 ausgewählt. 
Wenn ich raten müßte sollte das m2560def.inc lauten bei mir. Nur wo kann 
man gezielt nachschauen?

Edit:

habs gefunden:

C: ... Atmel\Atmel Toolchain\AVR Assembler\Native\2.1.1175\avrassembler

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> Wenn ich raten müßte sollte das m2560def.inc lauten bei mir. Nur wo kann
> man gezielt nachschauen?
>
> Edit:
>
> habs gefunden:
>
> C: ... Atmel\Atmel Toolchain\AVR Assembler\Native\2.1.1175\avrassembler

Die Namen der Includes sind ziemlich regulär aufgebaut. Die Megas 
beginnen mit einem "m", die Tinys mit "tn". Dann kommt 1:1 die 
Kennnummer, mit der man das Teil auch bestellen würde, also "2560" oder 
"1284p" oder "2313a". Und danach einheitlich "def.inc".

Man braucht also eigentlich nicht das Verzeichnis zu durchforsten, 
sondern kann den Namen der Include-Datei rein formal aus der 
Device-Bezeichnung ableiten.

Ausnahmen bestätigen wie immmer die Regel.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich möchte die Interrupt Routine in Assembler ins restliche C Programm 
einbinden. Wegen der höheren Geschwindigkeit. Ich habe mit Inline 
Assembler probiert und mit ausgelagerter .h und .S Datei. Ich bekomme es 
einfach nicht hin. Egal was ich versuche zu verstehen. Atmel Studio 
meckert laufend rum mit "constant value required" oder "expected comma 
after incrmnt" oder "unknown pseudo-op .def" und in Zeilennummern die es 
gar nicht gibt.

Könnte mir jemand zeigen was ich falsch mache?
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
/*
7
volatile uint8_t isr_SREG = r2;
8
volatile uint8_t isr_tmp  = r16;
9
volatile uint8_t tmp0     = r17;
10
*/
11
12
asm (
13
".equ outPORT  = PORTC"  "\n\t"
14
".equ incrmnt  = 0x20"  "\n\t"
15
".def isr_SREG = r2"  "\n\t"
16
".def isr_tmp  = r16"  "\n\t"
17
".def tmp0     = r17"  "\n\t"
18
);
19
20
void set_Timer1(void);  // Funktion deklarieren
21
void set_Timer1()       // CTC Mode
22
{
23
  cli();  //stop interrupts
24
25
  // set Timer-1 Register
26
  TCCR1A = 0;      // Reset TCCR1A Register
27
  TCCR1B = 0;      // Reset TCCR1B Register
28
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
29
  TCNT1  = 0;      // initialize counter value to 0
30
  
31
  OCR1A =  50;     // Compare Match Register A
32
  
33
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
34
  
35
  TCCR1B |= (1 << CS10);    // set Prescaler 1
36
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
37
  
38
  sei();   //allow interrupts
39
  
40
}  // end Funktion
41
42
43
ISR(TIMER1_COMPA_vect) {  // Timer.1.A Compare Interrupt
44
  asm (
45
  "in     isr_SREG,SREG"    "\n\t"
46
  "in     isr_tmp,outPORT"  "\n\t"
47
  "subi   isr_tmp,incrmnt"  "\n\t"       
48
  "out    outPORT,isr_tmp"  "\n\t"
49
  "out    SREG,isr_SREG"    "\n\t"
50
  );
51
}
52
53
54
int main(void)  {
55
  DDRC = 0xFF;   // alles Ausgänge
56
  PORTC = 0;     // alles aus
57
  
58
  set_Timer1();
59
  
60
  while(1)  {
61
62
  }
63
  
64
}   // Ende main()

von S. Landolt (Gast)


Lesenswert?

Das wird in dieser Form nicht funktionieren, woher soll c wissen, dass 
r2 und r16 reserviert sind. Versuchen Sie:
1
  push   r16
2
  in     r16,SREG
3
  push   r16
4
  in     r16,PORTC
5
  subi   r16,0x20
6
  out    PORTC,r16
7
  pop    r16
8
  out    SREG,r16
9
  pop    r16
Nun wird noch der Overhead der c-ISR drinstecken, wie Sie diesen 
wegbekommen, muss ein c-Programmierer beantworten.

von S. Landolt (Gast)


Lesenswert?

Und so betrachtet sollte reines c doch eigentlich genauso schnell sein, 
oder?

von Karl H. (kbuchegg)


Lesenswert?

S. Landolt schrieb:

> Nun wird noch der Overhead der c-ISR drinstecken, wie Sie diesen
> wegbekommen, muss ein c-Programmierer beantworten.

Das ist relativ einfach.
Man kann die ISR mit dem Attribut NAKED markieren, dann obliegt es dem 
Programmierer alle Register zu sichern und wieder herzustellen.

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

von Karl H. (kbuchegg)


Lesenswert?

S. Landolt schrieb:
> Und so betrachtet sollte reines c doch eigentlich genauso schnell sein,
> oder?

Nicht ganz.
Der gcc setzt in einer ISR einen Standard-Prolog und Standard-Epilog 
ein. In dem werden ein paar Register gesichert und wieder hergestellt. 
Es findet dabei keine Analyse statt, ob dies tatsächlich für alle im 
Prolog gesicherten Register notwendig wäre.
D.h. unter Umständen macht der Compiler hier etwas zuviel 
Sicherungsarbeit.

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> Nun wird noch der Overhead der c-ISR drinstecken, wie Sie diesen
> wegbekommen, muss ein c-Programmierer beantworten.

Es ist viel schlimmer: C-Kenntnisse helfen da garnicht. Es hängt allein 
vom verwendeten Compiler ab (konkret: von dessen Codegenerator für das 
Zielsystem), nicht von der Sprachdefinition. Und gerade der wegen seines 
günstigen Preises so beliebte avr-gcc kackt hier gar fürchterlich ab. 
Der kann das nämlich schlicht garnicht. Der ist wie die C-Fanbois 
allgemein: alles an sich raffen, alles muß C sein, sonst taugt's nix.

Aber auch die kommerzielle Konkurrenz mit etwas pragmatischerer 
Grundmotivation kann es nur eingeschränkt. D.h.: es können dort immerhin 
bestimmte Register vor dem Codegenerator sozusagen "versteckt" werden. 
Im konkreten Fall mit R2 und R16 als Delinquenten sollte es z.B. mit 
Keil durchaus gehen.

Damit das insgesamt funktioniert, darf natürlich nix binär hinzugelinkt 
werden, sondern der gesamte Code muß als Quelltext vorliegen und mit den 
entsprechenden Optionen übersetzt werden.

Fazit: Das einzige, was wirklich immer problemlos das Optimum erlaubt, 
ist Assembler. Pur, ohne jeden C-Rotz drumrum. Nur dann hat man die 
volle Kontrolle.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe derweile noch weiter probiert. Irgendwie sollte das schon 
funktionieren. Siehe den beiden Bsp. wo ich aber leider nur die Hälfte 
verstehe.
http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html
http://www.avrfreaks.net/forum/how-write-assembly-interrupts-within-c-program-solved

In meinem C Programm dachte ich, ich habe oben die 3 Register vorher 
definiert, damit der Assembler ISR weis womit er arbeiten soll.

Vielleicht muß das auch so aussehen, habe ich noch nicht probiert.

register uint8_t isr_SREG  asm("r2");
register uint8_t isr_tmp   asm("r16");
register uint8_t tmp0      asm("r17");
# define outPORT PORTC
# define incrmnt 0x20

Nur warum sieht jetzt eurer Assembler Code anders aus als der originale?
Mit einbinden durch asm ( ... ); geht wohl doch nicht so einfach wie 
überall beschrieben?

von S. Landolt (Gast)


Lesenswert?

> Nur warum sieht jetzt eurer Assembler Code anders aus als der originale?
Mein Fehler: ich wollte schnell helfen und dabei sicher sein, dass es 
läuft. Ich kann kein C und schon gar keine C-Tricks.

von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> ich habe derweile noch weiter probiert. Irgendwie sollte das schon
> funktionieren. Siehe den beiden Bsp. wo ich aber leider nur die Hälfte
> verstehe.
> http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html
> 
http://www.avrfreaks.net/forum/how-write-assembly-interrupts-within-c-program-solved
>
> In meinem C Programm dachte ich, ich habe oben die 3 Register vorher
> definiert, damit der Assembler ISR weis womit er arbeiten soll.

Das Problem ist, dass du dem Compiler nicht einfach so 3 Register klauen 
kannst!
Der restliche Code arbeitet doch mit diesen Registern. Woher soll denn 
der Compiler, wenn er ein anderes C-File compiliert, wissen, dass du 
diese 3 Register gerne zu deiner ausschliesslichen Verwendung haben 
willst?

Du agierst momentan wie jemand, der sich einfach ein Werkzeug aus dem 
Lager holt. Klar, in einer 1 Mann Bude kannst du das gerne machen. Aber 
in einer etwas größeren Firma geht das nicht mehr. Da gibt es einen 
Verantwortlichen, und dem muss man Bescheid geben. Da ist kooperatives 
Verhalten angesagt, oder man fliegt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

es kommt immer noch "constant value required"
Oder schreibt man im Definitionsabschnitt mit anderer Syntax für 
Assembler?
register uint8_t isr_SREG  asm("r2");  zum Bsp.? Wobei das so auch 
Fehlermeldungen hakelt.
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
asm (
7
 "push   r16"    "\n\t"
8
 "in     r16,SREG"  "\n\t"
9
 "push   r16"    "\n\t"
10
 "in     r16,PORTC"  "\n\t"
11
 "subi   r16,0x20"  "\n\t"
12
 "out    PORTC,r16"  "\n\t"
13
 "pop    r16"    "\n\t"
14
 "out    SREG,r16"  "\n\t"
15
 "pop    r16"    "\n\t"
16
 );
17
 
18
void set_Timer1(void);  // Funktion deklarieren
19
void set_Timer1()       // CTC Mode
20
{
21
  cli();  //stop interrupts
22
23
  // set Timer-1 Register
24
  TCCR1A = 0;      // Reset TCCR1A Register
25
  TCCR1B = 0;      // Reset TCCR1B Register
26
  TIMSK1 = 0;      // Reset TIMSK1 Register (disable Timer Compare Interrupts)
27
  TCNT1  = 0;      // initialize counter value to 0
28
  
29
  OCR1A =  50;     // Compare Match Register A
30
  
31
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
32
  
33
  TCCR1B |= (1 << CS10);    // set Prescaler 1
34
  TIMSK1 |= (1 << OCIE1A);  // enable Timer Compare Interrupt A
35
  
36
  sei();   //allow interrupts
37
  
38
}  // end Funktion
39
40
41
ISR(TIMER1_COMPA_vect) {  // Timer.1.A Compare Interrupt
42
  asm (
43
  "in     isr_SREG,SREG"    "\n\t"
44
  "in     isr_tmp,outPORT"  "\n\t"
45
  "subi   isr_tmp,incrmnt"  "\n\t"       
46
  "out    outPORT,isr_tmp"  "\n\t"
47
  "out    SREG,isr_SREG"    "\n\t"
48
  );
49
}
50
51
52
int main(void)  {
53
  DDRC = 0xFF;   // alles Ausgänge
54
  PORTC = 0;     // alles aus
55
  
56
  set_Timer1();
57
  
58
  while(1)  {
59
60
  }
61
  
62
}   // Ende main()

von Veit D. (devil-elec)


Lesenswert?

Hallo,

so wie ich das verstehe klauen die anderen doch auch in ihren Bsp. 
Register und es soll bei denen funktionieren, nehme ich an. Wie sagt man 
das dem Compiler richtig?

von S. Landolt (Gast)


Lesenswert?

Die Sequenz beginnend mit 'push r16' gehört natürlich in die ISR, und 
ist deren ausschließlicher Inhalt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

aha, okay, habs nochmal umgebaut. Leider bekomme ich weiterhin "constant 
value required" angezeigt und diesmal zeigen die Zeilennummern in die 
set_Timer1 Funktion. Warum auch immer. Die ist schon lange fehlerfrei.

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 =  50;     // Compare Match Register A
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) {  // Timer.1.A Compare Interrupt
30
  asm (
31
  "push   r16"    "\n\t"
32
  "in     r16,SREG"  "\n\t"
33
  "push   r16"    "\n\t"
34
  "in     r16,PORTC"  "\n\t"
35
  "subi   r16,0x20"  "\n\t"
36
  "out    PORTC,r16"  "\n\t"
37
  "pop    r16"    "\n\t"
38
  "out    SREG,r16"  "\n\t"
39
  "pop    r16"    "\n\t"
40
  );
41
}
42
43
44
int main(void)  {
45
  DDRC = 0xFF;   // alles Ausgänge
46
  PORTC = 0;     // alles aus
47
  
48
  set_Timer1();
49
  
50
  while(1)  {
51
52
  }
53
  
54
}   // Ende main()

von Stefan E. (sternst)


Lesenswert?

Veit D. schrieb:
> Leider bekomme ich weiterhin "constant
> value required"

Das bezieht sich auf SREG und PORTC. Das kannst du so innerhalb von 
Inline-Assembler nicht verwenden, weil der Preprocessor keine 
Ersetzungen innerhalb von String-Literalen durchführt.

von S. Landolt (Gast)


Lesenswert?

(Wenn's dem Landolt zu wohl wird, geht er aufs C-Eis)

Wäre das eine Lösung?:
1
ISR(TIMER1_COMPA_vect) {  // Timer.1.A Compare Interrupt
2
  asm volatile(
3
  "push   r16"    "\n\t"
4
  "in     r16,%[sreg]"  "\n\t"
5
  "push   r16"    "\n\t"
6
  "in     r16,%[port]"  "\n\t"
7
  "subi   r16,0x20"  "\n\t"
8
  "out    %[port],r16"  "\n\t"
9
  "pop    r16"    "\n\t"
10
  "out    %[sreg],r16"  "\n\t"
11
  "pop    r16"    "\n\t"
12
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
13
    [port] "I" (_SFR_IO_ADDR(PORTC)));

Und irgendwo noch KHBs 'NAKED'?

von Karl H. (kbuchegg)


Lesenswert?

1
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
2
  asm volatile (
3
   "push   r16"           "\n\t"
4
   "in     r16,__SREG__"  "\n\t"
5
   "push   r16"           "\n\t"
6
   "in     r16, %0"       "\n\t"
7
   "subi   r16,0x20"      "\n\t"
8
   "out    %0,r16"        "\n\t"
9
   "pop    r16"           "\n\t"
10
   "out    __SREG__,r16"  "\n\t"
11
   "pop    r16"           "\n\t"
12
   "reti"
13
  : 
14
  : "M" (_SFR_IO_ADDR (PORTC))
15
  );
16
}


Compiler drüber jagen, LSS File studieren

(Allerdings ist das mit der heissen Nadel gestrickt. Mit den Clobber 
Listen kenn ich mich selbst nicht wirklich aus)

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


Lesenswert?

Hallo,

man muß auch mal dünnes Eis betreten dürfen, mit Sicherheitsleine.  :-)

Also, Euer beider Code wird fehlerfrei kompiliert. Das macht Hoffnung. 
Auch wenn ich da nun Code lese technisch raus bin.

Bei dem von Karl-Heinz takten sie jedoch nicht jeweils mit der halben 
Frequenz der vorherigen. 2 takten gleich.

Bei dem von S.Landholt takten die 3 wie sie sollen. Aber leider ist das 
Gesamtpaket nicht schneller, eher minimal langsamer als reiner C Code. 
Was aber auffällt ist, dass es bei zu kurzem Compare Wert NICHT zu 
Taktaussetzer kommt wie unter reinem C. Wie wenn der ISR nicht 
hinterkommt oder so ähnlich. Der max. Takt hängt hier im Gemischtpaket 
bei 195kHz fest. Mit reinem C sind noch 220kHz möglich. In reinem 
Assembler ca. 500kHz.

Kann man vielleicht noch etwas optimieren wenn jetzt der richtige Weg 
eingeschlagen wurde? Bin fasziniert von euren Code Künsten.


Was bedeutet KHBs oder NAKED?
Wie komme ich an das LSS File?

Habe Atmel Studio.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Veit D. schrieb:


> Bei dem von S.Landholt takten die 3 wie sie sollen. Aber leider ist das
> Gesamtpaket nicht schneller, eher minimal langsamer als reiner C Code.

Logisch. Weil seine ISR nicht NAKED ist.
Sieht man sich das Listing File an, dann sieht man dass der Compiler 
beim Einstieg in die ISR ein paar Register sichert (zb das SREG) und 
dann sein Code nochmal.

> Was bedeutet KHBs oder NAKED?

Hab ich oben den Link vergessen? Mag sein.

NAKED bedeutet, dass sich der Compiler aus der ISR raushält. Er sichert 
nichts. Der Programmierer ist komplett alleine eigenverantwortlich.

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

> Wie komme ich an das LSS File?

Das ist in einem deiner Projektverzeichnisse. Aus dem Kopf raus: ich 
denke irgendwo im Debug Subverzeichnis

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Ein letzter Versuch meinerseits (aber auch nur, weil sich außer KHB 
(damit meinte und meine ich Karl Heinz Buchegger) niemand beteiligt):
1
ISR(TIMER1_COMPA_vect, ISR_NAKED) {  // Timer.1.A Compare Interrupt
2
  asm volatile(
3
  "push   r16"    "\n\t"
4
  "in     r16,%[sreg]"  "\n\t"
5
  "push   r16"    "\n\t"
6
  "in     r16,%[port]"  "\n\t"
7
  "subi   r16,0x20"  "\n\t"
8
  "out    %[port],r16"  "\n\t"
9
  "pop    r16"    "\n\t"
10
  "out    %[sreg],r16"  "\n\t"
11
  "pop    r16"    "\n\t"
12
  "reti"    "\n\t"
13
  ::[sreg] "I" (_SFR_IO_ADDR(SREG)),
14
    [port] "I" (_SFR_IO_ADDR(PORTC)));

von S. Landolt (Gast)


Lesenswert?

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 Veit D. (devil-elec)


Lesenswert?

Hallo,

die Beteiligung wird niedrig sein, weil das "Unterthema" im falschen 
Thread steckt. Nehme ich an. Soll/darf ich einen neuen Thread eröffnen? 
Wenn das Sinn macht.

Dein Code geht jetzt bis 235kHz. Schon einmal schneller als reines C. 
Ist aber komischerweise immer noch Meilenweit von reinem Assembler 
entfernt. Ich ging davon aus, dass man mit eingebauten Assembler Code 
genau die gleiche Geschwindigkeit erreicht für den Code Teil wie mit 
reinem Assembler. Da lag ich bestimmt falsch.

Jedenfalls vielen Dank an Euch für Eure Freizeit Opferung für mich und 
das Code schreiben. Hätte ich nie hinbekommen.

von S. Landolt (Gast)


Lesenswert?

Seltsam, da muss jetzt doch ein C-Spezialist ran.
Ich hatte gedacht, Aufruf und Routine haben 16 Takte, dazwischen noch 
ein rjmp mit 2 Takten, könnte aber auch ein jmp mit 3 sein, macht 
maximal 19. Bei 16 MHz käme ich dann auf 421 kHz.
Schauen Sie doch mal in diesem LSS-File nach, was der Compiler 
produziert.

In reinem Assembler könnte man 2 Arbeitsregister reservieren, das sparte 
2 out/in mit insgesamt 4 Takten, das rjmp ist immer möglich, also 
nochmal 1 weniger, macht 14 entsprechend 571 kHz. Dies hatten wir schon 
einmal viel weiter oben. Wenn die Interrupts OC1B und OC1C nicht 
benötigt werden, ließe sich noch das rjmp sparen, das wären dann 12 
Takte entsprechend 666 kHz (immer mit 16 MHz gerechnet).

> Freizeit Opferung
Gar so uneigennützig ist es nicht, man lernt auch beim Antworten etwas 
(z.B. wenn ich jetzt auf einen Fehler hingewiesen würde).

von S. Landolt (Gast)


Lesenswert?

Korrektur: der Einsprung in die ISR benötigt einen Takt mehr, und bei 
den großen Controllern auch der Rücksprung. So erreicht man mit einem 
ATmega16 615 kHz, mit einem ATmega1284 nur 571, bei reinem Assembler.
Das erklärt aber nicht die große Differenz zu Ihren 235 kHz bei C mit 
Assembler.

von c-hater (Gast)


Lesenswert?

S. Landolt schrieb:

> Korrektur: der Einsprung in die ISR benötigt einen Takt mehr, und bei
> den großen Controllern auch der Rücksprung.

Nein. Wenn man die ganz kleinen Tinys mit ihrer irregulärer Architektur 
mal wegläßt, also beim Tiny13 aufwärts mit der Analyse beginnt, gibt es 
folgende Fälle:

22Bit-PC (ja/nein)
Stack im externen RAM (ja/nein)
ISR-Zieladresse (InPlace/per rjmp erreichbar/per jmp erreichbar)

Insgesamt also zwölf denkbare Fälle.

Da "Stack im externen RAM" zwar technisch möglich, aber eine krasse 
designerische Fehlentscheidung ist, kann man das wohl schonmal komplett 
ignorieren.

Bleiben sechs Fälle.

ISR-Zieladresse "InPlace" ist nur bei recht speziellen Anwendungen 
möglich (ist dann allerdings oft eine durchaus attraktive Optimierung). 
Für den Normalfall kann man es aber vorläufig in den Skat drücken. 
ISR-Zieladresse nur per far jmp erreichbar ist wiederum eine krasse 
designerische Fehlentscheidung, muß man also nicht haben. Selbst mit der 
Krücke C hat man ja die Macht, die ISRs auf eine "naheliegendere" 
Adresse bezüglich der Vektortabelle zu drücken.

Bleiben also effektiv zwei Fälle:
22Bit-PC (ja/nein)

Nur darauf hat man echt absolut keinen Einfluß. Kostet halt entweder 8 
oder 12 Takte für den ISR-Frame, weil entweder 2 oder 3 Byte für das 
reti auf den Stack gelegt und von dort wieder zurück geholt werden 
müssen.

Zum Glück gibt es eigentlich nur ein AVR8-Device (Mega2560), bei dem man 
mit dieser Scheiße überhaupt konfrontiert wird. Und man muß den ja nicht 
benutzen. Zumal das Teil eigentlich außer dem großen Flash rein garnix 
wirklich attraktives zu bieten hat...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

den Mega2560 habe ich wegen dem vielen SRAM und großen Flash genommen, 
dass ich mir für den Anfang nicht so einen großen Kopf machen muß ums 
Speicher sparen usw. hat 3 hardware Serial dabei, 3 Timer usw. Ideal rum 
rumspielen.  Die kleineren µC wird man dann nehmen wenn man genau weis 
was man damit macht. Ich bin noch in der Findungsphase ...

Ich habe jedenfalls von Euch viel gelernt, auch wenn ich noch nicht 
alles verstanden habe. Dann werde ich in paar Tagen das Thema C und 
Assembler nochmal in einen neuen Thread eröffnen. Dürfte ja nicht unter 
Crossposting fallen. Hoffe ich.

Danke @ all.

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> den Mega2560 habe ich wegen dem vielen SRAM und großen Flash genommen,
> dass ich mir für den Anfang nicht so einen großen Kopf machen muß ums
> Speicher sparen usw. hat 3 hardware Serial dabei, 3 Timer usw. Ideal rum
> rumspielen.

Mega1284P wäre die weitaus weisere Wahl gewesen. Doppelt soviel SRAM, 
mehr Timer, viele Fähigkeiten der erneuerten AVR8-Architektur (z.B.: 2 
SPI-Master-fähige UARTs, toggelnde PINx-Schreibzugriffe, jeder Pin 
PCINT-fähig), obendrein billiger und auch in DIL für Raster-Layouts für 
Versuchszwecke verfügbar.

Und auch die 128k Flash des Mega1284P mußt du erstmal mit Code voll 
bekommen. Konstante Daten im Flash zählen nämlich i.d.R. nicht wirklich 
mit, die kann man (wenn's wirklich mal eng werden sollte im Flash-Space) 
größtenteil sehr leicht auf seriellen Zusatzflash auslagern. Der ist 
spottbillig, leicht verfügbar und leicht anzubinden.

von spess53 (Gast)


Lesenswert?

>Doppelt soviel SRAM,
>mehr Timer, viele Fähigkeiten der erneuerten AVR8-Architektur (z.B.: 2
>SPI-Master-fähige UARTs, toggelnde PINx-Schreibzugriffe, jeder Pin
>PCINT-fähig), obendrein billiger und auch in DIL für Raster-Layouts für
>Versuchszwecke verfügbar.

Eigentlich hat der ATMega2560 insgesamt 6 Timer. Die vier USARTs können 
theoretisch auch SPI. Allerdings haben dir Arduino-Jungs durch fehlende 
XCK-Anschlüsse die USARTs so kastriert, das das nicht nutzbar ist. PCINT 
wird überschätzt.

MfG Spess

von S. Landolt (Gast)


Lesenswert?

> Mega1284P wäre die weitaus weisere Wahl gewesen.
Erwähnt sei noch, dass er mit 20 MHz spezifiziert ist.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

oh ja, der 1284P hat 16kB SRAM. Upps. Man muß eben wie immer im Leben 
abwägen was man braucht. Viele Timer oder viel SRAM oder viele I/O Pins 
oder viele UARTs oder ... dafür gibts den 1284P im DIP Gehäuse. Hat 
seinen Reiz.
Beim nächstenmal.  :-)

Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon 
vorhanden ist. Man benötigt doch nur einen SPI Bus.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon
>vorhanden ist.

Weil USART im SPI-Mode universeller ist:

- Gepufferter Transmitter und Receiver
- Flexiblere Baudrateneistellung durch UBRR
- Die Problematik mit dem SS-PIN entfällt

>Man benötigt doch nur einen SPI Bus.

Es gibt aber 4 verschiedene SPI-Modes. Und es ist wesentlich entspannter 
dafür unterschiedliche Hardware zu nutzen.

MfG Spess

von Joachim B. (jar)


Lesenswert?

Veit D. schrieb:
> Viele Timer oder viel SRAM oder viele I/O Pins
> oder viele UARTs oder ... dafür gibts den 1284P im DIP Gehäuse. Hat
> seinen Reiz.

und als  kleine Arduino Clone Platine
http://www.ebay.com/itm/Mighty-Mini-ATMega1284p-compatible-with-Arduino-/331463717483

https://github.com/JChristensen/mini1284
https://www.mikrocontroller.net/attachment/244383/m1284p_m328p.jpg

von c-hater (Gast)


Lesenswert?

Veit D. schrieb:

> Nur warum soll man die UARTs Pins als SPI verwenden, wenn SPI doch schon
> vorhanden ist. Man benötigt doch nur einen SPI Bus.

Im Gegensatz zur dedizierten SPI-Hardware sind die USARTs double 
buffered. Das ermöglicht es, problemloser höhere Geschwindigkeiten zu 
erreichen und vor allem auch stabil zu halten. Außerdem ermöglicht es 
die Erzeugung einer wirklich kontinuierlichen Waveform, was zwar für die 
SPI-Anwendung recht unwichtig ist, aber für viele andere Zwecke sehr 
nützlich.

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.