Pré-requis

Le système d'exploitation utilisé est Ubuntu 10.04.2 LTS x64.

Les paquets suivants sont requis :

Ils sont installés depuis les dépôts standards, par exemple à l'aide de la commande apt-get :

~$ sudo apt-get install avr-libc gcc-avr scons

Arborescence

Le projet est situé dans le dossier idreammicro. Celui-ci contient le fichier source helloworld.c ainsi que le fichier SConstruct. C'est ce dernier que SCons cherche lors de la construction du logiciel.

idreammicro
|_helloworld.c
|_SConstruct

Programme minimal

On débute avec un programme minimal qui sera compilé à l'aide du premier compilateur trouvé par SCons, par exemple GCC.

Fichier source helloworld.c

Un main, rien de plus...

int
main
(
    void
){
    return 0;
}

Fichier SConstruct

Le fichier SConstruct est le plus simple possible :

  • création et initialisation de l'environnement de construction (ligne 2) ;
  • construction du programme (ligne 5).
# Create and initialize the environment.
env = Environment()

# Build the program.
env.Program(target = 'helloworld', source = 'helloworld.c')

target désigne le programme de sortie.
source désigne les sources du programme.

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

La construction du logiciel produit un fichier objet helloworld.o et un fichier binaire helloworld au format elf.

Adaptation de l'environnement de construction pour AVR-GCC

Fichier source helloworld.c

On modifie le programme de manière à utiliser un microcontrôleur AVR. Ce programme fait basculer le niveau de sortie de PB0 toutes les secondes.

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

int
main
(
    void
){
    // Define PORTB as output.
    DDRB = 0xFF;

    while (1)
    {
        // Set PB0.
        PORTB = 0x01;
        _delay_ms(1000);

        // Reset PB0.
        PORTB = 0x00;
        _delay_ms(1000);
    }
    return 0;
}

Fichier SConstruct

Création du fichier binaire

On adapte maintenant l'environnement de construction de manière à utiliser avr-gcc pour un microcontrôleur Atmel AVR Atmega 328 cadencé à 16 MHz. On complète alors le fichier SConstruct (lignes 4 à 24).

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

# Set environment for AVR-GCC.
env['CC'] = 'avr-gcc'
env['CPPPATH'] = '/usr/lib/avr/include'
env.Append(CCFLAGS = '-Os')

# 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.Append(CCFLAGS = '-mmcu=' + DEVICE)
env.Append(LINKFLAGS = '-mmcu=' + DEVICE)
env.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY)

# Define target name.
TARGET = 'helloworld'

# Define source file.
sources = 'helloworld.c'

# Build the program.
env.Program(target = TARGET + '.elf', source = sources)

Quelques explications :

  • Les lignes 5 et 6 indiquent respectivement l'utilisation du compilateur avr-gcc ainsi que son emplacement.
  • La ligne 7 définit le niveau d'optimisation, indispensable pour éviter un warning. Cela garantit le bon fonctionnement des fonctions de délai telles que _delay_ms. Ici, on optimise la taille.
  • Les lignes 11 et 13 décrivent respectivement le type de microcontrôleur ainsi que sa fréquence d'horloge.
  • Les lignes 16 à 18 configurent l'environnement de construction pour le microcontrôleur précédemment décrit.
  • La ligne 21 est une variable contenant le nom de la cible.
  • La ligne 24 indique les sources du programme.
  • Ligne 27, on remarquera l'ajout de l'extension elf à la cible.

La construction du logiciel produit un fichier objet helloworld.o et un fichier binaire helloworld.elf au format elf. Toutefois ce dernier n'est pas utilisable en l'état pour programmer le microcontrôleur car il contient notamment des informations non exécutables ou de débogage : noms de variables ou de fonctions, numéros de lignes, etc). Il est donc nécessaire de produire un fichier binaire au format Intel Hex.

Création du fichier binaire au format Intel Hex

On modifie à nouveau l'environnement de construction de manière à obtenir un fichier binaire au format Intel Hex qui pourra être téléchargé dans le microcontrôleur (lignes 7, 27-31). Pour ce faire on utilise l'outil avr-objcopy.

# 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.Append(CCFLAGS = '-Os')

# 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.Append(CCFLAGS = '-mmcu=' + DEVICE)
env.Append(LINKFLAGS = '-mmcu=' + DEVICE)
env.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY)

# Define target name.
TARGET = 'helloworld'

# Define source file.
sources = 'helloworld.c'

# Build the program.
env.Program(target = TARGET + '.elf', source = sources)

# Create hex binary file.
env.Command(TARGET + '.hex', TARGET + '.elf', env['OBJCOPY'] + ' -O ihex $SOURCE $TARGET')

La construction du logiciel produit, en plus des fichiers objet helloworld.o et binaire helloworld.elf, le fichier binaire helloworld.hex au format Intel Hex.

Calcul de l'empreinte mémoire

Il est possible de connaître l'empreinte mémoire de notre programme à l'aide de l'outil avr-size en ajoutant quelques lignes à l'environnement de construction (lignes 8, 34-35).

# 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.Append(CCFLAGS = '-Os')

# 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.Append(CCFLAGS = '-mmcu=' + DEVICE)
env.Append(LINKFLAGS = '-mmcu=' + DEVICE)
env.Append(CPPDEFINES = 'F_CPU=' + CPU_FREQUENCY)

# Define target name.
TARGET = 'helloworld'

# Define source file.
sources = 'helloworld.c'

# Build the program.
env.Program(target = TARGET + '.elf', source = sources)

# Create hex binary file.
env.Command(TARGET + '.hex', TARGET + '.elf', env['OBJCOPY'] + ' -O ihex $SOURCE $TARGET')

# Compute memory usage.
env.Command(None, TARGET + '.elf', env['SIZE'] + ' -C --mcu=' + DEVICE + ' $SOURCE')

A l'issue de la construction du logiciel, on observe désormais 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)

Conclusion

L'objectif de cet article étant de proposer la configuration d'un environnement de construction SCons pour AVR-GCC le plus simple possible, c'est ici qu'il s'achève. Un prochain article proposera une solution plus élégante et plus professionnelle en séparant la configuration de l'environnement de la construction du logiciel.