
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
main.c Télécharger
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 :
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
Télécharger
Ressources
Voici une liste des sites ayant permis de construire ce template. Merci à tous ces auteurs !