ATtiny84 Timer beeinflusst Ausführungsgeschwindigkeit

  • C

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Niko Ortner.

    ATtiny84 Timer beeinflusst Ausführungsgeschwindigkeit

    Ich hab hier ein interessantes Problem mit einem ATtiny84 (Atmel AVR Microcontroller).
    Die Funktion von Timer0 wird mit den TCCR0A- und TCCR0B-Registern gesteuert. Die relevanten Bits sind in Kommentaren im Code unten markiert. Die Detailierte Beschreibung findet man im Datenblatt, aber hier eine kurze Zusammenfassung:
    Bits A: "Output Compare Match B" wird auf den entsprechenden Ausgang geschaltet.
    Bits B: "Fast PWM Mode": Der Timer beginnt bei 0 und der Ausgang wird eingschaltet. Der Timer zählt nach oben. Beim Erreichen von OCR0B wird der Ausgang ausgeschaltet. Beim Erreichen von OCR0A wird der Timer auf 0 zurückgesetzt.
    Bits C: Prescaler. Bei 0b000 wird der Timer angehalten. bei 0b001 läuft der Timer mit der vollen Geschwindigkeit, bei höheren Werten (bis 0b101) läuft der Timer immer langsamer.
    Wichtig dabei: Der Timer läuft komplett unabhängig vom Code. Die Geschwindigkeit des Timers hat keinen Einfluss auf den Code... oder sollte keinen Einfluss auf den Code haben, denn leider passiert genau das bei mir.

    Folgender Aufbau:

    Die gelbe LED hängt am Timer-Ausgang. Die rote und grüne LED werden im Code unten manuell ein- und ausgeschaltet.

    C-Quellcode

    1. void setup()
    2. {
    3. // Alle Pins auf Ausgang stellen.
    4. DDRA = 0xFF;
    5. DDRB = 0xFF;
    6. // Periodendauer und Pulsweite
    7. OCR0A = 10;
    8. OCR0B = 1;
    9. // AA BB
    10. TCCR0A = 0b00100001;
    11. // BCCC
    12. TCCR0B = 0b00001000;
    13. }
    14. void loop()
    15. {
    16. for (byte b = 0b000; b <= 0b101; b++)
    17. {
    18. TCCR0B = 0b00001000 | b;
    19. PORTB |= 0b00000001; // Grün ein
    20. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    21. PORTB &= ~0b00000001; // Grün aus
    22. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    23. PORTB |= 0b00000010; // Rot ein
    24. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    25. PORTB &= ~0b00000010; // Rot aus
    26. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    27. }
    28. }

    Die Schleife in void loop() setzt den Prescaler und lässt dann zuerst die rote LED blinken, und dann die grüne. Die for-Schleifen dazwischen vertrödeln einfach nur etwas Zeit.
    Logischerweise gibt es keinen Grund, warum diese Wartezeiten pro Iteration der äußeren Schleife unterschiedlich sein sollten. Aber startet man den Microcontroller, zeigt sich folgendes Verhalten:
    • Die gelbe LED ist ausgeschaltet, da der Timer noch nicht läuft (Prescaler = 0b000).
      Die grüne LED blinkt, dann die rote. Das passiert relativ schnell. Es dauert ca. 0.5 Sekunden.
    • Die gelbe LED schaltet ein, weil der Timer jetzt läuft (Prescaler = 0b001).
      Die grüne LED blinkt, dann die rote. Das geschieht jetzt aber sehr langsam. Es dauert ca. 20 Sekunden.
    • Die grüne LED blinkt, dann die rote. Das geschieht jetzt schneller als vorher. Ca. 1 Sekunde.
    • Für die restlichen Iterationen dauert das Blinken dann wieder ca. 0.5 Sekunden (aber nie weniger).
    • Die gelbe LED schaltet wieder aus, sobald der Prescaler auf 0b000 gesetzt wird.


    Also aus irgend einem Grund beeinflusst der Wert in TCCR0B die Geschwindigkeit, mit der der Code ausgeführt wird.

    Kommentiert man die Zeile TCCR0B = 0b00001000 | b; aus, blinken die LEDs konstant ca. alle 0.5 Sekunden.

    Ich habe keinen Plan, was hier abgeht und würde mich über jede Hilfe freuen.

    *Topic verschoben*
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Der Timer hat sehr wohl Einfluss auf die Ausführungsdauer. Der Controller läuft nicht multithreaded. Dein Hauptprogramm wird ausgeführt, wenn das Register voll ist wird der Timer interrupt ausgelöst. Dann springt der ProgramPointer in deine Interruptroutine und die vorherige Position wird gespeichert. Ist dein Timercode durchgelaufen, dann springt der Zeiger auf die gespeicherte Adresse und es wird mit dem Hauptprogramm weiter gemacht. Währenddessen blinkt also Nix. Das ist auch der Grund warum die Interruptroutine nie länger dauern darf als die Interruptfrequenz. Dann wird der Timer nämlich nie wieder verlassen.
    @Gonger96
    Interessant. Wenn ich die Interrupts manuell deaktiviere (entweder durch das Deaktivieren der Timer-Interrupts mit TIMSK0 &= ~0b00000111;, oder durch das Deaktivieren aller Interrupts mit SREG &= ~0b10000000;), dann funktioniert alles wie erwartet.

    Aber das wundert mich doch sehr, weil ich weder eine Interrupt-Routine geschrieben habe (es gibt nur setup und loop), noch Timer-Interrupts aktiviert habe (und die sind laut Datenblatt standardmäßig deaktiviert).
    Laut dem folgenden Code ist TOIE0 jedoch direkt am Start aktiviert. Ich vermute deshalb, dass das Arduino-...Drumherum da vorher noch Code ausführt.

    C-Quellcode

    1. void setup()
    2. {
    3. DDRA = 0xFF;
    4. DDRB = 0xFF;
    5. while(true)
    6. {
    7. for (int i = 7; i >= 0; i--)
    8. {
    9. bool b = ((TIMSK0 >> i) & 1) != 0;
    10. PORTB |= 0b00000001; // Grün ein
    11. if (b) PORTB |= 0b00000010; // Rot ein
    12. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    13. PORTB &= ~0b00000001; // Grün aus
    14. if (b) PORTB &= ~0b00000010; // Rot aus
    15. for (volatile int i = 0; i < 0x1000; i++) asm volatile ("NOP");
    16. }
    17. for (volatile int i = 0; i < 0x4000; i++) asm volatile ("NOP");
    18. }
    19. }

    Dann würde es mich auch wenig wundern, wenn da irgendwo trotzdem noch 'ne Interrupt-Routine rumfliegen würde.

    Danke jedenfalls für den Hinweis.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils