Linux Embedded

Le blog des technologies libres et embarquées

Framework de build pour STM32

Introduction


L'objectif de cet article est la présentation d'un environnement de programmation open source pour les microcontrôleurs de type STM32. L'environnement se base sur les outils de GNU/Linux en ligne de commande et apporte une alternative aux outils proposés par ST-Microelectronics (dont ST-CubeIDE).

Le dépôt du framework est disponible sur Github : https://github.com/Openwide-Ingenierie/generic-microcontroller-project.git.

1. Présentation Générale


L'environnement de programmation (aussi appelé framework de build) regroupe un certain nombre d'éléments à la fois matériel et logiciel. La cible est un microcontrôleur de type STM32 qui est programmable sur une carte d'évaluation de type Nucléo. Pour illustrer le fonctionnement du framework, l'exemple le plus simple sera utilisé : le clignotement d'une LED. Mais il est d'abord important de connaître la structure du logiciel embarqué qui sera chargé dans le microcontrôleur.

1.1. Structure d'un logiciel embarqué

Le logiciel embarqué respecte une architecture où différentes couches d'abstraction se superposent :

  • Hardware : la carte d'évaluation qui contient le microcontrôleur.
  • HAL (Hardware Abstraction Layer) : contrôler et piloter les registres bas niveau (niveau microcontrôleur).
  • BSP (Board Support Package) : gérer les composants périphériques (niveau carte d’évaluation).
  • Third-Party : FreeRTOS dans notre cas qui est un système d'exploitation pour l'embarqué.
  • Application Layer : le code applicatif.
UM1061: Description of STM32F2xx Standard Peripheral Library
UM1061: Description of STM32F2xx Standard Peripheral Library

 

Pour avoir plus d'informations sur la carte d'évaluation et le microcontrôleur, des documents précieux pour le développeur sont disponibles comme :

  • Datasheets : décrit le matériel et les liaisons physiques entre les éléments du microcontrôleur et la carte d'évaluation.
  • Reference Manual :
    • Proposé par ST : décrit les registres bas niveau pour contrôler les périphériques et les mémoires du microcontrôleur.
    • Proposé par ARM : décrit le langage assembleur interprétable par le processeur.

1.2. Carte d'évaluation : Nucléo-F446RE

Cette carte permet de faire du prototypage rapide en intégrant différents éléments électroniques comme :

  • Le microcontrôleur cible (U5)
  • Un ST-Link (partie supérieure de la carte et comportant un microcontrôleur U2)
  • 2 Boutons-poussoirs (B1 et B2 dont un programmable)
  • 3 Leds (LD1, LD2 et LD3) => LD2 est la LED programmable.
  • Des connecteurs permettant d'insérer facilement des périphériques externes (CN5-10)

Ces spécifications sont décrites dans le User Manual spécifique à la carte d'évaluation : UM1724.

Lien pour trouver la carte d'évaluation sur le site du constructeur :

https://www.st.com/en/evaluation-tools/nucleo-f446re.html

UM1724: User Manual Nucleo-board
UM1724: User Manual Nucleo-Board

1.3. Microcontrôleur : STM32F446RE

Notre logiciel embarqué sera stocké et exécuté par le microcontrôleur.

https://www.st.com/en/microcontrollers-microprocessors/stm32f446re.html#overview
https://www.st.com/en/microcontrollers-microprocessors/stm32f446re.html#overview

 

Le microcontrôleur comprend plusieurs éléments qui le caractérisent (il en existe de nombreuses autres versions) :

  • Un processeur de type ARM Cortex-M4 avec 512kB de mémoire Flash (ROM) et 128 kB de mémoire SRAM (RAM).
  • Des mémoires externes de type SDRAM.
  • Des protocoles de debug : JTAG et SWD.
  • Une RTC (Real Time Clock) pour stocker la date et l'heure.
  • Des horloges externes.
  • Des périphériques de communication : UART/SPI/I2C/CAN.
  • Des entrées et sorties digitales et analogiques (GPIO).

Remarque : Notre LED sera pilotée par l'accès à sa GPIO.

1.4. HAL : Libopencm3

Comme vu précédemment, la HAL est une couche bas-niveau qui permet une utilisation des périphériques du microcontrôleur sans passer directement par la manipulation des registres.

