AVR: Verwenden von ATmega328 Timer1 zum Erstellen einer Verzögerung [Duplikat]

Ich habe einige Probleme, mich darum zu kümmern, und es könnte nur irgendwo ein kleiner Fehler sein, den ich immer wieder übersehe Code wird ausgeführt.

Zu Übungszwecken versuche ich einfach, eine LED an PD4 im Abstand von 1 Sekunde zu blinken.

Ich habe die folgende Formel verwendet, um den Prescaler und den Wert des Output Compare Register A (OCR1A) zu bestimmen:

Formel zur Berechnung

Mit einem Prescaler von 256 erhalte ich einen Wert für OCR1A von 62499:

Ergebnis der Berechnung

Wenn ich mir die Register TCCR1A und TCCR1B im Datenblatt ansehe, setze ich die Waveform Generation Mode-Bits (WGM) auf die Verwendung von CTC und die Clock Select-Bits auf die Verwendung des 256 Prescalers.

Jetzt beginne ich in meiner Hauptfunktion, indem ich die LED einschalte und dann meine Funktion aufrufe, die den Timer starten soll. Dann überprüfe ich, ob das Überlauf-Flag in TIFR1 gesetzt wurde, und wenn ja, schalte ich die LED aus und schreibe eine logische 1 an TOV1, um das Überlauf-Flag zurückzusetzen.

int main(void) {
   DDRD = 0xFF;
   PORTD = 0x00;

   while (1) {

      // Turn on LED at PD4
      PORTD |= (1 << PD4);

      oneSecondDelay();

      // Checking to see if the overflow flag has been set
      if (TOV1 == 1) {

         // Turn off LED at PD4
         PORTD &= ~(1 << PD4);

         // Set 1 in the Output Compare A Flag to reset the overflow flag
         TIFR1 = (1 << TOV1);
      }
   }

   return 0;
}

In meiner Funktion beginne ich damit, OCR1A auf den Wert zu setzen, den ich zuvor berechnet habe. Ich setze dann die Bits im TCCR1B-Register, die für die Verwendung des spezifischen Vorteilers und des CTC-Modus gesetzt werden müssen, und erstelle dann eine Schleife, während ich auf das Überlaufereignis warte; was ich mir vorstelle, sollte eine Sekunde dauern.

void oneSecondDelay() {
   // Set the target value to 62499
   OCR1A = 0xF423;

   // Set prescaler to 256 and start the timer
   TCCR1B |= (1 << WGM12) | (1 << CS12);

   // Waiting for the overflow event
   while ((TIFR1 & (1 <<  OCF1A))) {

   }
}

TLDR: Jetzt ist das Problem, dass, egal was ich mir ausdenken kann, die LED einfach ständig eingeschaltet ist und ich nicht sicher bin, ob ich die falschen Werte in den Registern einstelle oder ob ich nur ein paar Gehirnfurze habe?

Sie schalten die LED nach einer Sekunde aus, aber dann ohne "Verzögerung" schalten Sie die LED wieder ein.
Überprüfen Sie dies: 12 Stunden Verzögerung mit ATmega16A .
Vielen Dank @BenceKaulics! Verwenden des Interrupts, was ich nicht gefunden hatte, also war dies eine große Hilfe und ich denke endlich, dass ich es verstehe :)
G36 hat bereits einen Hinweis gegeben und ich schlage vor, dass Sie einen ISR für Timer1 (ISR (TIMER1_COMPA_vect)) verwenden, mit dem Sie Ihre LED umschaltenPORTD ^= 1 << PD4;
Ich habe versucht, das zu tun, worüber G36 gesprochen hat, aber das hat es für mich nicht getan, aber die Verwendung der ISR hat den Trick getan. Ich wusste einfach nichts über die ISR, bevor @BenceKaulics es erwähnt hat! :) Aber danke für die Antwort!

Antworten (1)

void oneSecondDelay() {

Das Konstrukt dieser Funktion ist nicht besonders optimal. Sie hätten leicht einen Parameter verwenden können, um die Länge der Verzögerung zu bestimmen, ganz zu schweigen von anderen Problemen.

Eine schnelle Lösung wäre so etwas - es hat immer noch Probleme, ist aber zumindest praktikabel.

//create a user-specified delay
//dly-duration in timer ticks
//timer1 presummed running, running at 256:1 prescaler
void myDelay(uint16_t dly) {
   // Set the target value to 62499
   OCR1A = TCNT1 + dly - 1;     //0xF423;

   // Set prescaler to 256 and start the timer
   //TCCR1B |= (1 << WGM12) | (1 << CS12);
   TCCR1B = (TCCR1B &~0x07) | (TMR1_PS256x & 0x07); //set timer1 prescaler to 256:1

   // Waiting for the overflow event
   while ((TIFR1 & (1 <<  OCF1A)) == 0) {

   }

   TIFR1 |= (1<<OCF1A);         //clear the flag

}

dieses Stück Code in Aktion, wenn TMR1_PS100ms als Parameter zugeführt wird:

Geben Sie hier die Bildbeschreibung ein

Das Ziel beim Schreiben von Code ist, dass Sie ihn nicht noch einmal schreiben müssen. Auf diese Weise ist alles, was Sie schreiben, eine Investition, keine Ausgabe.

Danke für die Antwort! Ich weiß, dass ich einen Parameter verwenden sollte, um die Länge der Dauer anzugeben, aber ich habe nur versucht, mir Gedanken darüber zu machen, wie das ganze AVR-Ding funktioniert, also habe ich versucht, es auf das absolute Minimum zu beschränken. Ich habe es am Ende geschafft, indem ich Interrupts verwendet habe. :) Hast du dich auch gefragt, wie du diese gut aussehende digitale Analyse am Ende gemacht hast? :)