Forum: Compiler & IDEs Unklarheit bei Berechnung von OCR1A / Timer1-Delay


von Robert F. (fastred)


Lesenswert?

Hallo,

ich stehe grade irgendwie auf dem Schlauch:

Ich habe einen atmega 168 mit 16Mhz externem Quarz. Ich möchte eine LED 
im 1-Sek-Takt blinken lassen (minimal Beispiel, natürlich ist die 
eigentliche Anwendung komplexer).
Dazu lasse ich den Timer1 (16bit) im CTC (clear timer on compare match) 
laufen. Um den Wert, mit dem der Timer geladen werden muss, findet sich 
im Datenblatt in Abschnitt "15.9.2  Clear Timer on Compare Match (CTC) 
Mode" folgende Formel:

Dabei bedeutet: (bitte korrigieren wenn falsch)
* N: der Prescaler (bei mir: 1024)
* f_OCnA : die Frequenz bei der Interrupt ausgelöst wird (Ziel: 1x pro 
Sec)
* f_clk I/O : die Taktgeschwindigkeit des uC
* OCR1nA: der Wert, der in den Timer geladen muss (das Datenblatt 
spricht von "TOP" im Zusammenhang mit CTC-Mode).

Um OCR1nA zu bestimmen wird die Formel umgestellt:

Ich setzte meine Werte ein (N = 1024, f_clkI/O = 1,6x10^7)
und erhalte für 1+OCR1nA den Wert 7812,5.

Diesen packe ich in meinen Code (Auszug):
1
  //PORTC &= ~(1 << PINC0); // spikes vermeiden
2
  DDRC = (1 << PINC0);
3
  // timer configurieren
4
  TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
5
  OCR1A = 7812; // wert vorladen 
6
  TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
7
  sei(); // interrupts aktivieren

In der ISR wird ein Pin getoggelt, an dem ne LED hängt.
Ich lade den Code in den uC und die LED blinkt munter, aber NICHT im 
1-Sec-Takt, sondern schneller.
Ein erster Verdacht: Fuses nicht richtig gesetzt, Systakt wird durch 8 
geteilt, und siehe da, wenn ich 8x7812 in OCR1A lade, blinkt die LED 
schön 1 Sec an, 1 Sec aus, wie ich es haben will.

Jedoch sagt AVRDude zu den Fuses:
1
avrdude: Device signature = 0x1e9406
2
avrdude: safemode: lfuse reads as FF
3
avrdude: safemode: hfuse reads as DF
4
avrdude: safemode: efuse reads as 1
Der Online Calc ( http://www.engbedded.com/fusecalc/ ) sagt dazu, dass 
Divide clock by 8 internally; [CKDIV8=0] aus ist. Ich interpretiere das 
so, dass der Systakt also die vollen 16Mhz abbekommt, wundere mich daher 
über das oben beschriebene Verhalten.

Kann mir jmd. sagen, an welcher Stelle der Fehler liegt?
Vielen Dank!


p.s. hach, schön ma wieder was in latex zu notieren 8)

von Karl H. (kbuchegg)


Lesenswert?

Robert F. schrieb:

> Ein erster Verdacht: Fuses nicht richtig gesetzt, Systakt wird durch 8
> geteilt, und siehe da, wenn ich 8x7812 in OCR1A lade, blinkt die LED
> schön 1 Sec an, 1 Sec aus, wie ich es haben will.

Deine Berechnung sieht erst mal gut aus.
Du kannst noch einen Test machen
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <utils/delay.h>
5
6
// einstellen, wo die LED hängt
7
#define LED_DDR  DDRx
8
#define LED_PORT PORTx
9
#define LED_PIN  PBx
10
11
int main()
12
{
13
  LED_DDR = ( 1 << LED_PIN );
14
15
  while( 1 ) {
16
    LED_PORT |= ( 1 << LED_PIN );
17
    _delay_ms( 1000 );
18
    LED_PORT &= ~( 1 << LED_PIN );
19
    _delay_ms( 1000 );
20
  }
21
}

wenn damit die LED 1 Sekunde an/aus ist, dann ist es nicht die CKDIV8. 
Ist die LED aber 8 Sekunden an/aus, dann ist es die CKDIV8

von Stefan E. (sternst)


Lesenswert?

Robert F. schrieb:
> * f_OCnA : die Frequenz bei der Interrupt ausgelöst wird (Ziel: 1x pro
> Sec)

Nein. In der Formel ist das die Frequenz, die an einem Ausgang entsteht, 
wenn man diesen bei jedem Interrupt toggelt.

Du willst aber eine Frequenz in der Bedeutung "Interrupthäufigkeit". 
Lass einfach in der Formel den Faktor 2 raus.

von Robert F. (fastred)


Lesenswert?

dank euch 2 für die Antworten.
Hab den Code mal in den uC geladen um die Einstellung der Fuses gegen zu 
checken.

Ergebnis:
mit den o.g. Fuses blinkt die LED 1x Sekunden (damit meine ich: 1 Sek 
an, 1 Sec aus, eine Periode dauert also 2 sec)

mit aktivierten 8er Teiler ( -U lfuse:w:0x7f:m -U hfuse:w:0xdf:m ) 
erwartungsgemäß 8sec an, 8sec aus

-> an den Fuses liegt es schon mal nicht...

Wenn ich aus der Formel die 2 raus lasse, erhalte ich ja eine Zahl 
DOPPELT so groß, ich habe aber beobachtet, das sie ACHTMAL so groß sein 
muss, um das gewünschte Ergebnis (1sec an, 1 sec aus) zu erhalten...

Stehe also nach wie vor auf dem Schlauch :/

