Cible unique
La première partie de cet article configure l'environnement de construction et construit le logiciel pour une seule cible.
Arborescence du projet
L'arborescence du projet est la suivante :
idreammicro |_helloworld.c |_env_avr.py |_env_target.py |_SConscript |_SConstruct
Avec :
- helloworld.c le fichier source ;
- env_avr.py le script de configuration de l'environnement de construction ;
- env_target.py le script d'adaptation de l'environnement de construction à la cible ;
- SConscript le script de construction du logiciel ;
- SConstruct le script de construction du projet.
[Télécharger l'archive complète]
Configuration de l'environnement de construction
On crée un fichier nommé env_avr.py dont le rôle est de configurer l'environnement de construction pour avr-gcc.
# Create and initialize the environment. env_avr = Environment() # Set environment for AVR-GCC. env_avr['CC'] = 'avr-gcc' env_avr['CPPPATH'] = '/usr/lib/avr/include' env_avr['OBJCOPY'] = 'avr-objcopy' env_avr['SIZE'] = 'avr-size' env_avr.Append(CCFLAGS = '-Os') # Export environment set for AVR-GCC. Export('env_avr')
L'export de l'environnement de construction env_avr ligne 12 permet à des scripts tiers de l'utiliser.
Cet environnement de construction générique pour microcontrôleur AVR peut être utilisé par des cibles multiples.
Adaptation à la cible
On crée un fichier nommé env_target.py dont le rôle est d'adapter l'environnement de construction à la cible : type de microcontrôleur, fréquence d'horloge, etc...
# Import environment set for AVR-GCC. Import('env_avr') # Create target environment by cloning AVR environment. env_target = env_avr.Clone() # Declare some variables about microcontroller. # Microcontroller type. DEVICE = 'atmega328' # Microcontroller frequency. CPU_FREQUENCY = '16000000UL' # Hz # Set environment for an Atmel AVR Atmega 328 microcontroller. env_target.Append(CCFLAGS = '-mmcu=' + DEVICE) env_target.Append(LINKFLAGS = '-mmcu=' + DEVICE) env_target.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY) # Export environment set for target. Export('env_target', 'DEVICE')
Quelques nouveautés sont à souligner :
- Ligne 2, import de l'environnement de construction pour microcontrôleur AVR.
- Ligne 5, création d'un nouvel environnement de construction dédié à la cible à partir de l'environnement pour AVR. En effet on souhaite conserver un environnement pour AVR générique et ne pas le modifier.
- Ligne 19, export de l'environnement env_target et de la variable DEVICE indiquant le type de microcontrôleur de la cible. Cette variable sera nécessaire pour calculer l'empreinte mémoire du logiciel.
En cas de changement de cible, seules les variables DEVICE (ligne 9) et CPU_FREQUENCY (ligne 11) nécessitent d'être modifiées.
Construction du logiciel
On crée un fichier nommé SConscript qui utilise l'environnement de construction précédemment configuré afin de compiler le projet.
# Import environment set for target. Import('env_target', 'DEVICE') # Set target name. TARGET = 'helloworld' # Set source file. sources = 'helloworld.c' # Build program. env_target.Program(target = TARGET + '.elf', source = sources) # Create hex binary file. env_target.Command(TARGET + '.hex', TARGET + '.elf', env_target['OBJCOPY'] + ' -O ihex $SOURCE $TARGET') # Compute memory usage. env_target.Command(None, TARGET + '.elf', env_target['SIZE'] + ' -C --mcu=' + DEVICE + ' $SOURCE')
Pour un autre projet, il suffit de modifier le nom de la cible à l'aide de la variable TARGET ligne 5 ainsi que les sources du projet spécifiées à l'aide de la variable sources ligne 8.
Fichier SConstruct
Enfin, le SConstruct exécute les différents scripts détaillés ci-dessus tout en assurant les exports et les imports d'environnements de construction d'un script à l'autre.
# Set environment for AVR-GCC. SConscript( 'env_avr.py' ) # Import environment set for AVR-GCC. Import('env_avr') # Set environment for target. SConscript( 'env_target.py', exports = 'env_avr' ) # Import environment set for target. Import('env_target', 'DEVICE') # Build program. SConscript( 'SConscript', exports = 'env_target' )
L'environnement de construction est désormais configuré. Voici venu le temps de passer à la construction du logiciel.
Construction du logiciel
Dans une console, se placer dans le répertoire du projet, par exemple /home/jlesech/workspace/idreammicro, à l'aide de la commande :
~$ cd /home/jlesech/workspace/idreammicro
Lancer la construction du logiciel à l'aide de la commande :
~workspace/idreammicro$ scons
Comme avec l'environnement de construction de l'article précédent, la construction du logiciel produit :
- un fichier objet helloworld.o ;
- un fichier binaire helloworld.elf au format elf ;
- un fichier binaire helloworld.hex au format Intel Hex.
A l'issue de la construction du logiciel, on observe toujours l'empreinte mémoire du logiciel :
AVR Memory Usage ---------------- Device: atmega328 Program: 176 bytes (0.5% Full) (.text + .data + .bootloader) Data: 0 bytes (0.0% Full) (.data + .bss + .noinit)
Cibles multiples
Afin de démontrer la pertinence d'une telle configuration d'un environnement de construction, la seconde partie de cet article construit le logiciel pour deux cibles.
Arborescence du projet
L'arborescence du projet est similaire :
idreammicro |_helloworld.c |_env_avr.py |_env_arduino_uno.py |_env_arduino_mega2560.py |_SConscript |_SConstruct
On remarque quelques changements :
- renommage du fichier env_target.py en env_arduino_uno.py ;
- création du fichier env_arduino_mega2560.py.
Ces deux scripts permettent d'adapter l'environnement de construction à deux cibles différentes, la première étant une carte Arduino Uno et la seconde une carte Arduino Mega2560.
Les fichiers env_avr.py et SConscript étant rigoureusement identiques à ceux de la première partie, ils ne seront pas passés en revue dans cette seconde partie.
[Télécharger l'archive complète]
Adaptation à la cible Arduino Uno
Cette cible étant la même que lors de la première partie, rien à signaler...
# Import environment set for AVR-GCC. Import('env_avr') # Create target environment by cloning AVR environment. env_target = env_avr.Clone() # Declare some variables about microcontroller. # Microcontroller type. DEVICE = 'atmega328' # Microcontroller frequency. CPU_FREQUENCY = '16000000UL' # Hz # Set environment for an Atmel AVR Atmega 328 microcontroller. env_target.Append(CCFLAGS = '-mmcu=' + DEVICE) env_target.Append(LINKFLAGS = '-mmcu=' + DEVICE) env_target.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY) # Export environment set for target. Export('env_target', 'DEVICE')
Adaptation à la cible Arduino Mega2560
Cette cible est nouvelle. Par rapport à la précédente, seule la variable DEVICE (ligne 9) diffère.
# Import environment set for AVR-GCC. Import('env_avr') # Create target environment by cloning AVR environment. env_target = env_avr.Clone() # Declare some variables about microcontroller. # Microcontroller type. DEVICE = 'atmega2560' # Microcontroller frequency. CPU_FREQUENCY = '16000000UL' # Hz # Set environment for an Atmel AVR Atmega 2560 microcontroller. env_target.Append(CCFLAGS = '-mmcu=' + DEVICE) env_target.Append(LINKFLAGS = '-mmcu=' + DEVICE) env_target.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY) # Export environment set for target. Export('env_target', 'DEVICE')
Fichier SConstruct
Le fichier SConstruct est celui qui évolue le plus et ordonne la construction du logiciel pour les deux cibles.
# Set environment for AVR-GCC. SConscript('env_avr.py') # Import environment set for AVR-GCC. Import('env_avr') # Define environments to use (one environment per target). environments = [ 'env_arduino_uno', 'env_arduino_mega2560' ] # Browse environments. for environment in environments: # Set environment for target. SConscript( environment + '.py', exports = 'env_avr' ) # Import environment set for target. Import('env_target') # Build program. SConscript( 'SConscript', variant_dir = environment, exports = 'env_target', duplicate = 0 )
Quelques explications s'imposent :
- Les lignes 1 à 5 créent toujours l'environnement de construction pour AVR.
- Les lignes 7 à 11 définissent une liste d'environnements pour lesquels construire le logiciel, chaque environnement correspondant à une cible.
- Les lignes 13 à 28 construisent le logiciel pour toutes les environnements de la liste.
Par rapport au SConstruct de la première partie, des arguments supplémentaires sont passés au SConscript :
- Ligne 25, le mot clé variant_dir permet de spécifier le dossier de sortie de la construction du logiciel.
- Ligne 27, on passe le paramètre duplicate avec la valeur 0 pour indiquer à SCons qu'il ne faut pas recopier les fichiers sources dans le dossier de sortie.
Construction des logiciels
La construction des logiciels se fait toujours de la même manière, en une seule fois à l'aide de la commande scons.
Les dossiers env_arduino_uno et env_arduino_mega2560 sont créés respectivement pour les cibles Arduino Uno et Arduino Mega2560. De la même manière que lors de la première partie, chacun d'eux contient les fichiers objet et binaires à l'issue de la construction du logiciel.
On observe également les empreintes mémoire pour les deux cibles.
- Cible Arduino Mega2560 :
AVR Memory Usage ---------------- Device: atmega2560 Program: 330 bytes (0.1% Full) (.text + .data + .bootloader) Data: 0 bytes (0.0% Full) (.data + .bss + .noinit)
- Cible Arduino Uno :
AVR Memory Usage ---------------- Device: atmega328 Program: 176 bytes (0.5% Full) (.text + .data + .bootloader) Data: 0 bytes (0.0% Full) (.data + .bss + .noinit)
Conclusion
Si l'environnement de construction configuré lors de l'article précédent est parfaitement fonctionnel, force est de reconnaître qu'il est monolithique.
La solution présentée dans ce second article est nettement plus professionnelle et offre désormais cette modularité, chaque opération étant effectuée dans un script dédié. Imaginez la duplication de code s'il avait été nécessaire de construire le logiciel pour deux cibles distinctes sans cette solution...