Ich habe auf Arduino nano ein Programm geschrieben und weiterentwickelt, welches ich nun auf einer Hardware einsetzen möchte für die es eigentlich nie gedacht war...
Speziell macht mir die Timer Konfiguration Probleme. Auf ATmega328 war der Timer folgendermaßen konfiguriert:
void startTimer()
{
TCCR1A = 0; // normal mode
TCCR1B = 0; // stop timer
TCNT1 = 0; // count back to zero
TCCR1B = bit(WGM12) | bit(CS11); // CTC, scale to clock / 8
if ( timerInterval > 65535 )
{
OCR1A = 65535;
}
else
{
OCR1A = timerInterval;
}
TIMSK1 = bit (OCIE1A);
timerOn = true;
}
void stopTimer()
{
TCCR1B = 0; // stop timer
TCNT1 = 0; // count back to zero
TIMSK1 = 0; // cancel timer interrupt
timerOn = false;
}
ISR (TIMER1_COMPA_vect)
{
}
Der Timer wird im Verlauf des Programms mehrfach gestoppt und mit neu berechnetem timerInterval neu gestartet.
Auf dem ATtiny1606 habe ich das folgendermaßen angepasst.
Ich habe die die neue Schaltung geplant und dabei den 1606 nach Datenblatt gewählt. Mein Fehler war, dass ich es zuvor nicht getestet habe. Nun sind die Platinen gefertigt und bin erst bei der in Betriebnahme auf das Problem gestossen. Klar, mein Fehler. Aber ich habe tatsächlich nicht damit gerechnet, das mir ein Timer solche Probleme machen könnte. Wenn sich das Problem nicht lösen lässt, kann die Platinen entsorgen und muss umplanen und neue herstellen lassen.
Ich wäre also tatsächlich an einer Lösung interessiert.
Bisher habe ich mich mit ATtiny85 und 4313 beschäftigt, der ATtiny1606 ist doch schon recht verschieden dazu. Da wird es andere hier im Forum geben, die sich besser damit auskennen.
Bei der Suche bin ich über ATtiny_TimerInterrupt gestolpert, kannst ja mal einen Blick drauf werfen oder Dir was abschauen
die Frage ist woran siehst du das dein ISR nicht angesprungen wird? Die ISR ist leer.
Programmierst du Bare Metall? Vielleicht nur vergessen die globalen Interrupts zu aktivieren? Weil der korrigierte Code funktioniert bei mir.
Noch paar Tipps.
Das Bit ALUPD musst du nicht setzen. Wird bei dir auch nicht gesetzt, schau dir deine Zuweisungen an.
Getrennte Byte Zuweisungen
.CMP0H = (65535 >> 8);
.CMP0L = (65535 & 0xFF);
sind auch nicht notwendig. Schreib den Wert ins .CMP0 oder .CMP0BUF Register.
Buffer-Register würde ich immer vorziehen, wenn es keine Gründe dagegen gibt. Lies einmal bitte im Manual was der Unterschied zwischen gepuffert und nicht gepufferten Register beim Timer ist.
Man muss auch nicht jedesmal den Timer für einen Restart komplett konfigurieren. Der Timer hat ein Bit zum ein- und ausschalten und wie gesagt gepufferte Register. Wenn dein timerInterval unsigned 16 Bit wäre, kann es weder kleiner 0 noch größer 65535 sein.
Ich habe hier mal einen Beispielcode zusammegestuppelt. Das Übliche. Es wird eine LED im Sekundentakt aus-/eingeschaltet. Ich habe leider nur einen Attiny1614. Sollte aber bei einem Attiny1606 (auf jeden Fall aber bei einem 1616) genau so funktionieren.
#include <Arduino.h>
#if defined(MILLIS_USE_TIMERA0) || defined(__AVR_ATtinyxy2__)
#error "This sketch takes over TCA0, don't use for millis here. Pin mappings on 8-pin parts are different"
#endif
constexpr uint8_t OutputPin {PIN_PB1};
volatile unsigned int counter {0};
ISR(TCA0_OVF_vect) {
++counter;
if (100 == counter) { // 100 counts = 1 Second
counter = 0;
PORTB.OUTTGL = PIN1_bm;
}
TCA0.SINGLE.INTFLAGS = TCA_SINGLE_OVF_bm; // clear interrupt flag
}
// F_CPU is 10 Mhz
// Prescaler = 8
// -> 1.250.000 Mhz
// Counter 0 -> 12499 = 12500 ticks = 0,01 seconds.
//
void setup() {
PORTB.DIRSET = PIN1_bm; // PB1 = output
PORTB.OUTSET = PIN1_bm; // PB1 = HIGH
takeOverTCA0(); // This replaces disabling and resettng the timer, required previously.
TCA0.SINGLE.INTCTRL = TCA_SINGLE_OVF_bm; // Enable overflow interrupt
TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm); // Count ticks not events
TCA0.SINGLE.PER = 0x30D3; // Upper threshold ticks (12499)
TCA0.SINGLE.CTRLA =
TCA_SINGLE_CLKSEL_DIV8_gc | TCA_SINGLE_ENABLE_bm; // Prescaler (clock frequency and start counter)
}
void loop() {}
Der Code den ich gepostet habe ist getestet und funktioniert. Vielleicht versuchst Du es erst mal damit ....
Ich habe es jetzt auch mit einem 1604 probiert. Geht auch. Da die 0er Tinys aber einen Timer weniger haben, muss hier
#if defined(MILLIS_USE_TIMERA0) || defined(__AVR_ATtinyxy2__)
#error "This sketch takes over TCA0, don't use for millis here. Pin mappings on 8-pin parts are different"
#endif
auskommentiert werden, damit das Programm compiliert werden kann. Wenn das beim 1606 auch funktioniert, kann man mit Deinem Code ja mal weiter schauen...
jetzt haben wir genau den Mist mit dem Crosspoting. Eigentlich habe ich darauf keine Lust mehr. Eine Bemerkung zu #10. Deckt sich mit der Antwort im Crossposting. Kennst du den Unterschied der Verknüpfungen/Zuweisungen? =, |= und &=.
Du stoppst den Timer mit: TCA0.SINGLE.CTRLA = 0; // Stop timer
Danach startest du ihn mit: TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
Welche Einstellung des Timers geht dabei verloren?
Wie setzt und löscht man einzelne Bits?
ich sag mal so. Der TO sollte sich entscheiden in welchen Forum er Hilfe haben möchte und sich darauf konzentrieren. Diese Entscheidung dem anderen Forum mitteilen. Das wäre fair für alle. Sein Eingangscode ist nicht komplett falsch. Enthält nur kleine Denkfehler.
Sorry Leute, ich weiß selbst nicht mal mehr welchen ich zuerst geschrieben hatte...
Das dasein dämliche Idee ist , ist einzusehen. Werde mich daran halten.
Das Timer Problem ist wohl gelöst. Ich habe die Zeiten versucht mitzuschreiben und ausgeben zu lassen, das ist sicher nicht hochpräzise, aber die Werte sind Plausibel.
Bleibt noch die Frage warum der Ausgang nicht gesetzt wird.
okay. Mehrere Änderungen stehen an. Du möchtest hoffentlich verstehen was falsch läuft.
Dein bool timerOn muss volatile sein, weil es innerhalb einer ISR geändert wird. Also außerhalb des normalen Programmablaufes. Ein Funktionsaufruf aus der ISR heraus gehört dazu.
In der ISR fehlt das löschen des Interrupts Flags. Das war schon einmal da.
Desweiteren löschst du in stopTimer() mit TCA0.SINGLE.CTRLA = 0;
das Register, heißt die gesamte Timer Konfig ist futsch.
Deshalb war die Frage als Hausaufgabe wie setzt und löscht man einzelne Bits ernst gemeint. Stichwort CTRLA Enable Bit.
Desweiteren löschst du das Interrupt Control Bit von INTCTRL, setzt es aber nicht wieder. ISR wird generell nicht mehr angesprungen. Das ständige löschen und setzen ist hier überflüssig.
sei() mit Arduino Framework ist nicht unbedingt notwendig. Nur bei Bare Metall Programmierung. Arduino schaltet das immer ein.
Überlege was alles in eine einmalige Änderung bzw. Konfiguration des Timers gehört und was zur Laufzeit immer geändert werden muss.
Wenn du für den Pin und timerInterval ebenfalls passende Datentypen verwendest, statt define, kannste noch die Sicherheitsabfrage von timerInterval optimieren. Welchen Datentyp bzw. Wertebereich kann CMP0 bzw. CMP0BUF annehmen? Von welchem Datentyp ist 65535 der Maximalwert? Stichwort #11.
CTRLESET musst du nicht behandeln, dafür verwendest du schon takeOverTCA0().
wenn er das am Laufen hat, muss man sowieso Nachfragen wofür die bool Variable noch genutzt wird und warum der Timer nicht selbstständig durchlaufen kann. Ich wollte aber nicht zu viel Durcheinander reinbringen. Am Ende können wir immer noch eindampfen.
Ich gehe doch richtig in der Annahme, dass die Compare Register nur benötigt werden, wenn man ein PWM Signal erzeugen will. Oder?
Darum verstehe ich nicht, warum CMP0BUF verwendet wird. Für den Timer verwendet man PER. So verstehe ich jedenfalls die ursprüngliche Intention des TO wenn ich mir seinen ATMega Beispielcode anschaue.