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...