Atmega 328 Timer 2: Hilfe für Frequenzerzeugung

Gast #5763523
Lesenswert?

Hallo,

mir raucht schon der Schädel von dem Datenbuch was mir auch nicht 
weiterhilft mit den Timern. Ich will einfach nur

Timer 2: 40khz Frequenz (25us Periode) auf Pin und das war es. Ein 
Ausschalten auch noch dazu.

Hat da jemand vielleicht einfach ein paar Zeilen Code, die ich anpassen 
kann?
Ich blick da nicht mehr durch zwischen Waveform Mode,  PWM Mode, 
Capture, welchen nehme ich denn nun?

Gruss,
Christian
Gast #5763527
Lesenswert?

Toll... ja, aber das nützt nix, ich muss an die Registerebenr dran! Der 
Arduino Murks bietet da nichts an.

Kann ja nicht so schwer sein, auf dem Cortex alles kein Thema, da bin 
ich schneller fertig als beim Arduino....

40 khz, d.h. 8E6 Hz : 1 : (199+1) = 40.000
Prescaler = 1
Timer Top bei 199

soweit bin ich schon. Da muss er dann zurücksetzen auf 0 und den Pin 
toggeln.
Angehängte Dateien:
Gast #5763548
Lesenswert?

Ich kann mit solchen Bruchstück-Sätzen leider nicht viel anfangen. Da 
gibt es noch einiges mehr, was gesetzt werden will. bei jedem anderen uC 
ist das einfacher als bei Atmel.

Wenn eine Periode 25us sind, dann muss er bei 25us schalten und bei 50us 
einen Auto-Reload machen. Dann habe ich 50% Duty Cycle.
Angehängte Dateien:
#5763579
Lesenswert?

Christian J. schrieb:
> und was mache ich mit diesen Waveform Bits? Wie müssen die gesetztt
> werden?

ProTipp (Von Cyberlord zu Mikrosklaven):
Das Datenblatt hat ja nicht nur diese Tabellen zu bieten, sondern sogar 
auch Prosa welche die verschiedenen Modi erklärt. Und ich fühle dass da 
bei dir etwas Bedarf besteht.

> bei jedem anderen uC ist das einfacher als bei Atmel.

Natürlich. Wenn man nicht schwimmen kann ist die Badehose schuld. AVR 
ist da echt ganz ganz doll kompliziert.
Gast #5763592
Lesenswert?

S. Landolt schrieb:
> Mann, das kostet mich ein Drittel der Kaffeepause.

Ja, das kommt davon, wenn man auf dem Handy tippt statt am PC :-)
Ist aber schon gut, ich hämmer das heute abend in einen Arduino rein, 
Oszi dazu und üblicherweise ist nach rund 20 Minuten etwas fertig, was 
genaudas macht, ohne dass ich mir die Romane aus dem Datenbuch 
durchlesen muss.
#5763602
Lesenswert?

Christian J. schrieb:

> Ist aber schon gut, ich hämmer das heute abend in einen Arduino rein,
> Oszi dazu und üblicherweise ist nach rund 20 Minuten etwas fertig, was
> genaudas macht, ohne dass ich mir die Romane aus dem Datenbuch
> durchlesen muss.

Warum machst du dann hier überhaupt so lange rum? Oben war Arduino noch 
Murks. Bist du Wankelmütig?

> Kann ja nicht so schwer sein, auf dem Cortex alles kein Thema, da bin
> ich schneller fertig als beim Arduino....

Wie kommt es das du das angeblich auf einem Cortex ohne in Datenblättern 
zu lesen mal so locker machst und beim popeligen AVR scheiterst? Passt 
doch nicht zusammen.

Ich persönlich habe dich in die Schublade "talentlosen Schwätzer" 
gesteckt.
Beitrag #5763614 wurde von einem Moderator gelöscht.
Gast #5782844
Lesenswert?

Ich hänge es hier nochmal drunter

Die unten stehende Kombination von Bits ist die einzige, die mir eine 
Frequenz von 40khz erzeugt. Alle denkbar anderen haben eine fixe 
Frequenz von 15.x khz (8 Mhz Takt). Das AVR Datenblatt ist teils schwer 
verständlich, auch nach x Mal durchlesen blicke ich teilweise nicht 
durch. Wie man in Foren sieht viele andere allerdings auch nicht.

Ich habe ein Rechteck an Pin B3 (Arduino = 11), also an OC2A. Hätte es 
zwar lieber gern an OC2B aber das habe ich nicht hin gekriegt. Falls es 
überhaupt möglich ist. zB mit OCRB. Aber bei CTC steht nur OCRA in der 
Tabelle.

Die Frage ist: Was hat dieses WGM22 Bit für eine Bewandnis? Eigentlich 
sollte es doch gelöscht sein für CTC Mode, oder? Könnte ja auch sein, 
dass ich den Fast PWM Mode erwischt habe. Auch der hat bei OCRA = TOP 
seinen Match.

In Tabelle 17-8 steht

Mode 2: WGM2 = 0, WGM1 = 1, WGM0 = 0   (CTC Mode), Top = OCRA.

Wenn man diese "Verallgemeinerung" anlegt steht da für Timer 2 doch
WGM22 = 0; WGM21 = 1, WGM20 = 0

Ob da ein x fehlt für die Timer Nummer?


Nur kommt da dann nichts aus dem Pin raus. Da ich eine IR Diode dran 
habe muss sicher gestellt sein, dass beim Abschalten der Pin auf 0 geht. 
Das scheint nicht immer der Fall zu sein, wenn man den Pin nicht in den 
Normalmode zurück stellt.
1
/* Startet oder stoppt den Timer 2 */
2
void Timer2Enable(bool stat)
3
{
4
  /* OC2B Pin = INT 1 Pin (PD 3)
5
     OC2A Pin = MOSI Pin (PB 3)
6
  */
7

8
  cli();  
9
  if (stat) {
10
    OCR2A  = 50;              // Compare Match A Register auf 40khz
11
    TCCR2A = (1 << COM2A0) | (1 << WGM20); 
12
    TCCR2B = (1 << WGM22)  | (1 << CS20);   // Mode 2 CTC Prescaler 1 und Timer starten  
13
  }
14
  else {
15
    TCCR2B  = 0;         // Prescaler 1 und Timer stoppen
16
    TCCR2A  = 0;     
17
    digitalWrite(11,LOW);  // Pin auf 0 setzen
18
  }
19
  sei();
20
}
Gast #5782881
Lesenswert?

