Outils pour utilisateurs

Outils du site


tuto_avr_eclipse_2nd_projet_-_timer_led

[TUTO] [AVR] [Eclipse] 2nd Projet - Timer & LED


Introduction

Pour faire suite aux précédents tutoriels 1) je vous propose un nouveau tutoriel qui consistera à faire clignoter une LED

Quoi ?!?! Encore ?!?! Mais on vient de le faire !

Oui mais là on va faire differement… on va utiliser un Timer. Et on va l'utiliser de sorte qu'il va nous reveiller toutes les secondes ou demi-secondes. Les Timer servent a pas mal de chose. L'interet dans le cas present, c'est d'avoir une base temps precise et reguliere. L'autre interet comparé a faire “attendre” le microcontrolleur dans une boucle de NOP, c'est qu'il reste disponible pour faire autre chose. Il ne sera interompu qu'a la fin du temps predefini qu'on lui aura precisé. Dans l'exemple qui va suivre, on ne fera rien faire d'autre au micrcontrolleur, par contre on le passera en mode d'economie d'energie.

Le debut est le meme que pour le tuto precedent .. La mise sous configuration reste facultative, mais recommandée. :-)

De mon coté, le projet existe deja, si vous voulez le telecharger pour l'etudier, il est là : https://github.com/spelle/AVR_Blink_A_LED_Timer.git.

Un clone de cette URL et “Voila !”. :-)


Explication de Code

Nous revoilà pour l'explication du code suivant :

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
 
 
 
ISR(TIMER1_COMPA_vect)
{
   // Toggle the LED
   PORTB ^= (1 << PORTB5);
}
 
 
 
int main( void )
{
   // Set Port B direction Register
   DDRB |= (1<<DDB5) ; // For LED on Pin PORTB5
 
   // Configure the Timer/Counter1 Control Register A
   TCCR1A = 0 ;
   // Configure the Timer/Counter1 Control Register B
   TCCR1B = (1 << WGM12) ; // Configure timer 1 for CTC mode
 
   TIMSK1 = (1 << OCIE1A); // Enable CTC interrupt
 
   OCR1A   = 62499 ; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 256
 
   sei(); //  Enable global interrupts
 
   TCCR1B |= (1 << CS12) ; // Start timer at Fcpu/256
 
   while( 1 )
   {
      sleep_mode();
   }
 
   return( 0 ) ;
}

Commençons par les directives #include. Dans l'ordre nous incluons : avr/io.h : Celui ci n'est plus a présenter, il nous sert à ce que tous mapper nos registres de controle et de donnée a un mnémonique. avr/interrupt.h : Celui-ci va nous servir a pouvoir definir des ISR 2). Il contient aussi les fonctions sei() et cli() qui, respectivement, active et desactive les interruptions. avr/sleep.h : Celui-ci permet, entre-autre de passer le microcontrolleur en mode d'economie d'energie (sleep_mode())

Passons au main() ; on reviendra à l'ISR plus tard. On commence tout d'abord par definir la direction de la Broche #5 du Port B en Sortie. Vient ensuite la configuration du Timer #1 en mode CTC(Clear Timer on Compare Match)). Le Timer #1 a été choisit ici afin de pouvoir definir la periode des “reveils”à une seconde, voir plus ; Il n'aurait pas été possible de prendre le Timer #0 ou #2 pour faire des periodes aussi longues, car ils ne font que 8-bit.

En mode CTC, pour les Timer #0 ou #2 (8-bit), la frequence minimale (avec un prescaler de 1/1024 et une valeur de comptage au maximum (0xFF = 255) est de ~30Hz. Pour le Timer #1 (16-bit), avec les memes conditions (prescaler à 1/1024 et valeur de comptage au max (0xFFFF = 65535) est de 0.1Hz.

NB : Cette frequence max est la frequence de la broche OCnA qui est togglé a chaque Compare Match (en mode CTC)

A tout moment, n'hesitez pas a vous referer a la datasheet, les explications y sont (beaucoup) plus exhaustives ! :-)

