Cours:Atmega328p
Interruptions
On rappelle qu'une interruption peut être imaginée comme un sous-programme appelé par un événement (et non pas depuis une autre partie du programme).
Les événements dont il s'agit sont en général des positionnements de bits spéciaux qui sont appelés drapeaux (on gardera souvent la terminologie anglaise de "flag").
Ces flags sont toujours positionnés par le matériel et non par le logiciel (ou par le programmeur si vous voulez).
Ainsi le programmeur n'a absolument aucun moyen de déclencher directement une interruption.
Activer/Désactiver les interruptions
Les fonctions suivantes permettent d'activer et désactiver globalement les interruptions :
cli(); //désactive toute interruption
sei(); //autorise les interruptions
Interruption externe
Cette partie détaille l'utilisation des interruptions INT0 et INT1, attachées aux pin PD2 et PD3.
Registre EICRA
Le registre EICRA permet de choisir le mode de déclenchement de l'interruption.
EICRA bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Fonction | ----- | ----- | ----- | ----- | ISC11 | ISC10 | ISC01 | ISC00 |
Valeur initiale | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Le tableau suivant donne la valeur des bits ISCx0 et ISCx1 pour configurer le mode de déclenchement associé à l'interruption INTx :
ISCx1 | ISCx0 | Déclenchement de l'interruption sur : |
---|---|---|
0 | 0 | Un niveau bas sur l'entrée INTx |
0 | 1 | Un changement d'état sur l'entrée INTx |
1 | 0 | Un front descendant sur l'entrée INTx |
1 | 1 | Un front montant sur l'entrée INTx |
Registre EIMSK
Le registre EIMSK permet d'autoriser ou non les interruptions INT1 et INT0.
EIMSK bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Fonction | ----- | ----- | ----- | ----- | ----- | ----- | INT1 | INT0 |
Valeur initiale | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Une mise à '1' du bit INTx permet d'autoriser l'interruption associée.
Registre EIFR Exernal Interrupt Flag Register
Le registre EIFR permet d'observer l'état des interruptions INT1 et INT0.
EIFR bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Fonction | ----- | ----- | ----- | ----- | ----- | ----- | INTF1 | INTF0 |
Valeur initiale | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Le bit INTFx passe à '1' lors du déclenchement de l'interruption.
Exemple
Pour déclencher une interruption à chaque changement d'état de la patte PD2 (donc sur les fronts montant et descendant), on pourra utiliser le code suivant :
ISR(INT0_vect) // programme d'interruption : le programme principal est interrompu,
{ // l'interruption exécutée et ensuite le programme principal continu normalement son exécution
PORTB ^= 1<<PB0; // modification de la sortie PB0
}
int main()
{
DDRB |= (1<<PB0) ; // configuration de PB0 en sortie
EICRA |= (1<<ISC00) ; // mode de déclenchement de l'interruption
EIMSK |= (1<<INT0) ; // choix des interruptions actives
sei(); // autorisation des interruptions
while(1)
{
}
}
Pour la première fois vous voyez apparaître un sous-programme particulier ISR(...). C'est lui qui va être appelé quand l'événement qui déclenche l'interruption correspondante arrive. Ce qui apparaît dans les parenthèses dépend de l'événement : ici INT0_vect est une constante associée à l'interruption sur un changement sur la broche PD2. On vous donnera toujours la constante associée à un événement.
Timer
Registre TIMSKx
Le registre TIMSKx permet d'autoriser les différents modes d'interruptions associés aux Timers. Le registre se décompose comme suit :
TIMSKx bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
Fonction | ----- | ----- | ICIEx | ----- | ----- | OCIExB | OCIExA | TOIEx |
Le bit TOIEx autorise l'interruption de débordement associé au TIMERx.
ISR(TIMER0_OVF_vect)
{
...
}
...
TIMSK0 |= 1 << TOIE1;
sei();
...
Les 2 bits OCIEx(A/B) autorise une interruption lorsque le timer atteint la valeur de comparaison OCRx(A/B).
ISR(TIMER2_COMPA_vect)
{
...
}
...
TIMSK2 |= 1 << OCIE2A;
sei();
...
Fonctions d'interruption
Le tableau donne la liste des interruptions et le vecteur d'interruption associé :
Nom de l'interruption | Description | Fonction |
---|---|---|
INT0 | Changement d'état de la patte PD2 | ISR(INT0_vect) |
INT1 | Changement d'état de la patte PD3 | ISR(INT1_vect) |
TIMERx_OVF | Débordement du Timer(0/1/2) | ex : ISR(TIMER2_OVF_vect) |
TIMERx_COMPY | Comparaison Timer(0/1/2) avec OCRxY | ex : ISR(TIMER2_COMPA_vect) |
Liaison série
Débit
Le registre UBRR0 et le bit U2X0 du registre UCSR0A permettent de configurer la vitesse de la transmission de la façon suivante :
- si U2X0 = 1 alors UBRR0 = Fcpu/(8*débit)-1 ou debit = Fcpu/(8*UBRR0+1)
- si U2X0 = 0 alors UBRR0 = Fcpu/(16*débit)-1 ou debit = Fcpu/(16*UBRR0+1)
Exemple, on souhaite un débit de 9600 bauds (ou bits/s) avec fcpu=16MHz
si U2X0 = 1 | si U2X0 = 0 |
UBBR0 = 207,3 => UBRR0=207 | UBBR0 = 103,2 => UBRR0=103 |
debitReel=9640 | debitReel=9687 |
code : ...
UCSR0A |= (1<<U2X0);
UBRR0 = 207
...
|
code : ...
UCSR0A &=~ (1<<U2X0);
UBRR0 = 103
...
|
Format des données
Il convient de choisir :
La parité | nbre de bits de stop | nbre de bits pour la donnée | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
sans / paire(even) / impaire(odd) | 1 ou 2 bits de stop | 5,6,7,8 ou 9 bits | ||||||||||||||||||||||||||||||||||||||||||
bits UPM01 et UPM00 dans registre UCSR0C | bit USBS0 dans registre UCSR0C | UCSZ02(UCSR0B), UCSZ01 UZSC00(UCSR0C) | ||||||||||||||||||||||||||||||||||||||||||
Par défaut : sans parité
|
Par défaut 1 stop
|
Par défaut 8 bits
|
Exemple : 6 bits de données, paire, 1 stop
...
UCSR0C|=(1<<UPM01);
//les bits UCSZ01 et UCSZ00 sont à 1 par défaut !
UCSR0C&=~(1<<UCSZ01);
//1 stop par défaut (bit USBS0 à 0)
...
Recevoir des données
Autoriser la réception | donnée à lire ? | recevoir la donnée |
---|---|---|
bit RXEN0 dans UCSR0B | bit RXC0 dans UCSRA | registre UDR0 |
mettre à 1 pour autoriser | RXC0 passe à 1 lorsqu'une donnée est arrivée | lire la donnée |
Exemple :
...
int main()
{
char c;
...
// autoriser la réception
UCSR0B |= (1<<RXEN0);
...
while(1)
{
// attendre une donnée
loop_until_bit_is_set(UCSR0A, RXC0);
// récupérer la donnée
c = UDR0;
if (c=='W') ...
...
}
}
Envoyer des données
Autoriser l'émission | prêt à émettre ? | envoyer la donnée |
---|---|---|
bit TXEN0 dans UCSR0B | bit UDRE0 dans UCSRA | registre UDR0 |
mettre à 1 pour autoriser | UDRE0 passe à 1 lorsqu'on peut émettre | écrire la donnée |
Exemple :
...
int main()
{
char c;
...
// autoriser la transmission
UCSR0B |= (1<<TXEN0);
...
while(1)
{
....
c = 'F';
// attendre de pouvoir envoyer
loop_until_bit_is_set(UCSR0A, UDRE0);
// envoyer la donnée
UDR0 = c;
...
}
}
Attention
Quand une interruption est appelée, les autres interruptions sont automatiquement désactivées par le µcontrôleur
Les variables partagées entre interruption et programme principal doivent être de type volatile
Les opérations de lecture/écriture sur des variables de plus de 8 bits doivent être protégées en désactivant les interruptions.
Exemple :
unsigned long c;
cli();
c = PulseCounts;
sei();