1. SDK mbed et LPC810, la rencontre
Le SDK mbed est une bibliothèque open source développée par ARM fournissant une API indépendante du hardware, simplifiant ainsi la programmation de votre microcontrôleur préféré. ARM propose le mbed Compiler, un IDE en ligne déjà présenté dans Hackable n°3, mais ne proposant qu'un sous-ensemble des plateformes supportées par le SDK mbed.
Une plateforme mbed Enabled doit avoir une interface USB, proposant un périphérique mass-storage pour reflasher le microcontrôleur, l'accès au port série, et une interface générique de debug CMSIS-DAP. Vous trouverez la liste des plateformes mbed Enabled sur https://developer.mbed.org/platforms/ et plus d'informations sur le « mbed HDK Handbook » sur https://developer.mbed.org/handbook/mbed-HDK#cmsis-dap-interface.
La plateforme mbed Enabled la plus proche du LPC810 est la « NXP LPC800-MAX », avec un LPC812 : 4 Ko de RAM et 16 Ko de Flash. Bien que ces deux microcontrôleurs soient de la même famille, les binaires compilés pour le LPC812 ne pourront être utilisés sur le LPC810 parce que la stack est placée à la fin de la RAM, bien au-delà de ce que supporte le LPC810.
Nous allons donc devoir sortir des sentiers battus et nous passer (des limitations) de l'IDE en ligne : c'est tout un univers de plateformes atypiques qui devient accessible ! Il est bon de noter qu'il ne suffit souvent que de petits changements pour pouvoir supporter une nouvelle plateforme si le microcontrôleur est supporté.
2. Mon premier build
2.1 Installation du cross-compilateur
Commençons par installer le cross-compilateur (toolchain) « bare-metal » pour les processeurs arm. Les utilisateurs de distributions Debian et dérivées pourront installer le paquet gcc-arm-none-eabi. Les autres distributions fournissent peut-être un paquet équivalent, ou peuvent télécharger une version « Bare-metal toolchain for Cortex-R/M » de Linaro depuis https://www.linaro.org/downloads/ (il y a même une version Windows...).
2.2 Récupération du SDK mbed
Il faut ensuite récupérer la dernière version depuis le dépôt officiel sur GitHub :
cyprien@linux-dev:~/hk$ git clone https://github.com/mbedmicro/mbed.git
Cloning into 'mbed'...
remote: Counting objects: 52343, done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 52343 (delta 7), reused 0 (delta 0), pack-reused 52324
Receiving objects: 100% (52343/52343), 31.29 MiB | 3.25 MiB/s, done.
Resolving deltas: 100% (34451/34451), done.
Checking connectivity... done.
Checking out files: 100% (5638/5638), done.
cyprien@linux-dev:~/hk$
Le SDK mbed requiert python 2.7, et l'outil python-setuptools. J'avoue tout de suite que j'avais sauté cette étape, mais elle est documentée dans mbed/docs/BUILDING.md et permet sûrement d'éviter de tomber sur des erreurs python lors de l'exécution des scripts mbed. L'installation des modules python pour mbed peut alors se faire :
cyprien@linux-dev:~/hk/mbed$ sudo python setup.py install
running install
running bdist_egg
[ . . . ]
Finished processing dependencies for mbed-tools==0.1.14
2.3 Extraction du test MBED_BLINKY
Le SDK est prévu pour être compilé pour une plateforme donnée, et de compiler des programmes de tests. Il est même possible d'automatiser l'exécution de ces tests sur une plateforme mbed Enabled connectée sur la machine. Mais pour plus de flexibilité, nous allons utiliser la fonction d'exportation pour obtenir une archive ne contenant que les fichiers nécessaires pour le LPC810, la toolchain GCC pour ARM, et le programme de test MBED_BLINKY :
cyprien@linux-dev:~/hk$ cd mbed/
cyprien@linux-dev:~/hk/mbed$ python workspace_tools/project.py -m LPC810 -i gcc_arm -n MBED_BLINKY
[WARNING] Using default settings. Define your settings in the file "workspace_tools/private_settings.py" or in "./mbed_settings.py"
Copy: test_env.h
. . .
Copy: main.cpp
Successful exports:
* LPC810::gcc_arm /home/cyprien/hk/mbed/build/export/MBED_BLINKY_gcc_arm_LPC810.zip
Sans paramètre, cette commande vous donnera la liste exhaustive des tests, chaînes de compilations et MCU supportés. Attention toutefois, toutes les toolchains ne sont pas supportées par tous les MCU, et le support de GCC pour les LPC810/812 n'est arrivé que récemment. Pour plus d'informations concernant les scripts existant, vous pouvez vous référer au document BUILDING.md cité plus haut.
Décompressons maintenant l'archive et regardons rapidement ce qu'elle nous réserve :
cyprien@linux-dev:~/hk/mbed$ cd ..
cyprien@linux-dev:~/hk$ unzip -x mbed/build/export/MBED_BLINKY_gcc_arm_LPC810.zip
Archive: /home/cyprien/hk/mbed/build/export/MBED_BLINKY_gcc_arm_LPC810.zip
inflating: MBED_BLINKY/GettingStarted.htm
inflating: MBED_BLINKY/Makefile
inflating: MBED_BLINKY/main.cpp
[ . . .]
inflating: MBED_BLINKY/mbed/hal/serial_api.h
cyprien@linux-dev:~/hk$ cd MBED_BLINKY/
cyprien@linux-dev:~/hk/MBED_BLINKY$ ls
env GettingStarted.htm main.cpp Makefile mbed
2.4 Compilation
Le Makefile indique l'étape suivante :
cyprien@linux-dev:~/hk/MBED_BLINKY$ make
arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb -c -x assembler-with-cpp -o
mbed/targets/cmsis/TARGET_NXP/TARGET_LPC81X/TOOLCHAIN_GCC_ARM/startup_LPC81X.o
mbed/targets/cmsis/TARGET_NXP/TARGET_LPC81X/TOOLCHAIN_GCC_ARM/startup_LPC81X.S
[ . . . ]
*****
***** You must modify vector checksum value in *.bin and *.hex files.
*****
arm-none-eabi-objcopy -O binary MBED_BLINKY.elf MBED_BLINKY.bin
arm-none-eabi-size MBED_BLINKY.elf
text data bss dec hex filename
3504 192 228 3924 f54 MBED_BLINKY.elf
cyprien@linux-dev:~/hk/MBED_BLINKY$ ls -l MBED_BLINKY.bin
-rwxr-xr-x 1 cyprien dialout 3696 Sep 6 13:54 MBED_BLINKY.bin
Votre premier exemple mbed est prêt à être flashé ! Notez la taille du binaire, 3696 octets, uniquement pour faire clignoter une LED. Il n'y a plus beaucoup d'espace libre pour faire des choses intéressantes...
Le résultat de la compilation nous met en garde qu'il nous faudra changer la somme de contrôle des vecteurs dans le fichier binaire. Au démarrage, la ROM va vérifier si le « vector checksum » est correct. Si incorrect, le code entrera en mode programmation. La plupart des outils permettant la reprogrammation de ces microcontrôleurs calculent ce checksum à la volée.
2.5 Outil de flashage
Mais comment flasher ce binaire si on utilise directement le microcontrôleur, sans l'interface de programmation mbed ? La famille des LPC se programme via le port série en utilisant le code « In-System Programming » en ROM lorsque la pin 5 nISP est à l'état bas lors de la mise sous tension. J'utilise l'outil lpctools de Nathaël Pajani (Techno Innov), disponible en paquet debian, ou sur le dépôt git http://git.techno-innov.fr/?p=lpctools.
Techno-Innov propose les LPC810 à 1€ pièce, mais aussi des LPC812 (20 pins, 4 Ko de RAM et 16 Ko de Flash) et des kits d'initiation à la soudure avec ces processeurs. Le LPC812 en TSSOP20 pourra être soudé sur un adaptateur DIP20 et être utilisé sur une plaque d'expérimentation. Bien sûr, les binaires générés par l'IDE mbed pourront directement être utilisés ! À trouver sur http://boutique.techno-innov.fr/.
3. Mon premier montage
Fig. 1 : Montage correspondant au test MBED_BLINKY. Notez bien que le LPC810 n'accepte que du 3.3V !
Le programme de test fait clignoter une LED connectée au LPC8120. Il s'agit de la LED1, définie comme étant le GPIO P0_2.
Le bouton poussoir ne sert qu'à entrer en mode programmation et peut être remplacé par un fil à ne brancher que lorsque nécessaire. Appuyez sur le bouton ou connectez la pin 5 à la masse pendant que vous connectez l'adaptateur USB série sur votre ordinateur. Vous devez voir dans vos logs l'apparition d'un périphérique ttyUSB0, que vous allez utiliser flasher le LPC810 :
cyprien@linux-dev:~/hk/MBED_BLINKY$ lpcprog -d /dev/ttyUSB0 -c flash MBED_BLINKY.bin
Device session openned.
Part ID 0x00008100 found on line 18
Flash now all blank.
Checksum check OK
Flash size : 4096, trying to flash 15 blocks of 256 bytes : 3840
Writing started, 15 blocks of 256 bytes ...
cyprien@linux-dev:~/hk/MBED_BLINKY$
Si vous obtenez une erreur à cette étape, il est possible que vous ayez loupé le processus de reboot, ou que vous ayez oublié de déconnecter un terminal du port série (oui, ça m'arrive très souvent!). Retentez votre chance.
Si vous avez utilisé un fil pour entrer en mode programmation, c'est le moment de le retirer. Provoquez alors un redémarrage en débranchant et rebranchant l'adaptateur USB, puis observez la LED clignoter. Félicitations !
Mises en garde habituelles :
- la tension d'alimentation du LPC810 est de 3.3V. Le LPC810 n'est pas tolérant au 5V. Il ne faut donc utiliser que des convertisseurs USB série fournissant du 3.3V ;
- seules les pins P0_2 et P0_3 du LPC810 (ainsi que P0_7 et P0_13 sur un LPC812) supportent un courant jusqu'à 20mA (idéal pour les LED), et il faudra se limiter à 4 mA pour les autres pins.
4. mbed et les 4 petits kilos...
Faire clignoter une LED, c'est un peu le Hello World du maker. En mbed, cela donne :
#include "mbed.h"
DigitalOut myled(LED1);
int main() {
while(1) {
myled = 1;
wait(0.2);
myled = 0;
wait(0.2);
}
}
Le code se trouve dans le fichier main.cpp. Ajoutons un petit message dans la boucle, puis compilons à nouveau :
#include "mbed.h"
DigitalOut myled(LED1);
int main() {
while(1) {
puts("Hello World!");
myled = 1;
wait(0.2);
myled = 0;
wait(0.2);
}
}
cyprien@linux-dev:~/hk/MBED_BLINKY$ make
[ . . . ]
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: MBED_BLINKY.elf section `.text' will not fit in region `FLASH'
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: region RAM overflowed with stack
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/bin/ld: region `FLASH' overflowed by 2464 bytes
collect2: error: ld returned 1 exit status
Makefile:64: recipe for target 'MBED_BLINKY.elf' failed
make: *** [MBED_BLINKY.elf] Error 1
Aïe ! Nous venons de déborder de la flash, avec un simple puts(). Nous sommes coincés, et il serait tentant de conclure dès maintenant par « mbed c'est uniquement pour les gros microcontrôleurs ». Pas si vite ! Regardons ce qu'est mbed et ce que l'on peut faire.
On peut énumérer ainsi les différentes couches du SDK mbed :
- les registres d'accès au matériel, c'est le mapping mémoire des périphériques du microcontrôleur, généralement fourni par le vendeur du microcontrôleur ;
- l'implémentation CMSIS-CORE, qui donne accès aux fonctionnalités des cœurs Cortex-M sous une interface générique, fournie par ARM ;
- l'implémentation de la couche d'abstraction mbed, qui fournit l'accès aux périphériques (GPIO, port série, timers...) via une API indépendante du matériel ;
- l'interface et l'implémentation de la mbed API.
Vous l'aurez sans doute remarqué, le code de test utilise la classe C++ DigitalOut. Celle-ci fait partie de la mbed API. Elle utilise en interne la HAL mbed, qui est entièrement écrite en C.
5. Utilisation de la mbed HAL en C
Nous allons nous passer de la mbed ABI en C++ pour n'utiliser que la HAL C. Il faut donc renommer notre fichier main.cpp en main.c, et faire un petit make clean pour se débarrasser des fichiers de dépendances.
Modifions donc le code de notre exemple :
#include "serial_api.h"
#include "gpio_api.h"
#include "wait_api.h"
/* Déclaration des objets GPIO et port série */
gpio_t myled;
serial_t pc;
/* L'interface pour le port série ne propose pas l'équivalent de puts */
static void serial_puts(serial_t *obj, const char *msg)
{
while(*msg) serial_putc(obj, *msg++);
serial_putc(obj, '\n');
}
int main() {
/* Initialisation du port série */
serial_init(&pc, P0_4, P0_0);
serial_baud(&pc, 115200);
/* Initialisation du GPIO de la LED */
gpio_init_out(&myled, LED1);
while(1) {
serial_puts(&pc, "Hello World!");
gpio_write(&myled, 1);
wait_ms(200);
gpio_write(&myled, 0);
wait_ms(200);
}
return 0;
}
Voyons ce qui a changé :
- utilisation de wait_ms(int) plutôt que wait(float). L'utilisation de nombres entiers évite au compilateur d'ajouter le code d'émulation des nombres flottants ;
- utilisation de l'API gpio_api.h pour faire clignoter la LED ;
- utilisation de l'API serial_api.h pour la gestion du port série. Utiliser puts() fait inclure les fonctions de la libc pour la gestion de la sortie standard.
Voyons voir le résultat de la compilation :
cyprien@linux-dev:~/hk/MBED_BLINKY$ make
arm-none-eabi-gcc -mcpu=cortex-m0plus -mthumb -c -x assembler-with-cpp -o
mbed/targets/cmsis/TARGET_NXP/TARGET_LPC81X/TOOLCHAIN_GCC_ARM/startup_LPC81X.o
mbed/targets/cmsis/TARGET_NXP/TARGET_LPC81X/TOOLCHAIN_GCC_ARM/startup_LPC81X.S
[ . . . ]
*****
***** You must modify vector checksum value in *.bin and *.hex files.
*****
arm-none-eabi-objcopy -O binary MBED_BLINKY.elf MBED_BLINKY.bin
arm-none-eabi-size MBED_BLINKY.elf
text data bss dec hex filename
3280 192 256 3728 e90 MBED_BLINKY.elf
cyprien@linux-dev:~/hk/MBED_BLINKY$ ls -l MBED_BLINKY.bin
-rwxr-xr-x 1 cyprien dialout 3472 Sep 6 15:46 MBED_BLINKY.bin
Ça rentre ! Il ne reste plus qu'à rebooter le LPC810 en pensant bien à appuyer sur le bouton pour entrer en mode programmation, réexécuter la commande pour flasher le nouveau code, et rebooter à nouveau. Vous pourrez alors voir sur le port série le nouveau message.
Vous voilà prêts pour explorer la HAL mbed et faire preuve de créativité. Malheureusement, il ne semble pas y avoir de documentation dédiée de ces API C, et il faudra donc se contenter du code source. Vous trouverez les principaux fichiers en-tête C dans mbed/libraries/mbed/hal/ et quelques-uns dans mbed/libraries/mbed/api/.
Références
- MBED pour les développeurs : https://developer.mbed.org/
- NXP LPC800 : http://www.nxp.com/products/microcontrollers/product_series/lpc800/