2017-11-13 3 views
0

Vor kurzem habe ich begonnen, die Atmega328P in reinem c/C++ zu programmieren, ohne alle Arduino-Bibliotheken und Arduino-IDE. Ich möchte eine LED mit einer Rate von 1 Hz (1 Sek. Ein, 1 Sek. Aus) blinken lassen. Leider kann ich nur Timer0, einen 8-Bit-Timer verwenden. Das MCU hat eine Taktfrequenz von 16MHz und ich habe einen Vorskalierer von 1024 gewählt, um die Anzahl der Überläufe so gering wie möglich zu halten, um Jitter zu reduzieren, da eine Interruptroutine immer einen Overhead hat (das macht mehr Sinn, wenn Sie den Rest meiner Frage lesen) . Mit einfacher Mathematik, ich Schluss kam, dass nach 1 Sekunde, Timer0 61 mal übergelaufen ist und dass das TCNT0 Register gleich 8.enAVR 8-Bit-Timer - was tun, wenn der Vergleichswert nicht in das Register passt?

Dann kam ich mit dieser Lösung:

#define F_CPU 16000000ul 

#include <avr/io.h> 
#include <avr/interrupt.h> 

#define bit(b) (1 << (b)) 

void initBlinkTimer() 
{ 
    //Timer0 with CTC Mode 
    TCCR0A |= bit(WGM01) | bit(WGM00); 

    //Compare TCNT0 with 8 
    OCR0B = 8; 

    //Interrupt at OCR0B compare match aka: execute interrupt when TCNT0 equals 8 
    TIMSK0 |= bit(OCIE0B); 

    //Set PB5 as output 
    DDRB |= bit(PB5); 

    //Set the prescaler to 1024 
    TCCR0B |= bit(CS02) | bit(CS00); 

    //Enable global interrupts, so that the interrupt routine can be executed upon the OCR0B compare match 
    sei(); 
} 

//Keeps track of the number of overflows 
volatile unsigned char nOverflows = 0; 

ISR(TIMER0_COMPB_vect) 
{ 
    if(nOverflows >= 61) 
    { 
     //Toggle PB5 
     PORTB ^= bit(PB5); 

     //Reset timer 
     nOverflows = 0; 
     TCNT0 = 0; 
    } 
    else 
    {  
     nOverflows++; 
    } 
} 

int main() 
{ 
    initBlinkTimer(); 
    while(1){} 
} 

Dieser Code initialisiert eine 8-Bit-CTC-Timer mit einem Prescaler von 1024. Eine Interrupt-Service-Routine (ISR) wird ausgeführt, wenn TCNT0 gleich OCR0B ist, was ich in meinem Code auf 8 gesetzt habe. In der ISR wird die nOverflows Variable mit 61 verglichen. Wenn nOverflows gleich 61 ist, ist eine Sekunde vergangen und der PB5 Pin wird umgeschaltet. Es führt auch eine Größer-als-Prüfung durch, falls der MCU den 61. Überlauf verfehlt hat (wenn das in diesem Fall irgendwie möglich ist). Der Timer und die Variable nOverflows werden gelöscht, nachdem der Pin umgeschaltet wurde. Der Timer beginnt dann wieder bei Null.

Meine Frage ist: Ist dies eine gute Möglichkeit, eine LED bei 1Hz zu blinken, wenn nur ein 8-Bit-Timer verfügbar ist? Kann/sollte ein Teil davon in der Hardware statt in der Software implementiert werden?

+0

Es ist so gut wie jeder ... –

+1

Wenn ein Timer keine lange Zeitspanne hat, konfigurieren Sie es so, dass es zu einem Bruchteil der erforderlichen Zeit unterbricht, und erhöht einen statischen Zähler, so dass Wenn die erforderliche Zeit erreicht ist, invertieren Sie den LED-Status und setzen den Zähler zurück. –

+0

@ user7353781 Ich dachte meine Antwort war ziemlich umfangreich, also bitte überlege, ob du das Häkchen anklickst, um es zu akzeptieren. –

Antwort

0

Sie brauchen keine super hohe Präzision in Ihrer LED blinken; niemand wird mit ihren Augen sehen können, wenn es etwas aus ist.

Ich würde sagen, Sie sollten den Timer für ein bis zehn Mal pro Millisekunde zum Überlauf konfigurieren. Nehmen wir an, dass es alle 251 Mikrosekunden überläuft (die genaue Anzahl spielt keine Rolle). Dann fügst du jedes Mal, wenn es überläuft, 251 zu einer uint16_t Variablen hinzu, um Mikrosekunden zu zählen. Wenn der Mikrosekundenzähler größer oder gleich 1000 ist, subtrahieren Sie 1000 davon und inkrementieren Sie Ihren Millisekundenzähler, der wahrscheinlich ein uint32_t sein sollte, so dass Sie es verwenden können, um lange Zeiträume zu messen.

Aber was, wenn die Überlaufperiode nicht eine exakte Anzahl von Mikrosekunden ist? Das ist immer noch gut, weil es wahrscheinlich in der Form "N/M Millisekunden" ausgedrückt werden kann, wo N und M ganze Zahlen sind. Jedes Mal, wenn der Überlauf auftritt, erhöhen Sie Ihren Zähler um N. Wenn der Zähler M erreicht, subtrahieren Sie M vom Zähler und fügen 1 zu Ihrem Millisekundenzähler hinzu. (Im vorliegenden Absatz haben wir nur den Fall M = 1000 berücksichtigt, und ich denke, mit diesem Fall ist es einfacher zu sehen, worüber ich spreche.)

Sie können entweder ein ISR verwenden, um Ihre Millisekunde zu aktualisieren Counter oder machen Sie es in Ihrem Haupt-Code, indem Sie häufig nach einem Überlauf-Flag suchen. So oder so würde es funktionieren.

So haben Sie jetzt eine Variable, die einmal pro Millisekunde, wie die Arduino millis() Funktion zählt, und Sie können es für alle Arten von Millisekunden-Timing, einschließlich blinkender LEDs verwenden.

Das niedrigstwertige Bit (Bit 0) des Millisekundenzählers wechselt einmal pro Millisekunde, seine Periode beträgt also 2 ms. Das nächste davon (Bit 1) hat eine Periode von 4 ms. Bit 10 hat eine Periode von 2048 ms.

So eine einfache Methode um mit einer Periode von 2048 ms LED zu blinken wäre:

led_state = millisecond_counter >> 10 & 1; 

(Der obige Code verwendet bitweise Operationen led_state festlegen eine Kopie von Bit 10 von der Millisekunden-Zähler sein. Angenommen, Sie führen diesen Code häufig aus und schreiben led_state mit den I/O-Registern an die LED, dann hätte Ihre LED eine Periode von 2048 ms.)

Wenn Sie wirklich Wert darauf legen, dass Ihre LED um 2,4% langsamer ist könnte etwas komplizierter wie folgt tun:

static uint16_t last_toggle = 0; 
if ((uint16_t)(millisecond_counter - last_toggle) >= 1000) 
{ 
    last_toggle += 1000; 
    toggle_the_led(); 
}