Programmer l'Arduino en langage C

Francesco Balducci

Traducteur

f-leb

J'adore jouer avec ma carte Arduino UNO et son environnement de développement graphique. C'est un outil qui facilite grandement la programmation en dissimulant une foule de détails. D'un autre côté, je ne peux m'empêcher d'observer dans les coulisses de l'EDI, et j'ai senti le besoin de me rapprocher des aspects matériels, de m'écarter des bibliothèques fournies par défaut et de l'EDI Java pour compiler directement mes programmes à partir de la ligne de commandes.

Commentez

- Titre : Programmer l'Arduino en langage C

- Auteur : Francesco Balducci

- Traducteur : f-leb

- Parution : 26 septembre 2016

- Public visé :

- Licence : 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.

Sommaire

- I. Pourquoi le langage C ?

- II. La chaîne de compilation avr-gcc

- III. Du « langage Arduino » au langage C : le programme blink

- IV. Notes de la Rédaction Developpez.com

I - Pourquoi le langage C ?

En particulier, utiliser le langage C pour programmer l'Arduino permet généralement de créer des programmes plus petits et davantage optimisés, avec un contrôle plus fin des tâches effectuées. Le C est adopté dans le monde entier pour la programmation de microprocesseurs, car il offre un bon compromis entre l'effort de développement et l'efficacité du programme, mais aussi, en raison de son histoire, il existe des bibliothèques optimisées, une documentation très complète et des manières de résoudre les problèmes. Donc, si vous trouvez que le langage Arduino crée des programmes trop lourds ou trop lents, et que vous préférez tirer parti au maximum des performances de votre carte, ou si vous voulez une approche plus modulaire, réécrire vos programmes en langage C pourrait être le bon choix à faire.

II - La chaîne de compilation avr-gcc

Fort heureusement, tous les outils sont déjà présents, dissimulés sous le capot dans l'EDI Arduino. Dans mon cas particulier, comme je développe sous Linux, Arduino utilise le compilateur avr-gcc et avrdude pour téléverser les programmes. Je peux donc utiliser ces outils pour développer un programme en langage C, au lieu du langage Arduino, et téléverser ce programme dans la carte. Dans les préférences de l'EDI Arduino, des options permettent d'augmenter la quantité d'informations et de notifications affichées à l'écran lorsqu'un programme est compilé et téléversé. Voilà qui est très utile pour comprendre l'enchaînement des commandes du processus de compilation C/C++ qui sont lancées en coulisse dans l'interface graphique. On peut reproduire ce processus, compiler notre programme en lançant la commande avr-gcc avec les bonnes options, puis exécuter avr-dude avec les bonnes options pour le téléversement. Le diagramme ci-dessous montre toute la chaîne de compilation, du code source en langage C jusqu'au téléversement dans la carte.

Du code source en langage C jusqu'au téléversement dans la carte Arduino avec la chaîne de compilation avr-gcc.

III - Du « langage Arduino » au langage C : le programme blink

Pour prendre un exemple simple, j'ai mis en œuvre le classique programme blink, qui fait clignoter une LED en faisant basculer l'état de la broche sur laquelle est connectée la LED jaune intégrée à la carte. La chaîne de compilation avr-gcc ne connaît pas la carte Arduino UNO elle-même, mais seulement le microcontrôleur qu'elle utilise, l'ATmega328P. Nous devons donc lire ou écrire dans les registres matériels d'entrées-sorties de ce composant, et regarder son brochage pour comprendre les noms des broches et leur disposition. Toutes ces informations sont contenues dans la documentation (datasheet) de l'ATmega328P. Les schémas de la carte Arduino Uno, quant à eux, montrent où sont connectées les broches de l'Atmel. En particulier, on se rend compte que la LED jaune intégrée à la carte est connectée à la broche PB5 de l'ATmega328P, et c'est donc cette broche que nous devons contrôler. Il reste maintenant à écrire le code qui fera basculer l'état de la sortie PB5. Une bibliothèque en C nommée avr-libc accompagne le compilateur AVR. Celle-ci comprend de nombreuses fonctions et entêtes très utiles pour accéder aux fonctionnalités d'entrées-sorties des composants AVR. Il devient alors très aisé d'écrire des programmes entièrement en C, sans passer par le langage assembleur. Le manuel avr-libc et la documentation de l'ATmega328P contiennent de nombreux exemples qui permettent de comprendre comment basculer l'état d'une sortie, et avec lesquels j'ai préparé le code suivant sauvegardé dans un fichier led.c.

led.c

#include <avr/io.h>
#include <util/delay.h>

#define BLINK_DELAY_MS 1000

