STM32 Partie 1: Les bases

Vous ne pouvez pas faire confiance au code que vous n'avez pas écrit complètement vous-même. - Ken Thompson
Peut-être ma citation préférée. C'est elle qui est devenue la raison pour laquelle j'ai décidé de plonger dans les profondeurs du terrier du lapin. J'ai commencé mon voyage dans le monde de la programmation tout récemment, seulement environ un mois s'est écoulé et j'ai décidé d'écrire des articles pour consolider le matériel. Tout a commencé par une tâche simple, synchroniser les lampes de votre studio photo en utilisant Arduina. Le problème a été résolu, mais je ne suis plus entré dans le studio photo, il n'y a pas de temps. A partir de ce moment, j'ai décidé de m'engager à fond dans la programmation des microcontrôleurs. Arduin, bien que séduisant par sa simplicité, je n'ai pas aimé la plateforme. Le choix s'est porté sur la société ST et leurs produits populaires. À ce moment-là, je n'avais toujours aucune idée de la différence, mais en tant que consommateur typique, j'ai comparé la vitesse du «processeur» et la quantité de mémoire, je me suis acheté une carte impressionnante avec un écran STM32F746NG - Discovery.Je vais manquer les moments de désespoir et aller droit au but.

Immergé dans l'image d'un programmeur, j'ai beaucoup lu, étudié, expérimenté. Et comme je l'ai déjà décrit ci-dessus, je voulais bien étudier, c'est tout. Et pour cela, je me suis fixé un objectif, pas des solutions toutes faites, seulement la mienne. Et si tout a fonctionné pour moi, vous réussirez.

Liste de tout ce dont vous avez besoin:

  1. Machine virtuelle Ubuntu 16+ ou autre
  2. compilateur de bras - téléchargement sur developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
  3. débogueur et programmeur openocd - vous ne pourrez pas télécharger à partir du lien, nous collectons à partir des sources, l'instruction est jointe

    git clone https://git.code.sf.net/p/openocd/code openocd
  4. Éditeur de texte à votre goût

Une fois que tout est installé et assemblé, nous pouvons passer au premier projet! Et non, ce n'est même pas une ampoule clignotante. Pour commencer, nous devons nous plonger dans le processus d'initialisation du micropuits lui-même.

Ce dont nous avons besoin:

  1. Makefile
  2. Linker.ld
  3. Init.c

Commençons par le dernier paragraphe Init.c. Tout d'abord, notre mk devrait charger l'adresse "pointeur de pile" est un pointeur vers l'adresse mémoire qui est utilisée pour les instructions PUSH et POP. Je vous recommande fortement d'étudier attentivement ces deux instructions, car je n'expliquerai pas en détail toutes les instructions. Comment implémenter cela, voir ci-dessous.

extern void *_estack;
void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack
}

Voyons maintenant cet exemple. Extern signifie que le symbole est externe, et nous avons déclaré ce symbole dans le fichier Linker.ld, nous y reviendrons un peu plus tard.

 __attribute__((section(".isr_vector"), used))

Ici, nous utilisons un attribut qui indique au compilateur de placer le tableau dans la section isr_vector et que même si nous ne l'utilisons pas dans le code, il doit toujours être inclus dans le programme. Et son premier élément sera ce même pointeur.

Voyons maintenant pourquoi ce tableau est et avec quoi il sera mangé. Contrairement à un processeur conventionnel, l'architecture micron sur le bras commence à s'exécuter quand ce n'est pas à partir de l'adresse zéro, mais à partir de l'adresse pointée par le pointeur dans ce tableau, tout est compliqué. De plus, le premier pointeur de ce tableau pointe toujours vers le début de la pile, mais le second pointe déjà vers le début de notre code.

Je vais vous donner un exemple. il est indiqué que la pile commence par 0x20010000 et que le code du programme est 0x0800008. alors le tableau peut être écrit comme

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    0x20010000,
    0x08000008
}

