ATtiny84 Timer0 und gleichzeitige Verwendung von millis()

Servus zusammen, ein Neuer.

Bislang hatte ich immer nur mitgelesen, um Lösungen für kleine Problemchen oder auch nützliche Anregungen zur Relalisierung bestimmter Dinge zu finden. Nun habe ich ein Problem an dem ich schier verzweifle und bisher keine Lösung gefunden habe.

Ich habe eine Applikation mit ATtiny84 bei der ich über den Timer0 mir ein Rechtecksignal mit 1 kHz erzeuge, das ich über einen Transistor an eine IR-Reflexlichtschranke sende (Sendediode).
Das Ausgangssignal der Lichtschranke (eigentlich sind es zwei Lichtschranken) lese ich über zwei Digitalpins ein und werte über PulseIn-Abfragen (HIGH und LOW) die Periodendauer T1 und T2 der Signale aus, um diese Informationen anschließend weiter zu verwenden. Bei der ersten Übereinstimmung der Periodendauern (konkret 1 ms zu-/abzüglich Toleranz) setze ich je Lichtschranke ein Status-Byte und stelle die Informationen über LED nach außen hin dar. Das funktioniert auch einwandfrei.

Sind bei Lichtschranken gleichzeitig nicht mehr aktiv, PulseIn liefert in dem Falle Timeouts, d. h. T1 = T2 = 0, frage ich diesen Status ab und möchte die Anzeigen sowie die Status-Bytes auch wieder nach einer Wartezeit von beispielsweise 3 s über die Abfrage von millis() wieder zurücksetzen. Die Abfrage funktioniert, die millis() allerdings nicht.

Ich weiß nicht was ich falsch mache.

Hier die Definition des Timers:

// Timer-Definitionen Timer 0 für ATtiny 84
 void setupTimer0_ATtiny84() 
{
  noInterrupts();
  // Clear registers
  TCCR0A = 0;
  TCCR0B = 0;
  TCNT0 = 0;

  // 1000 Hz (4 MHz/((63+1)*64)) zwei IR/Periode = doppelte IR-Frequenz
  // berechneter Wert 61, gewählt 63
  // count up to 63 set in Output Compare Register
  OCR0A = 61;
  // CTC
  TCCR0A |= (1 << WGM01);
  // Prescaler 64
  TCCR0B |= (1 << CS01) | (1 << CS00);
  // interrupt when Compare Match with OCR0A
  TIMSK0 |= (1 << OCIE0A);
  // Pin PB2 an PortB (pin Nr. 5) ist Ausgabe für Sendediode
  DDRB |= (1<<PB2);                    
  interrupts();
} 

Hier die Abfrage zu den millis():

// globale Variablen
unsigned long T1, T2, Zeit_prev;
unsigned long intervall = 3000;

// Abfrage in der Loop
      // Timeout LS1 und LS2, Lichtschranken inaktiv
      if (T2 == 0 && T1 == 0)                         
      {
        if (millis()-Zeit_prev >= intervall) 
        {
          Zeit_prev = millis();
          PORTA &= ~(1<<PA0);   // Status-LED LS1 aus
          LS2_state = 0;        // Status-Byte LS2 zurücksetzen
          PORTA &= ~(1<<PA1);   // Status-LED LS1 aus
          LS1_state = 0;        // Status-Byte LS1 zurücksetzen
        }
      }

Die erste Abfrage nach T1 && T2 funktioniert, auch das zurücksetzen der Bits PA0 und PA1, die Abfrage der millis() allerdings nicht.

Wo ist das Problem zu suchen.

Die IDE-Einstellung zur Funktion millis() ist auf enable sowie alle übrigen Einstellungen für ATtiny84 no Bootloader auf default und 8 Mhz interner Takt. Und ja, den Bootloader habe ich vorher natürlich auch aktualisiert.

Es wäre nett wenn mir jemand weiterhelfen könnte.

Grüße Thomas

(Google translate) Bei allen unterstützten Teilen befinden sich die Zeitmessfunktionen auf Timer0. Das bedeutet, dass eine Neukonfiguration von Timer0 durch Manipulation seiner Register millis() und delay() beschädigt. Dies wird nicht empfohlen, es sei denn, Millis ist vollständig deaktiviert.

