Timer problem

Hallo:)

ich hab ein kleines Problem mit der Timer initialisierung. Ich möchte jede Minute eine LED aus und ein geht. Und möchte das mit der Interrupt routine realisieren so wie ich es im Programm ausgeführt habe. Leider weiss ich nicht voher ich die Taktfrequenz herausbekommen soll ? Ich bin mal von 16 Mhz ausgegangen weil ich auf mein Arduino board ein 16 Mhz Oszilator habe. Die Taktfrequenz möchte ich auf 25khz runterschrauben damit ich alle 40 ns der zähler eine sekunde hochzählt und das gleiche auch für die Minute.

Ich weiss zwar wie ein Timer funktioniert aber ich weiss gerade nicht welche Register ich setzen muss. Um das zu überprüfen ob alles funkioniert habe ich mir gedacht das alles aus dem seriellen display auszugeben?

es wäre super wenn ich einige hilfen bekäme :slight_smile:

Timer.ino (892 Bytes)

Was willst du denn nun schreiben? Einen Arduino Sketch, oder AVR-GCC Code?

#define C_PU 16000000

Das gibt es nicht. Wenn dann 'F_CPU'. Wobei dieses weder in einen Arduino Sketch noch in eine c/h-Datei gehört.

int zaehler = 0;

sollte volatile sein.

 Serial.println(sekunde);

sowas gehört nicht in eine Interrupt Routine

digitalWrite(Pin,digitalRead(Pin)^1);

Behaupte, das wird so überhaupt nicht funktionieren.
Besser wäre hier eine status-Varaible anzulegen, oder direkte Portzugriffe nutzen.

int main(void)
{
	DDRA |= (1 << 5);
	
	while(1)
	{
		PORTA ^= (1<<5);
		_delay_ms(50);
	}
}
 TCCR1A + TCCR1B

Müssen nicht mit 0 initialisiert werden.

  digitalWrite(Pin,LOW);;

Ein ; zuviel und digitalWrite ohne den pin als Output deklariert zu haben :expressionless:

AdrianG90:
Die Taktfrequenz möchte ich auf 25khz runterschrauben damit ich alle 40 ns der zähler eine sekunde hochzählt und das gleiche auch für die Minute.

Ich verstehe weder anhand Deiner Beschreibung noch anhand Deines Codes, was Du eigentlich möchtest.

Wo schraubst Du mit 25 kHz was runter?

Und mit 40 Nanosekunden läuft auf einem Arduino generell nichts, weil eine Taktrate von 16 MHz einer Taktdauer von 62,5 Nanosekunden entspricht und unter 62,5 Nanosekunden läuft demzufolge nicht mal ein einziger Controllertakt.

Im übrigen fragt man Zeiträume auf einem Arduino über den Stand des millis() oder micros() Timers ab, den die Arduino-Software mit Hilfe des Timer0 bereits softwaretechnisch zur Verfügung stellt.

Und falls Du trotzdem Interruptroutinen programmieren möchtest, gewöhnst Du Dir am besten an:

  • Interruptroutinen müssen SO KURZ WIE MÖGLICH laufen
  • und "Serial.print" kannst Du in einer ISR schon mal komplett vergessen, weil Du damit Dein Programm zum Totalabsturz bringst, wenn Dir dabei der serielle Sendepuffer vollläuft (was bei zu schneller Ausgabe in ISR Routinen ja leicht passieren kann).

Für eine LED tut es auch millis()! Man muss nicht gleich zu Timern greifen

Die COM1A1/0 Bits hast du falsch verstanden. Die sind dazu da um Pins automatisch in Hardware zu toggeln, ohne dass ein Interrupt ausgelöst wird. Was du willst ist der CTC Modus. Eine extra Zählervariable ist überflüssig. Dafür hast du ja das Zählerregister.

In der ISR kann man keine serielle Ausgabe machen, also braucht man zwei Variablen um loop() mitzuteilen ob was auszugeben ist.

const int LED = 13;

volatile boolean secondPassed;
volatile boolean minutePassed;
volatile byte seconds;
volatile unsigned int minutes;

void setup()
{
  Serial.begin(115200);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, LOW);

  TCCR1A = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS10);	//CTC, Prescaler 1024
  TIMSK1 = _BV(OCIE1A);	//Compare Match Interrupt Enable
  OCR1A = 15625;	 //16.000.000MHz / 1024 / 1s
}

void loop()
{
  if(secondPassed)
  {
	secondPassed = false;
	Serial.print(F("Sec: "));
	Serial.print(seconds);
	Serial.print(F(" -- millis: "));
	Serial.println(millis());
  }

  if(minutePassed)
  {
	minutePassed = false;
	Serial.print(F("Min: "));
	Serial.println(minutes);
  }
}

ISR(TIMER1_COMPA_vect)
{
  secondPassed = true;
  digitalWrite(LED, !digitalRead(LED));

  seconds++;
  if(seconds == 60)
  {
	seconds = 0;

	minutePassed = true;
	minutes++;
  }
}

Hallo,
der Code ist "DDDR-Programmierung" Das ist auf dem Arduino nicht üblich.
Von wo hast Du es den kopiert?
Hier http://www.arduino.cc/en/Tutorial/Blink ist sehr schön beschrieben, wie man eine LED blinken lassen kann.
Gruß und Spaß
Andreas

sschultewolter:

 TCCR1A + TCCR1B

Müssen nicht mit 0 initialisiert werden.

Vorsicht damit. Da bin ich letztens auch übelst drauf reingefallen. Auf einem blanken AVR stimmt das. Aber auf dem Arduino sind die Timer auf PWM initialisiert. Man man das Register nicht zurücksetzt, läuft der Timer eventuell immer noch in 8 Bit PWM und setzt sich automatisch zurück wenn 8 Bit voll sind. Oder er läuft in einem anderen Modus als man denkt, weil die WGM Bits über zwei Register verteilt sind.