Christian J. schrieb:
> OCR2A  = 50;
> TCCR2A = (1 << COM2A0) | (1 << WGM20);
> TCCR2B = (1 << WGM22)  | (1 << CS20);

In der Reihenfolge steckt ein Problem und lauert nur darauf einen 
Wahnsinnig zu machen.

Ich habe nicht alles geprüft.
Aber dieses ist mir aufgefallen!
Ob Timer2 davon betroffen ist, weiß ich auch nicht genau.
Aber viele AVR Timer sind es.

Prüfe also selber die folgenden Aussagen !


Folgende Situation:

Der Timer befindet nach dem Reset in Mode0
Der Arduino Startup Code bereitet ihn für PWM vor.

Im PWM Mode wird OCR2A gelatscht.
Im CTC Mode nicht.

Du schreibst also 50 in das Latch.
Stellst dann den Mode um.
Folge: Der Latchinhalt landet nie im Register.

Mit einigen Timern(allen?) führt das zur Totalblockade des AVR
Gast #5782893
Lesenswert?

Arduino Fanboy D. schrieb:
> Folge: Der Latchinhalt landet nie im Register.

Dass er es doch tut lässt sich auf dem Oszi einwandfrei nachvollziehen. 
Jede Veränderung des Wertes ändert die Frequenz um n * 1/8E6 Schritte. 
39,95xx khz erziele ich. Das reicht auch. Nur die Frage: Bin ich 
überhaupt im CTC Mode ??? Habe noch stur alle relevanten Bits 
durchprobiert, nur mit dieser Kombination funzt es.
(Firma: Vescomp) #5782997
Lesenswert?

Christian J. schrieb:
> Die unten stehende Kombination von Bits ist die einzige, die mir eine
> Frequenz von 40khz erzeugt.

 Nein, mit OCR2A = 50 bestimmt nicht.

 Dein 328 läuft mit 8MHz, d.h. eine Clockperiode dauert 125ns.
 Du willst alle 25us Pin OC2A toggeln.
1
  DDRB |= (1<<PB3);
2
  TCCR2A = (1 << WGM21) | (1 << COM2A0); // CTC (MODE_2) + Toggle OC2A
3
  TCCR2B = (1 << CS20);                  // Clock(125ns) => Timer2
4
  OCR2A = 199;                           // 125ns * (199+1) = 25000ns = 25us
5
  TCNT2 = 0;                             // Reset Timer2 Counter

 Und das war es schon, funktioniert bestimmt, 1000 Mal erprobt.
Gast #5783004
Lesenswert?

Marc V. schrieb:
> Und das war es schon, funktioniert bestimmt, 1000 Mal erprobt.

Der einzige Unterschied bei dir ist doch das WGM22 Bit weg und dafür das 
WGM21 rein und der Matchwert 199.

Danke, ich probiere es aus!. Aber doch... 40khz kommen bei mir raus, 
ganz sicher nach gemessen :-) Nur warum das so ist war nicht klar.... 50 
ist 200 / 4... also schon mal ein Vielfaches.
Gast #5783021
Lesenswert?

Christian J. schrieb:
> Und womit würde man dann den Duty Cycle einstellen?
Willst du doch gar nicht....

Das ist alles gar nicht ganz so schwer...
Nur ausprobieren, dass ist hier die schlechteste aller Lösungswege.
Denn die Anzahl der möglichen Permutationen ist erstaunlich groß.

Es bleibt dabei:
Datenblatt lesen und das so oft und so lange, bis es verstanden ist.

Hier eine 16MHz Version.
Für 8MHz Arduinos könntest du OCR2A verdoppeln
Oder eine passendere Vorteiler/OCR2A Kombination ermitteln.
1
#include <util/atomic.h>
2
#define AtomicSection ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
3

4

5
void setTimer()
6
{
7
  AtomicSection
8
  {
9
    TCCR2A = _BV(COM2A0) |_BV(WGM21);
10
    TCCR2B = _BV(CS21);
11
    OCR2A  = 24;
12
    TCNT2  = 0;
13
  }
14
}
15

16

17

18

19
void setup() 
20
{
21
 Serial.begin(9600);
22
 Serial.println("Start");
23
 setTimer();
24
}
25

26
void loop() 
27
{
28
  pinMode(11, OUTPUT); // Burst
29
  delay(100);
30
  pinMode(11, INPUT);  // Pause
31
  delay(1000);
32
}
Beitrag #5783044 wurde von einem Moderator gelöscht.
Gast #5783130
Lesenswert?

Christian J. schrieb:
> Arduino Fanboy D. schrieb:
>> #define AtomicSection ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
>
> Ok. Bringt das hier Vorteile gegenüber sei und cli? Soll ja wohl
> heissen, dass da kein Int zwischenfunken darf denke ich mal.

Ja, es löscht erst das Flag und erzwingt dann, am Ende des Blocks, dass 
Interrupt Enable Flag wieder auf den alten Stand gesetzt wird.

Händisch ist oft/meist fehlerträchtiger, als ein erprobter Automatismus.
#5783146
Lesenswert?

Christian J. schrieb:
> Harry L. schrieb:
>> Was ist daran fehlerträchtig?
>
> Da bei einem Arduino eh dauernd ein INT läuft ist es auch wumpe den aus
> und wieder ein zu schalten. Schaltet man ihn ab funktioniert einiges
> nicht mehr, zb die Serielle nicht, delay usw. usw.

