-
Ach, ich dachte der Thread Titel lautet "NiboBee und Timer für Anfänger";
und ob eine LED 60x oder 58x / Sekunde blinkt ist für jemanden, der mit Netzfrequenz getakteten Radioweckern aufgewachsen ist eigentlich wurscht.
Aber Rabenauge hat recht, zu einem delay(x) addiert sich noch die Ausführungszeit der Hauptschleife, und je nach dem was sie gerade zu tun hat ist diese unterschiedlich lang.
Aber wie sollte man 4 Leds mit 2 Timern unabhängig von einander unterschiedlich blinken lassen?
In diesem Beispiel Programm wird also die Hauptschleife abgearbeitet, während im Hintergrund eine Zeit tickt. Vor dem nächsten Durchlauf der Hauptschleife wird gewartet, bis die volle Millisekunde um ist. Die Hauptschleife wird also alle ms 1x durchlaufen. Somit habe ich überall in der Schleife einen festen Zeitbezug. Allerdings darf man die Funktion delay() dann nicht mehr verwenden. Wenn die Zeit von 1 ms nicht mehr reicht, verstellt man die Zykluszeit eben auf 2, 4, 5 oder 10 ms. Die Zykluszeit ist dann auch die minimale Zeitbasis (bei 10ms kann man dann auch nur in 10ms Schritten verzögern).
Also ich denke, dass mit den Kommentaren auch Anfänger zurechtkommen sollten.
Beim Kompilieren gibt es 6 Warnungen; das liegt daran, dass in main() uninitialisierte Variablen abgefragt werden. Diese Variablen gehören eigentlich oberhalb von main() deklariert (ist aber alles Demo).
Und jedesmal, wenn eine timer Variable gesetzt wird, sollte es "Timersollwert"/Zykluszeit heissen - wenn also die Zykluszeit erhöht werden muss, würde der Zeitwert noch stimmen. 2000 / Zykluszeit blieben 2 Sekunden, egal ob Zykluszeit 1ms oder 5ms oder 10ms ist.
Code:
/*! @file nibobee_timer.c
* @brief Timer Routinen für Verzögerungen
* @author Birger Töpelmann
* @date 2010-03-21
*/
/*************************************************************
N I B O B E E T I M E R
*************************************************************/
#include <nibobee/iodefs.h>
//#include <nibobee/motpwm.h>
#include <nibobee/delay.h>
#include <nibobee/sens.h>
//#include <nibobee/odometry.h>
#include <nibobee/led.h>
/*************************************************************
Timer 0 des ATmega16 soll einen periodischen Interrupt auslösen.
Innnerhalb der Interruptserviceroutine (Interrupthandler) wird
ein Softwaretimer jede ms inkrementiert. Aus diesem Softtimer
kann ein feste Zykluszeit (1, 2, 5, 10ms..) für die Hauptschleife
abgeleitet werden. In der Hauptschleife inkrementierte oder
dekrementierte Variablen können dann als weitere Timer mit
Basis Zykluszeit verwendet werden,
--------------------------------------------------------------
Auswahl eines Teilers für das TimerCounterControlRegister:
Systemtakt 15 000 000/s; Überlauf Timer nach 256 Takten (OV);
Teiler /1: 15000000/s / 256 = 58593,75 /s = 58,6 /ms
Teiler /8; 15000000/s / 1024 = 14648,4375 /s = 14,6 /ms
Eine Quarzgenaue "ms" ist so nicht zu erreichen,
Auswahl des Zählerwertes für den Softtimer in der ISR
58: Abweichung = (58 - 58,6) / 58 = -1,03% (eilt vor)
59: Abweichung = (59 - 58,6) / 59 = +0,68% (hinkt nach)
14: Abweichung = (14 - 14,6) / 14 = -4,29%
15: Abweichung = (15 - 14,6) / 15 = +2,67%
*************************************************************/
#define TEILER_T0 0b001 // TCCR0: Systemtakt /1
#define ZAEHLR_T0 59 // ISR: Zusatzteiler s.o.
void timer0_init_mega16() // timer 0 eine ATmega 16 initialisieren
{
TCCR0 |= TEILER_T0; // TimerControlRegister = Teiler /8
TIMSK |= (1 << TOIE0); // Enable Overflow Interrupt
}
volatile uint8_t timer0_ms; // der ms Systemtimer Taktzähler
// volatile == die Variable für jeden Vergleich neu einlesen,
// weil sie in einer anderen Routine (Interrupt) geändert wird
/*
ohne volatile: --> Compiler Optimierung
lade register mit variable
warten:
if register < grenzwert goto warten --> Endlosschleife
mit volatile; --> Compiler soll hier nicht optimieren
warten:
lade register mit variable
if register < grenzwert goto warten
*/
/************************************************************/
// Timer 0 Overflow Interrupt Service Routine
volatile uint8_t tmr0_tick;
// alte Bezeichnung ISR (TIMER0_OVF_vect)
SIGNAL (SIG_OVERFLOW0)
{
if (tmr0_tick == 0)
{
tmr0_tick = ZAEHLR_T0;
timer0_ms++;
}
tmr0_tick--;
}
/************************************************************/
uint8_t timer0_wait(uint8_t ticks)
{
uint8_t merker; // Zwischenspeicher
while(timer0_ms < ticks); // warten auf Systemzeit ms Takte
merker = timer0_ms; // tatsächliche Takte merken
timer0_ms = 0; // Systemtimer Reset
return(merker); // tatsächliche Zykluszeit zurückliefern
}
/******************************************* V A R I A B L E N */
/* ...auch Konstanten sind variabel... ;-) */
// Hier gehören die im Hauptprogramm definierten Variablen rein,
// dann gibt's auch keine 8 Warnungen mehr vom Compiler !!!
uint16_t timer_LED1; // uint16 weil > 255 Definition aber Warnung
uint8_t timer_Sens; // uint8 für Zeiten < 255 ms
/******************************************* F U N C T I O N S */
void led_mask(uint8_t muster);
void led_aus(uint8_t muster);
void led_ein(uint8_t muster);
/***************************************************** M A I N */
int main()
{
timer0_init_mega16(); // Timer0 für periodischen Interrupt initialisieren
sens_init(); // Fühler Taster Initialisieren
led_init(); // LEDs initialisieren
while((sens_getLeft()==0) && (sens_getRight()==0)); // Warten auf Startsignal am Fühler
while(1==1) /* - - - - - - - - - - - M A I N L O O P */
{
enable_interrupts(); // Interrupts freigeben
#define Zykluszeit 1 // Hauptschleife Zykluszeit in ms
uint8_t tmr0_watch = timer0_wait(Zykluszeit); // Zykluszeit abwarten
if(tmr0_watch > Zykluszeit) // Zykluszeit überwachen
{
while(1==1) // Fehlerfalle Zykluszeit überschritten
{
if (tmr0_watch > 10) led_ein(15); // Zykluszeit > 10 alle LED ein
else led_ein(tmr0_watch); // Zykluszeit Binär ausgeben
timer0_wait(250); // Klassische Verzögerung LED Ein Zeit
led_aus(15); // LED aus für Blinklicht
timer0_wait(150); // Klassische Verzögerung LED Aus Zeit
}
}
// Ab hier wird die Hauptschleife in einer festen Zykluszeit abgearbeitet
// Da der Intervall feststeht, können beliebig viele Timer abgeleitet werden
// uint16_t timer_LED1; // uint16 weil > 255 Definition aber Warnung
if(timer_LED1-- == 0) timer_LED1 = 1000; // Zyklischer Timer 1000ms
if(timer_LED1 > 700) led_set(LED_L_YE,1); // LED Blinklicht
else led_set(LED_L_YE,0);
// uint8_t timer_Sens; // uint8 für Zeiten < 255 ms
int8_t SensLeft; // Sensor Status
if(timer_Sens != 0) timer_Sens--;
else
{
timer_Sens = 200 / Zykluszeit; // alle 200ms
SensLeft = sens_getLeft(); // Sensorabfrage
}
// Hier wieder normaler Zyklus
/*
solange der linke Fühler nach hinten gedrückt wird, leuchtet die rote LED links
wird der linke Fühler nach vorne gedrückt sollte die LED blinken, weil aber das
delay() verwendet wird, landet das Programm in der Zyklusüberwachung.
*/
switch(SensLeft)
{
case -1: led_set(LED_L_RD,1); break;
case +1: led_set(LED_L_RD,1);
delay(200);
// dieses delay darf in der Hauptschleife nicht mehr verwendet werden
led_set(LED_L_RD,0);
delay(200);
// dieses delay darf in der Hauptschleife nicht mehr verwendet werden
break;
default: led_set(LED_L_RD,0); break;
}
/* Rechts funktioniert die o.a. Funktion */
int8_t SensRight; // Sensor Status
// timer_Sens auf 1 abfragen, er wird oben bearbeitet und bei Zählerstand 0 neu geladen
if(timer_Sens == 1) SensRight = sens_getRight(); // alle 200ms Sensorabfrage
uint16_t timer_LED_R;
if(timer_LED_R-- == 0) timer_LED_R = 400; // Zyklischer Timer 400ms
switch(SensRight)
{
case -1: led_set(LED_R_RD,1); break;
case +1: if(timer_LED_R > 200) led_set(LED_R_RD,1); // LED Blinklicht
else led_set(LED_R_RD,0);
break;
default: led_set(LED_R_RD,0); break;
}
/*
Ein- und Ausschaltverzögerungen:
Wird der rechte Fühler dauernd betätigt, schaltet nach 2 Sek Verzögerung die rechte gelbe LED ein;
wird der rechte Fühler losgelassen, leuchtet die LED noch 5 Sekunden nach.
*/
uint8_t Flanke_rechts;
int16_t timer_Verz_Ein;
int16_t timer_Verz_Aus;
if((Flanke_rechts == 0) && (SensRight != 0)) timer_Verz_Ein = 2000; // Timer setzen bei
if((Flanke_rechts != 0) && (SensRight == 0)) timer_Verz_Aus = 5000; // Flankenerkennung
Flanke_rechts = SensRight;
led_set(LED_R_YE,0);
if((SensRight != 0) && (timer_Verz_Ein == 0)) led_set(LED_R_YE,1); // Einschaltverzögerung
if((SensRight == 0) && (timer_Verz_Aus != 0)) led_set(LED_R_YE,1); // Ausschaltverzöferung
if(timer_Verz_Ein > 0) timer_Verz_Ein--; // Die Timer sollten immer als Countdown arbeiten,
if(timer_Verz_Aus > 0) timer_Verz_Aus--; // und bei 0 stehen bleiben
} /* - - - - - - - - - - - M A I N L O O P */
return(0);
}
/*****************************************************************
Routinen zum direkten Setzen aller 4 Leds des NiboBee
LED_L_YE = 1, LED_L_RD = 2 ,LED_R_RD = 4, LED_R_YE = 8
led_mask(9); Schaltet gelbe LEDs ein und rote LEDs aus
led_ein(6); Schaltet rote LEDs ein, gelbe LEDs bleiben (Ein o. Aus)
led_aus(6); Schaltet rote LEDs aus, gelbe LEDs bleiben (Ein o. Aus)
*****************************************************************/
void led_mask(uint8_t muster)
{
for(uint8_t c = 0; c < 4; c++)
{
if (((muster >> c) & 1) == 1) led_set(c,1); else led_set(c,0);
}
}
void led_aus(uint8_t muster)
{
for(uint8_t c = 0; c < 4; c++)
{
if (((muster >> c) & 1) == 1) led_set(c,0);
}
}
void led_ein(uint8_t muster)
{
for(uint8_t c = 0; c < 4; c++)
{
if (((muster >> c) & 1) == 1) led_set(c,1);
}
}
Viel Spaß und Erfolg bei eigenen Projekten...
-
Hallo
In der Hauptschleife auf die ISR zu warten ist nicht so optimal. Basierend auf dem Code von oben würde ich das etwa so angehen:
Code:
// Vier LEDs ansteuern mit Timer 0 21.3.2010 mic
#include <nibobee/iodefs.h>
#include <avr/interrupt.h>
volatile uint8_t p=0; // Auf diese Variable greift die ISR und das Programm zu!
int main(void)
{
TCCR0 = (1<<CS02) | (1<<CS00); // Normal Mode, kein OC0-Pin, prescaler /1024
TIMSK |= (1<<TOIE0); // Timer0 Overflow-Interrupt erlauben
DDRB |= 0x0f; // alle Leds sind Ausgang
PORTB &= ~0x0f; // und low
enable_interrupts();
while(1)
{
if(!p)
{
p=4*57; // alle 4 Sek. (atomar weil byte!)
PORTB ^= 0x0f; // alle LEDs umschalten
}
// nix zu tun
}
}
ISR(TIMER0_OVF_vect)
{
static uint8_t led0=0, led1=0, led2=0, led3=0;
if(led0) led0--; else { PORTB ^= 1; led0=57; } // eine Sek.
if(led1) led1--; else { PORTB ^= 2; led1=57/2; } // 1/2 Sek.
if(led2) led2--; else { PORTB ^= 4; led2=57/3; } // 1/3 Sek.
if(led3) led3--; else { PORTB ^= 8; led3=57/4; } // 1/4 Sek.
if(p) p--; // Usertimer :)
}
Das ist natürlich auch nur eine Möglichkeit von vielen...
Gruß
mic
-
Bei sourceforge gibt es neue Dateien von Nils Springob für die NiboBeeLib = Revision 25 vom 16.3.2010:
Der Timer 2 isses:
Weil der mit einer festen Taktfrequenz zählt, und die PWM über Compare Match erzeugt wird, wird mit dem Timer 2 Overflow auch gleich für die NiboBee Software Clock zum Ticken gebracht.
Die Odometrie geht auf eine Regelung der Motordrehzahl, sogar Streckenvorgabe ist möglich. Probiert habe ich noch nicht, nur mal angeschaut. Ich weiß allerdings nicht sicher, ob die Uhr weitertickt, wenn die Motoren gestoppt werden. Das sollte aber so sein, weil in clock.c auch die Sekunden seit dem Einschalten des Nibobee gezählt werden.
Um im Simulator zu sehen, was die Bibliotheken mit den Registern machen, muss man die entsprechenden Header und Source Dateien direkt in das Projekt mit einbinden und gemeinsam mit der eigen main() compilieren. Beobachten, welches Bit in welchem Register gesetzt wird, geht einfacher, als all die defines durchzudenken.
-
Klingt interessant.
Leider (nunja, soo leider auch nicht) muss ich die Timer-Geschichte einige Tage zurückstellen, da ich mir fürs Bienchen was gebastelt habe, und es funktioniert sogar. :D
Werde aber demnächst (muss den Basteltüdel erst wieder hübsch machen) unbedingt reinschauen.
-
Hallo Leute,
oh nein, und ich habe mir in den letzten Wochen selber eine Regelung zusammengebaut - sie kann regeln, Strecke fahren und definierte Kurven fahren. Und was les ich hier? Nicai hat es selber der Bibliothek zugefügt.
Allerdings fahre ich einen ganz anderen Ansatz. Zum einen ist meins kein echter PID-Regler und zum anderen ist mein Regelalgorithmus langsamer und verbraucht deshalb viel weniger CPU-Resourcen.
Hintergrund: Ich habe die Biblitothek von Grund auf neu geschrieben. Bin allerdings noch mittendrin. Herzstück ist ein Timer-System, dass durchaus ähnlich den Tasks von NXC / NXQ ist. Man kann damit Funktionen zu einem zukünftigen Zeitpunkt ausführen, Auflösung ist 1 ms und maximale Verzögerung ist 60 s.
Bisher funktioniert das Timer-System, Tastenentprellung (Fühler), Motorsteuerung und die Motorregelung. Die LED-Steuerung hab ich noch von Nicai - die will ich aber auch noch überarbeiten. Das Thema Liniensensoren hab ich noch gar nicht in Angriff genommen...
Als nächstes habe ich eine ganz andere Geschichte auf dem Programm: Ich habe an den Port mit der USART einen IR-Empfänger angeschlossen und will da erstmal einen RC5-Receiver zusammenschustern. Dann kann man die Biene schön mit einer FB steuern :-)
Ich habe auch geplant, das ganze auf sourceforge zu veröffentlichen, will mich vorher noch mit Nicai abstimmen. Nicht, dass Herr Springob noch sauer wird...
Ciao bantyy