Libopencm3 est une HAL open source, libre et sous licence LGPLv3. Elle est encore en cours de développement mais écrite de zéro à partir des documents constructeurs (Datasheets et Reference Manual de ARM et de ST). Elle succède à un ancien projet baptisé “libopenstm32” (comme son nom l’indique c'était une HAL libre pour les microcontrôleurs de ST) mais en élargissant son champ aux autres microcontrôleurs possédant un processeur du type ARM Cortex-M3 (les Atmel SAM3U par exemple) et même certains microcontrôleur possédant un processeur M0, M4 (comme le STM32F446RE).

Elle a aussi été conçue pour utiliser les outils GNU/Linux tels que le compilateur GCC pour ARM et les outils de flashage de la carte (OpenOCD).

1.5. OS embarqué : FreeRTOS

Le microcontrôleur peut être programmé en baremetal ou avec un système d'exploitation (OS) pour des systèmes plus complexes. FreeRTOS a été lancé par Richard Barry en 2003 mais le projet est géré par AWS (Amazon Web Service) depuis 2017 environ. C’est un des systèmes d’exploitations les plus utilisés dans le milieu des microcontrôleurs. Il est portable (33 architectures supportées), très léger (entre 4kB et 9kB), versatile et le noyau (en anglais kernel) est sous licence MIT. Des versions commerciales existent avec OpenRTOS et SafeRTOS qui sont basées sur le noyau de FreeRTOS. De nombreuses librairies et supports sont proposés par AWS pour faciliter l’interface du système embarqué avec le cloud d’AWS.

L’OS apporte la possibilité de créer des tâches puis de les ordonnancer en fonction de leurs priorités (si il est préemptif) grâce à l'ordonnanceur (en anglais scheduler) qui fonctionne en “Round-Robin” (une liste d’attente circulaire). Les interruptions ne sont pas gérées par le noyau de l’OS mais des fonctions spécifiques sont présentes pour lui permettre son interfaçage.

L'image ci-dessous permet d'avoir un aperçu du déroulement des tâches dans l'OS en fonction du temps.

FreeRTOS: Scheduler
https://www.embedded.com/freertoss-tick-suppression-saves-power/

2. Configuration du Framework


Le framework dispose d'une configuration de base qui peut être très facilement adaptable et personnalisable. De plus, la HAL ou l’OS peuvent être modifiés avec les règles écrites dans les fichiers de configuration (et les règles Makefile). Pour plus d’informations, il faut se référer au fichier README.md.

2.1. Arborescence du projet

Le projet s'organise en plusieurs dossiers et fichiers. L'application se trouve dans les fichiers blink.c et blink.h mais il est tout à fait imaginable (et même conseillé) de créer un nouveau dossier APP pour contenir les fichiers (sources et headers) lorsque le projet devient plus imposant. Les autres dossiers et fichiers sont pour la configuration du framework. Le fichier Makefile apporte les règles pour automatiser certaines tâches (tout comme le fichier config.mk qui seront présentés plus tard en détail). Les dossiers HAL et OS contiennent également des règles pour interfacer les sources spécifiques (libopencm3 et FreeRTOS dans notre cas).

.
├── blink.c
├── blink.h
├── Makefile
├── config
│   ├── config.mk
│   ├── FreeRTOSConfig.h
│   └── openocd.cfg
├── HAL
│   ├── hal.mk
│   ├── libopencm3
│   └── rules
│       ├── libopencm3.mk
│       └── template.mk
├── OS
│   ├── FreeRTOS
│   ├── os.mk
│   └── rules
│       ├── baremetal.mk
│       ├── freertos.mk
│       └── template.mk
└── README.md

2.2. Configuration de la Toolchain

Le fichier config/config.mk permet de rajouter des règles Makefile adaptées à l’environnement matériel : type de MCU, interface de programmation, etc...

# MCU definitions
MCU_TYPE=stm32
MCU_FAMILLY=f4
MCU_CORE=cortex-m4
MCU_PRECISION=46re

# Flash/debug tool configuration
INTERFACE=stlink-v2-1
TRANSPORT=hla_swd
TARGET=$(MCU_TYPE)$(MCU_FAMILLY)x

# firmware name
FIRMWARE=firmware.elf

# build dir name
BUILD_DIR=build
RELEASE_DIR=$(BUILD_DIR)/release
DEBUG_DIR=$(BUILD_DIR)/debug

#  HAL (libopencm3)
HAL=libopencm3

# OS ("FreeRTOS" or "baremetal")
OS=FreeRTOS

# FreeRTOS config
# heap implementation
FREERTOS_HEAP_IMPLEM=4
# MCU port
FREERTOS_MCU_PORT=GCC/ARM_CM4F
# config file
FREERTOS_CONFIG=config/FreeRTOSConfig.h

# linker script name
LINK_FILE=$(LDSCRIPT)

# openocd config file
OPENOCD_CFG_FILE=config/openocd.cfg

# cross compile prefix
CROSS_COMPILE=arm-none-eabi-

# compilation flags
CC_OPT=-Os
CFLAGS=-W -Wall -Wextra
CFLAGS_DEBUG=-g -gdwarf-4
CC_DEFINES=

# link flags
LD_OPT=-flto
LDFLAGS=-Wl,--print-memory-usage,--gc-sections \
  -nostartfiles -lc -lgcc -lnosys -Xlinker -Map=output.map

# arch specific flags
ARCH_FLAGS=-mcpu=$(MCU_CORE) -mthumb -mfloat-abi=hard -specs=nano.specs

Le compilateur GCC permet d’afficher des informations utiles au développeur lors de la compilation comme par exemple la taille en mémoire avec les flags suivants :

-Wl,--print-memory-usage # Permet d’afficher à la compilation la taille de la mémoire utilisée (RAM et ROM)
Memory region         Used Size  Region Size  %age Used
           ram:       15704 B       128 KB       11.98%
           rom:        7660 B       512 KB        1.46%

Remarque : Avec un code minimal, libopencm3 et certaines fonctionnalités de FreeRTOS, l'empreinte en mémoire est d'environ de 20 kB.

-Map=output.map # Permet de générer le fichier de mapping

De nombreux flags existent pour optimiser le code ou avoir des informations supplémentaires, l’article suivant en mentionne :

2.3. Configuration de Libopencm3

L'étape de configuration de la HAL libopencm3 est effectué de manière automatique par les règles du Makefile comme expliqué précédemment. Cependant pour comprendre comment le framework récupére les bonnes informations, il faut se pencher sur le script de configuration présent dans libopencm3, ce script génère automatiquement le fichier linker spécifique à notre microcontrôleur avec le chargement de la configuration mémoire et du bootloader (generated.stm32f446re.ld).

  • libopencm3/ld/devices.data : une base de données avec les informations sur les microcontrôleurs.
  • libopencm3/linker.ld.S : le script de configuration.
stm32f446?e* stm32f4 ROM=512K RAM=128K

Plus d’informations sur la configuration et le fonctionnement :

https://interrupt.memfault.com/blog/how-to-write-a-bootloader-from-scratch

2.4. Configuration de FreeRTOS

La configuration de l'OS passe également par les règles du Makefile mais aussi par un fichier spécifique à FreeRTOS (config/FreeRTOSConfig.h), il permet de désactiver ou d'activer certains des paramètres propre à l'OS (https://www.freertos.org/a00110.html).

Les éléments importants du noyau de FreeRTOS sont les suivants :

3. Flashage et debug


Une fois la configuration effectuée, les outils logiciels sont mis en place et le développeur peur les utiliser à travers différentes commandes. Néanmoins, il est important de comprendre les liens qui existent entre la carte d'évaluation et le framework et comment ces éléments sont mis en place.

3.1. Automatisation

Le fichier Makefile permet de configurer des règles pour automatiser les actions de flashage, linkage, de debug et de compilation. Il permet aussi de définir l’arborescence du projet pour les fichiers sources et les headers (ici on a seulement les fichiers blink.h et blink.c à la racine du projet).

Les commandes importantes sont les suivantes :

$ make # compiler le projet
$ make flash # flasher le *.elf dans le microcontrôleur
$ make clean
$ make mrproper # nettoyer en profondeur le projet
$ make gdb # ouvrir une session de debug

3.2. STLink / SWD / OpenOCD

Le flashage et le debug du microcontrôleur regroupent des outils matériels et logiciels :

  • STLINK : c’est une sonde qui permet de faire le lien physique entre le pc et le microcontrôleur. Sur la nucleo-board, elle est présente sur la carte et le développeur à seulement besoin de brancher un câble micro-usb sur le connecteur CN1 (un UART est également connecté pour l’échange de données). La sonde supporte plusieurs protocoles pour flasher/debugger le microcontrôleur comme le JTAG ou le SWD par exemple.
  • SWD (Serial Wire Debug) : c'est une interface qui utilise seulement 2 signaux contrairement au JTAG qui en utilise 6. Cependant, le SWD est supporté par les processeurs ARM alors que le JTAG est plus universel dans le domaine des microcontrôleurs.
  • OpenOCD : c’est simplement l’interface logiciel entre les signaux physiques et l'ordinateur.

Pour plus d’informations :

3.3. GDB

Le debug est une étape indispensable dans le développement d’un logiciel, l’opération se fait avec l'outil GDB dans notre cas.

$ make gdb

Au lancement du debugger, le programme commence à la fonction suivante :

reset_handler()

Pour aller à la fonction main :

gdb> break main

gdb> continue

Le tableau suivant propose un récapitulatif des commandes GDB très utiles :

Commandes Arguments Commentaires
q quit   Quitter le débogueur.
c continue   Exécuter le programme jusqu'au prochain point d'arrêt.
b break

function-name

line-number

Mettre un point d'arrêt sur une fonction ou une ligne du programme.
watch watchpoints variable-name Mettre un point d'arrêt sur une variable.
n next   Avancer pas-à-pas sans rentrer dans les sous-fonctions.
s step   avancer pas-à-pas en entrant dans les sous-fonctions.
p print

variable-name

function-name

&variable-name

&function-name

Afficher la valeur d'une variable (& pour l'adresse de la variable).
p/x print/x

