Table of Contents

Compteurs/Timers

Les compteurs qui dans un certains sens peuvent s'appeler timers, sont l'une des sous fonctions les plus importante d'un micro-contrôleur. Ils permettent de préciser les processus de temps, de produire des événements de comptage et des signaux. Un compteur convertit le nombre de cycles d'entrées en une valeur binaire utilisant un tableau de triggers. Le nombre maximal de cycles comptés dépend de la longueur de ce tableau et est marqué par la longueur du code binaire. Un AVR a des compteurs de 8- à 16-bits. Si un minuteur a atteint sa valeur maximale (255 en 8 bits et 65535 avec les compteurs 16 bits), le cycle suivant produira un dépassement de capacité et le compteur redémarre de 0. Le signal d'horloge d'un compteur peut venir du signal d'horloge du micro-contrôleur et dans ce cas il est possible de diminuer sa valeur utilisant un prescaler (diviseur). Certains AVRS ont des générateurs de signaux d'horloge indépendants en interne, qui peuvent être modifiés pour exécuter plus rapidement l'utilisation d'un multiplicateur de fréquence. Les compteurs diffèrent aussi dans des cas d'application et des modes de travail.

Mode par défaut du compteur

Dans le mode par défaut, un compteur ne fait rien plus que compter des nombres séquentiels tout le temps. Sa valeur peut, bien sûr, être lue et changée à partir du programme à tout moment. La seule fonction supplémentaire dans le mode par défaut permet de déclencher une interruption sur le dépassement de capacité du compteur. Le mode par défaut est typiquement utilisé pour exécuter une section du programme à certains intervalles.

Exemple

Tâche: Réaliser l'exécution d'une interruption toutes les 10 ms (à une fréquence de 100 Hz) avec l'ATmega128 de 8 MHz. Pour cette tâche, le compteur 0 de 8 bits est approprié.

#include <avr/interrupt.h>
 
ISR(TIMER0_OVF_vect)
{
	// Give the counter such a value
	// that the next overflow occurs in 10 ms.
	// Formula: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178
	TCNT0 = 178;
}
 
int main()
{
	// To make the first overflow interrupt fire in 10 ms as well,
	// the counter needs to be initialized here.
	TCNT0 = 178;
 
	// Prescaler value 1024
	TCCR0 = 0x07;
 
	// Allow overflow interrupts
	TIMSK |= (1 << TOIE0);
 
	// Allow interrupts globally
	sei();
 
	// Endless loop
	while (1) continue;
}

Le compteur de cet exemple ne génère pas l'interruption exactement toutes les 10 ms, pour le faire il est nécessaire d'ajouter au compteur une valeur décimale et ce n'est pas possible. Pour obtenir un intervalle précis entre deux interruptions, on doit choisir à la fois la valeur de prescaler et la valeur initiale du compteur pour que leur division aboutisse à un nombre exact. Ce n'est pas toujours possible, particulièrement avec des compteurs de 8 bits comme leur gamme de valeur est petite. Pour réaliser un intervalle plus précis ou plus grand, on utilisera un compteur 16 bits.

Compteur d'horloge externe

Il est aussi possible d'utiliser une source d'horloge externe comme signal d'horloge du compteur. Pour cette fonction il existe une broche de l'AVR qui s'appelle Tn, avec n marquant le numéro du compteur. Le signal d'horloge externe et la polarité peuvent être sélectionnés en utilisant le registre du prescaler.

Evénements chronométrés

Depuis que les compteurs permettent les opérations chronométrées, des micro-contrôleurs AVR ont une option d'événements à un moment précis au niveau hardware. Cette partie du compteur est appelée unité de capture d'entrée. On a le choix entre deux types d'événements: la modification logique de la valeur d'une broche spécifique ou de la valeur du résultat du comparateur analogique. Si l'événement sélectionné intervient, la valeur du compteur est écrite dans un registre spécifique, à partir duquel la valeur peut être lue à tout moment. Si l'événement est plus grand que la valeur de la capacité du compteur, alors le programme compte le nombre de dépassement de capacité et prend cette valeur en compte au moment du traitement de la valeur finale.

Exemple

Tâche: Mesurer la fréquence d'un signal logique carré externe de 122 Hz - 100 kHz en utilisant un ATmega128 de 8 MHz. La mesure doit avoir une précision de 1 Hz. Le programme utilise un compteur de 16 bits avec une unité de capture d'entrée.

#include <avr/interrupt.h>
 
unsigned long frequency;
 
