Les Groupes ARM : STM32/toolchain sous Linux
Sommaire
Objectif
Il existe de nombreuses combinaisons d'outils pour développer sur des STM32. La méthode présentée ici n'est qu'une solution parmi d'autres. Les outils logiciels sont tous gratuits et la plupart est libre. L'outil matériel pour programmer (in-circuit debugger) coûte environ 12€.
Cette page explique comment programmer et débugger les STM32 sous Linux avec les outils suivants :
- Configurateur graphique STM32CubeMX
- Compilateur GNU GCC + débugger GNU GDB
- IDE Qt Creator
- OpenOCD, the Open On-Chip Debugger
- In-circuit debugger ST-Link V2 (par exemple celui d'une carte de démo Nucleo-64 à 10€ env.)
Cette méthode permet de compiler, programmer et débugger le code (points d'arrêt, valeur des variables, etc.). Une fois configurée, le confort d'utilisation est excellent, a fortiori si on est familier de Qt Creator.
Installer la toolchain
STM32CubeMX
STM32CubeMX est un logiciel pour générer le code d'initialisation : horloges, timers, interruptions, périphériques (I2C, SPI, etc.)...
Télécharger sur https://my.st.com (créer un compte). Extraire l'archive et lancer le programme d'
$ sudo ./SetupSTM32CubeMX-<version>.linux
Le répertoire d'installation par défaut est /usr/local/. Créer un alias (modifier PATH ne marche pas), par exemple dans ~/.bashrc ou dans ~/.bash_aliases :
alias stm32cubemx='/usr/local/STMicroelectronics/STM32Cube/STM32CubeMX/STM32CubeMX'
Menu Help > Install new libraries et choisir celles qui correspondent au composant sur lequel on développe.
OpenOCD
C'est l'Open On-Chip Debugger. Il permet de programmer le MCU et de débugger (points d'arrêt, valeur des variables), en lien avec gdb.
La version de la distribution Linux est peut-être trop ancienne pour supporter les STM32. Dans ce cas, il faut télécharger le code source et le compiler.
Installer le paquet pkg-config :
$ apt-get install pkg-config
Télécharger et sélectionner la dernière version stable :
$ cd ~/opt $ git clone git://git.code.sf.net/p/openocd/code openocd $ cd openocd $ git tag $ git checkout v0.10.0
Compiler :
$ ./bootstrap $ ./configure --enable-stlink --disable-werror $ make -j4 $ sudo make install $ which openocd /usr/local/bin/openocd
L'option --disable-werror est nécessaire en cas d'erreur de compilation avec gcc v7+.
Mettre à jour :
$ cd ~/opt/openocd $ git pull $ git tag $ git checkout <tag_de_la_nouvelle_version_stable> $ make clean
Puis refaire l'étape "compiler".
Pour tester, connecter soit une carte Nucleo avec les jumpers ST-Link installés, soit une carte Nucleo avec les jumpers ST-Link enlevés et connectée à une carte maison via le connecteur CN4 (SWD). Ctrl+C pour quitter).
$ sudo openocd -s /usr/local/share/openocd/scripts/target/ -f interface/stlink-v2-1.cfg -c 'transport select hla_swd' -f target/stm32l4x.cfg Open On-Chip Debugger 0.10.0 (2017-11-07-11:17) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html hla_swd Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD adapter speed: 500 kHz adapter_nsrst_delay: 100 none separate Info : Unable to match requested speed 500 kHz, using 480 kHz Info : Unable to match requested speed 500 kHz, using 480 kHz Info : clock speed 480 kHz Info : STLINK v2 JTAG v28 API v2 SWIM v17 VID 0x0483 PID 0x374B Info : using stlink api v2 Info : Target voltage: 3.240156 Info : stm32l4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Créer /usr/local/bin/openocd-stm32l4x pour lancer cette ligne de commande :
#!/bin/bash # Synopsis: start OpenOCD with the options required to connect to a STM32L4 # with ST-Link V2.1 (on Nucleo boards). if [ $USER != "root" ]; then echo "This script must be run as 'root'." exit 1 fi openocd -s /usr/local/share/openocd/scripts/target/ -f interface/stlink-v2-1.cfg -c 'transport select hla_swd' -f target/stm32l4x.cfg
Donner les permissions d'exécution au script :
$ sudo chmod a+x /usr/local/bin/openocd-stm32l4x
gcc
Visiter https://launchpad.net/gcc-arm-embedded et sous "Series and milestones", choisir la version sur le graphe des branches de développement. Si les fichiers ne sont pas disponibles, un lien indique où les trouver (https://developer.arm.com/open-source/gnu-toolchain/gnu-rm). gdb fourni avec cette distribution a le support Python, requis par Qt Creator. Extraire l'archive :
$ sudo tar -xf gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2 -C /usr/local/
Qt Creator
Plugins
Le menu Help > About plugins affiche une boîte de dialogue. Vérifier que les plugins suivants sont installés :
- QbsProjectManager
- BareMetal
Configuration
Dans le menu Tools > Options > Devices > Bare metal, créer un nouveau "GDB server provider" :
Paramètre | Valeur |
---|---|
Name | OpenOCD |
Startup mode | No startup |
Host | localhost:3333 |
Init commands | set remote hardware-breakpoint-limit 6 set remote hardware-watchpoint-limit 4 |
Reset commands | monitor reset halt |
Dans le menu Tools > Options > Devices > Devices, créer un device avec le GDB server provider définit plus haut.
Créer un kit avec le compilateur, le debugger, le device bare metal.
ST-Link
Pour programmer une carte maison avec le ST-Link d'une carte Nucleo64, souder R9 (boîtier 0603, de 1K à 47K) et dessouder SB12 (jumper NRST, face de dessous). Enlever les 2 jumpers sur CN2, et connecter la carte maison à CN4 (SWD).
Utilisation
Support du MCU
Pour choisir les options du compilateur/linker, voir dans le répertoire d'installation de GCC, le tableau dans /share/doc/gcc-arm-none-eabi/readme.txt
, sous "Architecture options usage".
Par exemple, pour un Cortex-M4 avec une FPU (floating point processing unit), les options de GCC sont :
- soft FP :
-mthumb -mcpu=cortex-m4 -mfloat-abi=softfp -mfpu=fpv4-sp-d16
- hard FP :
-mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
Les STM32L4 ont une FPU matérielle. La deuxième ligne est donc à privilégier.
Important : ces options doivent être passées à la compilation ET à l'édition de liens. Pour cela, LD ne doit pas être appelé directement, mais via le driver de GCC.
Générer le code d'initialisation
STM32CubeMX permet de générer le code d'initialisation du MCU :
- fonction attribuée à chaque broche
- configuration des horloges
- configuration des interruptions
- configuration des middlewares (USB, CAN, etc.)
STM32CubeMX a un gestionnaire pour télécharger les librairies ST. Il existe 2 types de librairies : HAL, de haut niveau, et LL (low-level) de plus bas niveau. La première est plus simple d'utilisation, mais est moins efficace que la seconde.
STM32CubeMX propose de faire un lien vers les libraries, ou de les copier dans le projet. La 2ème solution permet une maintenance plus facile du projet sur le long terme.
Une fois le projet configuré, il faut générer le code. Choisir SW4STM32 pour Toolchain/IDE et cocher la case Generate under root.
Le code généré contient des balises (à ne surtout pas toucher !) pour insérer le code utilisateur. Par exemple, pour faire clignoter la led LD2 de la Nucleo-64 avec une période de 1 s :
int main(void)
{
/* USER CODE BEGIN 1 */
HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
HAL_Delay(500); // ms
/* USER CODE END 1 */
Projet Qbs
Dans Qt Creator, créer un nouveau projet (non-Qt application, C application). Sélectionner le kit défini précédemment, et cliquer sur Configure project. Ajouter le fichier Qbs ci-dessous au projet.
Qbs (prononcer "cubes") est un outil fourni avec Qt Creator pour simplifier la compilation et épargner l'écriture de makefiles. Il est appelé à disparaître car il sera remplacé par CMake, qui est un standard libre. En attendant, voici un exemple de fichier Qbs à adapter.
import qbs
CppApplication {
consoleApplication: true
property string family: "STM32L4xx"
property string linkerScript: "STM32L476RGTx_FLASH.ld"
cpp.positionIndependentCode: false // make sure option -fPIC is not passed to GCC
// Make sure to call the linker through GCC driver :
cpp.linkerMode: "manual" // default : "automatic"
cpp.linkerName: "gcc" // this string is appended to "<full_path_to_toolchain>/arm-none-eabi-"
// Define some symbols (GCC -D flag)
cpp.defines: [
"USE_HAL_DRIVER",
"STM32L476xx",
"__weak=__attribute__((weak))",
"__packed=__attribute__((__packed__))"
]
// Options for compilation AND linking.
cpp.driverFlags: [
// CPU
"-mcpu=cortex-m4",
"-mthumb",
"-mfpu=fpv4-sp-d16",
"-mfloat-abi=hard",
"-specs=nano.specs", // use smaller libc
]
// Compiler flags for all langages (C, C++).
cpp.commonCompilerFlags: [
// Optimizations
// "-Os",
// "-O0",
"-Og",
"-Wall",
"-fdata-sections",
"-ffunction-sections",
// For debug
"-g",
"-gdwarf-2",
"-c", // don't run the linker
// "-v" // print a whole lot of details
]
// Linker flag only i.e. understood by LD !!!
// Qbs will prepend all these flags with "-Wl," so that GCC transfers them to LD.
// Other options required for linking should be set in the driverFlags section.
cpp.linkerFlags: [
"-T"+path+"/"+linkerScript,
"-static",
"--verbose", // displays library search
"-lc",
"-lm",
"-lnosys",
"-Map="+buildDirectory+"/memory.map", // file created at the end of the link step
"--cref", // map file formatting
"--gc-sections", // fixes "undefined reference to _exit" error
]
// Include directories.
cpp.includePaths: [
"Inc",
"Drivers/CMSIS/Include",
"Drivers/CMSIS/Device/ST/"+family+"/Include",
"Drivers/STM32L4xx_HAL_Driver/Inc",
"Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc",
"Middlewares/ST/STM32_USB_Device_Library/Core/Inc/"
]
// Source files.
files: [
"Inc/*.h",
linkerScript,
"Src/*.c",
"Drivers/CMSIS/Device/ST/"+family+"/Include/*.h",
"startup/*.s",
"Drivers/CMSIS/Device/ST/"+family+"/Source/Templates/*.c",
"Drivers/CMSIS/Include/*.h",
"Drivers/"+family+"_HAL_Driver/Inc/*.h",
"Drivers/"+family+"_HAL_Driver/Src/*.c",
"Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/*.c",
"Middlewares/ST/STM32_USB_Device_Library/Core/Src/*.c"
]
Properties {
condition: qbs.buildVariant === "debug"
cpp.defines: outer.concat(["DEBUG=1"])
}
Group { // Properties for the produced executable
fileTagsFilter: product.type
qbs.install: true
}
}
Programmer le STM32
- Connecter la carte Nucleo-64 (avec le ST-Link V2) au PC (USB).
- Dans un terminal, lancer OpenOCD via le script écrit plus haut en tant que root.
- Dans Qt Creator, taper Ctrl+R (run) pour compiler, programmer, et exécuter le code sur la cible.
Et voilà !
Erreurs connues
object_file uses VFP register arguments, target does not
Incompatibilité de FPU entre le code utilisateur et les libraries liées (-mfloat-abi=hard vs. -mfloat-abi=softfp).
Le programme compile mais bloque dans un error handler.
Vérifier que LD n'est pas appelé directement, mais via le driver de GCC. Vérifier que la ligne de commande de GCC au moment de la compilation ET au moment de l'édition de liens contient bien les options qui définissent le MCU et la libc : -mcpu=cortex-m4, -mthumb, -mfpu=fpv4-sp-d16, -mfloat-abi=hard, -specs=nano.specs.