Les lignes suivantes configurent donc notre Timer #1 en mode CTC :

// Configure the Timer/Counter1 Control Register A
TCCR1A = 0 ;
// Configure the Timer/Counter1 Control Register B
TCCR1B = (1 << WGM12) ; // Configure timer 1 for CTC mode

La ligne suivante, comme le commentaire l'indique, active les interruptions pour le Timer #1.

TIMSK1 = (1 << OCIE1A); // Enable CTC interrupt

La ligne d'apres definit la valeur jusque laquelle le Timer #1 va compter avant de generer son interruption. Si on n'avait pas activé les interruptions, il aurait fallu surveiller le Bit #1 - OCF1A3) du registre TIFR1 dans une boucle pour detecter son passage à 1.

OCR1A   = 62499 ; // Set CTC compare value to 1Hz at 16MHz AVR clock, with a prescaler of 256

La valeur 62499 est defini par la formule donnée dans la datasheet :

F_OCnA = F_clock_I/O / ( 2 * N * ( 1 + OCRnA ) )

Avec N etant la valeur du prescaler, soit 1, 8, 64, 256, ou 1024.

En choisissant un prescaler, il est ensuite facile de trouver la valeur qui va bien. Pour avoir une periode à 1 seconde, le prescaler de 256 permet de monter jusqu'a 31.25KHz, et de descendre jusqu'à ~0.47Hz.

Les interruptions sont ensuite activées. Jusqu'a present, elles etaient descativées ; on dit aussi masquées.

sei(); //  Enable global interrupts

Le Timer est ensuite demarré simplement en configurant le prescaler. Il faut garder a l'esprit que ecrire dans registre de controle, derriere il y a tout un ensemble de circuits logiques qui se mettent en route. En activant le prescaler, on vient demander l'application du signal d'horloge à F_CPU/256 = 62.5KHz sur notre Compteur/Timer.

TCCR1B |= (1 << CS12) ; // Start timer at Fcpu/256

Je passe sur la boucle principale a base de for( ; ; ) ou while( 1 )

L'appel a la fonction sleep_mode() passe le microcontrolleur en mode d'economie d'energie. Aucune idée si ca a un réel effet, je n'ai pas fait de mesure… ca serait interressant de voir ca ! :-)

Attendez partez pas, il nous reste la routine d'interruption !! La voilà :

ISR(TIMER1_COMPA_vect)
{
   // Toggle the LED
   PORTB ^= (1 << PORTB5);
}

Bon, là pas de grosse difficultées, on vient dire que l'on defini une Interrupt Sub-Routine pour le Comparateur A du Timer #1. Oui, il y a deux comprateur pour le Timer #1, le A et le B. L'ISR, quand elle est appellée va venir faire clignoter notre LED de la meme maniere que précédemment.

Ce qui se passe… Le Compteur/Timer #1 compte jusqu'a notre valeur a la vitesse de 62.5KHz… Il arrive à 62499 en une seconde… Une fois à cette valeur le module de comparaison detecte que la valeur actuelle du Compteur est egale a celle de OCR1A, il leve le Flag OCF1A, qui si les interruptions ne sont pas masquées, génère une interruption.

L'interruption est donc prise en compte par le mircoprocesseur, qui va s'interrompre et va appeller le vecteur d'interruption correspondant a l'interruption qui l'a interrompu ; dans notre cas TIMER1_COMPA_vect. Dans les faits, il s'agit d'un simple saut a l'adresse de la fonction definie comme ISR


Remarques, Propositions d'améliorations, Questions

Vous pouvez poster vos remarques, propositions d'amelioration, et questions sur le forum, dans la discussion prévue a cet effet : http://fablab-robert-houdin.org/fablab/phpBB-3.0.11-fr/phpBB3/viewtopic.php?f=3&t=13


2) Interrupt Sub-Routine
3) Output Compare Flag du Timer #1
tuto_avr_eclipse_2nd_projet_-_timer_led.txt · Dernière modification: 2013/11/10 21:05 par spelle