Ja und?
Man schaltet den ja deshalb auch nicht dauerhaft aus, sondern nur für 
kritische Abschnitte, die selten länger als wenige µs dauern.

"Wumpe" ist das ganz und gar nicht!
Gast #5783199
Lesenswert?

Oliver S. schrieb:
> Äh, warum nicht?
Weil der Latchinhalt erst in das Register geschrieben wird, wenn der 
Zähler TOP erreicht.

Oliver S. schrieb:
> Äh, warum?
Tja.....
Das Problem ist das Latch, in Verbindung mit dem Moduswechsel von PWM 
auf Compare Match


Hier kann ich dir ein Beispiel für eine solche Blockade liefern.
Nur Timer init und ISR
(es stammt nicht aus meiner Feder, Kritik am Stil wird verpuffen)

Geschrieben für Arduino Mega, also ATMEGA2560
1
// diese Version funktioniert     
2
void set_Timer2()         // CTC, Mode 2
3
{
4
  cli();                  // Interrupts ausschalten
5
  TCCR2A = (1<<WGM21);    // set Mode
6
  TCCR2B = (1<<CS22);     // Prescaler 64
7
  TCNT2 = 0;              // Reset Register                 
8
  OCR2A = 249;            // Compare
9
  TIMSK2 = (1<<OCIE2A);   // Compare Match A
10
  sei();                  // Interrupts einschalten
11
}  
12
    
13
/*  
14
// diese Version versagt, totale µC Blockade 
15
void set_Timer2()         // CTC, Mode 2
16
{
17
  cli();                  // Interrupts ausschalten
18
  TCNT2 = 0;              // Reset Register                 
19
  OCR2A = 249;            // Compare
20
  TIMSK2 = (1<<OCIE2A);   // Compare Match A
21
  TCCR2A = (1<<WGM21);    // set Mode
22
  TCCR2B = (1<<CS22);     // Prescaler 64
23
  sei();                  // Interrupts einschalten
24
}  
25
*/ 
26

27

28
ISR(TIMER2_COMPA_vect)          // Timer 2 Interrupt 
29
{  
30
  static bool state = LOW;
31
  state = !state;
32
  digitalWrite(pin_T2, state);                    
33
}
Gast #5783215
Lesenswert?

Harry L. schrieb:
> Arduino Fanboy D. schrieb:
>> Händisch ist oft/meist fehlerträchtiger, als ein erprobter Automatismus.
>
> Was ist daran fehlerträchtig?void blabla(void)
> {
> uint8_t sreg;
>   sreg = SREG;
>   cli();
> ...
> ...
> ...
>   SREG = sreg;
> }
>
> genau so wird das in einer Microchip-App-Note beschrieben.

Kann ja sein!
Tausend Fliegen beweisen: Scheiße schmeckt gut!

Im Ernst:

  cli();
...
...
...
  SREG = sreg;

Man könnte das ein oder andere cli() oder SREG = sreg, versehentlich 
löschen, falsch platzieren.

Bei Blockklammern fallen fehlende Klammern sofort auf.
Der Compiler jault erzürnt ....

Und durch die (hoffentlich vorhandene) Einrückung sieht man klar und 
deutlich was zusammen gehört.

Also:
Es ist grundsätzlich egal, welche Methode man verwendet.
Mir, ist allerdings die Block Methode lieber!

Und ja, natürlich kann man auch über diese Geschmacksfrage, 
Glaubenskriege anzetteln.
#5783249
Lesenswert?

Arduino Fanboy D. schrieb:
> Hier kann ich dir ein Beispiel für eine solche Blockade liefern.

Kannst du nicht. "Totale Blockade" gibt es beim ATMega schlicht gar 
nicht. Das Programm macht nicht das, was der Programmierer sich 
vorgestellt hat, aber laufen tut das immer.

Zudem setzt du in der angeblich fehlerhaften Version 2 Register OCRA vor 
allen anderen. Das Latch-Problem kann da also gar nicht auftreten.

Natürlich kann das ein Problem sein, wenn man eins der gelatchten 
Register erts nach der Anwahl des Timermodus schreibt. Der Wert wird 
dann erst beim nächsten BOTTOM/TOP geupdated, oder wann auch immer in 
dem Modus das Register geupdated wird. Je nachdem, was man da vorher in 
die Register geschrieben hat, passiert das eventuell gar nicht, wobei 
mir dazu beim OCR jetzt erst einmal kein Fall einfallen will.

Eine Blockade tritt da aber niemals auf.

Oliver
#5783432
Lesenswert?

Arduino Fanboy D. schrieb:
> Oliver S. schrieb:
>> Das Latch-Problem kann da also gar nicht auftreten.
> Irgendwas hast du da noch nicht ganz verstanden.

Äh, doch.


>
> Oliver S. schrieb:
>> Eine Blockade tritt da aber niemals auf.
> Getestet?
> Warum nicht?
> Die genannten Bedingungen eingehalten?

Natürlich. Es passiert - nix. Der Prozessor läuft in seiner 
Programmende-Endlosschleife fröhlich vor sich hin. Der Timer macht halt, 
was man ihm sagt, und nicht, was sich der Programmierer wünscht. Von 
totaler Blockade aber keine Spur.

Oliver
Gast #5783639
Lesenswert?

Ich will dich nicht schocken aber dein Code, copy & paste grad 
ausprobiert erzeugt leider nichts. Nacktes Pro Mini Board aus der Tüte. 
Nur einen High Pegel auf dem PB3 Pin. Nehme ich WGM22 wieder rein kommt 
40khz aber OCR2B hat keinerlei Auswirkungen.

Ok, Fehler suche ich jetzt aber nicht mehr, muss noch bügeln.....
(Firma: Vescomp) #5783663
Lesenswert?