// Interrupt for the event
ISR(TIMER1_CAPT_vect)
{
	// Counter to 0
	TCNT1 = 0;
 
	// The result is valid only if the counter
	// has not overflown yet
	if (!(TIFR & (1 << TOV1)))
	{
		// Calculating the frequency from the period
		frequency = (unsigned long)8000000 /
		            (unsigned long)ICR1;
	}
	else
	{
		// Frequency is less than 122 Hz
		frequency = 0;
 
		// Set the counter's overflow flag to 0
		TIFR &= ~(1 << TOV1);
	}
}
 
int main()
{
	// Register a rising front, prescaler value 1
	TCCR1B = (1 << ICES1) | (1 << CS10);
 
	// Allow event interrupts
	TIMSK = (1 << TICIE1);
 
	// Allow interrupts globally
	sei();
 
	// Endless loop
	while (1) continue;
}

Le programme exécute une interruption à chaque front montant du signal externe. Pendant l'interruption, le dépassement de capacité du compteur est surveillé - cela peut arriver si la fréquence du signal est au-dessous de 122 Hz (8 MHz / 216) et dans ce cas la valeur du compteur ne reflète pas la période réelle. La fréquence est calculée en utilisant des nombres de 32 bits pour obtenir l'inverse de la période. La première chose est de mettre le compteur à 0, parce le timer fonctionne sur le même signal d'horloge que le processeur et chaque exécution d'instruction qui arrive après l'événement externe le plus court la période mesurée fausse le résultat. La fréquence maximum mesurée est limitée au temps passé pendant l'interruption du programme.

La détection d'événements et l'enregistrement du temps qu'il a pris pour être réalisé peut aussi être résolu au niveau logiciel. Il est possible d'utiliser des interruptions externes ou autres et de lire la valeur du compteur pendant ces événements. La détection d'événements au niveau matériel doit être exécuté indépendamment du programme principal et du temps relativement court (ou fréquent) des événements.

Génération du signal

Des compteurs plus complexes peuvent générer un signal, en plus du chronométrage. Pour ce faire le compteur a une unité de comparaison de sortie et une unité de sortie de correspondance de comparaison. L'unité de comparaison de sortie a des registres de la même largeur de bits que le compteur et les valeurs de ces registres sont comparées à la valeur du compteur pendant son fonctionnement. Une interruption peut être générée et les valeurs des broches spéciales peuvent être changées à chaque fois que la valeur du compteur est égale à la valeur dans le registre de l'unité de comparaison. À ce moment une broche peut alors être mise haut ou bas ou inversées. Le signal est généré par les changements de la valeur de la broche de sortie.

Avec certains signaux produisant des modes, la valeur maximale du compteur peut être changée. La taille physique du compteur restera le même, mais un registre de comparaison est utilisé pour remettre le compteur à une valeur spécifique. Les exemples précédents pourraient aussi être résolus en utilisant cette méthode, mais la fonction sert plutôt à changer la période du signal. En plus de cela, un compteur peut être configuré dans un mode où il fonctionne aussi bien en croissance qu'en décroissance.

Les compteurs et le mode générant un signal sont l'un des modules de l'AVR les plus complexes. Toute écriture à ce propos prendrait énormément de temps et il n'est pas nécessaire de tout savoir pour pouvoir les utiliser. Le paragraphe suivant concerne les signaux PWM les plus courant en robotique. Le reste est à retrouver dans la documentation technique de l'AVR.

Modulation de la largeur de l'impulsion

La modulation de la largeur de l'impulsion (PWM) est un type de signal où la fréquence et la période sont des constantes, mais la longueur des demi-périodes change. Les signaux PWM sont utilisés pour contrôler les matériels électromécaniques, optiques ou autres. Par exemple, on peut utiliser un signal PWM de 50 MHz avec une demi période haute de 1 à 2 ms pour contrôler un servo moteur.

Exemple

Tâche: en utilisant un ATmega128 de 8 MHz, générer deux signaux de régulation de la vitesse du servo moteur. Utiliser la broche PB5 (OC1A) pour générer une impulsion de largeur de 1 ms et la broche PB6 (OC1B) pour générer une impulsion de largeur de 2 ms.

#include <avr/io.h>
 
int main()
{
	// Set pins as outputs
	DDRB |= (1 << PIN5) | (1 << PIN6);
 
	// Set outputs A and B low for comparison,
	// "Fast PWM" mode, prescaler value 8
	TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
	TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
 
	// Maximum value of the counter. Formula:
	//   TOP = 8 MHz / 8 / 50 Hz
	ICR1 = 20000;
 
	// Half-period of the first motor is 1 ms, and second 2 ms
	OCR1A = 1000;
	OCR1B = 2000;
 
	// Endless loop
	while (1) continue;
}