• Aucun résultat trouvé

[PDF] Arduino tutoriel button LED par la pratique | Cours Arduino

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Arduino tutoriel button LED par la pratique | Cours Arduino"

Copied!
9
0
0

Texte intégral

(1)

Apprendre à utiliser les interruptions

sur Arduino en langage C

Mise en œuvre d'une minuterie

Par Francesco Balducci

- f-leb (traducteur)

Date de publication : 8 avril 2017

Ce tutoriel est le quatrième de la série sur la programmation de carte Arduino en langage C : • Partie 1 : Programmer l'Arduino en langage C

• Partie 2 : Utiliser un buzzer avec Arduino en langage C

• Partie 3 : Apprendre à dessiner sur une matrice de LED avec Arduino en langage C

À chaque fois, l'idée est de s'affranchir des facilités offertes par le fameux « langage Arduino » dans l'EDI standard. Ici, les programmes seront développés en langage C « pur » grâce aux outils de la chaîne de compilation avr-gcc.

L'objectif est double :

• développer des codes optimisés, efficaces et compacts ;

• démystifier le fonctionnement d'un microcontrôleur et prendre le contrôle des entrées-sorties, sans fard, en attaquant directement les registres du microcontrôleur.

Dans ce nouveau volet, l'auteur manipule les interruptions matérielles au travers d'une application de minuterie fonctionnant avec un bouton et une LED.

Commentez

En complément sur Developpez.com • Programmer l'Arduino en langage C

(2)
(3)

Apprendre à utiliser les interruptions sur Arduino en langage C par Francesco Balducci

I - Mise en œuvre d'une minuterie avec un bouton et une LED...4

II - Les interruptions sur Arduino...4

III - Le code... 7

IV - Compilation et téléversement du code...9

V - Notes de la Rédaction Developpez.com...9

3

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(4)

I - Mise en œuvre d'une minuterie avec un bouton et une LED

Comme d'habitude, j'utiliserai le compilateur avr-gcc et sa bibliothèque avr-libc pour, cette fois, manipuler les interruptions du microcontrôleur. Pour illustrer les interruptions, l'idée simple d'une application de minuterie avec un bouton et une LED m'est venue à l'esprit. Cette application devra avoir le comportement suivant :

• la LED est normalement éteinte ;

• quand le bouton est pressé, la LED s'allume ;

• quand le bouton est relâché, la LED reste allumée un moment ; • après une temporisation, la LED s'éteint.

Cette application sera pilotée par deux interruptions : l'une pour agir sur changement d'état de la broche reliée au bouton, l'autre pour contrôler la temporisation avant d'éteindre la LED. Les informations utiles sont dans la

documentation complète (datasheet) de l'ATmega328P qui est le microcontrôleur de l'Arduino Uno.

II - Les interruptions sur Arduino

Parmi les 26 vecteurs d'interruption de l'ATmega328P (NDLR), il y en a trois en particulier nommés Pin Change Interrupts Requests : PCINT0, PCINT1 et PCINT2.

Note de la rédaction

En fait, pour gérer les interruptions externes, l'ATmega328P de l'Arduino Uno comprend deux types d'interruption : les interruptions « externes » (external interrupts) et celles sur « changement d'état » (pin change interrupts).

External interrupts

Il y a seulement deux broches d'interruption externe sur l'ATmega168/328 des Arduino Uno/ Nano/Duemilanove : INT0 et INT1, mappées respectivement aux connecteurs 2 et 3 de la carte Arduino. Ces interruptions peuvent être configurées pour se déclencher sur front montant (RISING), sur front descendant (FALLING), sur changement d'état (CHANGE) ou sur l'état bas (LOW) du signal.

Pin change interrupts

Ce sont celles utilisées par l'auteur dans cet article. Sur les cartes Arduino Uno, ces interruptions peuvent être activées depuis n'importe quel connecteur, et même la totalité des 20 connecteurs de l'Arduino (A0 à A5 et D0 à D13). Elles sont regroupées sous trois vecteurs d'interruption PCINT0, PCINT1 et PCINT2 pour l'intégralité des 20 connecteurs.

Pin Change Interrupt Request 0 (connecteurs D8 à D13) (PCINT0_vect) Pin Change Interrupt Request 1 (connecteurs A0 à A5) (PCINT1_vect) Pin Change Interrupt Request 2 (connecteurs D0 à D7) (PCINT2_vect)

Elles sont déclenchées indifféremment sur front montant ou sur front descendant, et c'est au code d'interruption auquel il revient de déterminer l'événement qui a produit l'interruption. Quelle broche a déclenché le vecteur d'interruption ? Est-ce un front montant ou un front descendant du signal ?

Tout ceci complique sensiblement la gestion de ce type d'interruption.

4

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(5)

Apprendre à utiliser les interruptions sur Arduino en langage C par Francesco Balducci À lire : http://playground.arduino.cc/Code/Interrupts http://arduino.stackexchange.com/questions/1784/how-many-interrupt-pins-can-an-uno-handle http://www.gammon.com.au/interrupts