Christian J. schrieb:
> Hast du auch ne Lösung für schnelle Stop/Starts? Den Takt abklemmen mit
> CS0. Für IR muss aber auf jeden Fall dann  PB3 auf low gehen.

 Was heißt schnell ?
 Nachdem du einmal Init gemacht hast:

 STOP:
1
 TCCR2B = 0;
2
 PORTB &= ~(1<<PB3);
 Das braucht selbst beim GCC nur 2+2 Takte, da meistens r1 dazu
 genommen wird und dessen Inhalt ist normallerweise immer 0.

 Erneuter START:
1
   TCCR2B = (1 << CS20);
 Das braucht 3 Takte.
Gast #5783814
Lesenswert?

Arduino Fanboy D. schrieb:

> cli();                  // Interrupts ausschalten
>   TCNT2 = 0;              // Reset Register
>   OCR2A = 249;            // Compare
>   TIMSK2 = (1<<OCIE2A);   // Compare Match A
>   TCCR2A = (1<<WGM21);    // set Mode
>   TCCR2B = (1<<CS22);     // Prescaler 64
>   sei();

Schießt den uC tatsächlich völlig ab. Der ist nirgendwo mehr. Öfter mal 
was Neues.... beim ARM käme wohl eine "Hard Fault Exception" aber beim 
AVR328P eher nicht. Die nachfolgende Blink Routine wird gar nicht mehr 
erreicht und ein Reboot Findet auch nicht statt. Nur der WDT holt ihn 
wieder raus.
Gast #5784114
Lesenswert?

> Schießt den uC tatsächlich völlig ab.

Sagen wir lieber "den Arduino" statt "den uC"; und wenn die 
Arduino-Umgebung nicht einen ganz ungewöhnlichen Kunstgriff in der ISR 
für OCIE2A verwendet, so läuft das Hauptprogramm sehr wohl weiter, aber 
extrem langsam, immer nur ein einziger Befehl pro ISR-Aufruf.
Gast #5784117
Lesenswert?

Hi

>Schießt den uC tatsächlich völlig ab. Der ist nirgendwo mehr.

Dann macht entweder die Arduino-Software total Mist oder du hast einen 
anderen Fehler.

Wenn ich die Initialisierung mit diese Werten in Assembler mache, 
springt der Controller wie gewünscht in den TIMER2_COMPA-Interrupt und 
gibt eine Frequenz von 250Hz aus.

MfG Spess
#5784121
Lesenswert?

Arduino Fanboy D. schrieb:
> Das Problem ist das Latch, in Verbindung mit dem Moduswechsel von PWM
> auf Compare Match

Kannst du mal für mein Verständnis ein vollständiges Programm zeigen? 
Denn in deinem Beispiel sehe ich gar kein PWM-Mode, da wird der Timer 
von was auch immer auf CTC Mode (nur WGM21=1) eingestellt.

"Was auch immer" war bei meinem Test Normal-Mode nach Reset.

Oliver
Gast #5784128
Lesenswert?

an Spess:
1
  sbi     DDRB,0              ; Blink-LED
2

3
  ldi     tmp0,(1<<WGM20)
4
  sts     TCCR2A,tmp0         ; hier auskommentieren
5

6
  ldi     tmp0,249
7
  sts     OCR2A,tmp0
8
  ldi     tmp0,(1<<OCIE2A)
9
  sts     TIMSK2,tmp0
10
  ldi     tmp0,(1<<WGM21)
11
  sts     TCCR2A,tmp0
12
  ldi     tmp0,(1<<CS20)
13
  sts     TCCR2B,tmp0
14
  sei
15

16
main_loop:
17
  rcall   wait100ms
18
  sbi     PINB,0
19
  rjmp      main_loop

Lassen Sie dies hier mal laufen, und anschließend das erste 'sts 
TCCR2A,tmp0' auskommentieren.
Gast #5784136
Lesenswert?

S. Landolt schrieb:
> Sagen wir lieber "den Arduino" statt "den uC"; und wenn die
> Arduino-Umgebung nicht einen ganz ungewöhnlichen Kunstgriff in der ISR
> für OCIE2A verwendet,

Ich habe da keine wiss. Untersuchung gemacht. Ein Dauer INT Feuer, weil 
ein Bit nicht zurück gesetzt wurde legt auch jede CPU lahm, auch den 
ARM. Und die Arduino IDE setzt hauptsächlich den Frame über den Code 
namens setup und loop. Zudem wird Timer 0 für Millis gebrauchtr. Alles 
was aber in den Routinen steht, da ist nur der GCC für zuständig. Ich 
kenne auch den Startup Code nicht, geschweige denn weiss ich nicht, ob 
da Default INT Handler installiert sind.

Möge sich damit jemand befassen, der Spass daran hat. zb mit AVR Studio 
und einem echten Debugger am JTAG Port. Das habe ich alles nicht.
Gast #5784157
Lesenswert?

S. Landolt schrieb:
> Aber Sie verstehen nun, weshalb ich vorsichtshalber das Nullsetzen von
> TCCR2A und B voranstellte.

Nein. Nur wenn da Mechanismen am Werk sind, die irgendein Schaltwerk 
bedienen. Register sind ja keine RAM Variablen, sondern Hardware Latches 
mit Leitungen dran von und zu irgendwelchen Peripherien. Auch beim ARM 
gibt es solche, die in einer bestimmten Reihenfolge gesetzt werden 
müssen, obwohl dieses im ersten Moment unsinnig erscheint. Um das zu 
ergründen müsste man den VHDL Code des AVR haben und den im Simulator 
laufen lassen. Und ich nehme an, dass der nicht Open Source ist.

Errata steht nix drin:
https://www.kitronik.co.uk/pdf/46101_atmega328_summary.pdf
Beitrag #5784244 wurde von einem Moderator gelöscht.
Gast #5784261
Lesenswert?

Ok....