variable-name

function-name

&variable-name

&function-name

Afficher la valeur d’une variable en hexadécimal (& pour l’adresse de la variable).
i info

b (or breakpoints)

watch (or watchpoints)

Afficher les points d'arrêts.

bt backtrace  

Afficher la pile d'exécution.

CTRL+L  

Rafraichir la fenêtre de GDB.

CTRL+C  

Arrêter le programme.

Source : http://www.yolinux.com/TUTORIALS/GDB-Commands.html

4. Exemple


Le dépôt Github du framework contient deux fichiers blink.h et blink.c qui permettent de faire clignoter la LED2 présente sur la carte d'évaluation présentée précédemment (Nucléo-F446RE pour rappel).

Le fichier blink.h contient uniquement les déclarations de la GPIO relié à la LED2 :

#pragma once

#include "libopencm3/stm32/rcc.h"
#include "libopencm3/stm32/gpio.h"

#define LED_PORT      (GPIOA)
#define LED_PIN       (GPIO5)
#define LED_PORT_RCC  (RCC_GPIOA)
#define BLINK_DELAY   (200)

Le fichier blink.c contient la tâche FreeRTOS qui changera l'état de la GPIO pour faire clignoter la LED.

#include "libopencm3/stm32/rcc.h"
#include "libopencm3/stm32/gpio.h"
#include "libopencm3/cm3/systick.h"

