Premiers pas avec FreeRTOS et ChibiOS

Si les systèmes d’exploitation n’étaient pas si utiles, nous ne les exécuterions pas sur chacun de nos systèmes de bureau. Dans le même ordre d’idées, les systèmes d’exploitation embarqués offrent des fonctionnalités similaires à celles de ces OS de bureau, tout en ciblant un marché plus spécialisé. Certaines d’entre elles sont des versions adaptées d’OS de bureau (par exemple Yocto Linux), tandis que d’autres sont construites à partir de zéro pour des applications embarquées, comme VxWorks et QNX. Cependant, peu de ces systèmes d’exploitation peuvent fonctionner sur un microcontrôleur (MCU). Lorsque vous devez exécuter un système d’exploitation sur quelque chose comme un AVR 8 bits ou un microcontrôleur Cortex-M 32 bits, vous avez besoin de quelque chose de plus petit.

Quelque chose comme ChibiOS («Chibi» signifiant «petit» en japonais), ou FreeRTOS (ici pas de points pour l’originalité). Peut-être plus précisément, FreeRTOS pourrait être résumé comme un cadre multi-threading ciblant les systèmes à faible puissance, tandis que ChibiOS est plus un système d’exploitation complet, comprenant une couche d’abstraction matérielle (HAL) et d’autres subtilités.

Dans cet article, nous examinerons plus en détail ces deux systèmes d’exploitation, pour voir quels avantages ils apportent.

Ensemble de fonctionnalités de base

FreeRTOS prend en charge quelques dizaines de plates-formes de microcontrôleurs, les plus remarquables étant probablement AVR, x86 et ARM (Cortex-M & Cortex-A). En revanche, ChibiOS / RT fonctionne sur peut-être moins de plates-formes, mais est livré avec un HAL qui supprime les périphériques matériels, y compris les périphériques I2C, CAN, ADC, RTC, SPI et USB. Les deux offrent un planificateur multi-tâches préemptif avec des niveaux de priorité et des primitives multi-threading, y compris des mutex, des variables de condition et des sémaphores.

Le corollaire de cette comparaison semble alors être que FreeRTOS est bon pour les fonctionnalités multi-threading de base, alors que ChibiOS / RT propose une approche plus holistique à travers son HAL. La présence du HAL signifie également que l’on peut théoriquement cibler ChibiOS / RT et recompiler le même code pour différentes plates-formes MCU. Pour FreeRTOS, il faudrait encore utiliser un autre framework pour utiliser des périphériques matériels, que ce soit CMSIS, ST’s HAL ou autre chose, et cela diminue la portabilité.

Dans les sections suivantes, nous allons travailler sur un exemple de base pour chacun de ces deux systèmes d’exploitation, afin de mieux comprendre à quoi ressemble le développement avec eux.

FreeRTOS avec CMSIS-RTOS

Une page HTML, servie à partir d’un MCU STM32F746ZG.

Pour un exemple simple de la façon de travailler avec FreeRTOS, l’exemple de serveur HTTP de ST pour la carte de développement Nucleo-746ZG STM32 est un bon début. J’ai également créé une version autonome avec toutes les dépendances et un Makefile à utiliser avec la chaîne d’outils Arm Cortex-M GCC.

Cet exemple de projet montre comment combiner l’API CMSIS-RTOS, FreeRTOS et la pile réseau LwIP pour créer un serveur HTTP basé sur Netconn qui peut servir des documents et des images. L’API Netconn de LwIP est une API de plus haut niveau que l’API brute, ce qui en fait le choix préféré si l’on n’a pas de besoins particuliers en matière de mise en réseau.

Le point d’entrée du projet de démonstration se trouve dans Core/Src/Main.cpp. Son but est principalement de mettre en place le firmware: configurer les périphériques et les horloges, puis initialiser les premiers threads. Ici, nous ne voyons pas la syntaxe des threads FreeRTOS (tâches) utilisés, mais celle de CMSIS-RTOS, par exemple:


osThreadDef(Start, StartThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE * 5);
osThreadCreate (osThread(Start), NULL);

static void StartThread(void const * argument)
{
    /* .. */ 
}

CMSIS-RTOS fait partie du Norme d’interface logicielle du microcontrôleur Cortex, ou CMSIS pour faire court. Il s’agit d’une couche d’abstraction matérielle (HAL) indépendante du fournisseur pour les microcontrôleurs Arm Cortex. Dans le cas des RTOS intégrés, CMSIS fournit la spécification CMSIS-RTOS, qui permet d’écrire un logiciel pour une API RTOS générique et donc de le rendre portable à travers Cortex-M (et Cortex-A). Chaque RTOS pris en charge fournit ensuite une implémentation CMSIS-RTOS qui mappe les deux ensembles d’appels d’API.

