Arborescence du projet

Pour plus de clarté, l'arborescence du projet évolue :

idreammicro
|_build_system
|    |_env_avr.py
|    |_env_arduino_mega2560.py
|    |_env_arduino_uno.py
|_libraries
|    |_digital_io
|_helloworld.c
|_SConscript
|_SConstruct
  • Le dossier build_system accueille les fichiers permettant de configurer l'environnement de construction, à savoir les fichiers env_avr.py, env_arduino_mega2560.py et env_arduino_uno.py.
  • Le dossier libraries contient les bibliothèques, chacune disposant de son propre sous-dossier. Par exemple la bibliothèque digital_io se trouve dans le dossier libraries/digital_io.

[Télécharger l'archive complète]

Construction de la bibliothèque

L'arborescence d'une bibliothèque a déjà fait l'objet d'une présentation dans un précédent article. On se contentera d'un simple rappel tout en l'adaptant à la bibliothèque digital_io.

digital_io
|_demo
|    |_demo_digital_io.c
|    |_SConscript
|_include
|_src
|    |_digital_io.c
|    |_SConscript
|_test
|    |_test_digital_io.c
|    |_SConscript
|_digital_io.h
|_SConscript
|_SConstruct

L'objectif de cet article est de présenter la construction d'une bibliothèque, et non la bibliothèque en elle même. Par conséquent, la bibliothèque digital_io fait l'objet d'un autre article.

Compilation de la bibliothèque

Le fichier digital_io/SConscript ne fait qu'appeler le script de construction de la bibliothèque. Son unique rôle est de masquer la complexité de la construction de la bibliothèque aux projets qui l'utilisent.

# Import environment set for target.
Import('env_target', 'env_name', 'DEVICE')

# Define directories to process.
directories = [
    'src/'
]

# Process directories.
for directory in directories:
    SConscript(
        directory + 'SConscript',
	    variant_dir = '#build/' + env_name + '/libraries/digital_io/' + directory,
        exports = { 'env_target' : env_target, 'env_name' : env_name },
	    duplicate = 0
    )

Le fichier digital_io/src/SConscript est le script le plus important : son rôle est de compiler la bibliothèque et de la rendre accessible. La construction d'une bibliothèque est très semblable à celle d'un programme. Les points remarquables sont les suivants :

  • ligne 13 : construction d'une bibliothèque statique ;
  • ligne 16 : ajout du chemin du dossier contenant la bibliothèque à la variable LIBPATH indiquant les dossiers de recherche des bibliothèques ;
  • ligne 17 : ajout de la bibliothèque à la variable LIBS indiquant les noms des bibliothèques avec lesquelles linker.
# Import environment set for target.
Import('env_target', 'env_name', 'DEVICE')

# Define target name.
TARGET = 'digital_io'

# Define source files.
sources = [
    'digital_io.c'
]

# Build static library.
env_target.StaticLibrary(target = TARGET, source = sources)

# Append LIBPATH and LIBS.
env_target.Append(LIBPATH = [ '#build/' + env_name + '/libraries/' + TARGET + '/src'])
env_target.Append(LIBS = [ TARGET ])

Logiciel de démonstration

Le fichier digital_io/demo/SConscript est le script de construction du logiciel de démonstration de la bibliothèque.

# Import environment set for target.
Import('env_target', 'env_name', 'DEVICE')

# Set target name.
TARGET = 'demo_digital_io'

# Set source file.
sources = [
    'demo_digital_io.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'
)

Logiciel de test

Le fichier digital_io/test/SConscript est le script de construction du logiciel de test de la bibliothèque.

# Import environment set for target.
Import('env_target', 'env_name', 'DEVICE')

# Set target name.
TARGET = 'test_digital_io'

# Set source file.
sources = [
    'test_digital_io.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'
)

Démonstration et test

Le fichier digital_io/SConstruct propose un environnement de construction minimal permettant de construire la bibliothèque ainsi que les projets de démonstration et de test.

# Create and initialize the environment.
env = Environment()

# Set environment for AVR-GCC.
env['CC'] = 'avr-gcc'
env['CPPPATH'] = '/usr/lib/avr/include'
env['OBJCOPY'] = 'avr-objcopy'
env['SIZE'] = 'avr-size'
env['AR'] = 'avr-ar'
env['RANLIB'] = 'avr-ranlib'
env.Append(CCFLAGS = '-Os')

# Declare some variables about microcontroller.
# Microcontroller type.
DEVICE = 'atmega328p'
# Microcontroller frequency.
CPU_FREQUENCY = '16000000UL' # Hz

# Set environment for an Atmel AVR ATmega328p microcontroller.
env.Append(CCFLAGS = '-mmcu=' + DEVICE)
env.Append(LINKFLAGS = '-mmcu=' + DEVICE)
env.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY)