int main (void)
{
/* set pin 5 of PORTB for output*/
DDRB |= _BV(DDB5);

while(1) {
/* set pin 5 high to turn led on */
PORTB |= _BV(PORTB5);
_delay_ms(BLINK_DELAY_MS);

/* set pin 5 low to turn led off */
PORTB &= ~_BV(PORTB5);
_delay_ms(BLINK_DELAY_MS);
}
}

L'état du port B du microcontrôleur peut-être modifié bit par bit avec les macros spécifiques sbi et cbi (NDLR : macros dépréciées depuis). En C, on a l'habitude d'utiliser les opérateurs d'affectation bit à bit |= et &= pour lire ou écrire dans une variable, mais le compilateur reconnaît ce genre d'accès et produit un code d'assemblage optimisé dans le cas d'opérations bit par bit, sans opération de lecture supplémentaire. La macro _BV (NDLR : #define _BV(bit) (1<<(bit))) avec le paramètre PORTB5 est utilisée pour modifier l'état du bit correspondant à la sortie PB5 du port B. La fonction principale main comporte une boucle infinie qui fait basculer le bit 5 du registre associé au port B, avec une pause d'une seconde entre chaque bascule grâce à la fonction _delay_ms. Si vous avez installé avr-gcc sur votre machine Linux, la définition des ports et des macros utiles (comme _BV) sont dans les fichiers d'entête /usr/lib/avr/include/avr/iom328p.h et /usr/lib/avr/include/avr/sfr_defs.h, ou dans les répertoires d'installation des bibliothèques.

Le compilateur permet de créer un fichier au format exécutable ELF qui contient le code compilé ainsi que diverses sections sur la configuration mémoire et des informations de débogage. Afin de compiler et téléverser un programme dans l'Arduino UNO, on doit créer un fichier IHEX et utiliser l'outil avrdude pour le transférer dans la mémoire Flash. L'outil pour convertir le fichier ELF en fichier IHEX est avr-objcopy dans notre cas.

Il est maintenant temps d'exécuter les différentes commandes qui vont compiler et téléverser notre programme de clignotement de LED. La plupart des paramètres et options des outils avr-gcc et avrdude pour la carte Arduino UNO sont dans le fichier hardware/arduino/boards.txt contenu dans le répertoire d'installation de l'EDI Arduino, et d'autres informations sont présentes dans le manuel avrdude. Les commandes que j'ai utilisées pour compiler et transférer le code du fichier led.c sont les suivantes :

$ avr-gcc -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o led.o led.c
$ avr-gcc -mmcu=atmega328p led.o -o led
$ avr-objcopy -O ihex -R .eeprom led led.hex
$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:w:led.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.00s

avrdude: Device signature = 0x1e950f
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "led.hex"
avrdude: input file led.hex auto detected as Intel Hex
avrdude: writing flash (88 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 88 bytes of flash written

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

La commande de la première ligne utilise le code source en C et le compile pour produire un fichier objet. Les options signalent au compilateur d'optimiser la taille du code et spécifient la fréquence du CPU (pratique pour la fonction delay par exemple) et le processeur utilisé. La seconde commande fait l'édition de liens à partir des fichiers objets et des bibliothèques (liens implicites si nécessaire) et produit un fichier au format exécutable ELF. La troisième commande convertit le fichier ELF en un fichier IHEX. La quatrième commande téléverse les données IHEX dans la mémoire Flash de la puce ATmega, et les options signalent au programme avrdude de communiquer avec l'Arduino en utilisant la liaison série via le port désigné par l'interface Linux /dev/ttyACM0, à la vitesse de transmission de 115 200 bits par seconde.

Une fois toutes ces commandes exécutées, le code est téléversé et la LED se met aussitôt à clignoter. Cela devrait fonctionner du premier coup, ce qui s'explique probablement par le fait que les outils sont bien supportés par l'Arduino.

Il est possible de voir le code assembleur du programme ELF en désassemblant le programme avec la commande :

$ avr-objdump -d led >led.lst

Il est également possible de faire une copie de sauvegarde du contenu de la mémoire Flash avec la commande :

$ avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyACM0 -b 115200 -U flash:r:flash_backup.hex:i

Notez que ce tutoriel a été rédigé pour les utilisateurs de Linux, mais il peut être adapté pour ceux qui travailleraient sur Windows ou Mac. En particulier, le processus de compilation qui conduit au programme exécutable ELF devrait être assez similaire, mais la partie téléversement peut être très différente.

IV - Notes de la Rédaction Developpez.com

Cet article est la traduction du tutoriel é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, lolo78 et milkoseck.