Dans cet exemple, nous utilisons l’API CMSIS-RTOS v1 plus basique avec FreeRTOS. Pour les microcontrôleurs plus récents avec prise en charge ARMv8 ainsi que multi-core et Cortex-A, l’interface RTOS v2 est une meilleure correspondance. L’interface RTOS v2 est également prise en charge par FreeRTOS, et les fichiers nécessaires pour cela se trouvent sous Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2, à côté du dossier contenant les fichiers pour RTOS v1.

Dans l’extrait de code précédent, nous avons vu comment un nouveau thread est créé. Au moment où nous avons créé le thread de démarrage, il n’y avait cependant pas encore de planificateur en cours d’exécution. Nous commençons cela par un appel à osKernelStart(). Après cela, le code dans le startThread la fonction est planifiée et exécutée. Sans surprise, cette fonction démarre les threads principaux qui formeront notre serveur HTTP:


static void StartThread(void const * argument)
{ 
  /* Create tcp_ip stack thread */
  tcpip_init(NULL, NULL);
  
  /* Initialize the LwIP stack */
  Netif_Config();
  
  /* Initialize webserver demo */
  http_server_netconn_init();
  
  /* Notify user about the network interface config */
  User_notification(&gnetif);
  
#ifdef USE_DHCP
  /* Start DHCPClient */
  osThreadDef(DHCP, DHCP_thread, osPriorityBelowNormal, 0, configMINIMAL_STACK_SIZE * 2);
  osThreadCreate (osThread(DHCP), &gnetif);
#endif

  for( ;; )
  {
    /* Delete the Init Thread */ 
    osThreadTerminate(NULL);
  }
}

Nous appelons d’abord tcpip_init(), qui crée le thread de traitement TCP / IP LwIP (tcpip_thread). Netif_Config() est la fonction de configuration de l’interface réseau dans notre code. Il appelle les fonctions LwIP NETIF netif_add() et netif_default() pour ajouter et définir notre nouvelle interface réseau par défaut. Avec cela, nous avons LwIP entièrement

Le http_server_netconn_init() fonction se trouve dans httpserver-netconn.c. Il crée un nouveau thread appelé HTTP, qui exécute le code dans http_server_netconn_thread. Cela configure le socket serveur sur le port 80 et attend les nouvelles connexions entrantes. Ceux-ci sont ensuite traités par le http_server_serve() , qui est un simple bloc if / else pour analyser les requêtes HTTP et servir soit le contenu du fichier statique (codé en dur dans des tableaux d’octets), soit afficher les informations dynamiques (pour un /STM32F7xxTASKS.html request) généré par DynWebPage():