La plupart des broches d'entrées-sorties sont sensibles au changement d'état et peuvent générer les interruptions. Elles sont repérées sur le brochage du processeur : PCINT0, PCINT1… PCINT23. J'ai décidé d'utiliser la requête d'interruption PCINT0_vect avec la broche PCINT4 (Port B, broche 4). Depuis les schémas de l'Arduino Uno, on peut tracer la broche PB4 jusqu'au connecteur D12 de la carte, et j'ai donc relié un bouton-poussoir entre le connecteur D12 et la masse.

Brochage ATmega328P / 28 broches PDIP

5

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(6)

Broche PB4 reliée au connecteur D12 de l'Arduino Uno

La broche restera à l'état haut lorsque le bouton est relâché grâce à la résistance interne de tirage (pull-up), et basculera à l'état bas quand l'utilisateur pressera le bouton qui reliera alors la broche à la masse.

Arduino Uno avec un bouton pour gérer une application de minuterie

Pour gérer la temporisation, j'utilise le Timer/Counter1 de l'ATMega328P, principalement parce que c'est le seul timer 16 bits tandis que les autres ne sont que des 8 bits, ce qui me permet ainsi de piloter des intervalles de temps plus longs. Ce timer est capable de générer des interruptions en fonction de différents événements, et je vais utiliser le vecteur d'interruption TIMER1 OVF, qui est déclenché lorsque le compteur déborde. Timer1 sera configuré en mode normal, fixé à une valeur avant d'être démarré ; le compteur s'incrémentera à une fréquence donnée par celle de l'horloge principale et affectée par un diviseur de fréquence, puis quand il atteindra la valeur 0xFFFF, il débordera et générera l'interruption dont j'avais besoin. Pour compter jusqu'à 100, je dois fixer la valeur du compteur

6

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(7)

Apprendre à utiliser les interruptions sur Arduino en langage C par Francesco Balducci

à 0xFFFF - 100, le démarrer puis attendre le débordement. Le plus grand diviseur de fréquence possible est de 1024, l'horloge tourne à 16 MHz et le compteur peut compter jusqu'à environ 216. Le temps maximum avant débordement est donc de 216 x 1024 / 16.106 ≈ 4,2 secondes. Dans mon cas, je vais utiliser une temporisation de 2 secondes. Pour la LED, je vais utiliser celle qui est intégrée à la carte de mon Arduino Uno, reliée au connecteur 13, c'est-à-dire la broche 5 du port B (PB5) de l'ATmega328P.

III - Le code

L'environnement Arduino propose plusieurs fonctions pour attacher des interruptions et les activer/désactiver. La bibliothèque avr-libc utilise des méthodes différentes (voir le manuel en ligne). La routine d'interruption devra être définie avec la macro ISR et le nom du vecteur d'interruption souhaité, comme ISR(PCINT0_vect) dans mon cas. Puis j'utilise les macros sei() et cli() pour activer et désactiver les interruptions. J'ai également utilisé sleep_mode() pour implémenter une boucle principale qui tourne dans le vide tout en consommant moins de ressources.

Note de la rédaction

Utiliser les instructions de la bibliothèque sleep permet de réduire considérablement la consommation du microcontrôleur en désactivant certaines horloges du système pendant la mise en veille. Seule une interruption permet alors de sortir du mode veille. Les sous-ensembles désactivés ne peuvent bien sûr plus générer d'interruption pour réveiller le système. On choisit en conséquence un mode veille parmi les six modes possibles en fonction des exigences de l'application et de la consommation du système. Le mode par défaut « idle » utilisé ici permet entre autres de réveiller le système au moyen de l'interruption extérieure Pin Change et de l'interruption du timer interne Timer Overflow. Une fois réveillé, le microcontrôleur redémarre, exécute la routine d'interruption, puis reprend l'exécution à l'instruction qui suit l'instruction sleep_mode().

Voici le code qui implémente tout cela :

timeswitch.c

#include <stdbool.h> #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> #define T1_MAX 0xFFFFUL #define T1_PRESCALER 1024

#define T1_TICK_US (T1_PRESCALER/(F_CPU/1000000UL)) /* 64us @ 16MHz */ #define T1_MAX_US (T1_TICK_US * T1_MAX) /* ~4.2s @ 16MHz */

static void led_on(void) {

PORTB |= _BV(PORTB5);

}

static void led_off(void) {

PORTB &= ~_BV(PORTB5);

}

static void led_init(void) {

led_off();

DDRB |= _BV(DDB5); /* PORTB5 as output */

}

static void timer_stop(void) {

7

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(8)

timeswitch.c

TCCR1B &= ~(_BV(CS10)|_BV(CS11)|_BV(CS12)); /* stop timer clock */ TIMSK1 &= ~_BV(TOIE1); /* disable interrupt */

TIFR1 |= _BV(TOV1); /* clear interrupt flag */

}

static void timer_init(void) { /* normal mode */ TCCR1A &= ~(_BV(WGM10)|_BV(WGM11)); TCCR1B &= ~(_BV(WGM13)|_BV(WGM12)); timer_stop(); }

