_delay_us et _delay_ms fonctionnent par boucle software, les timers hard des AVRs permettent de faire la même chose (et même plus).
Cette exemple n'utilise pas d'interruptions, le programme est en pooling sur le registre du timer1 (timer 16 bits).

#include <avr/io.h>
 
void main()
{
	DDRA = 0xFF;
 
	TIMSK = 0;
	TCCR1A = 0;
	TCCR1B =(0<<ICNC1) 
			|(0<<ICES1)
			|(0<<WGM13)
			|(0<<WGM12)
			|(1<<CS12)		//
			|(0<<CS11)		//choix du prescaler
			|(1<<CS10);		//
 
	while(1){
		while(! ((TIFR & 0x4) == 0x4));
			PORTA =~ PORTA;
			TIFR |= 1<<TOV1;	//remise à zéro de TOV1
	}
}

Ici, le prescaler est réglé à 1024, Avec une horloge de 16MHz le portA change d'état toutes les : (1/16MHz) x 1024 x 65536 = 4.1943 secondes

Voila, les avantages des tempos par timer sont:

  • L'ajout de tests, actions... dans la boucle de temporisation
  • Etre indépendant du compilateur et de ses optimisations
  • Faire des tempos variables (_delay_us et _delay_ms prennent en arguments des constantes)