von Stefan E. (sternst)


Lesenswert?

Robert F. schrieb:
> Ergebnis:
> mit den o.g. Fuses blinkt die LED 1x Sekunden (damit meine ich: 1 Sek
> an, 1 Sec aus, eine Periode dauert also 2 sec)

Dann würde ich mal vermuten, dass der Controller mit dem internen 
8MHz-Oszillator läuft (habe jetzt nicht die Lust, deine Fuse-Angaben zu 
checken).

Denn wenn du in obige Formel für f_OCnA eine 1 einsetzt, ergeben sich 
auf jeden Fall 2 Interrupts pro Sekunde, also LED 0,5 Sek an und 0,5 Sek 
aus = 1 Hz Ausgangsfrequenz.

von Karl H. (kbuchegg)


Lesenswert?

Robert F. schrieb:

> -> an den Fuses liegt es schon mal nicht...

Gut. Schon mal was.

Dann sollte man als nächstes die Timer Einstellung prüfen :-)

Du setzt CS12 und sonst nichts.
Ich hab jetzt kein Datenblatt zum 168 zur Hand, aber beim Mega16 ist das 
ein Vorteiler von 256 und nicht 1024.

Das macht einen Faktor von 4.

Dazu nehmen wir noch, dass du 2 ISR Aufrufe für einmal blinken brauchst, 
und wir haben die 8 beisammen :-)

von Robert F. (fastred)


Lesenswert?

Stefan Ernst schrieb:
> Dann würde ich mal vermuten, dass der Controller mit dem internen
> 8MHz-Oszillator läuft (habe jetzt nicht die Lust, deine Fuse-Angaben zu
> checken).

Verlangt ja auch keiner von dir :)
Deine Vermutung kann aber nicht zutreffend sein, da angenommen der Takt 
ist 8Mhz statt 16, müsste das Ergebnis ja halb so schnell sein. Wie im 
1. Post beschrieben, blinkt die LED aber 8x so schnell.
Hardwaremäßig wurde durch ziehen des Quarzes (Steckbrett) verifiziert, 
dass die Schaltung nicht mit dem Internen Quarz läuft.

Falls du doch mal eine Fuse chkn willst, bereite ich mal den (dir 
bestimmt wohlbekannten) Link zum anklicken vor :)
http://www.engbedded.com/fusecalc/

Stefan Ernst schrieb:
> Denn wenn du in obige Formel für f_OCnA eine 1 einsetzt, ergeben sich
> auf jeden Fall 2 Interrupts pro Sekunde, also LED 0,5 Sek an und 0,5 Sek
> aus = 1 Hz Ausgangsfrequenz.

Ich HABE für f_OCna eine 1 eingesetzt, und erhalte dann wie oben 
beschrieben:

Mit
1
 OCR1A = 7812; // wert vorladen
erhalte ich aber nicht das gewünschte Ergebnis (2Hz Ausgangsfrequenz - 
1sec an, 1 sec aus) sondern das 8 fache...

Mir geht es grade nicht darum, ob es 1 oder 2 Hz Ausgangsfrequenz 
ist/heißt, sondern warum die Berechnungen um den Faktor 8 daneben gehen 
:(

Ich gebe mal das vollständige Programm an, ich denke es ist grade so 
noch kurz genug um es direkt anzugeben:
1
/*
2
 * Eine LED soll in einer definierten zeit blinken
3
 *
4
 *
5
 */
6
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
#include <stdint.h> // uint8_t
10
11
static volatile uint8_t flag;
12
void init(void)
13
{
14
  // datenstrukuren init
15
  flag = 0;
16
  // ausgangsport setzen
17
  //PORTC &= ~(1 << PINC0); // spikes vermeiden
18
  DDRC = (1 << PINC0);
19
  // timer configurieren
20
  TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
21
  OCR1A = 7812; // wert vorladen (eigentlich Interrupts abschalten (vgl 15.3, s116ff, inbesonsere )
22
  TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
23
  sei(); // interrupts aktivieren
24
25
  
26
}
27
28
ISR(TIMER1_COMPA_vect){
29
  flag = !flag; // toggeln
30
  // ausgang setzen
31
  if (flag)
32
  {
33
    PORTC |= (1 << PINC0);
34
  }
35
  else
36
  {
37
    PORTC &= ~(1 << PINC0);
38
  }
39
}
40
41
void main(void) 
42
{
43
  init();
44
  
45
  for(;;) {}
46
}

von Robert F. (fastred)


Lesenswert?

Karl heinz Buchegger schrieb:
> Robert F. schrieb:
>
>> -> an den Fuses liegt es schon mal nicht...
>
> Gut. Schon mal was.
>
> Dann sollte man als nächstes die Timer Einstellung prüfen :-)
>
> Du setzt CS12 und sonst nichts.
> Ich hab jetzt kein Datenblatt zum 168 zur Hand, aber beim Mega16 ist das
> ein Vorteiler von 256 und nicht 1024.
>
> Das macht einen Faktor von 4.
>
> Dazu nehmen wir noch, dass du 2 ISR Aufrufe für einmal blinken brauchst,
> und wir haben die 8 beisammen :-)

kopfaufdentischschlag

1024 ist CS12 und CS10, nicht nur CS12 argl

Es muss natürlich
1
  TCCR1B = ( (1 << CS12) | (1 << CS10)| (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
 heißen.

Danke für den entscheidenden Hinweis Karl Heinz!

Und wie Stefan richtig angemerkt hat, ist die Ausgangsfrequenz jetzt 1Hz 
( 0,5sec an, 0,5sec aus).

Vielen Dank, die Unklarheit ist beseitigt!

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.