Les Groupes ARM : STM32/toolchain sous Linux

De Wiki_du_Réseau_des_Electroniciens_du_CNRS
Aller à : navigation, rechercher
Accueil - ARM

ArmADEUS - Cortex M3/M4 et le STM32 - Toolchain sous linux pour STM32 - Olinuxino A13-micro - Raspberry Pi - Eukréa - LabVIEW - GCC


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
monitor reset halt
load
monitor reset halt

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

  1. Connecter la carte Nucleo-64 (avec le ST-Link V2) au PC (USB).
  2. Dans un terminal, lancer OpenOCD via le script écrit plus haut en tant que root.
  3. 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.