#include "FreeRTOS.h"
#include "task.h"

#include "blink.h"

// needed by FreeRTOS
uint32_t SystemCoreClock;

static void led_blink_task(void * pvParameters) {
    // avoid warning about unused parameters
    (void)pvParameters;

    while(1) {
        gpio_toggle(LED_PORT, LED_PIN);
	vTaskDelay(pdMS_TO_TICKS(BLINK_DELAY));
    }
}

int main(void) {
    // give the used system clock frequency to FreeRTOS
    SystemCoreClock = rcc_ahb_frequency;

    // systick configuration
    systick_set_frequency(configTICK_RATE_HZ, SystemCoreClock);

    // led port peripheral clock enabling
    rcc_periph_clock_enable(LED_PORT_RCC);
    // led pin configuration (output mode, low speed, push/pull output)
    gpio_mode_setup(LED_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_PIN);

    // blinky task creation
    TaskHandle_t led_bink_task_handle = NULL;
    BaseType_t ret_status;
	ret_status = xTaskCreate( led_blink_task,
                            ( const char * ) "led_blink",
                            configMINIMAL_STACK_SIZE + 200,
                            NULL,
                            tskIDLE_PRIORITY + 1,
                            &led_bink_task_handle );

	// start scheduler
	if(ret_status == pdPASS) {
	   vTaskStartScheduler();
	}

    // should never fall here
    while(1) {};

	return 0;
}

De nombreux exemples existent sur internet (ou livres) pour utiliser les caractéristiques du microcontrôleur et de FreeRTOS :

Conclusion

L'article a permis de présenter le framework de build pour les microcontrôleurs STM32 mais aussi de décrire les étapes préliminaires avant le développement du logiciel embarqué (prise en compte de l'architecture du processeur et du microcontrôleur mais également de la carte d'évaluation). Les outils GNU/Linux tels que Makefile, GCC et GDB ont également été abordés pour prendre en main rapidement et facilement le framework. De nombreux liens internet ont été rajoutées à l'article pour inviter le lecteur à creuser les informations présentées. La structure de ce framework est versatile et personnalisable, il peut donc être modifié pour utiliser d'autres microcontrôleurs, HAL (libopencm3) ou OS (FreeRTOS).

Sources

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.