Le Cahier de F4FEZ
STM32
Template d'application

Présentation

Ce chapitre met en place un squelette pour nos applications ainsi que la configuration du poste de travail, une fois tout les outils en place, il ne restera plus qu'a personaliser ce template pour réaliser le programme souhaiter. J'ai essayer d'être le plus clair possible sur chaque étape pour bien assimilé le pourquoi de la chose. Pour les habitués aux microcontroleurs 8 bits cela risque d'être un peu déroutant au départ car beaucoup d'éléments ce raproche de la réalisation d'applications PC. La puissance de ces microcontroleurs est justement le fait de pouvoir réaliser des applications de haut niveau. Moins de compléxité et plus de performances !

Les documents

Un certain nombre de document sont néscéssaire à la mise en oeuvre des STM32. Cette litérature est disponible sous forme de PDF sur le site de ST microelectronics :
  • La datasheet. Document de base sur les caractéristique du composant. Pour le microcontroleur ultilisé ici, il s'agit de la STM32F103x8/B
  • Le Reference Manual (RM0008). L'essentiel de la programmation des STM32 se trouve ici. Toutes les ressources et périphérique sont détaillés. Les 1000+ pages de ce PDF sont la bible des STM32.
  • Un autre document intérescent est le pdf "An Introduction to the ARM Cortex-M3 Processor". Ce fichier est en téléchargement sur le site de ARM.

Les outils

Sourcery G++

Sourcery G++ est un jeux d'outils pour la compilation C/C++ d'application ARM Cortex M3. L'édition Lite EABI est gratuitement téléchargeable pour Windows et Linux. De multiple compilateurs alternatif sont disponible, Sourcery nous propose une chaine d'outils complète gratuite et simple d'utilisation. La version utilisé ici est la 2010q1-188.

La Standard Peripheral Library

Il s'agit des API de programmation en C/C++ des microcontroleur de la famille. Elle comprend les fonctionnalités standard des Cortex M3 fournit par ARM (Le fameux CMSIS) ainsi que des fonctions pour chacun des périphériques. Le package vient avec de nombreux exemples pour rapidement prendre la main sur le composant. La version utilisé ici est la 3.3.0

Un editeur de texte

Pour le moment nous n'utiliserons pas d'IDE mais un simple editeur de texte. Je vous en laisse le libre choix, pour ma part j'utilise notepad++. Même si le bloc note de Windows est suffisant, la coloration syntaxique ainsi que quelques options des éditeurs avancés font, qu'ils sont bien plus agréable à utiliser.

C'est partie !

Sourcery G++ EABI pour Cortex M3 à été installé, la Standard Peripheral Library est extraite dans un coin du disque et nous avons pris connaissance des documents. Alors démarrons notre template ! Créons en premier un dossier pour notre projet et ajoutons un répertoire "stm32-api" ou nous copions le contenu du répertoire "Libraries" de la Standard Peripheral Library. Profitons de l'instant pour également créer un dossier "build" qui contiendra les fichiers objets issus de la compilation.

Fichier main.c

Voici notre premier fichier du projet.
/**
  *	@file    main.c 
  * @author  Florian MAZEN
  * @version V1.0
  * @date    06/26/2010
  * @brief   Main
  */

#include <stm32f10x.h>
#include <stdio.h>

void RCC_Configuration();

/**
  * @brief  Main program.
  * @retval None
  */
int main()
{
	/* Configure the system clocks */
	RCC_Configuration();
	
	while (1)
	{
	}
}

