Ich versuche, alle 12 Stunden eine LED für 5 Sekunden einzuschalten. Ich habe es versucht:
#include <mega16.h>
#include <delay.h>
void main(void)
{
DDRC.0 = 1;
PORTC.0 = 0;
while(1)
{
unsigned long i;
unsigned long j;
PORTC.0=1;
for(i=1; i<432; i++)
{
delay_ms(100);
}
PORTC.0 = 0;
for(j=1; j<100; j++)
{
delay_ms(50);
}
}
}
aber die LED war jede Woche etwa 2 Stunden lang an.
Ich hatte das Bedürfnis, eine nicht blockierende Lösung für das Problem zu finden, insbesondere weil wir hier über zwölf Stunden Verzögerung sprechen.
Die Bibliothek util/delay.h und ihre Funktionen _delay_ms() und _delay_us() sind die Software-Verzögerungsfunktionen. Sie sind praktisch in kleinen Programmen und für schnelles Prototyping und Experimentieren. Sie sind einfach und die Genauigkeit könnte in vielen Fällen ausreichen, aber wir haben hier einen Nachteil. Da es sich um Softwareverzögerungen handelt, blockieren sie die CPU. Tatsächlich verwenden wir die CPU als Zähler/Timer, sodass wir in der Zwischenzeit nichts anderes tun können.
Mit Hardware-Timern ist es möglich, im Hintergrund zu zählen und Ereignisse mit Interrupts zu verarbeiten, sodass die CPU frei verwendet werden kann, während der Timer unabhängig läuft.
Der "Nachteil" ist, dass wir etwas mehr Dokumentation durchgehen und etwas mehr Code schreiben müssen, dafür aber eine höhere Genauigkeit und eine freie CPU bekommen .
Ich werde den Timer1 von Atmega16A verwenden , der ein 16-Bit-Zähler ist. Die Taktfrequenz ist und ich möchte, dass der Timer alle Sekunden einmal überläuft, und ich werde diese Sekunden zählen, bis 12 Stunden (43200 Sekunden) erreicht sind.
Schritte:
Ausführung:
Wir beginnen mit:
, wenn wir diese Frequenz ohne Vorskalierung verwenden, hat der Timer in jedem ein Häkchen:
Leider ist es zu schnell, also werden wir die Taktfrequenz um 256 vorskalieren.
Für einen 1-Sekunden-Überlauf
Ticks benötigt werden, (In diesem 1 s- Fall gibt die Frequenz diesen Wert eindeutig an.)
Bei einem 16-Bit-Timer ist der maximal mögliche Wert:
, also passt unser Wert, nicht wie mit
Wo
wäre zu hoch.
Nachdem wir die oben genannten Werte ermittelt haben, ist es an der Zeit, den Timer zu konfigurieren. Wir werden den CTC-Modus (Clear Timer on Compare match) des Timers verwenden, der es ermöglicht, einen benutzerdefinierten Maximalwert festzulegen.
Im CTC-Modus wird der Zähler auf Null zurückgesetzt, wenn der Zählerwert (TCNT1) mit dem OCR1A (WGM13:0 = 4) übereinstimmt.
Der nächste Schritt besteht darin, den Prescaler durch die Clock-Select-Bits im TCCR1B- Register auf 256 zu setzen. Hinweis: Die Zählung beginnt, sobald die Clock-Quelle ausgewählt ist.
Nächste; Aktivieren Sie den entsprechenden Timer1-Interrupt im TIMSK- Register (Timer/Counter Interrupt Mask Register), der bei Erreichen des Maximalwerts ausgelöst wird.
#include <avr/io.h>
#include <avr/interrupt.h>
#define LED PC0
// global variable to count the seconds
volatile uint16_t sec_cnt = 0;
// Timer1 initializtion
void init_timer1()
{
// Timer Mode 4: Clear Timer on Compare match (CTC)
TCCR1B |= (1<<WGM12);
// Initialize Timer staring value
TCNT1 = 0;
// Set Compare value for 1s overflow
OCR1A = 31249;
// Enable Timer Compare A Match interrupt
TIMSK |= (1<<OCIE1A);
// Start Timer & Clock Select: Prescale I/O clock by 256
TCCR1B |= (1<<CS12);
}
// Timer1 output compare match A interrupt rutine
ISR(TIMER1_COMPA_vect)
{
if(sec_cnt < 5)
{
PORTC |= (1<<LED); // turn on LED in first 5 sec
}
else
{
PORTC &= ~(1<<LED);
}
sec_cnt++;
if(sec_cnt == 43200) // 12*60*60 = 43200
{
sec_cnt = 0; // restart counting
}
}
void main(void)
{
// init Timer1
init_timer1();
// enable global interrupts
sei();
while(1)
{
// Let the ISR handle the LED ...
// and do other stuff
}
}
Dies ist nur eine Möglichkeit, die ISR zu schreiben, eine andere könnte ein Flag verwenden, das einen Zeitraum von 12 Stunden angibt, und dann die LED in der Hauptfunktion behandeln, wenn dieses Flag gesetzt ist. Für die 5-Sekunden-Verzögerung könnte entweder das _delay_ms(5000)
(da es nicht so lang ist) oder ein separates Flag verwendet werden. Die Interrupt-Vektoren (zB: TIMER1_COMPA_vect
) sind im Datenblatt aufgeführt.
Ich habe einen Pin innerhalb des ISR umgeschaltet, um das 1-Sekunden-Timing der Interrupts zu messen, und ich habe die LED alle 30 Sekunden für 5 Sekunden eingeschaltet. Das Ergebnis ist im Bild unten:
Beim 1-Sekunden-Wert konnte ein kleiner Fehler (~0,9 %) beobachtet werden, der durch die Ungenauigkeit des internen Oszillators verursacht wird. Laut Datenblatt ist es normal:
Bei 5 V, 25 °C und einer ausgewählten Oszillatorfrequenz von 1,0, 2,0, 4,0 oder 8,0 MHz ergibt diese Kalibrierung eine Frequenz innerhalb von ± 3 % der Nennfrequenz.
Zuerst müssen Sie die Taktgeschwindigkeitsmaskerade behandeln, die in den Kommentaren besprochen wird. Wenn Sie sicherstellen, dass Ihre Uhren richtig sind, würde ich den Code wahrscheinlich ein wenig umgestalten:
int32_t second;
for (second = 0; second < 60LL*60*12; ++second) {
if (second < 5) {
PORTC.0 = 1;
} else {
PORTC.0 = 0;
}
delay_ms(1000);
}
Beachten Sie jedoch, dass dieser Ansatz etwas ungenau ist, da Ihre Gesamtperiode länger als 1 Sekunde ist. Die Ausführung des Codes braucht Zeit, sei es sehr kurz im Vergleich zu den 1000 ms Wartezeit.
Ja, ich hasse auch diese 12-Stunden-Sperrschleife.
int
Litergröße, um lang genug zu sein.unsigned int seconds,mins,hours;
for(hours=0;hours<12;hours++)
{
for(mins=0;mins<60;mins++)
{
for(seconds=0;seconds<60;seconds++)
{
delay_ms(1000);
}
}
}
led = 1;
delay_ms(5000);
led = 0;
Dzarda
F_CPU
Makro ist für diesen Zweck.Asmyldof
Bence Kaulics
Matt Jung
#define F_CPU
?Nick Johnson
Al Longley