Ich glaube ich muss einen kleinen Rückzug machen!
Denn das Problem ist gerade auch bei mir nicht reproduzierbar.

Schade eigentlich, den original Testcode habe/finde ich nicht mehr.
Den mit der Totalblockade.
Hinter der Totalblockade muss ich jetzt also ein deutliches Fragezeichen 
platzieren.

Was allerdings weiterhin gültig bleibt:

Der OCR2A Wert wird nicht übernommen.
Das gilt auf jeden Fall für den Timer2 vom ATMEga328P und ATMega2560
1
OCR2A  = 50;
2
TCCR2A = (1<<WGM21);   
3
TCCR2B = (1<<CS22);
>
> In der Reihenfolge steckt ein Problem und lauert nur darauf einen
> Wahnsinnig zu machen.
>
> .... Folgende Situation:
>
> Der Timer befindet sich nach dem Reset in Mode0
> Der Arduino Startup Code bereitet ihn für PWM vor.
>
> Im PWM Mode wird OCR2A gelatscht.
> Im CTC Mode nicht.
>
> Du schreibst also 50 in das Latch.
> Stellst dann den Mode um.
> Folge: Der Latchinhalt landet nie im Register.
Gast #5784278
Lesenswert?

Oliver S. schrieb:
> Kannst du mal für mein Verständnis ein vollständiges Programm zeigen?
> Denn in deinem Beispiel sehe ich gar kein PWM-Mode, da wird der Timer
> von was auch immer auf CTC Mode (nur WGM21=1) eingestellt.
>
> "Was auch immer" war bei meinem Test Normal-Mode nach Reset.

Den komplett blockierenden Code finde ich gerade nicht.
Ist allerdings auch ein Arduino Programm gewesen, weiß nicht ob dir das 
dann helfen würde.

Der Arduino Startupcode erzeugt folgenden Timer2 Zustand:
1
    // Arduino Startup Code Fragment
2
    TCCR2A = 1;
3
    TCCR2B = 4;
4
    TCNT2  = 51; // ca. zaehlerstand zum Zeitpunkt des Eintritts in die Main loop
5
    TIMSK2 = 0;
6
    OCR2A  = 0;
Gilt für Mega2560 und 328P,
TCNT2 unterscheidet sich geringfügig
Gast #5784287
Lesenswert?

S. Landolt schrieb:
>> TCCR2B = (1<<CS22);
>
> Was passiert, wenn statt des Vorteilers 64 (CS22) der Vorteiler 1 (CS20)
> genommen wird?

Ja, so sieht es stehend aus

Die Serielle Ausgabe kann sich gerade noch eine 0 abquetschen, dann 
nichts mehr.

Auch wieder Mega2560 und 328P identisches Verhalten.
1
   
2
void setTimer2()         // CTC, Mode 2
3
{
4
  cli();                  // Interrupts ausschalten
5
  TCNT2 = 0;              // Reset Register                 
6
  OCR2A = 249;            // Compare
7

8
  TIMSK2 = (1<<OCIE2A);   // Compare Match A
9
  TCCR2A = (1<<WGM21);    // set Mode
10
  TCCR2B = (1<<CS20);     // Prescaler 1
11
  //TCCR2B = (1<<CS22);     // Prescaler 64
12
  sei();                  // Interrupts einschalten
13
}  
14

15

16

17

18
void setup(void) 
19
{
20
  Serial.begin(9600);
21
  DDRB |= (1 << PB7); // LED an ArduinoMega Pin 13
22
  setTimer2();   
23
}
24

25
void loop(void) 
26
{
27
  Serial.println(millis());
28
  delay(500);
29
}
30

31

32
 
33
ISR(TIMER2_COMPA_vect)          // Timer 2 Interrupt 
34
{  
35
   PINB = (1 << PB7); // toggle
36
}
Gast #5784313
Lesenswert?

Nachtrag

So wie ich es jetzt sehe:

Die Interrupt Priorität von Timer 2 ist höher als die die von Timer0 
(delay,millis) und auch höher als die von der UART.
Also kommen beide nicht mehr dran.

So entsteht der Eindruck des völligen Stillstands.

Richtig so?
Gast #5784338
Lesenswert?

Oliver S. schrieb:
> wie Tim schon anmerkte, müsste der PB7
> trotzdem togglen. Die ISR sollte ja aufgerufen werden.

Ja, das ist auch so...
Welches bei 16MHz Takt zu einem ca. 250kHz Rechteck am Pin führt.

Der LA zeigt es.

Der LED_BULITIN (an PB7) sieht man es nicht an.
Schätze mal, dass der Operationsverstärker da nicht mit kommt.
Ja, der Arduino Mega und UNO hat einen  Operationsverstärker zwischen 
LED und Prozessor Pin, damit die LED das Signal nicht belastet, wenn der 
Pin als Eingang genutzt werden soll.
Was den Eindruck des Stillstands verstärkt.
#5784351
Lesenswert?

Wenn es nur Timer 2 gewesen wäre, könnte das auch noch mit dessen 
Asynchron-Funktionalität zusammenhängen. Aber wenn 0 und 1 auch 
betroffen snd, dann wirds bei allen Timer so sein.

Ist aber m.E. immer noch ein Fehler, der in die Errata gehört, und kein 
generelles Latch-Problem.

Oliver
Beitrag #5784378 wurde von einem Moderator gelöscht.
Beitrag #5784387 wurde von einem Moderator gelöscht.
Gast #5784397
Lesenswert?

Wie auch immer...

Meinen Dank an euch!

Vorher war mir klar:
Initialisierungsreihenfolge beachten, sonst evtl. derbe Probleme.

Jetzt habe ich ein deutlicheres Bild davon, was wirklich passiert.
Und warum es diese Probleme gibt.

Danke!
Gast #5784483
Lesenswert?

> Fehler, der in die Errata gehört