/**
  * @brief Configures the different system clocks.
*/
void RCC_Configuration()
{
  /* RCC system reset(for debug purpose) */
  RCC_DeInit();
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  */
void assert_failed(uint8_t* file, uint32_t line)
{ 
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif
Comme tout programme en c, nous disposons d'une fonction main, la petite particularité est que ce programme ne doit pas se terminer, par conséquent il se termine avec une boucle infini ! Alors pourquoi déclarer cette fonction de type int ? Tout simplement car il s'agit d'un standard en c même si nous ne l'exploiterons pas. Avez-vous remarque la fàçon de noter les commentaires ? Il s'agit de balise doxygen, cela permet de générer une documentation convenable de notre projet si on le souhaite. L'utilisation de cet outil dépasse les objectifs de cette page mais de nombreuses informations sont disponible sur le site Internet du projet Doxygen ou via la communauté developpez.com

Gestion des interruptions (stm32f10x_it.c et stm32f10x_it.h)

Les STM32 possèdent beaucoup d'interruptions (68 pour le stm32f103). La gestion du système d'interruption est décrite dans le chapitre 9 du manuel de référence. Pour des raisons de simplicité, le code qui leur est associé est géré dans des fichiers à part. Ceux ci sont présent dans la Standard Peripheral Library dans le dossier "Project/STM32F10x_StdPeriph_Template/" il s'agit des fichiers stm32f10x_it.c et stm32f10x_it.h. Leurs structures est simple, pour le moment seul les interruptions sytèmes sont traitées. Copions simplement ces deux fichiers dans le répertoire racine de notre template, nous étudirons dans une prochaine page leurs fonctionnement.

stm32f10x_conf.h

Le fichier stm32f10x_conf.h permet de sélectionner quels sont les headers de la Standard Peripheral Library à inclure dans le projet, un fichier exemple peu être copier depuis le dossier "Project\STM32F10x_StdPeriph_Template". La personalisation consiste à décommenté ou commenter les lignes néscéssaires (Les pariphériques utilisés).

stf_syscalls_minimal.c

Sourcery G++ est un regroupement de logiciel libre et notament le compilateur largement connu GCC. GCC est à l'origine conçu pour la compilation des programmes sous Unix (et dérivés) et non pas pour la réalisation de programme pour microcontroleur sans système d'exploitation. Tout programme en C interface avec des entrées sorties tel que le clavier, l'écran ou les fichiers, et donc tous les compilateurs doivent fournir une librairie pour les exploiter. Sous GNU/Linux, cette librairie attachée à GCC est LIBC, elle s'occupe de négocier avec le système d'exploitation. Par exemple lors de l'ouverture d'un fichier avec fopen, une appel système est effectuer et le système d'exploitation s'occupe entre autre des problèmes de droit d'accès et de gestion du support de stockage. Mais nous somme loin de notre microcontroleur sans OS donc la librairie stantard C doit être revu. Soucery G++ vient avec une librairie nommée newlib, celle-ci fournit toutes les fonctions standard du C, tout en restant la plus légère possible. Reste un petit problème : Comme il vient d'être expliqué, les fonctions standard requiert un système d'exploitation pour gérer certains appels, hors nous n'avons pas de système sur notre micro ! C'est pour cela que les appels système ne sont pas implémentés, ou plutot sont implémenté avec un bouchon sans effet. Cependant si le besoin s'en fait sentir il est possible de reédfinir les fonctions standard, d'où le fichier stf_syscalls_minimal.c.
/**
  *	@file    stf_syscalls_minimal.c
  * @author  Florian MAZEN
  * @version V1.0
  * @date    06/26/2010
  * @brief   newlib syscall declaration
  */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>

// Function declaration.
void _exit(int i);
int _open(const char *name, int flags, int mode);
int _read(int file, char *ptr, int len);
int _write(int file, char *buffer, unsigned int count);
int _lseek(int file, int ptr, int dir);
int _fstat(int file, struct stat *st);
int _link(char *old, char *new);
int _unlink(char *name);
int _stat(char *file, struct stat *st);
int _close(int file);
int _execve(char *name, char **argv, char **env);
int _fork();
int _getpid();
int _isatty(int file);
int _kill(int pid, int sig);
caddr_t _sbrk(int incr);
int times(struct tm *buf);
int _wait(int *status);

#undef errno
extern int errno;
char *__env[1] = {0};
char **__environ = __env;
extern unsigned int _sheap;
extern unsigned int _eheap;
static caddr_t heap = NULL;


// Function definition.

void _exit(int i)
{
	while (1);
}

int _write(int file, char *buffer, unsigned int count)
{
	return -1;
}

int _close(int file)
{
	return -1;
}

int _fstat(int file, struct stat *st)
{
	st->st_mode = S_IFCHR;
	return 0;
}

int _isatty(int file)
{
	return 1;
}

int _lseek(int file, int ptr, int dir)
{
	return 0;
}

int _read(int file, char *ptr, int len)
{
	return 0;
}

caddr_t _sbrk(int incr)
{
	caddr_t prevHeap;
	caddr_t nextHeap;

	if (heap == NULL)
	{ // first allocation
		heap = (caddr_t) & _sheap;
	}

	prevHeap = heap;

	// Always return data aligned on a 8 byte boundary
	nextHeap = (caddr_t) (((unsigned int) (heap + incr) + 7) & ~7);

	// Check enough space and there is no collision with stack coming the other way
	// if stack is above start of heap
	if (nextHeap >= (caddr_t) & _eheap)
	{
		errno = ENOMEM;
		return NULL; // error - no more memory
	}
	else
	{
		heap = nextHeap;
		return (caddr_t) prevHeap;
	}
}

int _open(const char *name, int flags, int mode)
{
	return -1;
}

int _link(char *old, char *new)
{
	errno = EMLINK;
	return -1;
}

int _unlink(char *name)
{
	errno = ENOENT;
	return -1;
}

int _stat(char *file, struct stat *st)
{
	st->st_mode = S_IFCHR;
	return 0;
}

int _execve(char *name, char **argv, char **env)
{
	errno = ENOMEM;
	return -1;
}

int _fork()
{
	errno = EAGAIN;
	return -1;
}

int _getpid()
{
	return 1;
}

int _kill(int pid, int sig)
{
	errno = EINVAL;
	return (-1);
}

int times(struct tm *buf)
{
	return -1;
}

int _wait(int *status)
{
	errno = ECHILD;
	return -1;
}
stf_syscalls_minimal.c Télécharger
En jetant un coup d'oeil au fichier joint vous pourrez constater la présence de la fonction _open qui est appelé lors de l'ouverture d'un fichier en reprenant notre exemple avec fopen. Toutes les fonctions présente dans le fichier sont bouchonnées à l'execption de la fonction _sbrk. L'utilité de celle-ci est d'allouer dynamiquement de la mémoire, par exemple dans un programme en C, lors de l'appel de la fonction malloc où un appel au système est fait pour réserver de la mémoire. Comme vous l'aurez compris c'est la fonction _sbrk qui est appelée en interne et donc elle a la charge de trouver de la mémoire pour l'application. Comme nous le verrons juste après, de la mémoire dans notre microcontroleur ne vas pas être réservé par le compilateur (Ou plutot l'éditeur des liens), cette espace mémoire s'appel en anglais le heap (Le tas en Français) et notre fonction va plonger dedans pour fournir au programme la mémoire demandée.

system_stm32f10x.c

Ce fichier est à copier dans notre templat à partir du dossier "stm32-api\CMSIS\CM3\DeviceSupport\ST\STM32F10x". Il contient des paramètres de configuration appliqué lors du boot pour définir notament les vitesses d'horloge.

stm32_flash.ld

Le script du linker est probablement un fichier dont la manipulation ne vous est pas familière mais il est indispensable pour l'utilisation de GCC sur les microcontroleurs. En fait il est indispendable pour toutes les opérations d'édition des liens mais l'utilisation habituel du linker pour programme classique fait appel à un script par défaut.
Dans notre processus de construction du programme, chaque fichier C est compilé en un fichier objet (.o). Le code contenu dans ce dernier est dit relogeable, c'est a dire que chaque instruction, variable ou pointeur ne fait réference à aucune adresse physique. Lors de l'édition des liens, les adresses définitives sont attribuées et les références misent à jour. Cependant notre linker à besoin de connaitre la taille et la position de la mémoire (Flash et RAM) pour fonctionner. Compte tenu de la diversité des possibilité de configuration de mémoire interne et potentielment externe, il est néscéssaire de personalisé ce fichier pour chaque microcontroleur ou application.

Pour faire simple notre projet utilise seulement la RAM et la FLASH interne au microcontroleur Voici tout d'abord une petite liste des zones mémoire utilisées par notre linker :
  • isr_vector : En premier lieu le tableau d'adresses des vecteurs d'intéruption, il est situé au tout début de la Flash interne.
  • text : Il sagit de la zone de stockage de code. Bien evidament en Flash.
  • rodata : Les valeurs initiales des variables globales initialisées. Ces données sont stocké dans la flash mais copier dans la RAM lors du boot.
  • data : Zone des variables globales initialisées, les valeurs d'initialisation sont prélévées de la zone rodata. En RAM.
  • bss : Les variables globales (ou statique) non initialisées. Par défaut, elle sont initialisées à 0 lors du boot.
  • La pile (stack) : Contrairement aux zones précédemment décrite, la taille de la pile n'est pas connu au moment de l'édition des liens. La pile évolue dynamiquement au fur et à mesure de l'exécution. Elle stock les variables locales de chaque fonction, lors de l'appel les paramètres sont passés par la pile et les variables locales sont gérées dans cette espace, ceci permet de résoudre les problèmes d'appel récursif des fonctions. Une fois l'exécution de la fonction terminée, l'espace est libéré. Ne connaissant pas la taille néscéssaire lors de la compilation, il faut déterminé une valeur initial suffisante mais pas excessive pour laisser de la place pour le heap (voir ci-après). Si la taille n'est pas suffisante, il y a débordement et plantage  Pour cette application, la valeur de 1Ko est choisi.
  • Le tas (heap) : L'espace de la RAM restant n'est pas laissé inutilisé. Nous l'utilisons pour y placer le tas (The heap). Cet espace mémoire est voué à l'allocation dynamique sur demande du programme, avec notament la fonction malloc du c ou le mot clé "new" en c++.
/*
Default linker script for STM32F10x_512K_64K
Copyright RAISONANCE S.A.S. 2008

Modified for High Density devices
by Francois Gervais, August 2009
Modified for C++ support with the help of generic.ld
from CodeSourcery G++ Lite
by Francois Gervais, Septembre 2009

Modifications for F4FEZ templates 2010
*/

/* include the common STM32F10x sub-script */

/* Common part of the linker scripts for STM32 devices*/


/* default stack sizes. 

These are used by the startup in order to allocate stacks for the different modes.
*/

__Stack_Size = 1024 ;

PROVIDE ( _Stack_Size = __Stack_Size ) ;

__Stack_Init = _estack  - __Stack_Size ;

/*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/
PROVIDE ( _Stack_Init = __Stack_Init ) ;

/*
There will be a link error if there is not this amount of RAM free at the end.
*/
_Minimum_Stack_Size = 0x100 ;


/* include the memory spaces definitions sub-script */
/*
Linker subscript for STM32F10x definitions with 512K Flash and 64K RAM */

/* Memory Spaces Definitions */

MEMORY
{
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
  FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB2 (rx) : ORIGIN = 0x00000000, LENGTH = 0
  EXTMEMB3 (rx) : ORIGIN = 0x00000000, LENGTH = 0
}

/* higher address of the user mode stack */
_estack = 0x20010000;



/* include the sections management sub-script for FLASH mode */

/* Sections Definitions */

SECTIONS
{
    /* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */
    .isr_vector :
    {
		_stext = .;
		. = ALIGN(4);
        KEEP(*(.isr_vector))            /* Startup code */
		. = ALIGN(4);
    } >FLASH
 
    /* for some STRx devices, the beginning of the startup code is stored in the .flashtext section, which goes to FLASH */
    .flashtext :
    {
		. = ALIGN(4);
        *(.flashtext)            /* Startup code */
		. = ALIGN(4);
    } >FLASH
    
    /* the program code is stored in the .text section, which goes to Flash */
    .text :
    {
	    . = ALIGN(4);
        
        *(.text .text.* .gnu.linkonce.t.*)
    	*(.plt)
    	*(.gnu.warning)
    	*(.glue_7t) *(.glue_7) *(.vfp11_veneer)

    	*(.ARM.extab* .gnu.linkonce.armextab.*)
    	*(.gcc_except_table)
	   
    } >FLASH
    
    
    
    .eh_frame_hdr : ALIGN (4)
	{
		KEEP (*(.eh_frame_hdr))
	} >FLASH
	.eh_frame : ALIGN (4)
	{
		KEEP (*(.eh_frame))
	} >FLASH
	/* .ARM.exidx is sorted, so has to go in its own output section.  */
	__exidx_start = .;
	.ARM.exidx :
	{
		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
	} >FLASH
    __exidx_end = .;
    
    .rodata : ALIGN (4)
	{
		*(.rodata .rodata.* .gnu.linkonce.r.*)
	
		. = ALIGN(4);
		KEEP(*(.init))
	
		. = ALIGN(4);
		__preinit_array_start = .;
		KEEP (*(.preinit_array))
		__preinit_array_end = .;
	
		. = ALIGN(4);
		__init_array_start = .;
		KEEP (*(SORT(.init_array.*)))
		KEEP (*(.init_array))
		__init_array_end = .;
	
		. = ALIGN(4);
		KEEP(*(.fini))
	
		. = ALIGN(4);
		__fini_array_start = .;
		KEEP (*(.fini_array))
		KEEP (*(SORT(.fini_array.*)))
		__fini_array_end = .;
	
		. = ALIGN(4);
		KEEP (*crtbegin.o(.ctors))
		KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
		KEEP (*(SORT(.ctors.*)))
		KEEP (*crtend.o(.ctors))
	
		. = ALIGN(4);
		KEEP (*crtbegin.o(.dtors))
		KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
		KEEP (*(SORT(.dtors.*)))
		KEEP (*crtend.o(.dtors))
	
		. = ALIGN(4);
		_etext = .;
	    /* This is used by the startup in order to initialize the .data secion */
		_sidata = _etext;
	} >FLASH
    PROVIDE ( _sflash = _stext );
    PROVIDE ( _eflash = _etext );
    

    /* This is the initialized data section
    The program executes knowing that the data is in the RAM
    but the loader puts the initial values in the FLASH (inidata).
    It is one task of the startup to copy the initial values from FLASH to RAM. */
    .data  : AT ( _sidata )
    {
	    . = ALIGN(4);
        /* This is used by the startup in order to initialize the .data secion */
        _sdata = . ;
        
        KEEP(*(.jcr))
		*(.got.plt) *(.got)
		*(.shdata)
		*(.data .data.* .gnu.linkonce.d.*)

	    . = ALIGN(4);
	    /* This is used by the startup in order to initialize the .data secion */
   	 	_edata = . ;
    } >RAM
    
    

    /* This is the uninitialized data section */
    .bss :
    {
	    . = ALIGN(4);
        /* This is used by the startup in order to initialize the .bss secion */
        _sbss = .;
        
        *(.shbss)
		*(.bss .bss.* .gnu.linkonce.b.*)
		*(COMMON)
        
	    . = ALIGN(4);
	    /* This is used by the startup in order to initialize the .bss secion */
   	 	_ebss = . ;
	. = ALIGN(4);
    } >RAM
    
    /* Heap start after the last allocate block */
    PROVIDE ( _sheap = _ebss );
    PROVIDE ( _eheap = __Stack_Init );

    /* Heap end before the stack */
    
    /* This is the user stack section 
    This is just to check that there is enough RAM left for the User mode stack
    It should generate an error if it's full.
     */
    ._usrstack :
    {
	    . = ALIGN(4);
        _susrstack = . ;
        
        . = . + _Minimum_Stack_Size ;
        
	    . = ALIGN(4);
        _eusrstack = . ;
    } >RAM
}
stm32_flash.ld Télécharger
Le script du linker contient de multiple variables qui sont utilisées par le fichier de boot (voir plus bas) ou stf_syscalls_minimal.c. Précédement nous avions parlé de la fonction _sbrk qui s'occupe de la fonction malloc, il se trouve que cette fonction s'appui sur des variables du script ld pour connaitre l'adresse de départ et de fin du heap(_sheap et _eheap). En effet la taille de la mémoire utilisée et donc de l'espace libre pour le heap ne peu être connu qu'au moment de l'édition des liens. Si vous utilisez un microcontroleur ayant des tailles mémoire différentes de celles utilisées ici, il faut veiller à modifier les constantes de ce fichier.

Fichier startup

Nous venons de voir comment était répartie l'occupation de mémoire, maintenant nous allon étudier ce qui se passe lors du démarage du microcontroleur. Les premiers octets de la flash contiennent la table des vecteurs d'interuptions, où on trouve tout d'abord l'adresse de la pile qui sera charger dans le registre approprié lors du reset, suivi du vecteur d'intéruption de reset qui contient donc l'adresse de boot. Ce tableau est ditué à la fin du fichier bien qu'il soit inclu au début de la flash. Quelques taches sont requises pour pouvoir éxecuter convenablement le programme en c : L'initialisation à 0 de la zone BSS et le chargement de la zone data. La fin de l'initialisation se termine par l'appel de la fonction main. La réalisation de ces tâches ainsi que la définition de la table des vecteurs d'intéruption sont réalisés dans un fichier assembleur. Copier le fichier correspondant à notre architecture (startup_stm32f10x_hd.s) depuis "stm32-api\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\GC_ride7" dans la racine de notre projet.

Makefile

Directement venu de l'univers Unix, les outils de Sourcery G++ s'appui sur make pour construire les projets. Même si nous somme pas obligé de l'utiliser il va nous simplifier grandement la construction du projet pour obtenir un fichier pret à télécharger dans le STM32. Pour les non habitués à make, la commande "make" recherche un fichier "Makefile" dans le répertoire courant et éxecute les règles de construction à la façon d'un script. Il est possible d'éxécuter une règle particulière en la précisant sur la ligne de commande, par exemple "make clean" pour nétoyer les fichiers du projet. Le fichier utilisé ici s'inspire de ce que j'ai pu voir à droite et à gauche sur des exemples d'applciation du STM32. Il est utilisable avec un peu de personalisation consistant à ajouter ses fichiers sources dans la section "# Source files" et les fonctionalités de l'API utilisé dans "# ST Library source files"
# Project name
PROJECT_NAME=Base

# Directory definition.
ST_LIB_DIR=./stm32-api/STM32F10x_StdPeriph_Driver
ARM_CMSIS_DIR=./stm32-api/CMSIS
STARTUP_FILE=./startup_stm32f10x_hd.S

# Directory for output files (lst, obj, dep, elf, sym, map, hex, bin etc.).
OUTDIR = build

# Toolchain definition.
CC=arm-none-eabi-gcc
OBJCOPY=arm-none-eabi-objcopy
OBJDUMP=arm-none-eabi-size
NM = arm-none-eabi-nm
LDSCRIPT=stm32_flash.ld

# should use --gc-sections but the debugger does not seem to be able to cope with the option.
LINKER_FLAGS=-Xlinker -o$(PROJECT_NAME).axf -Xlinker -M -Xlinker -Map=$(PROJECT_NAME).map -Xlinker --no-gc-sections

# Debugging format.
#DEBUG = stabs
#DEBUG = dwarf-2
DEBUG= gdb

# Optimization level, can be [0, 1, 2, 3, s].
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s
#OPT = 2
#OPT = 3
#OPT = 0

# Compiler flag to set the C Standard level.
# c89   - "ANSI" C
# gnu89 - c89 plus GCC extensions
# c99   - ISO C99 standard (not yet fully implemented)
# gnu99 - c99 plus GCC extensions
CSTANDARD = gnu89

# Compiler flags definition.
CFLAGS=-g$(DEBUG) \
		-O$(OPT) \
		-std=$(CSTANDARD) \
		-T$(LDSCRIPT) \
		-I . \
		-I ./STCode \
		-I $(ST_LIB_DIR)/inc \
		-I $(ARM_CMSIS_DIR)/CM3/DeviceSupport/ST/STM32F10x \
		-I $(ARM_CMSIS_DIR)/CM3/CoreSupport \
		-D STM32F10X_HD \
		-D USE_STDPERIPH_DRIVER \
		-D VECT_TAB_FLASH \
		-D GCC_ARMCM3 \
		-D inline= \
		-D PACK_STRUCT_END=__attribute\(\(packed\)\) \
		-D ALIGN_STRUCT_END=__attribute\(\(aligned\(4\)\)\) \
		-mthumb \
		-mcpu=cortex-m3 \
		-ffunction-sections \
		-fdata-sections

ADEFS=-D STM32F10X_HD \
		-D USE_STDPERIPH_DRIVER \
		-D VECT_TAB_FLASH \
		-D GCC_ARMCM3 \
		-D inline= \
		-D PACK_STRUCT_END=__attribute\(\(packed\)\) \
		-D ALIGN_STRUCT_END=__attribute\(\(aligned\(4\)\)\) 
# Assembler flags
ASFLAGS = $(ADEFS) -Wa,-adhlns=$(<:.S=.lst),-g$(DEBUG)
ALL_ASFLAGS = -mcpu=cortex-m3 -mthumb-interwork -I. -x assembler-with-cpp $(ASFLAGS)

# Source files
SOURCE=	main.c \
		stm32f10x_it.c \
		stf_syscalls_minimal.c \
		system_stm32f10x.c \

# ST Library source files.
ST_LIB_SOURCE= $(ARM_CMSIS_DIR)/CM3/CoreSupport/core_cm3.c \
		$(ST_LIB_DIR)/src/misc.c \
		$(ST_LIB_DIR)/src/stm32f10x_rcc.c \
		$(ST_LIB_DIR)/src/stm32f10x_gpio.c \

SOURCE+=$(ST_LIB_SOURCE)

# List of all source files without directory and file-extension.
ALLSRCBASE = $(notdir $(basename $(SOURCE)))

LIBS=

# List of all objects files.
OBJS = $(addprefix $(OUTDIR)/, $(addsuffix .o, $(ALLSRCBASE)))

# Define Messages.
# English
MSG_BEGIN = -------- begin --------
MSG_END = --------  end  --------

# Rules definition. ***********************************************************
all: begin gccversion $(OUTDIR)/$(PROJECT_NAME).bin end

$(OUTDIR)/$(PROJECT_NAME).bin : $(PROJECT_NAME).axf
	$(OBJCOPY) $(PROJECT_NAME).axf -O binary $(PROJECT_NAME).bin

$(PROJECT_NAME).axf : $(OBJS) $(OUTDIR)/startup_stm32f10x.o Makefile
	$(CC) $(CFLAGS) $(OBJS) $(OUTDIR)/startup_stm32f10x.o $(LIBS) $(LINKER_FLAGS)

# Compile: create object files from C source files.
define COMPILE_C_TEMPLATE
$(OUTDIR)/$(notdir $(basename $(1))).o : $(1)
##	@echo
	@echo $$< "->" $$@
	$(CC) -c  $$(CFLAGS) $$< -o $$@
endef
$(foreach src, $(SOURCE), $(eval $(call COMPILE_C_TEMPLATE, $(src))))
#
$(OUTDIR)/startup_stm32f10x.o : $(STARTUP_FILE) Makefile
	$(CC) -c -mthumb $(ALL_ASFLAGS) -O1 $(STARTUP_FILE) -o $(OUTDIR)/startup_stm32f10x.o

clean :
	cs-rm -f $(OBJS)
	cs-rm -f $(OUTDIR)/startup_stm32f10x.o
	cs-rm -f startup_stm32f10x_hd.lst
	cs-rm -f $(PROJECT_NAME).axf
	cs-rm -f $(PROJECT_NAME).map
	cs-rm -f $(PROJECT_NAME).bin

log : $(PROJECT_NAME).axf
	$(NM) -n $(PROJECT_NAME).axf > $(PROJECT_NAME)_SymbolTable.txt
	$(OBJDUMP) --format=SysV $(PROJECT_NAME).axf > $(PROJECT_NAME)_MemoryListingSummary.txt
	$(OBJDUMP) $(OBJS) > $(PROJECT_NAME)_MemoryListingDetails.txt

# Eye candy.
begin:
##	@echo
	@echo $(MSG_BEGIN)

end:
	@echo $(MSG_END)
##	@echo

# Display compiler version information.
gccversion :
	@$(CC) --version
Makefile Télécharger

Conclusion

Pou lancer le build du projet, aller dans le dossier du projet à l'aide d'une Invite de commande MS-DOS et taper la commande "cs-make". Sous linux lancer un "make" dans le dossier à partir d'une console. Ca sera tout pour ce chapitre à partir de là, un template de base est ptet pour venir accueillir nos applications. Le prochain chapitre sera un "Hello world" s'appuyant sur cette base.

Téléchargement

Le projet est disponible complet, près à être téléchargé puis compilé :
Télécharger

Ressources

Voici une liste des sites ayant permis de construire ce template. Merci à tous ces auteurs !

Copyright © 2008-2011 Florian MAZEN