Vous voici venu au bout de ce cours. Il est temps d’en faire le bilan.
Rapide bilan
Vous avez vu dans les chapitres précédents différents outils et moyens pour programmer en C une cible embarquée basée sur un microcontrôleur. À travers ces activités, vous avez découvert les fondements qui régissent le fonctionnement des périphériques et comment concrètement les programmer sur un STM32F103.
Vous avez aussi certainement compris que chaque microcontrôleur à ses spécificités et que les périphériques ne sont pas strictement identiques d’une architecture à une autre. Par contre, la prise en main d’une nouvelle architecture est grandement facilitée si vous avez déjà une connaissance générale des périphériques et de leurs fonctionnalités. Que ce soient les timers, les interruptions, où des périphériques avec des fonctions plus spécifiques comme l’ADC ou le PWM, leurs principes de fonctionnement restent les mêmes quelle que soit l’architecture. Normalement, avec les connaissances que vous avez acquises, débuter sur un nouveau microcontrôleur ne devrait pas être un énorme problème.
Par contre, si vous avez besoin d’optimiser le fonctionnement d’un système que ce soit en terme de rapidité, de consommation d’énergie, de taille mémoire, ou de traitement dédié, vous n’aurez pas d’autres choix que de vous saisir de la documentation et de comprendre en profondeur les mécanismes spécifiques qui sont offerts par le microcontrôleur. Cela nécessite souvent du temps et une compréhension de votre système qui dépasse le simple cadre de la programmation du microcontrôleur.
Enfin, nous n’avons pas abordé les bonnes pratiques de codage et de structuration de votre code. Cela dépasse largement le cadre de la simple programmation de microcontrôleur et déborde sur le génie logiciel et l’architecture des systèmes logiciels. Regardons quand même quelques éléments pour vous permettre d’aborder cela sereinement.
Généricité, réutilisabilité et décomposition en couches
Vous avez dû vous rendre compte que le même code était souvent réutilisé. Par exemple, la configuration des broches est quelque chose d’incontournable et devoir réécrire la fonction et chercher dans la documentation à chaque configuration peut être fastidieux. Autant faire le travail une bonne fois pour toute et le réutiliser à chaque fois que cela est nécessaire.
Par contre, en écrivant des librairies dédiées à la configuration, il faut prévoir de les structurer. Une bonne manière de faire est, par exemple, de distinguer ce qui relève de la configuration et de l’utilisation d’un périphérique, de ce qui est spécifique à l’application.
Pour comprendre cela, prenons l’exemple utilisé dans le chapitre sur l’ADC. L’application que vous avez réalisée consistait à faire varier l’intensité lumineuse d’une led en fonction de la tension mesurée sur un potentiomètre. Pour ce système, nous avons donc un ensemble de fonctions liées à la configuration des périphériques (configuration des ports, configuration de l’ADC, configuration de la PWM). Ces fonctions peuvent être réutilisées sans problème pour un autre système qui utilise les mêmes périphériques. Par contre, le traitement qui fait le lien entre la mesure de la tension et la durée de l’impulsion de la PWM est propre à l’application et doit être distingué des fonctions de configuration des périphériques. Une manière de structurer son application est de s’appuyer sur des pilotes pour configurer les périphériques, puis sur des pilotes spécifiques pour les services propres au système et enfin l’application elle-même.
Bibliothèques et outils de configuration
Ce besoin de disposer de code générique pour configurer un système n’a évidemment pas échappé aux fondeurs de microcontrôleur. Ainsi ST met à disposition un ensemble de pilotes pour configurer et manipuler les périphériques de leurs microcontrôleurs.
Ces pilotes couvrent la plus grande partie des périphériques disponibles et ont une très grande adaptabilité. Si vous maîtrisez les concepts associés aux différents périphériques, leur utilisation à travers Keil µVision 5 est extrêmement simple.
On trouve, par exemple, parmi ces pilotes, un ensemble de fonctions pour configurer et manipuler les ports. Ce pilote est implémenté dans les fichiers stm32f10x_gpio.c et contient des fonctions telles que GPIO_Init pour configurer le mode des broches ou encore GPIO_WriteBit pour écrire sur une broche en sortie.
Ainsi pour configurer la broche PA.5 en output open-drain et écrire 1 en sortie, on peut directement utiliser ce pilote (ainsi que celui des horloges) et écrire le code
// Structure de configuration d'un port
GPIO_InitTypeDef config;
config.GPIO_Pin = GPIO_Pin_5;
config.GPIO_Speed = GPIO_Speed_2MHz;
config.GPIO_Mode = GPIO_Mode_Out_OD;
// Activation de l'horloge du port
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Initialisation du port
GPIO_Init(GPIOA, &config);
// Ecriture d'un bit sur la broche 5 du port PA
GPIO_WriteBit(GPIOA, GPIO_Pin_5, 1);
Comme vous le voyez, l’utilisation de ces librairies facilite grandement l’écriture et la compréhension du code de l’application. De plus, les pilotes de ST ont été largement testés et débugués, ce qui ne sera pas forcément le cas d’un code que vous écrivez vous-même. La réutilisation n’est pas simplement un gain de temps, mais permet aussi d’avoir du code plus sûr. Par contre, pour avoir une utilisation efficace de ces pilotes, il vaut faut comprendre le fonctionnement des périphériques et de leur programmation.
Un autre projet, baptisé STMCube, a été développé par ST pour faciliter la prise en main des architectures STM32. Ce logiciel propose de configurer à l’aide d’une interface graphique son microcontrôleur et de générer en C le code d’initialisation du microcontrôleur et de ses périphériques. À cela s’ajoute de nouvelles librairies permettant l’appel de fonctions de haut niveau pour interagir avec les composants du microcontrôleur. L’intérêt d’une telle approche est de faciliter la prise en main d’une nouvelle architecture et de masquer complètement à un développeur l’architecture matérielle. Cela apporte un gain de temps conséquent pour le prototypage et le développement d’application, par contre on y perd souvent en performance et maîtrise générale du système. A vous de voir ce que vous recherchez.
Systèmes d’exploitation
À toutes ces librairies s’ajoute aussi la possibilité de déployer un système d’exploitation (SE) pour faciliter le développement d’applications concurrentes. Il ne faut pas croire que les systèmes d’exploitation soient réservés à des architectures matérielles sur-vitaminées, il existe aussi des SE pour les microcontrôleurs de petite puissance.
Par exemple, pour la gamme STM32, ST propose un noyau baptisé RTX avec les principaux mécanismes de gestion de tâches, de synchronisation et de communication. Le déploiement de ce SE est très simple à travers µVision et nécessite très peu de manipulations pour l’utiliser.
En quelques lignes, il est ainsi possible de créer des tâches et de les lancer pour laisser ensuite le SE s’en occuper. Le code suivant montre la création de deux tâches et leur activation.
#include "cmsis_os.h"
// Fonction de traitement de la tâche 1
void function1(void const *arg){
while(1)
{
osDelay (10); // Traitement à réaliser
}
}
// Fonction de traitement de la tâche 2
void function2(void const *arg){
while(1)
{
osDelay (10); // Traitement à réaliser
}
}
// Déclaration des tâches et de leur priorité
osThreadDef(function1, osPriorityAboveNormal, 1, 0);
osThreadDef(function2, osPrioritNormal, 1, 0);
int main (void) {
osKernelInitiliaze();
// Création des tâches
osThreadCreate(osThread(function1), NULL);
osThreadCreate(osThread(function2), NULL);
// Lancement des tâches
osKernelStart();
}
Choisir son SE est important si vous prévoyez de développer une application complexe. En fonction du domaine d’application, vous aurez le choix entre plusieurs SE, chacun ayant été développé pour répondre à des besoins spécifiques comme une meilleure maîtrise de la consommation d’énergie, une gestion du temps des traitements, la possibilité de communiquer via de nombreux médiums, etc. En général, les SE industriels proposent de nombreux portages (plus ou moins faciles à prendre en main) sur les principaux microcontrôleurs du marché. Dans le cas du STM32F103, on trouve par exemple un portage du système d’exploitation temps réel uC/OS de micrium directement intégré dans les paquets de l’outil µVision. Il ne tient qu’à vous de l’utiliser pour vous faciliter vos futurs développements.
Conclusion
Au-delà de la simple compréhension des périphériques et de leur utilisation, il est possible de rapidement entrer dans le monde de la programmation bas niveau pour les architectures pouvant être embarquées. Les outils facilitant cette prise en main sont devenus très accessibles et devraient vous permettre de développer vos propres projets en très peu de temps. N’hésitez pas à piocher dans les librairies existantes et dans les nombreux exemples disponibles, mais n’oubliez pas qu’il faudra toujours à un moment comprendre le fonctionnement sous-jacent si vous voulez vraiment faire quelque chose de performant.