Was sollte Atmel/Microchip dort schreiben?
Für mich ist es ein typischer Programmierfehler, der nur deshalb leicht 
tückisch ist, weil hier zwei "Programmierer" arbeiten, einmal die 
Arduino-Umgebung und dann der eigentliche Programmierer.
#5784533
Lesenswert?

Das ist kein Programmierfehler, und es hat nichts mit Arduino zu tun.

Das Datenblatt gibt keine Reihenfolge der Initialisierung vor. Natürlich 
sollte beim Umschalten von einem gelatchten auf einen ungelachten Modus 
das entsprechend Register aus dem Latch geupdated werden, wenn da ein 
noch nicht geschriebener Wert drinsteht.

Das ist ein klarer Implementierungsfehler (wenn es denn ohne Reihenfolge 
funktionieren sollte), oder ein Fehler im Datenblatt, wenn es eine 
bestimmte Reihenfolge benötigt.

Oliver
Gast #5784545
Lesenswert?

> beim Umschalten von ... Modus ... geupdated

Zugegeben, das wäre eine Möglichkeit. Trotzdem bin ich der Meinung, dass 
das etwas viel verlangt ist von der Hardware, nur wegen eines solchen 
Sonderfalles. Und aus dem Datenblatt lässt es sich herauslesen, mit ein 
wenig Mühe.
#5784575
Lesenswert?

S. Landolt schrieb:
> Und aus dem Datenblatt lässt es sich herauslesen, mit ein
> wenig Mühe.

Na ja, man kann da alles mögliche hineininterpretieren. Da steht:

"The OCR2x Register access may seem complex, but this is not case. When 
the double buffering is enabled, the
CPU has access to the OCR2x Buffer Register, and if double buffering is 
disabled the CPU will access the OCR2x
directly."

Da steht natürlich nicht, daß bei einem Mode-Wechsel (der ein Normal- 
und kein Sonderfall ist) Buffer und Register synchronisiert werden, es 
steht aber auch nicht da, daß dies nicht geschieht. Das wäre aber 
durchaus erwähnenswert.

Statt dessen stehen breit und ausführlich die eigentlich nicht 
erwähnenswerten Fälle drin, daß ein Compare-Match bei einen Wert größer 
TOP niemals triggert, oder daß ein ein neuer Top-Wert kleiner als der 
aktuelle Zählerstand erst im nächsten Timerzyklus triggert.

Oliver
Gast #5784654
Lesenswert?

S. Landolt schrieb:
> Und aus dem Datenblatt lässt es sich herauslesen, mit ein
> wenig Mühe.

Mit am erstaunlichsten finde ich daran, dass der OCR2A Wert (falsch?) 
auslesbar ist!

OCR2A  = 0;
PWM mode aktivieren
OCR2A  = 200;
Compare Match aktivieren

Danach:
Beim  OCR2A auslesen bekommt man die 200 gereicht.
Obwohl der Timer mit einem OCR2A == 0 arbeitet.

Das scheint mir dann doch schon sehr gemein zu sein.
Und hätte ein Sprüchlein im Datenblatt verdient.
Gast #5784680
Lesenswert?

> hätte ein Sprüchlein im Datenblatt verdient

Vielleicht dieses hier?
"When the double buffering is enabled, the CPU has access to the OCR2x 
Buffer Register ..."
Betonung auf 'Buffer-'.

"Gemein" oder nicht, ich kann nichts dafür und kann's auch nicht ändern.
#5784704
Lesenswert?

Arduino Fanboy D. schrieb:
> Compare Match aktivieren

Offiziell heisst der CTC.

S. Landolt schrieb:
> "Gemein" oder nicht, ich kann nichts dafür und kann's auch nicht ändern.

Sacht ja keiner. Aber im CTC-Modus hat die CPU direkten Zugriff aufs 
Register, liest da 200, der Timer arbeitet aber mit 0. Das ist nicht 
einfach nur im Datenblatt ungünstig beschrieben oder gemein, daß ist ein 
Fehler.

Oliver
Gast #5784744
Lesenswert?

Okay, eben ausprobiert, und - Arduino Fanboy & Oliver S., ich sei, 
gewährt mir die Bitte ...: Dass im CTC-Modus, also einem ungepufferten, 
beim Auslesen nicht der aktuelle Stand von OCR2A zurückgegeben wird, 
betrachte auch ich als (gemeinen) Fehler.
Beitrag #5784863 wurde von einem Moderator gelöscht.
Gast #5785105
Lesenswert?

Weiss von euch Arduino Experten jemand wie man den Serial.print auf eine 
andere Funktion umleitet? Der geht ja auf die Hardware Uart. Oder eine 
vergleichbare Funktion aufsetzt? Ich verwende nur 2 Möglichkeiten

serial.print("Text...");
und
serial.print(buffer);

Ich habe aber eine IR Diode. Und das F Makro funktioniert nicht, wenn 
man zb nimmt

SendIRString(char* text)

Gibt es da einen Trick?

Bisher sieht es so aus


/* Sende einen 0 terminierten String per IR */
void SendIRString(char* text)
{
  while (*text)
    SendIRByte(*text++);

  SendIRByte(0);
}

IRLED ist mit einer Handykamera so eben zu sehen, die dimmt auch munter
drauf los.

ich könnte alles auf sprintf_P umschreiben aber das wäre eine irre 
Arbeit. Mache ich nur bei komplexen Ausgaben.
Gast #5785138
Lesenswert?

1
#include <Streaming.h> // findest du schon... ;-)
2
// ist nicht unbedingt nötig, erlaubt allerdings das Streaming
3

4

5

6

7

8
struct IrZeugs: Print
9
{
10
  virtual size_t write(uint8_t value)
11
  {
12
     SendIRByte(value);
13
     return 1;
14
  } 
15
};
16

17

18
IrZeugs ir;
19

20

21

22