Original text:
On all supported parts, timekeeping functions are on timer0. This means that reconfiguring timer0 by manipulating it's registers will break millis() and delay(); this is not recommended unless millis is disabled entirely.

von hier:

1 Like

Danke für die schnelle Antwort und Erklärung. Das würde natürlich zum seltsamen Verhalten bzw. zur Nichtfunktion der millis()-Funktion passen. Ich hatte das kontrollhalber auch nochmal mit einem kleinen Testsketch ausprobiert. Dort funktioniert millis() auch nicht. Also ist das so.

Das heißt für mich im Klartext, daß ich das Rechtecksignal über Timer1 erzeugen muß. Ich habe es an dem Testsketch ausprobiert. Es funktioniert. Die gewünschte Frequenz wird erzeugt. Allerdings ist das Rechteck an der abfallenden Flanke nun stark verzerrt, so als würde sich ein Kondensator entladen.
An der Stromversorgung liegt das nicht, da ich ein leistungsstarkes Labornetzteil mit exakt 5 V angeschlossen habe. Sicherheitshalber habe ich auch nochmal GND und VCC mit 47 uF gepuffert. Bringt aber auch nix. Sowas hatte ich bei Benutzung von Timer0 nicht. Dort war der Kurvenverlauf exakt rechteckförmig.

Ich teste das morgen nochmal mit anderen Timer-Einstellungen.

Hier nochmal meine jetzigen Timer-Einstellungen:

  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 2000 Hz (8000000/((499+1)*8))
  OCR1A = 499;
  // CTC
  TCCR1B |= (1 << WGM12);
  // Prescaler 8
  TCCR1B |= (1 << CS11);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();

Vielleicht erhöhe ich den Prescaler, und verringere den Inhalt des Vergleichsregisters.

Nochmal danke für die schnelle Antwort.

Grüße Thomas

Das Rechtecksignal ist nun sauber. Ein Pull-Down-Widerstand am Frequenzausgang hat gefehlt. Mit 120 kOhm bekomme ich nun eine saubere Rechteckschwingung hin. Kleinere Widerstandswerte dämpfen das Signal zu stark.

Mein Problem ist gelöst. Timer1 verwenden und Pull-Down-Widerstand vorsehen.

Grüße Thomas

Das hört sich an als wäre der Ausgang nicht als Output konfiguriert. Und du schaltest nur den internen pull up ein und aus

1 Like

Hallo Werner, Du hast vermutlich recht. Was die Verwendung von Timern anbelangt, bin ich tatsächlich ein "Newbie".

Hier mein Sketch zur Einstellung des Timers und zur Ausgabe mittels Portmanipulation:

void setupTimer1_ATtiny84() {
  noInterrupts();
  // Clear registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;

  // 2000 Hz (8000000/((499+1)*8))
  OCR1A = 500;
  // CTC
  TCCR1B |= (1 << WGM12);
  // Prescaler 8
  TCCR1B |= (1 << CS11);
  // Output Compare Match A Interrupt Enable
  TIMSK1 |= (1 << OCIE1A);
  interrupts();
}

ISR(TIMER1_COMPA_vect) 
{
  PORTB ^= (1<<PB2);    // clear Bit an PB2 von Port B
}

tippe oder füge den Code hier ein

Ich denke, daß in der Timerdefinition die die Definition als Output fehlt.

Ohne im Handbuch nachzuschauen vermutlich dies hier:

  DDRB |= (1<<PB2);

In meinem Testsketch fehlte die Information, dort bekam ich auch die Probleme mit dem Rechteck und habe das fälschlicherweise mit einem externen Pulldown-Widerstand behoben. In meiner Applikation mit sauberem Rechtecksignal ist das drin und funktioniert auch ohne Pulldown.

Gut, daß wir drüber gesprochen haben. Wieder was gelernt.

Meine Applikation funktioniert nach Umstellung auf Timer1 nun so wie sie soll. Alles gut.

Grüße Thomas

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.