C'est-à-dire que le contrôleur initialise d'abord la pile, puis considère l'adresse de la première instruction et la charge dans le registre de compteur de programme. Maintenant, la chose la plus importante, selon le modèle, ces chiffres peuvent être différents, mais avec l'exemple de stm32f7, je peux dire en toute confiance que ce tableau doit être en mémoire à l'adresse 0x08000000. C'est à partir de cette adresse que mk commencera son travail après avoir allumé ou réinitialisé.

Maintenant, nous allons arrêter et faire attention à la façon de mettre ce tableau dans la section dont nous avons besoin. Cela se fait par "ld" ou linker. Ce programme rassemble l'ensemble de notre programme ensemble et le script Linker.ld est utilisé pour cela. Je donne un exemple et l'analyserai davantage.

MEMORY{
	ROM_AXIM (rx) : ORIGIN = 0x08000000, LENGTH = 1M
	RAM_DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 64K
}

_estack = LENGTH(RAM_DTCM) + ORIGIN(RAM_DTCM);

SECTIONS{
	.isr_vector : {
	KEEP(*(.isr_vector))
	} >ROM_AXIM</code>
}

Voyons ce qui se passe ici. MEMORY définit des sections de mémoire et SECTIONS définit des sections. nous voyons ici notre _empilement et le fait qu'il est égal à la somme du début de la mémoire et de sa longueur, c'est-à-dire la fin de notre mémoire. La section .isr_vector dans laquelle nous mettons notre tableau est également définie. >ROM_AXIMà la fin de notre section signifie que cette section doit être placée dans la section mémoire qui commence par 0x08000000 comme requis par nos microns.

Nous mettons le réseau là où c'est nécessaire maintenant nous avons besoin d'une sorte d'instruction pour que notre micron fonctionne. Voici l'init.c augmenté:

extern void *_estack;

void Reset_Handler();

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
    &Reset_Handler
};

void __attribute__((naked, noreturn)) Reset_Handler()
{
    while(1);
}

Comme je l'ai mentionné précédemment, la deuxième adresse doit être un pointeur vers la première fonction ou instruction. Et encore une fois, les attributs, mais tout est simple, la fonction ne renvoie aucune valeur et suit n'importe quel ABI à l'entrée, c'est-à-dire nu «nu». Dans de telles fonctions, l'assembleur habituel est poussé.

Il est maintenant temps de compiler notre code et de voir ce qui se cache sous le capot. Nous ne toucherons pas au Makefile pour l'instant.

arm-none-eabi-gcc -c init.c -o init.o -mthumb

arm-none-eabi-gcc -TLinker.ld -o prog.elf init.o -Wl,--gc-sections -nostartfiles -nodefaultlibs -nostdlib

Et ici, nous voyons beaucoup d'incompréhensible. en ordre:

  1. -mthumb compile pour armv7, qui utilise uniquement les instructions du pouce
  2. -TLinker.ld spécifiez le script de l'éditeur de liens. par défaut, il compile pour être exécuté dans l'environnement Linux
  3. -Wl, - gc-sections -nostartfiles -nodefaultlibs -nostdlib supprime toutes les bibliothèques standard, les fichiers d'initialisation de l'environnement C et toutes les autres bibliothèques auxiliaires telles que les mathématiques.

Bien sûr, cela n'a aucun sens de charger cela en microns. Mais il y a un sens à voir et à étudier le binaire.

objcopy -O ihex prog.elf prog.bin

hexdump prog.bin

Et puis nous verrons la conclusion. «00000000: 00 01 00 02 09 00 00 08» qui symbolisera notre succès. Ceci est mon premier article et je n'ai pas été en mesure de révéler entièrement tout le matériel et l'essence, donc dans le prochain, je décrirai les mécanismes plus en détail et nous, amis, nous pourrons passer à un programme qui ne clignotera pas, mais configurera l'horloge du processeur et des bus.

All Articles