23
void setup() 
24
{
25

26
 // Streaming
27
 ir << F("sende dieses ") << "jenes" << 42  << '\0';
28

29

30
 // alternativ:
31
 ir.print(F("sende dieses "));
32
 ir.print("jenes");
33
 ir.print(42);
34
 ir.write(0);
35
}
36

37
void loop() 
38
{
39

40
}
Gast #5785201
Lesenswert?

Arduino Fanboy D. schrieb:
> #include <Streaming.h> // findest du schon... ;-)
> // ist nicht unbedingt nötig, erlaubt allerdings das Streaming

Funzt :-)
Nur ein Prototypen Problem. Arduino braucht ja keine und mich ärgert das 
etwas dass das System .h und .c da nicht so läuft. Ins .h kommt das 
Interface rein, ins .c der private Code.
Gast #5785218
Lesenswert?

Christian J. schrieb:
> Nur ein Prototypen Problem. Arduino braucht ja keine und mich ärgert das
> etwas dass das System .h und .c da nicht so läuft. Ins .h kommt das
> Interface rein, ins .c der private Code.

> Arduino braucht ja keine
Das stimmt so nicht wirklich!
Es macht sich selber welche, wenn du keine baust.
Das betrifft aber nur *.ino Dateien.

Keine *.c, *.cpp und *.h Dateien, die stehen in deiner Verantwortung.

Wieso überhaupt *.c?
Nimm *.cpp, dann musst du nicht die Extrawürste backen um mit dem C++ 
Rest klar zu kommen.
Gast #5786080
Lesenswert?

C++.... ne, alter Hase bleibt bei C.

struct IrZeugs: Print
{
  virtual size_t write(uint8_t value)
  {
     SendIRByte(value);
     return 1;
  }
};

Aber dieses zweifach verschachtelte Konstrukt hier .... für mich nicht 
so ganz verstehbar. Funktioniert aber. irgendwie verwurstelt das 
Zeichenweise den Stream. Eine Funktion ist einem Struct, öfter mal was 
Neues.

Wie würde das aussehen, wenn in einem Rutsch die IR Ausgabe erfolgen 
soll und die normale Print Ausgabe dahinter? Ich baue mir einige 
komplexe Ausgaben mit sprintf_P zusammen und schiebe die nach buf rein. 
Wäre gut, wenn die Ausgabe meiner Bastelei auf Seriell und IR kommen 
würde. Einen Nachteil sehe ich nur darin, dass serial.print die Ausgabe 
per INT erledigt und dadurch IR beeinflusst würde, weil ich da zwingend 
die INT abschalten muss, damit die Zeiten stimmen. Aber man kann ja auch 
serial.flush() vorher aufrufen oder die IR Ausgabe vorziehen.

Die EEPROM Ausgabe liesse sich sicher auch verstreamen, nur müsste da 
dann ein zeiger mitlaufen aus das aktuelle Element.

Wäre schwer?
Gast #5786157
Lesenswert?

Christian J. schrieb:
> C++.... ne, alter Hase bleibt bei C.
Dann musst du wohl von Arduino weg, oder deine Einstellung ändern.

Denn:
> Aber dieses zweifach verschachtelte Konstrukt hier .... für mich nicht
> so ganz verstehbar. Funktioniert aber.
Das ist naives, aber dennoch lupenreines C++.
Siehe: https://playground.arduino.cc/Code/Printclass/
So wie vieles, sehr vieles, SEHR *SEHR* vieles in der Arduino Welt.
Alles? Nee, aber sehr vieles!

Aber wenn du willst, dann mache dir deine Problem!
Soviele du möchtest.


Christian J. schrieb:
> Wäre schwer?
Nein, quasi nur ein Fingerschnippen...
;-) aber nicht für C++ Verweigerer ;-)

Tipp:
Von der EEPROM Klasse erben und das Print Interface implementieren.
Und ja, einen Index wird man mitführen müssen, und auch ein bisschen um 
die Grenzen kümmern, damit da keine ganz gruseligen Dinge passieren.
Oder besser sofort Stream implementieren... dann klappts auch mit dem 
Lesen.
#5800050
Lesenswert?

Arduino Fanboy D. schrieb:
> Auch wieder Mega2560 und 328P identisches Verhalten.

Hm....

Ich habe jetzt dann doch mal meinen Arduino Mega und den Logikanalysator 
aus dem Keller gekramt, um das mal nachzuvollziehen.
Alledings ohne Arduino-Framework, und ohne serielle Ausgabe.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3

4
void setTimer2()         // CTC, Mode 2
5
{
6
  cli();
7
  // Interrupts ausschalten
8
  TCNT2 = 0;              // Reset Register
9
  OCR2A = 249;            // Compare
10

11
  TIMSK2 = (1 << OCIE2A);   // Compare Match A
12
  TCCR2A = (1 << WGM21);    // set Mode
13
  TCCR2B = (1 << CS20);     // Prescaler 1
14
  //TCCR2B = (1<<CS22);     // Prescaler 64
15
  sei();
16
  // Interrupts einschalten
17
}
18

19
ISR(TIMER2_COMPA_vect)          // Timer 2 Interrupt
20
{
21
  PINB = (1 << PB7); // toggle
22
}
23

24
int main(void)
25
{
26
  DDRB = 1 << PB7;
27
  setTimer2();
28
  while (1)
29
  {}
30
}

Ergebis: PB7 toggelt nicht mit 250kHz, sondern brav mit 32khz, so wie es 
auch sein sollte.

Oliver
Gast #5800105
Lesenswert?

Oliver S. schrieb:
> sondern brav mit 32khz, so wie es
> auch sein sollte.

Deinen Code habe ich jetzt nicht überprüft, kann ich mir aber lebhaft 
vorstellen.
Denn der Timer kommt im Mode 0 aus dem Reset.
Dann klappt das auch alles.

Lass den Timer mal kurze Zeit in einem PWM Mode laufen...
Und dann schalte um.
Du wirst sehen.....
#5800376
Lesenswert?

