mikrocontroller.net

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


Autor: Robert F. (fastred)
Datum:

Bewertung
0 lesenswert
nicht 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):
  //PORTC &= ~(1 << PINC0); // spikes vermeiden
  DDRC = (1 << PINC0);
  // timer configurieren
  TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
  OCR1A = 7812; // wert vorladen 
  TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
  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:
avrdude: Device signature = 0x1e9406
avrdude: safemode: lfuse reads as FF
avrdude: safemode: hfuse reads as DF
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)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
#define F_CPU 16000000UL

#include <avr/io.h>
#include <utils/delay.h>

// einstellen, wo die LED hängt
#define LED_DDR  DDRx
#define LED_PORT PORTx
#define LED_PIN  PBx

int main()
{
  LED_DDR = ( 1 << LED_PIN );

  while( 1 ) {
    LED_PORT |= ( 1 << LED_PIN );
    _delay_ms( 1000 );
    LED_PORT &= ~( 1 << LED_PIN );
    _delay_ms( 1000 );
  }
}

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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Robert F. (fastred)
Datum:

Bewertung
0 lesenswert
nicht 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 :/

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Robert F. (fastred)
Datum:

Bewertung
0 lesenswert
nicht 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
 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:
/*
 * Eine LED soll in einer definierten zeit blinken
 *
 *
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h> // uint8_t

static volatile uint8_t flag;
void init(void)
{
  // datenstrukuren init
  flag = 0;
  // ausgangsport setzen
  //PORTC &= ~(1 << PINC0); // spikes vermeiden
  DDRC = (1 << PINC0);
  // timer configurieren
  TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
  OCR1A = 7812; // wert vorladen (eigentlich Interrupts abschalten (vgl 15.3, s116ff, inbesonsere )
  TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
  sei(); // interrupts aktivieren

  
}

ISR(TIMER1_COMPA_vect){
  flag = !flag; // toggeln
  // ausgang setzen
  if (flag)
  {
    PORTC |= (1 << PINC0);
  }
  else
  {
    PORTC &= ~(1 << PINC0);
  }
}

void main(void) 
{
  init();
  
  for(;;) {}
}

Autor: Robert F. (fastred)
Datum:

Bewertung
0 lesenswert
nicht 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
  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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.