PC-RM4 / STM32F407VET6

© Pascal Chour - 2019-2022

Introduction

Cette page donne quelques informations sur l'utilisation d'une carte de développement STM32F407VET6 que j'ai utilisé pour réaliser le prototype du chronocomparateur PC-RM4. Si vous êtez arrivé directement sur cette page via le Web, je vous conseille de regarder d'abord la page concernant ce chronocomparateur. A noter que finalement, pour la version définitive avec circuit imprimé, j'ai utilisé une carte DevEBox très proche mais plus facile à implanter sur le circuit.

J'ai initialement créé cette page pour mes propres besoins. Son objectif était de disposer d'un résumé des informations nécessaires pour le développement du chronocomparateur et d'un historique des essais que j'ai pu faire avec les éventuels problèmes ou échecs. Mais je me suis dit que ça pouvait aussi être utile à d'autres, y compris pour réaliser autre chose qu'un chronocomparateur...

Environnement de développement utilisé :

Matériels utilisés

La carte de développement utilisée pour la réalisation du chronocomparateur est une ST32F407VET6 avec un écran interface parallèle, le tout, acheté sur Aliexpress. J'ai également utilisé un ST-LINK V2 également acheté sur Aliexpress pour programmer et contrôler la carte.

Les différents éléments se présentent ainsi :

Carte de développement STM32F407VET6 et écran

stm32F407vet6
Carte STM32F407VET6 avec son écran

La carte peut être fournie avec un écran 3.2 pouces en 320x240 pixels, contrôleur ILI9341 (et XPT2046 pour le TouchScreen), qui se connecte directement sur les broches marquées TFT de la carte de développement. J'ai également acheté un écran plus grand avec une meilleure résolution (320x480) et une interface SPI pour faire d'autres essais.

Voici quelques liens qui m'ont été utiles à propos de cette carte en espérant qu'ils resteront actifs...

Sonde ST-Link V2

st-link V2
Sonde ST-Link V2

Connexion sonde et carte

La connexion de la sonde ST-Link V2 avec la carte se fait de la façon suivante (points jaune sur l'image) :

st-link V2, connexion à la carte de dev
Sonde ST-Link V2

Il n'est pas utile de connecter RST pour ce que j'ai lu (et je ne l'ai pas connecté).

Attention : le branchement tel que décrit implique l'alimentation par la sonde. Dans ce cas, il ne faut pas brancher d'alimentation externe sur la carte de développement (et donc, la carte ne doit pas être reliée via sa prise USB). Si toutefois vous devez le faire (par exemple, parce que l'alimentation fournie par la sonde n'est pas assez puissante), il faut alors déconnecter le 3,3V de la sonde.

Première mise sous tension

Une fois la connexion de la sonde et de la carte de développement faite, et l'écran branché sur la carte de développement , branchez la sonde sur un port USB. Sur ma configuration, j'ai l'affichage suivanté :

MCUDEV STM34F4
STM32F407VET6 system TEST
mcudev.taobao.com
2015/8/15
KEY0:Read sector 0
SD Card Error / Please Check

Comme je n'ai pas mis de SD Card, l'erreur signalée est compréhensible.

ST Link Utility

Vous pouvez vérifier que vous arrivez à communiquer avec votre sond ST-Link et qu'elle reconnait bien la carte de développement. Lancez l'utilitaire STM32 ST-LINK Utility qui est normalement disponible en téléchargement sur le site de ST Microelectronics.

Sélectionnez "connect to the target". Si vous avez une erreur, commencez par débrancher la sonde de son port USB, rebrancher la et sélectionnez de nouveau "connect to the target". Je fais l'hypothèse que la connexion s'effectue correctement (sinon, je ne sais pas).

L'utilitaire affiche les informations suivantes (certaines dépendent de la sonde, de la carte de développement et du moment ou vous faites la connexion).

StLink utility
Sonde ST-Link V2

Vous pouvez profiter que l'utilitaire est lancé pour faire une mise à jour du firmware de la sonde. Pour ce faire, mieux vaut débrancher et rebrancher la sonde de son port USB. Ceci fait, sélectionnez l'option ST-LINK dans le menu puis firmware update et laissez vous guider.

Le STM32F407

Le STM32F407 est un magnifique microcontrôleur, rapide et offrant de nombreuses possibilités. Pour ce qui me concerne, outre la rapidité et un fonctionnement natif en 32bits, j'ai retenu ce processeur car il disposait de deux timers 32 bits dont j'avais besoin pour réaliser les pesures sur PC-RM4.

Evidemment, la documentation est à l'avenant : la version du datasheet que j'ai utilisée ne fait pas moins de 1700 pages.

Ce datasheet ne pertubera pas les personnes qui ont déjà eu à travailler avec des processeurs modernes : il reprend la présentation habituelle des différentes fonctions telles qu'on les trouve, par exemple, chez Texas Instrument (je prends cet exemple car pour ma part, j'ai pas mal développé sur certains processeurs basse consommation de ce fabricant).

Prendre en main un tel processeur en partant de zéro a de quoi rebuter. Heureusement, ST propose quelques outils qui permettent de traiter le problème de base habituel sur ce genre de processeur : les initialisations et en particulier, les initialisation des horloges. Ce point est présenté dans le chapitre suivant.

Il y a néanmoins quelques points noirs qui sont signalées par pas mal de personnes dont une qui a, pendant un temps, répertorié les erreurs, incohérences et difficultés d'utilisation de différentes versions des datasheet, jusqu'à abandonner.

Une des difficultés que j'ai rencontrée a concerné la gestion des timers. Visiblement, je ne suis pas le seul et je trouve la documentation sur ce point peu abordable et très mal faite, sans compter les erreurs.

Pour information, les timers, qui offrent de nombreuses possibilités, disposent de plus de 20 registres qu'il faut comprendre et initialiser.

Dans mon cas, la difficulté a été de faire fonctionner les registres 32 bits (TIM2 et TIM5) en mode dit gated.

Lorsque j'ai développé PC-RM4, je n'ai pas trouvé d'exemples fonctionnels d'utilisation de ce mode. Par contre, j'ai trouvé de nombreuses questions de personnes qui étaient bloquées et qui malheureusement, n'ont pas eu de réponse.

En théorie, le mode gated est d'une utilisation très simple dans la présentation qui en est faite dans le datasheet : le compteur TIM2 (on prendra celui là comme exemple), alimenté par une horloge, commence à compter sur un signal bas TI1 sous réserve que le bit CEN (qui s'appelle aussi parfois CNT_EN ce qui ne simplifie pas les choses) soit actif dans le registre TIM2_CR1. Il s'arrête de compter lorsque TI1 revient à l'état haut.