Arduino Fanboy D. schrieb:
> Lass den Timer mal kurze Zeit in einem PWM Mode laufen...

Bitte sehr:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4

5
void setTimer2_PWM()         // PWM, Mode 5
6
{
7
  cli();
8
  // Interrupts ausschalten
9
  TCNT2 = 0;              // Reset Register
10
  OCR2A = 249/4;            // Compare
11

12
  TIMSK2 = (1 << OCIE2A);   // Compare Match A
13
  TCCR2A = (1 << WGM20);    // set Mode
14
  TCCR2B = (1 << WGM22) | (1 << CS20);     // Prescaler 1
15
  sei();
16
  // Interrupts einschalten
17
}
18

19
void setTimer2_CTC()         // CTC, Mode 2
20
{
21
  cli();
22
  // Interrupts ausschalten
23
  TCNT2 = 0;              // Reset Register
24
  OCR2A = 249;            // Compare
25

26
  TIMSK2 = (1 << OCIE2A);   // Compare Match A
27
  TCCR2A = (1 << WGM21);    // set Mode
28
  TCCR2B = (1 << CS20);     // Prescaler 1
29
  sei();
30
  // Interrupts einschalten
31
}
32

33
ISR(TIMER2_COMPA_vect)          // Timer 2 Interrupt
34
{
35
  PINB = (1 << PB7); // toggle
36
}
37

38
int main(void)
39
{
40
  DDRB = (1 << PB6) | (1 << PB7);
41
  setTimer2_PWM();
42
  _delay_ms(100.0);
43
  PORTB |= (1 << PB6);
44
  setTimer2_CTC();
45
  while (1)
46
  {}
47
}

Tut, was es soll.

Oliver
Gast #5800441
Lesenswert?

Oliver S. schrieb:
> void setTimer2_PWM()         // PWM, Mode 5
> {
>   cli();
>   // Interrupts ausschalten
>   TCNT2 = 0;              // Reset Register
>   OCR2A = 249/4;            // Compare
>
>   TIMSK2 = (1 << OCIE2A);   // Compare Match A
>   TCCR2A = (1 << WGM20);    // set Mode
>   TCCR2B = (1 << WGM22) | (1 << CS20);     // Prescaler 1
>   sei();
>   // Interrupts einschalten
> }
Hier mogelst du!
> OCR2A = 249/4;

Der Problemzustand ist
 OCR2A = 0;
Versuchs mal damit.
Gast #5800467
Lesenswert?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <util/atomic.h>
5

6
#define AtomicSection ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
7

8

9
void setTimer2_Arduino_PWM()         
10
{
11
  AtomicSection
12
  {
13
  // Arduino Startup Code Fragment
14
    TCCR2A = 1;
15
    TCCR2B = 4;
16
    TCNT2  = 51; // ca. zaehlerstand zum Zeitpunkt des Eintritts in die Main loop
17
    TIMSK2 = 0;
18
    OCR2A  = 0;
19
  }
20
}
21

22
void setTimer2_Reset()         
23
{
24
  AtomicSection
25
  {
26
    TCCR2A = 0;
27
    TCCR2B = 0;
28
    TCNT2  = 0; 
29
    TIMSK2 = 0;
30
    OCR2A  = 0;
31
  }
32
}
33

34
void setTimer2_CTC()         // CTC, Mode 2
35
{
36
  AtomicSection
37
  {
38
      TCNT2 = 0;              // Reset Register
39
      OCR2A = 249;            // Compare
40
    
41
      TIMSK2 = (1 << OCIE2A);   // Compare Match A
42
      TCCR2A = (1 << WGM21);    // set Mode
43
      TCCR2B = (1 << CS20);     // Prescaler 1
44
  }
45
}
46

47
ISR(TIMER2_COMPA_vect)          // Timer 2 Interrupt
48
{
49
  PINB = (1 << PB7); // toggle
50
}
51

52
int main(void)
53
{
54
  sei();
55
  DDRB = (1 << PB6) | (1 << PB7);
56

57

58
  PORTB |= (1 << PB6);
59

60
  while (1)
61
  {
62
    setTimer2_Arduino_PWM();
63
    _delay_ms(20.0);
64
    setTimer2_CTC(); // ca 250kHz
65
    _delay_ms(20.0);
66
    
67
    setTimer2_Reset();
68
    _delay_ms(20.0);
69
    setTimer2_CTC(); //ca 32kHz
70
    _delay_ms(20.0);
71
  }
72
}
#5801024
Lesenswert?

S. Landolt schrieb:
> Das trifft es nicht ganz - der Trick ist, OCRA2 beim vorlaufenden
> PWM-
> größer zu machen als im späteren CTC-Modus, dann bleibt der zweite Wert
> im Pufferregister stecken.

Scheint auch nicht ganz zuzutreffen, in meinem Beispiel oben ist er ja 
im PWM-Modus kleiner als im CTC-Modus. Mit viel kleineren Werten, nicht 
nur bei 0, wird er nicht übernommen. Vermutlich, wenn der Wert kleiner 
als der aktuelle Zählerstand ist.

Der Arduino-Code initialisiert den Timer 2 auf PWM 8 Bit mit OCRA=0. 
Warum die überhaupt da dran rumfummeln, keine Ahnung.

Ich muß mir das die Tage nochmals genauer anschauen. Dafür war mir der 
Sonntag Nachmittag dann doch zu schade.

Oliver
Gast #5801031
Lesenswert?

Oliver S. schrieb:
> Der Arduino-Code initialisiert den Timer 2 auf PWM 8 Bit mit OCRA=0.
> Warum die überhaupt da dran rumfummeln, keine Ahnung.

Im Startcode vorbereiten für PWM, damit das nicht in analogWrite() 
gemacht werden muss.
So wie auch andere Vorbereitungen für andere Arduino Features, wie 
millis(), delay() usw durchgeführt werden.

Das muss man hinnehmen, wenn man in der Welt unterwegs ist.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren