Implémentation d'un noyau temps réel (chibiOS) sur une carte STM32-P103

Introduction Générale

Le but de cette série d'articles est de présenter une utilisation avancée du micro-contrôleur Cortex-M3 de chez STMicroElectronic (abrégé en STM32) basé sur une architecture de type ARM. Nous allons pour cela implémenter un système d'exploitation temps-réel permettant ainsi une abstraction matérielle pour nos programmes.
L'ensemble de la démarche et des outils utilisés seront présentés pas-à-pas dans la suite.
 
Rapellons que vous pouvez déposer un commentaire en bas d'article pour chacune de vos questions et/ou remarques !

Sommaire

Introduction au micro-contrôleur ARM cortex-M3

Définition

Processeur:
Un processeur est l'élément capable d'interpréter et d'exécuter les instructions d'un programme.
 
Micro-contrôleur
Un micro-contrôleur est un processeur auquel a été ajouté une batterie de périphériques, allant de la mémoire contenant le programme aux périphériques d'entrées-sorties...
 
Nous allons tout au long de ces articles, utiliser le micro-contrôleur (abrégé en µC) ARM cortex-M3 sur sa platine de développement STM32-P103.
 
Il existe différents types de processeurs fondés sur des architectures entièrement différentes. Les deux principales architectures sont CISC et RISC.
 
CISC
Acronyme de Complex Instruction Set Computer, l'architecture CISC désigne un processeur à "jeu d'instruction étendu" (env 200), c'est à dire offrant de nombreuses instructions et couplé à des modes d'adressage complet.
Les processeurs CISC sont principalement représentés par les x86 d'Intel. Ce sont les processeurs équipant la quasi-totalité des PCs. Ils permettent une programmation utilisant des instructions préprogrammées complexes et rapides, souvent plus efficaces que celles proposées par les compilateurs. En contrepartie, les processeurs CISC sont complexes et consommateurs de ressources.
 
RISC
Acronyme de Reduced Instruction Set Computer, l'architecture RISC désigne un processeur à "jeu d'instruction réduit" (env 50), c'est à dire offrant peu d'instructions mais homogènes en temps d'exécution.
Les processeurs RISC sont principalement représentés par les ARM d'ARM Ldt. Ce sont des processeurs très répandus dans les appareils à électronique embarquée (téléphones, mp3, robots, ...). Ils permettent une programmation utilisant des instructions préprogrammées simples et rapides, facilitant le pipeline, mais difficile de lecture. Ces processeurs ont en revanche une faible consommation de ressources.
 
Le micro-contrôleur que nous utiliserons présente une architecture ARM. Nous l'emploierons interfacé sur une carte de prototypage comprenant une alimentation stabilisée et de nombreux périphériques.

Caractéristique de la platine STM32-P103

- Processeur: ARM™ 32 bits CORTEX M3™ STM32F103RBT6

- 128 K de mémoire Flash - 20 K de RAM
- Port USB (connecteur présent sur le module)
- Port CAN (avec driver et sortie sur bornier)
- 2 x I2C™
- 2 x ADC 12 bits
- 3 x UART (dont 1 port avec driver et prise SUB-D 9 broches)
- 2 x SPI™
- 3 Timers
- Connecteur d'extension présent sur la platine
- Connecteur au dos de la platine pour carte SD™/MMC™
- Connecteur pour alimentation externe par pile
- Fréquence d'opération jusqu'à 72 MHz
- BP reset présent sur le module
- BP configurable présent sur le module
- Led état + Led alimentation présents
- Connecteur JTAG (ARM's 2 x 10 broches)
- Etage de régulation intégré 3,3 V jusqu'à 800 mA
- Alimentation directe via le port USB ou via alim. externe par prise DC.
- Quartz 8 MHz
- Quartz 32,768 KHz et RTC avec connecteur pour sauvegarde externe
- Circuit imprimé haute qualité (plan de masse, sérigraphie des composants, etc...)
- Large zone pastillée vierge permttant le développement de votre application
- Dimensions: 100 x 90 mm

Introduction à chibiOS/RT

Introduction

Afin de proposer par la suite des programmes génériques aux processeurs ARM et d'utiliser au mieux les capacités de notre micro-contrôleur, nous avons décidé d'implémenter un noyau temps réel sur notre carte.
Bénéficiant du large choix de noyaux temps réel existants et de la notoriété des processeurs ARM, un choix conséquent de noyaux déjà portés sur notre architecture s'offrait à nous. En particulier peuvent être cités freeRTOS et µC/OS-II, tous deux portés sur de multiples architectures et bien documentés (en anglais). Toutefois, nous avons décider d'utiliser chibiOS/RT car outre son existence pour notre architecture, il a déjà été partiellement porté sur notre carte, d'où un gain de temps considérable pour nous.
Insistons bien sur le fait que plus qu'un portage sur l'architecture ARM, ce noyau a commencé à être adapté à la configuration exacte de la carte de prototypage que nous souhaitons utiliser, et est fourni avec un programme de démonstration. De plus, ce projet indépendant très récent nous a semblé prometteur et nous tenions à remercier Giovanni Di Sirio pour avoir commencé le portage et fait la partie la plus dur du travail: l'assembleur bas niveau requis par la processeur. ( => lien vers le site officiel <= )
 
Pour une documentation plus abondance de chibiOS concernant son fonctionnement et sa mise en oeuvre, consultez les pages d'aide de chibiosVF; la mouture d'axiomcafé!

Vocabulaire

ChibiRT/OS, que nous abrégerons par la suite en chibiOS est un noyau temps réel préemptif et multi-tâches. En vrac, citons qu'il permet la gestion des threads, des sémaphores, des mutex, des événements, du temps, et des chaînes d'entrées-sorties.
 
Noyau:
Le noyau est une API (Application Programming Interface), c'est à dire un ensemble de fonctions offrant une couche d'abstraction matérielle et apportant des fonctionnalités élémentaires tels que l'ordonnancement des tâches, la gestionnaire de mémoire, les appels système et la gestion du matériel.
 
 
 
Système temps réel:
Un système est dit temps réel ou réactif quand il est caractérisé par une latence (c'est à dire un temps de réponse) bornée.
C'est là une définition académique, basée sur les contraintes imposées par les applications, qui pourrait aussi bien s'appliquer au déclenchement d'un airbag qu'aux prévisions météo. Dans un cas comme dans l'autre, si la réponse du système arrive trop tard, elle n'est plus d'aucun intérêt.
(Jacques-Oliver KLEIN, Prof. Université Paris SUD 11)
 
Système préemptif:
Un système est dit préemptif s'il est capable de gérer l'ordonnancement des tâches en interrompant une tâche au profit d'une autre de priorité supérieure.
 
Evenement:
Un événement est une excitation extérieure sur le système. Deux événements ne peuvent être simultanés.
 
Thread:
Un thread, ou tâche, est un flux d'instruction partageant le même espace mémoire avec d'autres threads. Il partage donc ses variables avec d'autres.
 
Mutex:
Un mutex est une technique utilisée pour sérialiser l'accès à une ressource pour plusieurs threads. Cette variable est binaire, les threads ayant accès à la ressource les uns après les autres.
 
Sémaphore:
Une sémaphore restreint le nombre d'accès simultanés à une ressource partagée. Cette variable est décimale. On peut la ramener à un mutex en limitant sa valeur maximale à 1.

Compiler et implémenter la démo de ChibiOS

Introduction

Afin de prendre en main chibiOS et de rentrer dans le vif du sujet, nous allons commencer par compiler, en guise d'essai, le programme de démonstration fournit avec chibiOS. Il sera considéré que le lecteur est capable de lui même d'installer les programmes spécifiés, et, soit de comprendre, soit de se documenter.

Téléchargement des divers outils

1- Afin d'utiliser chibiOS, nous devons déjà le télécharger.
Nous vous conseillons d'utiliser la version 1.0.0. pour réaliser votre premier essai car c'est sur cette dernière que nous travaillerons par la suite.
( => lien vers chibiOS <= )
La documentation détaillée de chibiOS (en anglais) se trouve sur le site principal.
( =>lien vers la doc <= )
Note: chibiOS peut être décompressé dans le dossier de travail de votre choix car nous travaillerons pour ce premier exemple en ligne de commande en se plaçant dans le répertoire de travail (vous verrez pas la suite). En revanche, plus loin, nous automatiserons le processus de "compilation-implémentation-run-debugage" ce qui nécessitera de travailler avec un chemin ne contenant aucun espace. Les dossiers comme "Document and Settings" ou "Progam Files" ne pourront être utilisé. De plus, il est vivement conseillé de travailler à la racine de votre disque.
Je vous propose de prendre directement cette bonne habitude.
 
2- Créer un dossier ARM à la racine du disque (sous windows: "C:\ARM").
Ce dossier contiendra les divers outils de compilation nécessaires à nos projets. Dans ce dossier, nous allons créer un nouveau dossier appelé "projets" et décompresser chibiOS dedans.
Note: Nous allons utiliser maintenant des outils issus du toolchain Yagarto. Ce projet met en place un ensemble d'outil comprenant tout ce qu'il faut pour développer-compiler-debugger.
( => lien vers la documentation <= )
 
3- Relier la carte STM32-P103 au PC au travers de la sonde JTAG. 
Vous prendrez évidément soin d'installer les drivers de ladite sonde. Afin de pouvoir interfacer PC et carte, nous avons besoin d'un outil capable d'effectuer les communications entre ces deux périphériques. Cet outil se nomme openOCD. Il s'agit d'un serveur permettant de transcrire des messages de type telnet ou de debug en messages compréhensibles par la sonde JTAG, ceci permettant de programmer la carte et de deboger le programme in situ.
( => lien direct vers le téléchargement <= )
 
4- Installer cet outil directement dans le dossier précédemment créé (C:\ARM).
Au cours de l'installation, veillez à choisir l'option automatique de modification de la variable PATH (en cas d'oubli, il est évidement possible de le faire manuellement).
 
5- Installer de la même façon les outils yagarto tools et yagarto GNU ARM toolchain.
Le premier vous fournira un ensemble d'outils nécessaire à la compilation et le second des utilitaires tels que le makefile.
( => lien direct vers le téléchargement de yagarto tools <= )
( => lien direct vers le téléchargement de yagarto GNU ARM toolchain <= )
NOTE: ces deux ensemble d'outils doivent être installé dans des dossiers du même nom dans votre répertoire C:\ARM

Compilation de la démo

1- Nous devons tout d'abord commencer par nous placer dans le dossier de la démo. Nous utiliserons évidément le portage de notre carte, c'est à dire la démo pour STM32-P103 dans le dossier ChibiOS_1.0.0\demos\ARMCM3-STM32F103-GCC.
Pour cela, ouvrez un nouvel invite de commande (sous Windows: démarrer/exécuter/cmd).
Taper la commande:

cd C:\ARM\projets\ChibiOS_1.0.0\demos\ARMCM3-STM32F103-GCC

2- Pour compiler, vous devez vous assurer que le projet est propre, c'est à dire qu'aucun fichier issu de compilation précédente n'existe encore. Le but étant bien-sûr de recompiler l'ensemble du projet. 
Utiliser la commande:

make clean

Puis, compiler le projet dans son ensemble en utilisant:

make all

De multiples fichiers devraient avoir été créés: il s'agit de fichiers de divers extensions représentant les différents "morceaux" du fichier de compilation final. Dans votre dossier principal (celui dans lequel nous travaillerons) vous devriez avoir deux fichiers finaux ch.bin et ch.elf
Le premier représente le programme compilé, en binaire, à implémenter dans votre carte. Le second représente le même programme auquel a été ajouté des options de débugage, des points d'arrêts et pleins de choses utiles pour débuger, c'est à dire vérifier le bon fonctionnement du programme.

Transmition du programme à la carte

1- Nous allons en premier lieu devoir définir pour openOCD un fichier de configuration lui expliquant comment se connecter à notre carte en particulier.
- créer un fichier maconf.cfg dans C:\ARM par exemple.
- insérer les lignes suivantes:
#******
# fichier de configuration par Miroslav, pour des détails 
# ou des options supplémentaires, consulter la documentation
#******
#
# --
# définition du port pour le serveur telnet 4444
# openOCD permet en effet de définir une connexion de type telnet 
# en localhost avec la carte (celle-ci devenant en quelque sorte le serveur).
telnet_port 4444
# --
# définit le premier port sur lequel écouter la réponse de GDB provenant de la carte 
# (les informations de débugage).
# Le système écoute sur le port indiqué puis sur le port+1 etc... 
# jusqu'à obtenir le signal.
gdb_port 3333
# --
# définit le drive à utiliser pour la connexion. 
# Ici celui fourni par le fabricant (ftdi2xx)
interface ft2232
# --
# description du type de branchement JTAG. Ici nous utiliserons le Olimex JTAG-Tiny
ft2232_device_desc "Olimex OpenOCD JTAG TINY A"
# --
# définition du modèle des GPIO (quelle patte sert à quoi!) de notre modèle 
#(ici toujours le modèle standard Olimex-JTAG)
ft2232_layout olimex-jtag
# --
# fournit l'ID du fabricant du connecteur et l'ID Produit exact.
ft2232_vid_pid 0x15ba 0x0004
# --
# définition de la vitesse maximale de connexion 
#(ne doit pas dépasser 1/6 de la valeur du processeur de la carte)
# pour le driver ftd2xx la vitesse se calcule comme suit:
# maxSpeed= 6Mhz/(nombre+1)
jtag_speed 20
# --
# définit le temps qu'openOCD doit attendre avant toute nouvelle opération 
# de type nSRST (en ms)
jtag_nsrst_delay 200
# --
# définit le temps qu'openOCD doit attendre avant toute nouvelle 
# opération de type nTRST (en ms)
jtag_ntrst_delay 200
# --
# définit la forme des signaux utilisés comme reset de la connexion
reset_config trst_and_srst separate
# --
# définit différentes tailles d'instructions et de registres
jtag_device 4 0x1 0xf 0xe
jtag_device 5 0x1 0x1 0x1e
# --
# définit l'action par défaut lors du démarrage de la connexion: 
# ici on lance toujours un rdaemon_startup reset
# --
# définit à quelle carte se connecter (jusqu'à présent nous travaillions 
# sur le type de connection: ici, ce qu'il y a à l'autre bout du fil!)
# Ici nous indiquons que le type de notre carte est cortex-m3
# puis qu'elle fonctionne en little endian (ordre des bits d'un mot binaire: 
# en Little Endian, le bit de point de plus faible est à gauche)
# ce sera la connexion 0 (par défault)
target cortex_m3 little run_and_init 0
# --
# définit ce qui doit être fait lorsque la connexion est établie.
# Ici nous laissons tourner le programme déjà existant puis l'on s'arrête.
# Nous laissons donc tourner la carte définie par la connexion 0 durant 30ms 
run_and_halt_time 0 30
# --
# définit l'espace que peut utiliser le debug sur la carte.
# Ici sur la connexion 0, le debug peut utiliser les 16384 octets commencant à l'adresse
0x20000000
# nobackup signifie que lorsque q'un programme est envoyé,
# toute la zone de debug est systématiquement réécrite.
working_area 0 0x20000000 16384 nobackup
# --
# définit la zone dans la flash à utiliser
flash bank stm32x 0x08000000 0x00010000 0 0 0
2- Ouvrir une nouvelle invite de commande et se placer à nouveau dans le dossier du projet:
cd C:\ARM\projets\ChibiOS_1.0.0\demos\ARMCM3-STM32F103-GCC
puis lancer un nouveau serveur de connexion distante:
openocd-ftd2xx -f C:\ARM\maconf.cfg
Cette invite de commande a maintenant créée un serveur distant qui se trouve virtuellement sur la carte. Nous devons à présent nous y connecter pour envoyer notre programme
 
3- Se connecter au serveur distant. Pour cela, -sans fermer la fenêtre actuelle du serveur crée-, ouvrir une nouvelle invite de commande et lancer:
telnet localhost 4444
Votre invite de commande s'est transformée en invite de requête sur un serveur distant. Tout ce que vous ferez dans cette fenêtre aura une incidence sur le serveur (la carte) comme le prouve la fenêtre-serveur.
 
4- Pour transférer et lancer le programme, il suffit de lancer la séquence suivante:
soft_reset_halt
wait_halt
poll
flash probe 0
stm32x mass_erase 0
flash write_bank 0 ch.bin 0
soft_reset_halt
resume
Cette séquence initialise la carte, efface la mémoire, la remplie avec notre fichier ch.bin puis lance le programme.

Conclusion

Sur votre carte, une petite LED doit s'être mise à clignoter!
Certes cela semble bien peu et décevant au vue de la complexité de la manœuvre. Relativisons!
D'une part cette manœuvre est indépendante de la complexité du code à compiler et transmettre.
D'autres part si l'on ne voit qu'une simple LED il se passe en réalité beaucoup plus! C'est un noyau complet qui tourne et s'exécute la dessous, avec notamment un test et un benchmark de l'ensemble des fonctions.
Pour s'en convaincre, même si je ne décrirai pas la procédure dans l'article, le lecteur est invité à brancher un câble série entre son ordinateur et celui de la carte et d'observer les résultats de cette procédure de test.
 
Utiliser une connexion hyperterminal (Accessoire/Communications/HyperTerminal) en détection automatique sur le bon port COM :
- baudrate: 38400
- bit de stop: 1
- aucune parité
- pas de contrôle de flux
La procédure de test démarre lorsque l'on appuie sur le bouton poussoir.