La documentation indique que le signal TIF (Timer Interrupt Flag) est positionné lorsque le comptage démarre ou s'arrête.

Sur le graphique de la documentation, on voit aussi le bit CEN positionné à l'état inactif en fin de comptage ce qui implique de le remettre actif pour qu'un nouveau comptage puisse démarrer. On ne sait pas à ce stade si le passage du bit CEN à l'état inactif est fait par le processeur ou par le programme. Disons simplement qu'il est probable qu'il soit fait par le processeur du fait du timing des opérations. Mais la documentation ne le précise pas dans ce chapitre.

Les choses se gâtent lorsque si vous utilisez l'outil de configuration STM32CubeMX (ce qui était mons cas) : la configuration générée ne correspond pas à celle indiquée dans la documentation.

Quelques points utiles pas forcéments clairs dans la documentation :

A suivre...

Environnement de développement

STM32Cube

J'ai utilisé l'environnement STM32CubeIDE proposé par ST Microelectronics qui s'appuie sur Eclipse. L'avantage théorique est qu'il est adapté au STM32 et qu'il intègre STM32CubeMX pour configurer le processeur et la carte de développement. A noter cependant que STM32CubeMX peut également s'exécuter en dehors de cet environnement. C'est pratique si vous n'utilisez pas STM32CubeIDE, ce qui a été mon cas dans d'autres développements en 2019 car trop de bugs dans STM32CubeIde.

En 2022, la situation s'est améliorée (version 1.9.0). Globalement, j'ai pu en général développer, compiler et debugger sans plantage pendant des heures d'affilées. Mais j'ai eu aussi quelques gros problèmes qui m'ont fait perdre plusieurs heures :

J'ai lu beaucoup de complaintes a propos de cet environnement et aussi, des bibliothèques HAL. J'ai mieux compris pourquoi... Un peu tard.

Utilisation de HAL ou pas ?

Si vous développez à l'aide de cet environnement, vous aurez immédiatement à faire un choix : utilisation de la bibliothèque HAL (Hardware Abstraction Level) ou pas.

Evidemment, l'utilisation d'une telle bibliothèque est tentante car elle vous permet d'éviter de faire à la main certaines configurations de registres fastidieuses ainsi que certains contrôles de cohérence... Sous réserve que vous preniez en compte les codes d'erreur de la bibliothèque dans votre programme.

Couplée avec l'utilisation de STM32CubeMX, la partie du programme qui configure le matériel selon vos besoins est générée automatiquement. Pour le reste, un grand nombre de macro ou de fonctions utiles sont immédiatement disponibles.

Pourtant, on peut lire de nombreuses critiques sur cette bibliothèque et beaucoup de développeurs en déconseille l'utilisation. Pour ma part, elle m'a fait gagner du temps sur pas mal de sujet et m'en a fait perdre beaucoup dès que quelque chose ne fonctionnait pas comme prévu. Voici ce que je peux en dire à l'usage :

Alors, faut-il utiliser HAL ? Je ne sais pas répondre à cette question car ayant démarré avec cette bibliothèque, j'ai continué avec elle.

Ce que je pense est que l'utilité de HAL varie en fonction de l'expérience que l'on a sur le STM32 et sur la bibliothèque elle même.

Le problème est que la maitrise du STM32 et celle de HAL n'évoluent pas au même rythme.

Premiers essais avec la carte STM32F407VET6

Afin de ne pas démarrer de zéro comme je l'avais fait pour la carte DiyMore que j'utilisais précédemment, j'ai téléchargé l'archive d'une implémentation de la gestion d'écran (BLACK407VE) disponible sur GitHub.

Pour l'installation, il suffit de recopier l'ensemble des fichiers décompressé dans wotre workspace ce qui créé le projet.

Evidemment, tout ne s'est pas passé sans quelques adaptations, à commencer par la configuration des répertoires (à mettre à jour manuellement dans les propriétés du projet).

Le programme n'était pas compilable. Un peu par hasard, je me suis aperçu que le fichier BALCK407VE.ioc n'était pas à jour par rapport à ma version de l'environnement (il suffit d'essayer de l'ouvrir dans l'environnement de développement et s'il n'est pas à jour, l'environnement vous le signale).

Pour mémoire, ce fichier contient toute la configuration du processeur, l'affectation des pins, des horloges, etc. et est généré automatiquement par STM32CubeMX qui est lancé lorsqu'on clique sur BALCK407VE.ioc.

Si vous êtes dans le même cas que moi, l'environnement vous propose de migrer le fichier vers la nouvelle version de STM32CubeMX ce qu'il faut faire. La migration semble mettre également à jour un certain nombre de librairies.

J'ai alors pu aller un peu plus dans la compilation mais j'ai eu des erreurs dans certaines librairies STM32F4xx_HAL. Après quelques recherches, je me suis aperçu qu'elles ne correspondaient pas complètement à celles disponibles sur GitHub (il manquait des #define). J'ai donc téléchargé les fichier .h erronés.

Je ne sais pas trop bien d'où vient ce problème. Peut-être s'agit-il de résidus d'anciennes versions des librairie. Pour mémoire, ma dernière installation de STM32Cube datait de 2019 et peut-être que certaines mises à jours n'ont pas été effectuées correctement lorsque je l'ai relancé en 2022.

Ceci fait, le programme a pu être compilé sans erreurs ni warnings.

Après avoir vérifié que les ports configurés correspondaient bien à ceux sur lesquels était connecté l'écran, j'ai lancé une exécution du programme de test le plus basique (#define USE_LVGL à mettre en commentaire dans main.c). L'environnement m'a proposé une mise à jour du logiciel de dialogue avec la sonde et ceci fait, le programme s'est téléchargé et s'est exécuté sans problème sur la carte de développement (ce qui m'a un peu étonné !).

J'ai ensuite utilisé le programme de tests plus élaboré auquel on accède en définissant #define USE_LVGL dans main.c. Tout a fonctionné correctement (mais cette fois, j'a été moins étonné ! On s'habitue à tout.).

Cette première étape m'a aidé à démarrer et à prendre en main l'environnement mais je n'ai pas conservé grand chose des sources que j'avais récupéré.

Portage de PC-RM3

L'étape suivante a été le portage du programme développé pour PC-RM3.

Evidemment, ça n'a pas été aussi simple que prévu car en pratique, les bibliothèques bas niveau que j'ai récupéré à droite ou à gauche étaient souvent incomplète et assez mal fichues et surtout, je n'ai pas trouvé un gestionnaire de TouchScreenfonctionnel pour le XPT2046. J'ai donc finalement réécrit la plupart des fonctions de bas niveau. J'aborde le point un peu plus loin.

Après quelques jours, j'ai eu une application fonctionnelle avec l'écran 320x240. Comme il me manquait quelques composants électroniques pour aller plus loin, j'ai décidé d'adapter le programme pour qu'il prenne en compte un écran 480x320 comme celuis don dispose PC-RM3, mais cette fois-ci, en utilisant un bus SPI.

Ca m'a pris beaucoup plus de temps que je l'imaginais mais cela m'a permis de trouver quelques bugs dans la version avec écran 320x240 !

Le résultat est que le programme est proposé à la fois avec un écran 320x240 (bien suffisant et plus facile à installer sur la carte de développement) et d'un écran 480x340 avec bus SPI.

Gestion des écrans

Il y a plusieurs points à considérer dans le choix d'un écran pour cette application :

Le contrôleur de TouchScreen XPT2046

Généralités

Si vous avez des difficultés avec la mise en œuvre du contrôleur XPT2046 (TouchScreen), la suite peut vous intéresser.

En préambule, comme sans doute la plupart des gens, j’ai cherché une implémentation sur le Web. J’en ai trouvé pas mal mais…Beaucoup étaient boguées ou ne compilaient même pas, y compris lorsque le contributeur indiquait que c’était la version qui tournait chez lui…

Dans ce cas, il faut partir de la documentation qui dans ce cas, n’est pas toujours d’une très grande clarté.

De tout ça, il résulte le mode opératoire et les conseils suivants :

Gestion de la pression

La valeur de la pression dépend de la position en X et Y sur l'écran. Le problème est que pour connaitre X et Y, il faut faire une moyenne sur plusieurs mesures. Mais pour que ces mesures soient valides, il faut qu'il y ait eut une pression !

On peut s'en sortir de la façon suivante :

lorsqu'il n'y a aucune pression, Z1 vaut 0 ou quelques unités et Z2 est maximale (sur mon écran, environ 2000).

Lorsqu'il y a une pression, la valeur de Z1 augmente et peut valoir quelques dizaines à quelques centaines selon la position en X et Y. La valeur de Z2 diminue (de 1000 a moins de 2000 sur mon écran en fonction de la position).

L'algorithme peut alors être le suivant :

  • Lecture de Z1 et Z2
  • Si Z1 ou (Z2-Z1) sont inférieurs à un certain seuil (Z1_THRESHOLD et Z2_THRESHOLD), alors, on ne prend pas en compte l'appui.
  • Sinon, on détermines des valeurs pour X et Y (moyenne sur plusieurs lectures).
  • On peut ensuite calculer la pression en fonction de X, Y, Z1 et Z2.

Pour les besoins de l'application, je n'ai implémenté que le premier test de seuil qui est suffisant et qui fonctionne très bien.

Calibration X et Y

En préambule, la plupart des implémentations que j'ai vu inversent les codes pour la lecture de X et Y par rapport à la documentation. En pratique, ça n'a pas d'importance car de toute façon, il faut faire un traitement en fonction de l'orientation. Il ne s'agit pas d'une erreur, juste une curiosité que je signale.

La principale difficulté consiste à calibrer le TouchScreen, c'est à dire, à transformer les données brutes reçues en coordonnées valides sur l'écran pour une orientation donnée.

La fonction de calibration que j'ai écrite donne de très bons résultats pour toutes les orientations de l'écran. Elle détermine de facteurs permettant de calculer un x (ou un y) en pixel à partir des données brutes renvoyées par le contrôleur. Toutefois, ce facteur n'est pas suffisant pour une bonne précision car les données brutes renvoyées par le contrôleur ne sont pas linéaires (i.e. un facteur valable pour un x de grande valeur n'est pas très bon pour un x de petite valeur et inversement (idem pour y). J'introduit donc une légère correction qui prend en compte cette non linéarité pour limiter les écarts pour toutes les valeurs de x et y.

Le programme complet, est disponible dans le fichier touch.c (bientôt en téléchargement).

Mémoire non volatile

Il est habituel de conserver des informations de paramétrage dans une mémoire non volatile (qui conserve l'information même si la carte de développement n'est plus alimentée). La carte de développement STM32F407VET6 (et son processeur) propose plusieurs possibilités :

Je n'ai pas exploré comment utiliser les deux premières possibilités. Mais si l'on souhaite éviter l'utilisation d'une batterie, ces deux possibilités sont à considérer. Pour les deux dernières, une batterie est nécessaire pour maintenir la RTC en fonctionnement et le contenu de la SRAM (mémoire statique). La très faible consommation de ces deux "périphériques" (quelques µA) permet une durée de conservation très longue avec une simple petite batterie au lithium.. Comme celle qui est livrée avec la carte de développement.

L'intérêt d'utiliser une de ces deux dernières possibilités est que l'on n'est pas limité par un éventuel vieillissemment des mémoires flash ou EEPROM.

Si vous avez très peu de données à conserver (maximum, 80 octets), l'utilisation de la RTC est à considérer car elle consomme moins que la SRAM.

Dans mon cas, je ne voulais pas trop me limiter et j'ai donc utilisé la SRAM.

On trouve assez facilement sur le net quelques exemples de mise en oeuvre sur lesquels je me suis basé. Heureusement car il semble par ailleurs que la fonctionnalité soit assez mal documentée (disons qu'il faut se palucher le datasheet du STM32F407 et que si on peut trouver directement l'information ailleurs, ce n'est pas plus mal !).

Pour pouvoir utiliser la SRAM, , il faut :

Le programme complet (initialisation, lecture, écriture) qui fait environ 70 lignes, commentaires compris, se trouve dans les fichiers nvmemory.c et nvmemory.h du programme du chronocomparateur.