void DynWebPage(struct netconn *conn)
{
  portCHAR PAGE_BODY[512];
  portCHAR pagehits[10] = {0};

  memset(PAGE_BODY, 0,512);

  /* Update the hit count */
  nPageHits++;
  sprintf(pagehits, "%d", (int)nPageHits);
  strcat(PAGE_BODY, pagehits);
  strcat((char *)PAGE_BODY, "


<pre>
Name          State  Priority  Stack   Num" );
  strcat((char *)PAGE_BODY, "
---------------------------------------------
");
    
  /* The list of tasks and their status */
  osThreadList((unsigned char *)(PAGE_BODY + strlen(PAGE_BODY)));
  strcat((char *)PAGE_BODY, "

---------------------------------------------");
  strcat((char *)PAGE_BODY, "
B : Blocked, R : Ready, D : Deleted, S : Suspended
");

  /* Send the dynamically generated page */
  netconn_write(conn, PAGE_START, strlen((char*)PAGE_START), NETCONN_COPY);
  netconn_write(conn, PAGE_BODY, strlen(PAGE_BODY), NETCONN_COPY);
}

La partie intéressante de cette fonction est qu’elle donne un aperçu des threads actifs, obtenus à partir d’un appel à osThreadList(). Bien que n’étant pas une partie officielle de l’API CMSIS-RTOS v1, il fournit des fonctionnalités utiles. Cela montre cependant que bien que le CMSIS-RTOS HAL soit utile, il est imparfait et peut ne pas couvrir par défaut des cas d’utilisation plus exotiques, ou échouer à exposer les API du système d’exploitation sous-jacent.

Une carte de développement Nucleo-F746ZG.

Cela mis à part pour l’instant, le reste de la StartThread() la fonction réserve peu de surprises: la User_notification() fonction (trouvée dans app_ethernet.c) règle les voyants de la carte de développement Nucleo pour indiquer l’état de la connexion. Si nous avons activé la prise en charge DHCP, un thread est également créé pour cela, en utilisant DHCP_thread() à partir de ce même fichier source. Le thread DHCP essaie d’obtenir une adresse IP en utilisant la fonctionnalité DHCP dans LwIP et définissez ceci pour l’interface que nous avons créée précédemment.

À ce stade, nous pouvons compiler le projet. En supposant que nous ayons obtenu le bras-aucun-eabi Chaîne d’outils GCC via la page de téléchargement Arm ou via le gestionnaire de packages de notre système d’exploitation afin que le compilateur soit sur le chemin du système, la compilation du projet basé sur Makefile peut être effectuée avec un simple appel à make. Le flashage sur une carte de développement Nucleo-746ZG nécessite l’installation d’OpenOCD, après quoi un simple make flash avec la carte connectée suffit.

Chibi: Peut-être pas si petit

Développer avec ChibiStudio.

Comme évoqué précédemment, ChibiOS est (ironiquement) beaucoup plus grand que FreeRTOS en termes de fonctionnalités. Cela devient également évident lorsqu’il s’agit de simplement démarrer avec un nouveau projet ChibiOS. Alors que FreeRTOS, comme nous l’avons vu précédemment, peut confortablement être juste le RTOS dans un HAL comme CMSIS-RTOS, ChibiOS a beaucoup de fonctionnalités qui ne sont pas couvertes par cette API. Pour cette raison, le projet ChibiOS a son propre IDE (basé sur Eclipse) sous la forme de ChibiStudio, qui est livré avec des projets de démonstration préinstallés.

Sur le site Play Embedded, un grand nombre de tutoriels et d’articles sur ChibiOS peuvent être trouvés, comme cet article d’introduction, qui couvre également la prise en main de ChibiStudio. La complexité de ChibiOS apparaît également dans les fichiers de configuration, qui comprennent:

  • chconf.h, pour configurer les options du noyau.
  • halconf.h, pour configurer le HAL.
  • mcuconf.h, contenant des informations relatives à la MCU spécifique ciblée.

L’exemple de projet ‘Blinky’ fourni avec le package de téléchargement ChibiOS pour le MCU STM32F042K6 (tel que trouvé sur la carte ST Nucleo-F042K6) donne un aperçu assez solide de ce à quoi ressemble un projet ChibiOS. A noter ici l’utilisation du module ChibiOS / HAL, qui permet d’utiliser le périphérique UART2, en utilisant le pilote série de ChibiOS.

Le reciblage du code vers un autre MCU devrait être une question de mise à jour des fichiers de configuration et de recompilation, bien que l’on ait l’impression que cela est censé être fait via l’EDI, et pas tellement à la main. L’intégration avec d’autres IDE ne semble pas non plus être une chose, d’un regard superficiel. Cela signifierait probablement devenir très à l’aise avec la documentation générée par Doxygen et d’autres informations disponibles.

Dans le même temps, ChibiOS prend en charge CMSIS-RTOS et propose également deux noyaux différents: le noyau RT (temps réel) et NIL, qui essaie simplement d’être le plus petit possible en termes de taille de code. Ce compromis ne semble pas trop affecter les performances si l’on en croit leurs benchmarks, ce qui en fait une option intéressante pour un nouveau projet de système d’exploitation intégré (RT).

Emballer

Dans cet article, nous avons examiné certaines des choses que l’on rencontrera lors de la décision de développer en utilisant FreeRTOS ou ChibiOS. Bien que les deux aient leurs points forts et leurs points faibles, le principal point à retenir est qu’ils sont deux bêtes très différentes en fin de compte. Tant dans les fonctionnalités qu’ils fournissent, que dans les besoins qu’ils ciblent.

Si l’on utilise déjà CMSIS, le positionnement dans FreeRTOS est simple et direct, ce qui permet d’utiliser un autre code de ciblage CMSIS avec peu ou pas de changements. ChibiOS d’autre part est plus sa propre chose, ce qui n’est pas nécessairement un négatif. Il est peut-être plus utile de considérer FreeRTOS comme un module utile que l’on peut boulonner à CMSIS et à d’autres frameworks pour ajouter un support multi-threading, alors que ChibiOS s’apparente davantage à NuttX et Zephyr, en tant que solution unique pour tous vos besoins.

François Zipponi
Je suis François Zipponi, éditorialiste pour le site 10-raisons.fr. J'ai commencé ma carrière de journaliste en 2004, et j'ai travaillé pour plusieurs médias français, dont le Monde et Libération. En 2016, j'ai rejoint 10-raisons.fr, un site innovant proposant des articles sous la forme « 10 raisons de... ». En tant qu'éditorialiste, je me suis engagé à fournir un contenu original et pertinent, abordant des sujets variés tels que la politique, l'économie, les sciences, l'histoire, etc. Je m'efforce de toujours traiter les sujets de façon objective et impartiale. Mes articles sont régulièrement partagés sur les réseaux sociaux et j'interviens dans des conférences et des tables rondes autour des thèmes abordés sur 10-raisons.fr.