SkobyMobil:
Hallo,
der Code ist "DDDR-Programmierung" Das ist auf dem Arduino nicht üblich.
Von wo hast Du es den kopiert?
Hier http://www.arduino.cc/en/Tutorial/Blink ist sehr schön beschrieben, wie man eine LED blinken lassen kann.
Gruß und Spaß
Andreas

Den hab ich schnell aus Atmel Studio von meinen Attiny841 rübergezogen.

Das es auf dem Arduino nicht üblich ist, weiß ich. Der ganze Code/Sketch sieht nach reinen wirwar aus Arduino typischen Funktionen und AVR-GCC Funktionen aus. Einmal den Mixer an und fertig ist der Code -> sieht dann wie oben aus :smiley:

Aber nur weil es in Arduino nicht üblich ist, heißt es nicht, dass es nicht dort auch funktioniert. Nur hat man dann nicht zB. die Pins 0 - 13 zur Verfügung. Dir DDR Schreibweise orientiert sich an die Ports. (PortA 0 - 7, PortB 0 -7, ....)

Ich weiß jetzt zwar nicht genau, ob es das ist, was der TO möchte.

Aber ich habe jetzt mal ein eher Arduino-typisches Programm gemacht, das das Timing der Arduino-Funktion millis() überläßt, und das Hantieren mit Timern auf Registerebene der Arduino Core-Library. Dadurch benötigt das Programm keine Interruptprogrammierung und man kann bei der Interruptprogrammierung dann auch keine Fehler machen. Außerdem ist das Programm dann automatisch auch auf Arduino-DUE lauffähig.

Beispielprogramm zeigt einmal pro Sekunde die aktuelle Sekunde und schaltet einmal pro Minute einen LED-Status um:

#define LED 13

void setup() {
  Serial.begin(9600);
  pinMode(LED,OUTPUT);
}

unsigned long now(){   // Sekundenzähler
  static unsigned long prevMillis;
  static unsigned long sysTime;
  while( millis() - prevMillis >= 1000){      
    sysTime++;
    prevMillis += 1000;	
  }
  return sysTime;
}


unsigned long sekunde;
void loop() {
  if (now()!=sekunde)
  {
    sekunde=now();
    if (sekunde%60==0)
    {
      digitalWrite(LED,!digitalRead(LED));
      Serial.print("Minute gestartet ");
      Serial.println(sekunde/60);
    }
    else
    {
      Serial.print("Sekunde ");
      Serial.println(sekunde%60);
    }
  }
}

Vielen dank an sschultewolter , Serenifly und besonders an die anderen:)

ihr habt mir sehr geholfen genauso wollte ich mein programm schreiben. Einiges habe ich dazu lernen können. Ich hab immer vorher gedacht beim arduno muss man genauso die Reigster für timers setzen wie bei einem normalen atmel mikrokontroller. Letzlich was im Arduino dirn ist ja ein Atmega Mikrokontroller. Aber jetzt weiss ich für die Zukunft wie ich Timer beschalten sollte. Für das Wirwar möchte ich mich entschuldigen ich hab mir schnell aus dem ärmel ein code selber zusammengeschrieben und versucht dass zum laufen zu bringen. Leider hat die Sekunde was länger gedauert als sonst:D aber jetzt funktioniert :slight_smile:

Auf dem Arduino ist durch die Software schon ein Timer auf Timer0 eingerichtet. Der macht einmal PWM, aber löst auch alle 1ms einen Interrupt aus um einen Zähler zu erhöhen. Dessen stand kann dann mit micros() und millis() abfragen. Das reicht für die meisten Sachen aus.

Timer direkt programmieren hat schon noch seine Berechtigung. Aber nicht für diese Anwendung. Das kann man machen wenn man andere PWM Modi oder Funktionen braucht. Oder wenn man mit einem Timer externe Pulse zählen will, entweder indem man den Timer extern taktet oder die Pulsbreite per Input Capture Modus misst. Oder wenn man etwas sehr genau und regelmäßig braucht und sich nicht sicher ist immer jeden Schritt von millis() mitzubekommen. Oder wenn es sehr schnell gehen muss.

Zum CTC Modus übrigens:
Das steht für "Clear Timer on Compare". Der Timer zählt je nach Prescaler bis zu einem bestimmten Wert hoch. Dieser steht in OCR (Output Compare Register). Dann wird der Zähler automatisch zurückgesetzt. Hier wird jetzt noch der Interrupt aktiviert, weil man etwas mehr machen will. Aber man kann da auch bestimmte Pins schalten ohne dass wirklich das Programm unterbrochen wird. 100% in Hardware. Das geschieht auch bei PWM.

AdrianG90:
Ich hab immer vorher gedacht beim arduno muss man genauso die Reigster für timers setzen wie bei einem normalen atmel mikrokontroller. Letzlich was im Arduino dirn ist ja ein Atmega Mikrokontroller.

Beim Arduino KANNST Du genau so Register für Timer setzen wie bei einem normalen Atmel Controller. Letztlich steckt ja ein Atmega Controller drin.

Aber Du brauchst es nicht. Denn für Timer0 haben sich andere bereits den Kopf für Dich zerbrochen und stellen über den Arduino-Core die Funktionen "millis()", "micros()", "delay()" und "delayMicroseconds()" zur Verfügung, mit denen Dur Dir Vielfache von Milli- oder Mikrosekunden in Deinem Programm leicht ableiten kannst - ohne die Register des Atmega wie Deine Westentasche zu kennen.