static void timer_start(unsigned long us) {

unsigned long ticks_long; unsigned short ticks;

ticks_long = us / T1_TICK_US; if (ticks_long >= T1_MAX) { ticks = T1_MAX; } else { ticks = ticks_long; }

TCNT1 = T1_MAX - ticks; /* overflow in ticks*1024 clock cycles */ TIMSK1 |= _BV(TOIE1); /* enable overflow interrupt */

/* start timer clock */

TCCR1B &= ~(_BV(CS10)|_BV(CS11)|_BV(CS12));

TCCR1B |= _BV(CS10)|_BV(CS12); /* prescaler: 1024 */

}

static void timer_start_ms(unsigned short ms) {

timer_start(ms * 1000UL);

}

ISR(TIMER1_OVF_vect) /* timer 1 interrupt service routine */

{

timer_stop();

led_off(); /* timeout expired: turn off LED */

}

ISR(PCINT0_vect) /* pin change interrupt service routine */

{

led_on(); timer_stop();

if (bit_is_set(PINB, PINB4)) /* button released */ {

timer_start_ms(2000); /* timeout to turn off LED */ }

}

static void button_init(void) {

DDRB &= ~_BV(DDB4); /* PORTB4 as input */ PORTB |= _BV(PORTB4); /* enable pull-up */

PCICR |= _BV(PCIE0); /* enable Pin Change 0 interrupt */ PCMSK0 |= _BV(PCINT4); /* PORTB4 is also PCINT4 */

}

int main (void) {

led_init(); button_init(); timer_init();

sei(); /* enable interrupts globally */ while(true)

8

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

(9)

Apprendre à utiliser les interruptions sur Arduino en langage C par Francesco Balducci timeswitch.c { sleep_mode(); } }

La fonction principale initialise les ressources matérielles, autorise les interruptions et tourne en boucle en mode veille. Quand une requête d'interruption arrive, le CPU est réveillé et la routine d'interruption associée est appelée. Premièrement, la routine d'interruption (ISR) PCINT0 est appelée lorsqu'on appuie sur le bouton. Du fait que la routine est activée aussi bien sur front montant que sur front descendant, il faut tester le registre PINB pour connaître l'état de la broche connectée au bouton. Si l'état haut est constaté, le bouton est relâché et on démarre le timer. Il y a ici une complication due aux rebonds du bouton qui provoquent de multiples requêtes d'interruption lors d'un simple appui ou relâchement du bouton, mais en général cette mise en œuvre gère les rebonds de façon efficace. Lorsque le timer déborde, la routine d'interruption TIMER1 OVF ISR est appelée et la LED est éteinte.

IV - Compilation et téléversement du code

J'ai enfin compilé le programme et l'ai téléversé dans l'Arduino Uno connecté au port USB avec les commandes suivantes (notez que je développe sur une machine sous Linux Debian, mais ces mêmes commandes devraient fonctionner normalement sur d'autres systèmes d'exploitation moyennant quelques modifications) :

avr-gcc -g -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o timeswitch.o timeswitch.c avr-gcc -mmcu=atmega328p timeswitch.o -o timeswitch

avr-objcopy -O ihex -R .eeprom timeswitch timeswitch.hex

avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:timeswitch.hex

En pressant le bouton reset, l'application démarre…

V - Notes de la Rédaction Developpez.com

Cet article est la traduction du billet écrit par Francesco Balducci (alias Balau). Retrouvez la version originale de cet article ainsi que les autres chapitres consacrés à Arduino sur son blog.

Nous remercions les membres de la Rédaction de Developpez.com pour le travail de traduction et de relecture qu'ils ont effectué, en particulier : f-leb, Vincent PETIT , Delias et Claude Leloup.

9

-Le contenu de cet article est rédigé par Francesco Balducci et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Références

Documents relatifs

Saisir la façon dont est prise en charge aujourd’hui l’architecture produite en situation coloniale par les acteurs du patrimoine invite à élargir la focale d’analyse

We will also asso- ciate statistical prioritization to more classical test selection criteria like transition coverage and state coverage in order to improve the quality of

F or ea h neighbor entry in the neighbor table, whose link status is not uni-dire tional, a new route entry is re orded in the routing table where destination and next-hop addresses

Cependant, par sa dimension, par sa situation urbaine, par la publicité nationale qui a été donnée à cette opération, modeste en taille (114 logements), mais

Les positions socialistes et marxistes classiques voient dans l'avènement des TIC une possible nouvelle forme d'exploitation du travail (ce qui sera nommé « digital labor » mais qui

The MISE approach (mediation information system engineering) aims at defining and designing a platform, dedicated to initiate and support any collaborative situation among

When a read request for line X accesses the L2 cache, if this is a miss or a prefetched hit (i.e., the prefetch bit is set), and if X and X + D lie in the same memory page, a

expliquait en disant qu’en cas d’obligation naturelle, le « paiement est une renonciation de fait aux exceptions sans lesquelles l’action eût été admise ;