env.Append(CPPPATH = [ '#../' ])

# Build library.
SConscript(
    'src/SConscript',
    variant_dir = '#build/lib/',
    exports = { 'env_target' : env, 'env_name' : 'env_arduino_uno', 'DEVICE' : DEVICE },
    duplicate = 0
)

# Append LIBPATH and LIBS.
env.Append(LIBPATH = [ '#build/lib/'])
env.Append(LIBS = [ 'digital_io' ])

# Build demonstration program.
SConscript(
    'demo/SConscript',
    variant_dir = '#build/demo/',
    exports = { 'env_target' : env, 'env_name' : 'env_arduino_uno', 'DEVICE' : DEVICE },
    duplicate = 0
)

# Build test program.
SConscript(
    'test/SConscript',
    variant_dir = '#build/test/',
    exports = { 'env_target' : env, 'env_name' : 'env_arduino_uno', 'DEVICE' : DEVICE },
    duplicate = 0
)

Construction du projet

Fichier source

Le fichier source helloworld.c inclut et utilise la bibliothèque digital_io pour faire basculer le niveau de la sortie PB5 (port B, broche 5).

/**************************************************************************//**
 * \file helloworld.c
 ******************************************************************************/

/******************************************************************************
 * Header file inclusions.
 ******************************************************************************/

#include <digital_io/digital_io.h>

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

/******************************************************************************
 * Main function.
 ******************************************************************************/

/**************************************************************************//**
 * \fn int main(void)
 *
 * \brief Main function.
 ******************************************************************************/
int
main
(
    void
){
    // Declare pin.
    digital_io__pin_position_t pin = DIGITAL_IO__PORT_B | DIGITAL_IO__PIN_5;
    
    // Configure pin as output.
    digital_io__configure_pin(pin, DIGITAL_IO__DIRECTION__OUTPUT);

    while (1)
    {
        // Set pin level.
        digital_io__set_pin_level(pin, DIGITAL_IO__LEVEL__HIGH);
        _delay_ms(1000);

        // Reset pin level.
        digital_io__set_pin_level(pin, DIGITAL_IO__LEVEL__LOW);
        _delay_ms(1000);
    }

    return 0;
}

Fichier SConscript

Désormais le projet utilise une bibliothèque. Il est donc nécessaire de modifier le fichier SConscript afin de la construire :

  • les lignes 7 à 10 indiquent la liste des bibliothèques utilisées ;
  • les lignes 12 à 18 ordonnent leur construction.

On remarquera que seules les bibliothèques utilisées par le projet seront compilées. Contrairement à d'autres systèmes de construction tel qu'Arduino qui compilent toutes les bibliothèques avant de compiler le projet.

# Import environment set for target.
Import('env_target', 'env_name', 'DEVICE')

# Set target name.
TARGET = 'helloworld'

# Set libraries to use.
libraries = [
    'digital_io'
]

# Build libraries.
for library in libraries:
    SConscript(
        '#libraries/' + library + '/SConscript',
        exports = { 'env_target' : env_target, 'env_name' : env_name },
	    duplicate = 0
    )

# 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'
)

Fichier SConstruct

Le fichier SConstruct situé à la racine du dossier régit la construction de tout l'environnement, c'est à dire des projets et des bibliothèques utilisées.

Par rapport au fichier SConstruct d'une bibliothèque, celui-ci ne diffère que par la ligne 8. Elle permet d'ajouter le chemin du dossier contenant les bibliothèques à la variable CPPPATH, et plus particulièrement leurs fichiers d'entête.

# Set environment for AVR-GCC.
SConscript('#build_system/env_avr.py')

# Import environment set for AVR-GCC.
Import('env_avr')

# Append CPPPATH with the root path of libraries.
env_avr.Append(CPPPATH = [ '#libraries/' ])

# 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(
        '#build_system/' + environment + '.py',
        exports = 'env_avr'
    )
    # Import environment set for target.
    Import('env_target')
    # Build program.
    SConscript(
        'SConscript',
        variant_dir = '#build/' + environment,
        exports = { 'env_target' : env_target, 'env_name' : environment },
        duplicate = 0
    )

Conclusion

Ce principe de construction d'une bibliothèque sera repris par toutes les bibliothèques iDreamMicro.