Contrôleur de servomoteurs FPGA sur plate-forme Armadeus

Magazine
Marque
Open Silicium
Numéro
3
|
Mois de parution
juillet 2011
|
Domaines


Résumé

La robotique expérimentale utilise souvent comme actionneurs des servomoteurs de radiocommande en grand nombre. Leur pilotage demande la génération d’autant de signaux temporellement précis et synchrones.Même si les systèmes embarqués disposent aujourd’hui d’une puissance de calcul impressionnante, elle n’est pas suffisante pour produire des signaux de commandes ayant ces caractéristiques à cause des problèmes de latences du système d’exploitation.Seule une alternative matérielle peut y pallier. Le FPGA est la solution disponible aujourd’hui, polyvalente et facilement accessible.Le projet proposé ici permet de commander jusqu’à 32 servomoteurs de radiocommande avec un système embarqué Armadeus : la puissance d’un ARM9 sous GNU/Linux alliée à celle d’un FPGA.


Body

1. Introduction

Les servomoteurs de radiocommande existent sous la forme actuelle depuis le début des années 70. Il a fallu cependant attendre les années 2000, avec l’avènement de la robotique amateur, pour qu’ils sortent du monde du radio modélisme et deviennent des périphériques informatiques. Ces dispositifs électromécaniques permettent de mettre en mouvement un robot, ils constituent en quelque sorte ses muscles.

Il n’existe pas de contrôleur standard de servomoteurs de radiocommande, car contrairement à d’autres périphériques, comme les disques durs, leur utilisation n’est pas très répandue. Pourtant, la commande de servomoteurs de radiocommande semble assez simple : générer une impulsion d’une largeur comprise entre 0.5 ms et 2.5 ms répétée environ toutes les 20 ms est un jeu d’enfant, ou presque ! Mais lorsque ces signaux doivent être synchrones entre eux, très précis en durée et en plus multiples, cela devient quasiment irréalisable avec un processeur classique et de simples lignes d’entrée/sortie.

La bonne démarche consiste à s’appuyer sur du matériel pour arriver à nos fins. Les timers matériels sont peu nombreux sur un système embarqué et généralement l’un d’entre eux est dédié à l’horloge système. Réaliser une carte contrôleur en logique séquentielle ou avec 32 timers matériels est une conception qui est digne du siècle dernier.

La solution proposée ici est aussi matérielle, mais elle est réalisée grâce à un FPGA dans lequel est programmé le contrôleur de servomoteurs. Cette alternative est à la fois performante, souple et peu onéreuse. Elle permet la commande de 32 servomoteurs de radiocommande avec une synchronisation et une précision parfaites, sans aucune surcharge pour le processeur du système. Le nombre de 32 est arbitraire, car seul le nombre de broches disponibles sur le FPGA constitue la limite, à savoir 62 dans le cas présent. La plate-forme matérielle accueillant ce projet est un système embarqué Armadeus fonctionnant sous GNU/Linux avec à son bord un microprocesseur ARM9 et un FPGA Xilinx Spartan 3. Le projet est complet et aborde de nombreux domaines. Nous commencerons avec la conception d’une interface matérielle permettant la connexion des servomoteurs, puis nous verrons la mise en œuvre du firmware FPGA. Ensuite, l’implantation du pilote de périphérique Linux en mode noyau sera présentée. Nous passerons alors au mode utilisateur avec un serveur de commande réseau pour finir avec une application graphique cliente de ce serveur. L’article est donc dense et éclectique en exposant un système embarqué complet, du fer à souder au clic de souris.

2. Présentation générale du système

2.1 Les servomoteurs de radiocommande et leur principe de commande

Un servomoteur, en terme général, est un système capable d’atteindre et de maintenir une position de consigne malgré les perturbations extérieures. Le maintien de cette position est assuré par une boucle d’asservissement qui mesure la position courante du dispositif et la corrige pour atteindre la position de consigne. Un servomoteur est donc constitué au minimum d’un organe moteur, d’un capteur de position et d’un asservissement. Ce dernier reçoit la valeur de la position de consigne et la valeur de la position courante. Il les compare et génère le signal commande qui convient pour l’organe moteur. Il existe des servomoteurs de toutes tailles, utilisant des technologies variées.

 

Servo

 

Fig. 1 : Servomoteurs de radiocommande d’entrée de gamme utilisés pour les expérimentations.

Nous allons nous intéresser au type de servomoteur qui est certainement le plus accessible, les servomoteurs de radiocommande. Ceux-ci comportent toutes les caractéristiques citées ci-dessus, mais tout y est intégré dans un boîtier, l’alimentation et les signaux de commande sont standardisés. Ils ont été conçus initialement pour le radio modélisme, mais leur usage s’est étendu à la robotique amateur et expérimentale. Ils sont fabriqués en série par de nombreux constructeurs et le prix d’entrée de gamme tourne autour de 15€. Leur organe moteur est un palonnier rotatif qui a un débattement d’environ 180 degrés. Cela rend ce type de matériel particulièrement attractif pour mettre en mouvement, avec un peu d’ingéniosité, à peu près n’importe quel dispositif mécanique et en particulier les petits robots.

Un servomoteur de radiocommande, que nous nommerons maintenant simplement servo, est connecté à son interface de commande via trois fils, deux pour l’alimentation et un pour le signal de commande. L’alimentation est en général comprise entre 4,8V et 6V. Le courant consommé par un servo est variable en fonction du modèle et de l’effort qu’il doit fournir. Mais en cas de blocage, lorsque le servo lutte pour atteindre sa position de consigne sans y arriver, l’intensité peut rapidement dépasser 1A sur des modèles courants (Hitec HS-422, par exemple). Il conviendra donc de prévoir une alimentation de puissance assez robuste et de préférence distincte de l’alimentation du système à microprocesseur. En effet, lors d’une forte charge, les variations de tension et les parasites générés par les servos sur l’alimentation peuvent geler ou réinitialiser la carte processeur.

 

Servo-Chronogramme-Positon

 

Fig. 2 : Chronogrammes d’un signal de commande d’un servomoteur de radiocommande et les positions correspondantes du palonnier.

Les servos sont commandés par un signal en contrôle de largeur d’impulsion (PWC, Pulse Width Control). Le contrôle de largeur d’impulsion ressemble un peu à la modulation de largeur d’impulsion (Pulse Width Modulation, PWM), mais seule la durée de l’impulsion est prise en compte en PWC, alors qu’en PWM, c’est le rapport cyclique qui est signifiant. Ce signal de commande doit être compris entre 0V et 5V. La position prise par le servo sera directement proportionnelle à la largeur de l’impulsion de commande. Ainsi, avec une impulsion de 0,5 ms, le servo sera en position extrême anti-horaire. Une impulsion de 1,5 ms le placera en position médiane et avec une impulsion de 2,5 ms, il sera en positon extrême horaire. Pour maintenir la position, ces impulsions doivent être répétées à intervalle régulier compris entre 16 ms et 20 ms. La position à laquelle correspond une certaine largeur d’impulsion est appelée position de consigne. Si une force extérieure vient modifier la position du servo, celui-ci s’y opposera pour maintenir sa position de consigne, ceci dans ses limites physiques bien sûr. Une absence de signal de commande place le servo en roue libre, c’est-à-dire que le moteur n’exerce plus aucune force sur le palonnier et celui-ci peut tourner par l’action d’une force extérieure. La figure 2 présente les chronogrammes des signaux de commande et les positions correspondantes des servos. Les positions en fonction des durées des impulsions sont données à titre indicatif. Les positions extrêmes ainsi que les décalages sont spécifiques pour chaque modèle de servo, voire pour chaque servo. Le système proposé ici permet d’ajuster ces paramètres finement.

Voici la présentation des servos faite. Ils présentent de nombreux avantages : faible coût, large diffusion, signaux de commande simples, variété dans les modèles, la taille et la puissance. Ils ont cependant un inconvénient : leur débattement est limité à un peu moins de 180 degrés, ce qui pose problème lorsqu’on veut contrôler une révolution complète.

2.2 Pourquoi de simples GPIO ne suffisent pas

Comme il a été dit, l’un des côtés attrayants des servos est la simplicité des signaux de commande à générer. D’ailleurs, il existe de nombreux projet sur Internet décrivant la réalisation de contrôleurs de servos. La plupart sont développés autour d’un micro-contrôleur 8 bits et ne pilotent que quelques servos. Lorsque le nombre de servos devient plus important, plusieurs problèmes surgissent :

- Cas 1 : Le système est conçu sur la base d’un micro-contrôleur 8 bits peu puissant sans système d’exploitation. Les lignes GPIO du micro-contrôleur sont utilisées pour commander les servos. Le programme principal de commande des servos est constitué d’une boucle qui génère les changements d’états des GPIO. Une routine d’interruption peut être mise en place pour recevoir via un port série ou USB les commandes de positionnement en provenance du système hôte. Les impulsions de commande des servos débutent toutes au même instant. Par contre, si le système pilote N servos, elles pourront potentiellement se terminer à N instants différents. Que le temps soit géré par attente active ou par une interruption issue d’un timer, si deux positions de servo sont très proches l’une de l’autre, sans être égale, le problème survient. Le processeur gère la fin de génération de la première impulsion, ce qui lui prend un certain temps. Pendant ce temps, où le processeur est occupé, la seconde impulsion devrait se terminer aussi. Malheureusement, celle-ci durera plus longtemps que prévu, car le processeur n’est pas disponible pour effectuer sa terminaison. Lorsque peu de servos sont commandés, ce problème peut passer inaperçu ou être tolérable. Par contre, comme il est généralement cumulatif, il devient visible avec de nombreux servos. Imaginons le pire des cas où 32 servos sont commandés, avec des positions très proches, l’erreur de positionnement peut atteindre 10 degrés. Un autre cas flagrant consiste à placer un servo à une certaine position et tous les autres à une position qui nécessite une impulsion d’une durée supérieure. Puis les 31 servos sont amenés à des positions différentes, mais très voisines les unes des autres, nécessitant une impulsion d’une durée inférieure à celle du premier servo. La gestion des fins d’impulsion des 31 servos va retarder la génération de la fin de l’impulsion du servo dont la position de consigne n’a pas changé. L’impulsion de commande de ce servo sera donc plus grande et il changera de position. Il y existe d’autres possibilités d’implantation logicielle, mais ce problème étant intrinsèque à la gestion du temps, il resurgira obligatoirement.

- Cas 2 : Le système est conçu sur la base d’un système embarqué tournant sous GNU/Linux. La puissance processeur est assez importante et la vitesse d’horloge de celui-ci l’est aussi. Les lignes GPIO du processeur seront utilisées pour commander les servos. Le programme de commande des servos est un module noyau pour bénéficier d’une gestion du temps fine grâce à la fonction du noyau udelay() et ne pas être perturbé par les processus en mode utilisateur. Grâce à la vitesse du système, le phénomène présenté ci-dessus n’est pas perceptible. Lorsque le système n’est pas chargé au niveau du processeur et des entrées/sorties, c’est-à-dire lorsqu’il ne fait rien d’autre que de commander les servos, le système fonctionne de manière acceptable. Par contre, comme le noyau Linux n’est pas temps réel par nature, même l’attente active réalisée avec udelay() en mode noyau peut être interrompue par une interruption provenant d’une carte réseau ou d’un contrôleur de disques. Et lorsque le système est chargé, les durées des impulsions de commande ne sont plus respectées et une erreur d’une durée aléatoire leur est additionnée. Il résulte qu’au lieu d’être fixes, les durées des impulsions varient continuellement et aléatoirement. L’effet observable sur les servos, qui pourrait être comique dans un autre contexte, donne l’impression qu’ils sont atteints par la maladie de Parkinson : ils tremblent de manière fébrile. Ce phénomène s’appelle la gigue (jitter[1] en anglais). Si le système était conçu avec une extension temps réel, ce phénomène serait moins apparent, mais il surviendrait tout de même sous de très fortes charges. En effet, une attente active de 2,5 ms sur 16 ms représente un peu plus de 15 % du temps du système, ce qui est énorme et même un système temps réel dur a ses limites.

Ces deux exemples de conception ont été expérimentés avant d’arriver à une conclusion : seul un système basé sur un contrôleur matériel peut donner de bons résultats en toutes situations. Le contrôleur de servos matériel pourrait être conçu de différentes manières :

  • Intégralement en logique séquentielle à partir de circuits logiques standards. Il faudrait alors des dizaines de circuits intégrés pour l’ensemble du contrôleur.
  • En utilisant des circuits timer spécialisés tels le 8254 [2] qui comporte trois timers. Il faudrait donc au moins 11 circuits 8254, sans compter la logique de décodage d’adresses.
  • En utilisant un FPGA directement connecté au bus système du processeur principal.

Les deux premières solutions sont dignes du siècle dernier et ne devraient plus être envisagées, même dans un contexte amateur. Un FPGA (Field Programmable Gate Array) est un circuit intégré qui peut être programmé pour se comporter comme un circuit électronique qui a été décrit avec un langage de description matériel (HDL, Hardware Description Language), tel le VHDL, ou un outil de conception graphique. Durant la conception, le circuit peut être simulé grâce à des outils libres, comme GHDL [3],ou propriétaires, comme Xilinx ISE® [4]. Le lecteur pourra consulter cet article d’introduction sur les FPGA [5]. Pour la programmation en VHDL, les articles GLMF[6] [7] pourront être consultés. Enfin, l’article [8] constitue un bon exemple d’utilisation d’un FPGA sur une plate-forme Armadeus.

La mise en œuvre d’un FPGA permet d’obtenir un contrôleur contenu dans un seul circuit intégré. Il permet aussi une mise au point plus facile qu’avec des circuits traditionnels, car la simulation permet d’éviter d’utiliser le fer à souder à chaque erreur commise dans la conception. De plus, si ce FPGA est intégré directement sur la carte processeur et connecté au bus système du processeur, la communication avec le contrôleur devient aussi directe qu’avec n’importe quel autre périphérique du système. Ce modèle de conception permet donc d’obtenir un contrôleur matériel à faible coup, très polyvalent car complètement reconfigurable, totalement intégré au système et tout cela avec un temps de développement beaucoup plus court. Le contrôleur de servos décrit ici n’est qu’un exemple et seule l’imagination limite les créations possibles avec ce genre d’approche.

2.3 Présentation des systèmes Armadeus

Ce projet a été réalisé initialement sur une carte Armadeus APF9328 [9] puis adapté à la carte Armadeus APF27 [10] lorsque celle-ci est apparue. Elles peuvent être obtenues en étant membre d’Armadeus Project [11]. Armadeus Project, qui a déjà été présenté dans les colonnes de GLMF[12], se propose de promouvoir l’informatique embarquée sous GNU/Linux en fournissant à ses membres des systèmes embarqués professionnels. Ceux-ci sont basés sur un processeur ARM9 Freescale (ex Motorola) auquel est adjoint un FPGA Xilinx. Ce type d’architecture correspond exactement à celle nécessaire pour mettre en œuvre le modèle de conception présenté plus haut. Aujourd’hui, il existe de nombreux systèmes embarqués ARM9 fonctionnant sous GNU/Linux, mais rares sont ceux qui à la fois intègrent un FPGA et sont accessibles aux particuliers à un prix abordable. De plus, le FPGA étant connecté directement au bus système du processeur ARM9, la communication directe via les fonctions readw() et writew() sera possible. Ces deux cartes processeurs sont assez similaires, bien que l’APF27 soit plus puissante que l’APF9328.

L’APF9328 dispose :

  • d’un processeur Freescale i.MXL (MC9328MXL) ;
  • de 16 Mo de SDRAM ;
  • d’un FPGA Xilinx Spartan 3.

L’APF27 dispose :

  • d’un processeur Freescale i.MX27 ;
  • de 64 Mo de DDR ;
  • d’un FPGA Xilinx Spartan 3A.

Ces modules processeur peuvent être connectés sur une carte de développement qui les accueille comme l’APF9328 DevLight [13] pour l’APF9328 ou l’APF27 Dev [14] pour l’APF27. Les cartes de développement permettent d’accéder facilement aux différentes lignes d’entrée/sortie disponibles et ajoutent certains périphériques. Il est aussi possible de développer sa propre carte d’accueil avec des périphériques supplémentaires ou avec le minimum nécessaire.

Seules les lignes d’entrée/sortie des FPGA sont nécessaires à la commandes des servos et elles sont routées vers des connecteurs ou des pastilles soudables sur les cartes de développement. Donc il n’a pas été utile de développer une carte d’accueil spécifique et seules les cartes de développement ont été utilisées. Malgré la similitude des deux systèmes, il existe cependant quelques différences à prendre en compte aux niveaux du câblage, de la conception FPGA et du module noyau Linux :

  • Les lignes d’entrée/sortie des FPGA ne sont pas les mêmes et les connexions sur les cartes de développement sont différentes.
  • Les lignes d’adresse A0 à A12 sont connectées entre le processeur et le FPGA sur l’APF9328 alors que seules les lignes d’adresse A1 à A12 le sont sur l’APF27. Cela signifie que la granularité d’accès est l’octet ou le mot de 16 bits dans le premier cas, alors que c’est uniquement le mot de 16 bits dans le second cas.
  • L’horloge qui cadence le FPGA est à 96 MHz sur l’APF9328 et à 100 MHz par défaut sur l’APF27. Lors de la conception d’une base de temps, ce paramètre est à prendre en compte dans le facteur de division.
  • Les adresses physiques et virtuelles de base du FPGA sur le bus système sont différentes. L’adresse de base physique est 0x12000000 pour l’APF9328 et 0XD6000000 pour l’APF27. L’adresse de base virtuelle est 0xEA000000 pour l’APF9328 et 0XF4300000 pour l’APF27. Il convient donc d’utiliser la constante ARMADEUS_FPGA_BASE_ADDR_VIRT pour accéder au FPGA au lieu des adresses en dur dans le code C du module noyau Linux et lors de l’accès direct à la mémoire depuis U-Boot, il faut utiliser les adresses physiques correspondant à la plate-forme.

Il existe bien sûr d’autres différences, mais elles n’ont pas influencé ce projet.

2.4 Architecture du système

Le système est architecturé selon un empilement de couches matérielles et logicielles, comme c’est souvent le cas dans le domaine informatique :

  • Une interface matérielle d’adaptation de niveaux de tension entre le FPGA et les servos.
  • Le firmware FPGA constituant le contrôleur de servos matériel développé en VHDL et chargé dans le FPGA.
  • Le pilote de périphérique du contrôleur de servos qui prend la forme d’un module noyau Linux écrit, bien sûr, en langage C.
  • Le serveur qui communique avec le module noyau et qui prend en charge les requêtes d’un client réseau écrit lui aussi en langage C.
  • L’application graphique cliente développée en langage C++ sous Qt qui tourne sur un système classique.

Le schéma de cette architecture est présenté sur la figure 3.

 

Architecture-Contoleur-Servo

 

Fig. 3 : Architecture générale du système de commande de servomoteurs de radiocommande.

3. Description détaillée du système

Maintenant que les différentes présentations sont faites, nous pouvons donc entrer dans le détail de chaque partie du projet. Cette section décrit la conception, le code et la mise en œuvre sur les cartes APF9328 et APF27 de chaque élément du contrôleur de servos, en partant du plus bas niveau matériel en allant vers le plus haut niveau d’abstraction logiciel.

Pour que les manipulations présentées ici fonctionnent, il convient que le Kit de Développement Logiciel (SDK) Armadeus version 3.3 (la dernière version stable lors de la rédaction de cet article) soit installé sur la machine hôte. La description détaillée de cette procédure peut être trouvée sur le Wiki d’Armadeus Project [15]. Le système cible doit être connecté au système hôte via une liaison série et via Ethernet, comme présenté en [16]. Enfin, il faut aussi que les images binaires générées par le SDK Armadeus (U-Boot, noyau Linux, système de fichier racine) soient flashées sur la carte cible APF9328 ou APF27, comme décrit en [17]. La description de ces différentes actions dépasse le cadre de cet article, mais le Wiki d’Armadeus Project [11] contient toutes les informations nécessaires. En cas de problème, la liste de diffusion du Forum Armadeus [18] permet d’obtenir un support rapide.

 

Systeme-complet

 

Fig. 4 : L’APF27 sur sa carte de développement connectée à une interface expérimentale pour 8 servos.

3.1 L’interface matérielle

L’interface matérielle d’adaptation de niveaux est extrêmement simple. Elle consiste simplement en un ensemble de tampons, un par servo à commander. Ils servent à adapter le niveau de tension de sortie du FPGA, qui est de 3,3V, vers une tension de 5V nécessaire aux servos. Par la même occasion, l’adaptation en courant et la protection des sorties du FPGA sont assurées. Les circuits utilisés sont des 74HCT244. Ils renferment 8 tampons, il faudra donc 4 circuits pour interfacer les 32 servos. Les condensateurs assurent le découplage de l’alimentation. Le schéma de l’interface est présenté sur la figure 5.

 

Interface-Materielle

 

Fig. 5 : Schéma de l’interface matérielle

Les entrées de l’interface PWCOUT00 à PWCOUT31 correspondent aux sorties du FPGA pareillement nommées. Les connecteurs SERVO00 à SERVO31 correspondent aux branchements des servos. Il convient d’adapter ces connecteurs à ceux des servos utilisés, qui sont différents selon les constructeurs. Le connecteur de servo Hitec, très répandu chez d’autres constructeurs, est présenté à titre d’exemple sur la figure 6.

 

Connecteur-Hitec

 

Fig. 6 : Connecteur de Servomécanisme Hitec

Il reste maintenant à connecter l’interface à la carte de développement. Selon la carte utilisée, le tableau ci-dessous donne les liaisons à effectuer :

 

Nom du signal sur le schéma Figure 5

Nom de la broche sur l’APF9328 DevLight

Numéro de broche sur le connecteur J20 de l’APF27 Dev

PWCOUT00

L24N_3

3

PWCOUT01

L24P_3

5

PWCOUT02

L23N_3

7

PWCOUT03

L40P_2

9

PWCOUT04

L40N_3

13

PWCOUT05

L40P_3

15

PWCOUT06

L24N_2

17

PWCOUT07

L24P_2

19

PWCOUT08

L40N_2

21

PWCOUT09

L22P_2

23

PWCOUT10

L23N_2

25

PWCOUT11

L23P_2

27

PWCOUT12

L21N_2

29

PWCOUT13

L21P_2

31

PWCOUT14

L22N_2

33

PWCOUT15

L01P_2

35

PWCOUT16

L20N_2

2

PWCOUT17

L20P_2

4

PWCOUT18

L32N_0

6

PWCOUT19

L32P_0

8

PWCOUT20

L01N_2

14

PWCOUT21

L31P_0

16

PWCOUT22

L31N_0

18

PWCOUT23

L32N_1

20

PWCOUT24

L30P_0

22

PWCOUT25

L30N_0

24

PWCOUT26

L27P_0

26

PWCOUT27

L28N_1

28

PWCOUT28

L28P_1

30

PWCOUT29

IO1

32

PWCOUT30

L01P_1

34

PWCOUT31

L01N_1

36

VCCO1 3,3 Volts

Sans Objet

1

VCCO3 3,3 Volts

Sans Objet

12

Les deux dernières lignes du tableau ne concernent que l’APF27 Dev pour l’alimentation des lignes d’entrée/sortie du FPGA. Ces dernières sont organisées par banc et nécessitent une alimentation par banc comprise entre 1,2V et 3,3V, au choix du concepteur. Sur l’APF9328 DevLight, celles-ci sont câblées sur du 3,3V, mais sur l’APF27 Dev, le choix a été laissé à l’utilisateur. Il faut donc brancher les broches 1 et 12 du connecteur J20 à une source d’alimentation de 3,3V, comme la broche 39 du connecteur J20.

Les noms de broche donnés pour l’APF9328 DevLight correspondent à des îlots soudables situés sous la carte de développement, entre les connecteurs du module processeur. Il est possible soit de souder un connecteur, soit de souder directement les fils de liaison avec l’interface. La figure 7 présente cette zone encadrée en rouge. L’emplacement des différentes broches peut être trouvé à la page 9 du datasheet de l’APF9328 DevLight [19].

 

APF9328-DevLight-Selection

 

Fig. 7 : Dessous de l’APF9328 DevLight. Le rectangle rouge correspond aux îlots sur lesquels sont accessibles les 32 lignes de sortie du FPGA.

Sur l’APF27, le connecteur J20 au pas de 2,54 mm est utilisé. Il est situé sur le bord de carte, à côté du module processeur. Il rend la connexion avec l’interface beaucoup plus facile et un ancien câble nappe IDE 40 conducteurs pourra être utilisé pour faire la liaison. Le détail du connecteur J20 peut être consulté à la page 22 du datasheet de l’APF27 Dev [20].

3.2 Le firmware FPGA du contrôleur de servomoteurs R/C

Maintenant que le matériel est câblé, nous allons pouvoir nous intéresser à ce qui constitue le cœur du contrôleur de servos : le firmware FPGA. Il est à la limite du logiciel et du matériel. En effet, sans le code VHDL qui décrit le contrôleur, le FPGA est un composant inanimé. À la différence des autres codes sources, le code VHDL n’est ni compilé, ni interprété. Il est synthétisé. Cela signifie qu’il est transformé en un fichier compréhensible par le FPGA qui permettra à celui-ci de se configurer à l’identique d’un circuit électronique équivalent réalisé avec des portes logiques et des bascules. La synthèse nécessite des outils spécifiques à chaque modèle de FPGA.

3.2.1 Les outils Xilinx : installation et prise en main

Chaque fondeur de FPGA propose une suite d’outils permettant la mise en œuvre de leurs produits. Nous allons utiliser des FPGA Xilinx Spartan 3 [21] ou Spartan 3A [22] et Xilinx met à disposition la suite ISE® WebPACK™ Design Software, que nous nommerons simplement ISE. C’est un environnement de développement intégré propriétaire, comme la plupart dans ce domaine, mais il est gratuit pour l’usage limité que nous en ferons. Il permet :

  • la gestion de projets ;
  • l’édition du code VHDL ou Verilog ;
  • la conception graphique de circuits ;
  • la simulation de circuits ;
  • l’analyse de la conception de circuits ;
  • la synthèse de circuits.

Il fonctionne sous Windows, mais aussi sous GNU/Linux. Bien que seules les distributions RedHat® Enterprise Linux et SUSE® Enterprise Linux ne soient officiellement supportées, ISE 12.4, la dernière version lors de l’écriture de cet article, fonctionne sans aucun problème sur une distribution Debian Squeeze.

L’installation d’ISE est un peu fastidieuse. Pour pouvoir télécharger ISE, il est en effet obligatoire de créer au préalable un compte Xilinx à cette URL [23]. Ensuite, l’archive est accessible à cette URL [24]. Elle fait plus de 3,5 Go et Xilinx utilise un « Download Manager » qui nécessite une version récente du JRE Java. Pour finir, comme ISE n’est pas libre, il requiert une licence, même si celle-ci est gratuite. Pour l’obtenir, le lecteur est invité à se rendre à l’URL [25]. Là, une page de gestion de licence s’affiche. Deux licences sont disponibles gratuitement :

  • une licence d’évaluation de 30 jours de la version complète d’ISE ;
  • une licence WebPACK limitée en fonctionnalités, mais pas dans le temps.

Il faudra bien sûr sélectionner la seconde en cochant la ligne correspondante et en validant le formulaire. En suivant les quatre formulaires suivants, le fichier de licence est généré et envoyé en pièce jointe à l’adresse mail de contact du compte. Xilinx offre ses logiciels, mais les utilisateurs doivent fournir quelques efforts !

Maintenant que l’archive d’ISE et le fichier de licence sont téléchargés, l’installation peut être réalisée :

$ tar xvf /RAID/Logiciels/D-Z/Xilinx/ISE12.4/Xilinx_ISE_DS_Lin_12.4_M.81d.2.0.tar

$ ./Xilinx_ISE_DS_Lin_12.4_M.81d.2.0/xsetup

Un bel installeur Qt se lance. Sur le quatrième écran, il faudra choisir ISE WebPACK, sur le cinquième, cocher uniquement Acquire or Manage a license Key, sur le sixième, sélectionner un répertoire dans lequel l’utilisateur courant a des droits en écriture. L’installation dure une bonne vingtaine de minutes. À la fin, le gestionnaire de licence se lance. Le fichier de licence préalablement obtenu pourra être configuré en sélectionnant Locate Existing License(s), puis le bouton Copy License permet de localiser ce fichier et de le copier dans ~/.Xilinx/Xilinx.lic. Voilà, ISE est maintenant prêt à être utilisé en exécutant :

./Xilinx/12.4/ISE_DS/ISE/bin/lin/ise

Une application Qt se lance, qui ressemble aux IDE actuels. Les manipulations nécessaires à la création d’un projet et à sa synthèse seront vues plus bas.

3.2.2 Architecture et description du firmware FPGA

Le firmware FPGA du contrôleur de servos est architecturé en quatre unités de conception VHDL distinctes :

  • Le module principal (SERVO_TOP.vhd) qui dialogue avec le processeur ARM9, instancie et contrôle les autres modules.
  • Un module de remise à zéro (RESET.vhd), utilisé au démarrage et lors d’une réinitialisation à chaud.
  • Un module de comptage (COUNTER.vhd) fournissant une base de temps unique et synchrone pour tous les modules PWC.
  • Un module générant les signaux de commande des servos en PWC (PWC.vhd).

L’architecture du firmware est présentée sur la figure 8.

 

Architecture-Firmware-controleur-Servo-VHDL

 

Fig. 8 : Architecture du firmware FPGA du contrôleur de servos.

3.2.2.1 L’unité de conception principale SERVO_TOP

Cette unité de conception englobe et instancie les autres. Elle est connectée au monde extérieur, à savoir les bus d’adresses, de données, et de contrôle du processeur ARM9, ainsi qu’aux servos via l’interface d’adaptation de niveaux. Elle contient les instances des autres unités de conception : un compteur, un circuit de remise à zéro et 32 générateurs de signaux PWC. Les différents registres nécessaires au fonctionnement du contrôleur y sont aussi placés. Pour finir, elle assure le décodage d’adresses lorsque le processeur accède au contrôleur de servos pour lire ou écrire dans les registres internes. La connexion au monde extérieur est définie de la façon suivante :

[...]

entity SERVO_TOP is

 generic ( nbrServos : integer := 32);

 port (

  Data : inout std_logic_vector(15 downto 0);

  Addr : in std_logic_vector(11 downto 0);

  WR : in std_logic;

  RD : in std_logic;

  CS : in std_logic;

  clkIn : in std_logic;

  pwcOut : out std_logic_vector(nbrServos-1 downto 0)

 );

end entity SERVO_TOP;

[...]

Addr : in std_logic_vector(11 downto 0) est un vecteur de 12 signaux en entrée seule connectés au bus d’adresses de l’ARM9. Comme je l’ai signalé plus haut, seules les lignes A1 à A12 du bus d’adresses sont connectées au FPGA sur l’APF27, alors que sur APF9328, A0 à A12 sont connectées. Cela a pour conséquence que la granularité d’accès sur l’APF27 est le mot de 16 bits, alors que c’est l’octet pour l’APF9328. Pour avoir une compatibilité entre les deux cartes processeurs, A0 a été ignorée et l’accès aux données se fera par mots de 16 bits uniquement. Cela convient parfaitement puisque seules les lignes D0 à D15 du bus de données sont connectées de l’ARM9 au FPGA via le vecteur de 16 signaux en entrée/sortie Data : inout std_logic_vector(15 downto 0).

Concernant le bus de contrôle, c’est plus subtile. SERVO_TOP reçoit les signaux en entrée seule CS, RD, WR et clkIn. CS signifie Chip Select ou sélection de circuit. Il est généré par le processeur principal lorsque la plage de mémoire accédée correspond à celle définie matériellement pour le FPGA. Il correspond au signal CS5 de l’i.MX27 pour l’APF27, actif à l’état bas pour la plage de mémoire 0xD6000000 à 0xD7FFFFFF. Sur l’APF9328, il correspond au signal CS1 du MC9328MXL, actif aussi à l’état bas pour la plage de mémoire 0x12000000 à 0x12FFFFFF. Cela signifie que toute l’activité sur les bus d’adresses, de données et de contrôle devra être ignorée si CS n’est pas actif. RD est un signal actif à l’état bas uniquement lorsque l’ARM9 effectue une opération en lecture sur le bus de données. Il est connecté au signal OE de l’i.MX27 et du MC9328MXL. WR est un signal actif à l’état bas uniquement lorsque l’ARM9 effectue une opération d’écriture sur le bus de données. Il est connecté au signal EB0 de l’i.MX27 et au signal EB3 du MC9328MXL. Ces trois signaux de contrôle sont utilisés pour déterminer les actions à mener par le firmware en fonction de ce que demande le processeur principal.

Le dernier signal de contrôle est clkIn. C’est le signal d’horloge principal provenant du signal CLKO de l’i.MX27 et du MC9328MXL. Il a une fréquence de 100 MHz par défaut sur l’APF27 et de 96 MHz sur l’APF9328. Cette différence doit être prise en compte dans la conception du module d’horloge. Ce signal est ensuite distribué vers toutes les autres unités de conception.

La spécificité de ce signal, le fait qu’il soit un signal d’horloge, mérite qu’on s’attarde un peu sur lui. En effet, il est essentiel que toutes les unités de conception soient synchrones sur un seul et unique signal d’horloge. L’utilisation de plusieurs signaux d’horloge doit toujours être évitée, sauf dans les cas ayant une réelle nécessité. L’avantage de la logique séquentielle synchrone sur une horloge est qu’elle permet de simplifier les problèmes d’aléas rencontrés dans les systèmes asynchrones. Ces aléas sont principalement dus au temps de propagation des portes logiques et à l’impossibilité d’assurer la simultanéité des changements de plusieurs signaux. Ils ont pour conséquence la génération de « glitchs » parasites [26]. Un glitch peu être, par exemple, une impulsion de très courte durée qui n’est pas souhaitée. Si un comptage des impulsions est fait en aval, le glitch viendra fausser celui-ci. Lorsqu’une horloge unique est utilisée, tous les changements sont faits sur le même front d’horloge, ce qui minimise le risque d’avoir des comportements aléatoires. L’outil de synthèse doit donc pouvoir identifier clairement les signaux d’horloge. Dans le code VHDL, cela se traduit dans la description d’un bloc séquentiel PROCESS par l’utilisation inconditionnelle de la fonctionRISING_EDGE(CLK) comme condition unique de déclenchement d’une action, où CLK est l’horloge unique. Si une condition supplémentaire doit être vérifiée, elle doit l’être dans un bloc conditionnel inclus dans le bloc IF (RISING_EDGE(CLK)) THEN. Le code suivant illustre la bonne manière de procéder :

PROCESS(clkIn)

BEGIN

 IF (RISING_EDGE(clkIn)) THEN

  IF (count = "1000000000000000") THEN

   pwmValue <= servosReg;

   enable <= enableReg;

  END IF;

 END IF;

END PROCESS;

clkIn est l’horloge unique et les changements de pwmValue et enable ne pourront se faire que de manière synchrone à clkIn. L’erreur aurait été d’omettre IF (RISING_EDGE(CLK)) THEN en testant directement count, ou en incluant le test de count dans la condition de front montant de l’horloge : IF RISING_EDGE(clkIn) AND count = "1000000000000000" THEN.

Cela peut sembler fastidieux, mais est néanmoins nécessaire. Il faut garder à l’esprit que derrière le VHDL, il y a une synthèse de circuit physique qui respecte la description faite. Pour finir cette digression sur l’horloge unique, la figure 9 présente un glitch généré par une mauvaise conception. Au début de la conception du contrôleur de servos, j’avais intercalé une horloge intermédiaire à 4 MHz entre l’horloge principale et les générateurs de signaux PWC. Ainsi, ces derniers n’étaient pas synchronisés sur l’horloge principale, mais sur l’horloge intermédiaire. Le résultat est visible sur l’oscillogramme : un glitch apparaît sur PWCa un coup d’horloge à 4 MHz avant le début de l’impulsion PWC. Le problème a disparu lorsqu’une seule horloge a été utilisée. Ce genre d’observation ne peut être faite qu’avec un oscilloscope très performant, celui utilisé a une bande passante de 1 GHz. Le glitch était passé complètement inaperçu avec mon humble oscilloscope personnel à 40 MHz ! Je remercie Fabien Marteau d’Armadeus pour ses conseils éclairés.

 

Glitch

 

Fig. 9 : Un glitch généré par une conception non synchronisé sur une horloge unique. En jaune : Le signal PWC de commande de servo pollué par un glitch entouré en rouge. En bleu : La vue numérique du même signal. En rouge : L’horloge intermédiaire à 4 MHz.

Revenons à la connexion au monde extérieur. Les nbrServos signaux de sortie contenus dans le vecteur pwcOut commandent les servos. L’utilisation du paramètre générique nbrServos permet de définir de manière réutilisable et élégante le nombre d’unités de conception PWC_MODULE instanciées et ainsi le nombre de servos commandés. generic ( nbrServos : integer := 32) définit par défaut 32 servos commandés. Mais c’est arbitraire, et comme le FPGA comporte encore 25 lignes d’I/O disponibles pour l’APF9328 DevLight et 30 pour l’APF27 Dev, ce nombre pourrait être porté à 57 ou 62.

Cette description s’applique uniquement au niveau logique. Elle ne donne aucune information sur la manière avec laquelle tous ces signaux sont connectés aux broches physiques du FPGA. C’est là l’un des rôles des fichiers *.ucf (pour User Constraints File) spécifiques à Xilinx. Ce sont des fichiers texte qui contiennent les contraintes définies par le concepteur, comme les connexions des ports aux broches physiques, les niveaux de tension utilisés sur celle-ci, la fréquence maximale des signaux et bien d’autres beaucoup plus subtiles. Contrairement au code VHDL qui doit être le plus générique possible, ces fichiers sont spécifiques à un modèle de FPGA particulier, Spartan 3 XC3S200 ou Spartan 3A XC3S200A dans notre cas. Ils diffèrent aussi selon le boîtier utilisé par le FPGA, TQ144 ou FT256. Le projet comporte donc deux fichiers distincts, un pour l’APF9328, SERVO_APF9328.ucf et un autre pour APF27, SERVO_APF27.ucf :

# User Constraint File for the APF27 Board

# Address bus

NET "Addr<0>" LOC="N5" | IOSTANDARD=LVCMOS18; # ADDR1

NET "Addr<1>" LOC="L7" | IOSTANDARD=LVCMOS18; # ADDR2

NET "Addr<2>" LOC="M7" | IOSTANDARD=LVCMOS18; # ADDR3

[...]

NET "Addr<9>" LOC="N11"| IOSTANDARD=LVCMOS18; # ADDR10

NET "Addr<10>" LOC="N12"| IOSTANDARD=LVCMOS18; # ADDR11

NET "Addr<11>" LOC="P13"| IOSTANDARD=LVCMOS18; # ADDR12

# Control signals

NET "CS" LOC="P10" | IOSTANDARD=LVCMOS18; # CS5

NET "WR" LOC="P9" | IOSTANDARD=LVCMOS18; # EB0

NET "RD" LOC="R9" | IOSTANDARD=LVCMOS18; # OE

NET "clkIn" TNM_NET = "CLKO";

TIMESPEC "TS_CLKO" = PERIOD "CLKO" 10 ns HIGH 50 %;

NET "clkIn" LOC="N9" | IOSTANDARD=LVCMOS18;# CLKO

# Data bus

NET "Data<0>" LOC="T5" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA0

NET "Data<1>" LOC="T6" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA1

NET "Data<2>" LOC="P7" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA2

[...]

NET "Data<13>" LOC="R5" | DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA13

NET "Data<14>" LOC="M10"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA14

NET "Data<15>" LOC="T10"| DRIVE=8 | IOSTANDARD=LVCMOS18; # DATA15

# PWC signals connected to the R/C Servos

NET "pwcOut<0>" LOC = "C16" | IOSTANDARD = LVCMOS33 ;

NET "pwcOut<1>" LOC = "C15" | IOSTANDARD = LVCMOS33 ;

NET "pwcOut<2>" LOC = "D16" | IOSTANDARD = LVCMOS33 ;

[...]

NET "pwcOut<29>" LOC = "E1" | IOSTANDARD = LVCMOS33 ;

NET "pwcOut<30>" LOC = "D3" | IOSTANDARD = LVCMOS33 ;

NET "pwcOut<31>" LOC = "C1" | IOSTANDARD = LVCMOS33 ;

La plupart des lignes décrivent une connexion physique entre un signal défini en VHDL et une broche physique du FPGA. Ainsi, NET "Addr<0>" LOC="N5" | IOSTANDARD=LVCMOS18 signifie que le signal   du vecteur Addr est connecté à la broche N5 du FPGA en utilisant des niveaux de tension standards LVCMOS18 (Low Voltage CMOS 1,8 Volts). La correspondance entre les broches physiques du FPGA (par exemple N5) et leur câblage sur les cartes processeur peut être trouvée sur les schémas électriques des cartes APF9328 [27] et APF27 [28] en liaison avec les datasheets de l’APF9328 DevLight [19] et de l’APF27 Dev [20].

NET "clkIn" TNM_NET = "CLKO" définit un groupe de signaux CLKO auquel appartient le signal clkIn. Des contraintes temporelles pourront être appliquées à ce groupe. C’est ce que décrit la ligne TIMESPEC "TS_CLKO" = PERIOD "CLKO" 10 ns HIGH 50 % : les signaux du groupe CLKO auront une période minimale de 10 ns, actif sur front montant avec un rapport cyclique de 50 %. Cela correspond à un signal d’horloge à 100 MHz. Si le FPGA ne peut pas assurer cette vitesse pour le circuit décrit, une alerte sera générée lors de la synthèse du circuit. C’est la seule contrainte temporelle nécessaire, car l’outil de synthèse vérifie celle-ci pour toutes les unités auxquelles le signal clkIn est connecté, ce qui représente tout le circuit dans notre cas. D’autre part, c’est la plus contraignante, car aucun autre signal ne dépasse 100 MHz.

Maintenant que la description des connexions avec le monde extérieur est définie, passons à la description interne. Pour son fonctionnement, le contrôleur de servos requiert un certain nombre de registres internes, qui sont les suivants :

[...]

type reg12Array is array(0 to nbrServos-1) of std_logic_vector(11 downto 0);

signal servosReg : reg12Array;

signal regEnable : std_logic_vector(31 downto 0);

signal regStatus : std_logic;

signal regCmd : std_logic_vector(15 downto 0);

signal reset : std_logic;

signal requestReset : std_logic;

signal count : std_logic_vector(15 downto 0);

[...]

reg12Array définit un type de tableau ayant nbrServos éléments. Chaque élément de ce tableau est un vecteur de 12 signaux, c’est-à-dire un registre de 12 bits. servosReg est de ce type et contient la position angulaire de consigne de chaque servo sur 12 bits. Cela permet à un servo d’avoir 4096 positions différentes. En considérant qu’un servo a un débattement d’environ 180° (beaucoup moins dans la réalité), la précision théorique de 0,04° par pas est obtenue, ce qui dépasse largement la précision physique que peut atteindre un servo.

regEnable est un registre de 32 bits. Chaque bit contient l’état d’activation d’un servo. Si regEnable(N) vaut 1, le signal PWC pour le servo N est généré et le servo N maintient sa position de consigne. Si regEnable(N) vaut 0, le signal n’est pas généré et le servo correspondant est en roue libre.

regStatus est inutilisé dans la version actuelle. regCmd est un registre de 16 bits utilisé pour stocker le code d’une commande. La seule commande implantée actuellement est la remise à zéro ayant pour code 0X0001.

reset est positionné par la logique de remise à zéro matériel et requestRest est utilisé pour demander une remise à zéro à chaud. count est un tampon qui stocke un compteur 16 bits cadencé à 4 MHz utilisé par tous les modules PWC.

Le code ci-dessous présente l’instanciation des unités de conception contenues dans l’unité principale :

[...]

resetGenerator : entity work.RESET_MODULE(behavioral)

port map (

 clkIn => clkIn,

 resetIn => requestReset,

 resetOut => reset

);

counterGenerator : entity work.COUNTER_MODULE(behavioral)

generic map (

 ratio => 25

)

port map (

 clkIn => clkIn,

 counterOut16Bits => count

);

PWC : for N in 0 to nbrServos-1 generate

 PWC_MOD : entity work.PWC_MODULE(behavioral)

 port map (

  count      => count,

  clkIn => clkIn,

  servosReg => servosReg(N),

  enableReg => regEnable(N),

  pwcOut     => pwcOut(N)

 );

end generate PWC;

[...]

Les entités sont instanciées directement, sans déclarer les composants au préalable, ce qui allège le code.

RESET_MODULE est une unité qui génère un signal de remise à zéro sur sa sortie resetOut à froid et lorsque son entrée resetIn passe à 1.

COUNTER_MODULE est une unité de comptage qui prend en entrée un signal d’horloge sur clkIn et qui fournit en sortie un compteur 16 bits. Elle accepte un paramètre générique ratio qui fixe le facteur de division entre la fréquence d’horloge d’entrée (96 MHz pour l’APF9328 et 100 MHz pour l’APF27) et la fréquence d’incrémentation du compteur 16 bits de sortie. Pour obtenir une fréquence de 4 MHz sur le compteur, ratio doit être fixé à 24 sur une APF9328 et à 25 sur une APF27. C’est un des seuls points du projet à modifier, avec les fichiers *.ucf, pour assurer sa compatibilité avec les deux cartes processeurs.

La boucle suivante donne un bon exemple de boucle spatiale pour instancier les 32 entités PWC_MODULE. Elles génèrent les signaux PWC pour tous les servos. En entrée sont connectés l’horloge clkIn, le compteur 16 bits à 4 MHz count, le registre de position de consigne servoReg(N) et le bit d’activation regEnable(N). En sortie, elles fournissent les signaux PWC pwcOut(N) connectés à l’extérieur du FPGA.

Le dernier rôle de l’unité de conception SERVO_TOP est le décodage d’adresse pour accéder aux différents registres du contrôleur de servos depuis le processeur principal en lecture et en écriture et de retourner ou de stocker les informations requises.

Le tableau suivant présente les différents registres du contrôleur et leurs adresses physiques vus de l’ARM9 :

 

Nom du registre

Adresse Physique APF9328

Adresse Physique APF27

regMagicIDAddr

0x12000000

0xD6000000

regFirmwareVersionAddr

0x12000002

0xD6000002

regNumberServosAddr

0x12000004

0xD6000004

regStatusAddr

0x12000006

0xD6000006

regCmdAddr

0x12000008

0xD6000008

regServosValidationLSBAddr

0x1200000A

0xD600000A

regServosValidationMSBAddr

0x1200000C

0xD600000C

addrRegServosPositionBaseAddr

0x12000010

0xD6000010

Reg. position consigne servo 0

0x12000010

0xD6000010

Reg. position consigne servo 1

0x12000012

0xD6000012

[...]

[...]

[...]

Reg. position consigne servo 5

0x1200001A

0xD600001A

[...]

[...]

[...]

Reg. position consigne servo 8

0x12000020

0xD6000020

[...]

[...]

[...]

Reg. position consigne servo 16

0x12000030

0xD6000030

[...]

[...]

[...]

Reg. position consigne servo 31

0x1200004E

0xD600004E

Le bus de données connecté au FPGA ayant une largeur de 16 bits, tous les registres ont une taille de 16 bits. Voici une brève description de chaque registre :

  • regMagicIDAddr : Contient un identifiant magique (0x7207) permettant au driver Linux d’identifier ou non la présence du firmware dans le FPGA.
  • regFirmwareVersionAddr : Contient la version du firmware.
  • regNumberServosAddr : Nombre de servo gérés par le firmware.
  • regStatusAddr : Inutilisé.
  • regCmdAddr : Permet d’envoyer une commande au contrôleur de servos. Actuellement, seule la commande de remise à zéro à chaud est implantée.
  • regServosValidationLSBAddr : Registre de validation des servos 0 à 15. Si le bit N vaut  , le servo N est en roue libre, s’il vaut 1, le servo N maintient sa position de consigne. Il correspond à regEnable(0..15).
  • regServosValidationMSBAddr : Identique à regServosValidationLSBAddr, mais pour les servos 16 à 31.
  • addrRegServosPositionBaseAddr est l’adresse de base des 32 registres 12 bits de position de consigne des servos. L’adresse du registre de position du servo N est addrRegServosPositionBaseAddr + (2*N).

Le code suivant assure le décodage d’adresse et l’action correspondante pour un accès en lecture :

[...]

constant regMagicIDAddr : std_logic_vector(11 downto 0) := "000000000000";

constant regFirmwareVersionAddr : std_logic_vector(11 downto 0) := "000000000001";

constant regNumberServosAddr : std_logic_vector(11 downto 0) := "000000000010";

constant regStatusAddr : std_logic_vector(11 downto 0) := "000000000011";

constant regCmdAddr : std_logic_vector(11 downto 0) := "000000000100";

constant regServosValidationLSBAddr : std_logic_vector(11 downto 0) := "000000000101";

constant regServosValidationMSBAddr : std_logic_vector(11 downto 0) := "000000000110";

constant addrRegServosPositionDaseAddr : std_logic_vector(11 downto 0) := "000000001000";

[...]

registerRead : process (clkIn)

begin

 if rising_edge(clkIn) then

  if CS = '0' and RD = '0' then

   case Addr is

    when regMagicIDAddr =>

     Data <= regMagicID;

    when regFirmwareVersionAddr =>

     Data <= regFirmwareVersion;

    when regNumberServosAddr =>

     Data <= std_logic_vector(to_unsigned(nbrServos,16));

    when regStatusAddr =>

     Data <= X"000" & "000" & regStatus;

    when regCmdAddr =>

     Data <= regCmd;

    when regServosValidationLSBAddr =>

     Data <= regEnable(15 downto 0);

    when regServosValidationMSBAddr =>

     Data <= regEnable(31 downto 16);

    when others  =>

     Data <= (others => '0');

   end case;

   for N in 0 to nbrServos-1 loop

    if (std_logic_vector(unsigned(addrRegServosPositionBaseAddr) +     N)=Addr) then

     Data <= "0000" & servosReg(N)(11 downto 0);

    end if;

   end loop;

  else

   Data <= (others => 'Z');

  end if;

 end if;

end process registerRead;

[...]

Pour chaque registre à accéder, une constante est définie. Leurs valeurs correspondent à l’écriture binaire, les bits 1 à 12 des adresses des registres présentés dans le tableau ci-dessus. Ensuite vient le décodage d’adresses. Il est englobé dans un processus déclenché par clkIn, puis rising_edge(clkIn) agit uniquement sur un front montant pour conserver la même synchronisation dans tout le contrôleur. if CS = '0' and RD = '0' then est la condition à vérifier pour une écriture. Comme il a été dit plus haut, CS vaut 0 lorsque le processeur accède à la plage d’adresses du FPGA et RD vaut 0 lors d’une opération de lecture. Un case sur le vecteur Addr compare la valeur du bus d’adresses aux différentes adresses de registres définies par les constantes. En cas de correspondance, la valeur voulue est affectée à Data, dans le cas contraire, Data est mis à zéro. La boucle suivante traite la correspondance d’Addr avec l’adresse d’un registre de position de servo et affecte la valeur de ce registre à Data. Il faut garder à l’esprit que cette boucle n’est qu’une élégance syntaxique, nous écrivons du VHDL, elle n’est pas procédurale, mais 32 tests individuels seront synthétisés. Pour finir, si la condition if CS = '0' and RD = '0' n’est pas remplie, le bus de données est placé en haute impédance par Data <= (others => 'Z') pour ne pas générer de conflit sur celui-ci. Le décodage d’adresses pour les opérations d’écriture est similaire, seule la condition d’accès est remplacée par if CS = '0' and WR = '0' then. À ce stade, le FPGA est complètement interfacé avec le processeur principal, connecté au servos et dispose de ses registres internes ainsi que de ses entités incluses.

3.2.2.2 L’unité de conception de remise à zéro

Cette unité assez simple gère la remise à zéro du contrôleur à froid et à chaud lors d’une requête explicite. Sa raison d’être est que contrairement au processeur principal et à ses circuits périphériques, le FPGA ne peut pas bénéficier du circuit matériel de remise à zéro de la carte processeur. En effet, le firmware du contrôleur de servos est chargé au sein du FPGA par U-Boot lorsque tout le matériel est initialisé et juste avant le lancement du noyau Linux. Il faut donc que le contrôleur sache générer lui-même un signal de remise à zéro. En voici le code :

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

Entity RESET_MODULE is

port

(

 clkIn : in std_logic ;

 resetIn : in std_logic ;

 resetOut : out std_logic

);

end entity;

Architecture behavioral of RESET_MODULE is

signal delayCold: std_logic := '0';

signal resetCold: std_logic := '0';

signal resetReqPulse: std_logic := '0';

begin

 resetReqPulseGen: process(clkIn,resetIn)

  variable resetInEdgeDetect : std_logic := '0';

 begin

 if(rising_edge(clkIn)) then

  if (resetInEdgeDetect = '0' and resetIn ='1') then

   resetReqPulse <= '0';

  else

   resetReqPulse <= '1';

  end if;

  resetInEdgeDetect :=resetIn;

end if;

 end process resetReqPulseGen;

 

 resetColdGen: process(clkIn)

 begin

 if(rising_edge(clkIn)) then

  delayCold <= (not(resetReqPulse) and delayCold and not(resetCold))

   or (not(delayCold) and resetCold);

  resetCold <= (not(resetReqPulse) and not(delayCold) and not(resetCold) );

end if;

 end process resetColdGen;

 resetOut <= resetCold;

end architecture behavioral;

L’impulsion unique de remise à zéro au démarrage est générée par le processus resetColdGen pour deux fronts montants sur clkIn. Une impulsion est aussi générée sur resetCold pour une impulsion sur resetReqPulse. Celle-ci est créée par le processus resetReqPulseGen pour un front montant sur resetIn, qui est connecté au signal requestReset de SERVO_TOP. Ainsi, lorsque la commande 0x0001 sera écrite à l’adresse regCmdAddr, le contrôleur sera remis à zéro matériellement à chaud.

3.2.2.3 L’unité de conception de comptage

La gestion du temps est capitale pour le contrôleur de servos, puisque c’est la nature même d’un signal PWC d’être précis temporellement. Plutôt que de gérer individuellement le temps dans chaque générateur PWC à partir du signal d’horloge principal, cette tâche est confiée à une unité de conception dédiée qui distribue ensuite une référence à tous les générateurs PWC.

Le temps est géré grâce à un compteur 16 bits incrémenté à la fréquence de 4 MHz. Le temps de révolution de ce compteur est donc de (4e6 ^ -1) * 2^16 = 16,384 ms. Cette période est acceptable comme rythme de génération des impulsions PWC de commande qui doivent se reproduire toutes les 16 ms à 20 ms (voir 2.1). D’autre part, la résolution d’un tel compteur est de 4e6 ^ -1 = 0,25 µs. Si une amplitude de 2 ms, qui est la différence entre les largeurs extrêmes de l’impulsion de commande, représente un débattement du servo de 180°, 0,25 µs donne une précision de 0,02°. Elle est largement suffisante en regard de la précision des servos.

Voici le code de l’unité de comptage :

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

use IEEE.NUMERIC_STD.ALL;

entity COUNTER_MODULE is

 generic(ratio : integer := 24);

 port (

  clkIn : in STD_LOGIC;

  counterOut16Bits : out std_logic_vector(15 downto 0)

 );

end entity COUNTER_MODULE;

architecture behavioral of COUNTER_MODULE is

 signal counter : std_logic_vector(15 downto 0) := X"0000";

begin

 process (clkIn)

  variable count : integer range 0 to ratio;

 begin

  if (rising_edge(clkIn)) then

   count := count + 1;

   if (count = ratio) then

    count := 0;

    counter <= std_logic_vector(unsigned(counter) + 1);

    counterOut16Bits <= counter;

   end if;

  end if;

 end process;

end architecture behavioral;

L’entité comporte le paramètre générique ratio déjà évoqué plus haut, qui permet l’adaptation à la différence de fréquence d’horloge entre l’APF9328 et l’APF27. Comme pour les autres unités de conception, le compteur est synchronisé sur les fronts montants de clkIn. Pour les lecteurs qui ne sont pas familiers au VHDL, l’incrémentation du compteur peu sembler incroyablement compliquée : deux conversions de type sont nécessaires ! Dans la sémantique VHDL, un std_logic_vector est un ensemble de bits mais pas un nombre. Il s’agit donc de le convertir en nombre avec la fonction unsigned() puis de le reconvertir en std_logic_vector avec la fonction std_logic_vector().

3.2.2.4 L’unité de conception PWC

Cette unité de conception génère les signaux de commande PWC de tous les servos. Elle est instanciée 32 fois, une par servo à piloter. Les sorties pwcOut sont connectées à l’interface d’adaptation de niveaux via l’entité SERVO_TOP. Voici le code de cette unité de conception :

library IEEE;

use IEEE.STD_LOGIC_1164.ALL;

entity PWC_MODULE is

 Port (

  clkIn : in std_logic;

  count : in std_logic_vector(15 downto 0);

  servosReg : in std_logic_vector( 11 downto 0);

  enableReg: in std_logic;

  pwcOut : out std_logic);

end PWC_MODULE;

architecture behavioral of PWC_MODULE is

 signal pwcValue : std_logic_vector(11 downto 0) := "000000000000";

 signal enable: std_logic := '0';

begin

 updateReg: process(clkIn)

 begin

  if (rising_edge(clkIn)) then

   if (count = X"8000") then

    pwcValue <= servosReg;

    enable <= enableReg;

   end if;

  end if;

 end process updateReg;

 pwcOut <=

   '0' when (enable = '0') else

   '1' when (count > X"F830") else

   '0' when (count > ("000" & pwcValue(11 downto 0) & "0")) else

   '1';

end behavioral;

Elle comporte une partie séquentielle et une autre concurrente. Le processus updateReg transfère la valeur des registres servosReg et enableReg en provenance de SERVO_TOP vers des registres internes de l’entité. Pour éviter des fluctuations asynchrones de la sortie pwcOut, le transfert est réalisé à un instant où la sortie est toujours à 0. Arbitrairement, il a lieu en milieu de comptage.

Les cinq lignes de la partie concurrente constituent le cœur de la génération PWC. Tout d’abord, lorsque le servo est configuré en roue libre ( enableReg(N) = enable = 0), la sortie est forcée à 0. La génération de l’impulsion PWC peut être vue en deux parties. La première est commune à toutes les valeurs de position de consigne, c’est l’impulsion de largeur minimale de 500 µs. L’autre est la partie variable, comprise entre 0 et 2000 µs, correspondant à la position de consigne. La première partie de l’impulsion est générée par '1' when (count > X"F830"). F830 en hexadécimal donne 63536 en décimal. Entre les valeurs 63536 et 65536 du compteur, la sortie PWC est forcée à 1. Cela correspond à 2000 cycles à 0,25 µs de compteur ou 500 µs. Nous obtenons donc la partie inconditionnelle de 500 µs de l’impulsion.

La partie variable de l’impulsion est produite par '0' when (count > ("000" & pwcValue(11 downto 0) & "0")). La valeur de pwcValue est multipliée par 2 via un décalage à gauche et la sortie est forcée à 0 lorsque le compteur dépasse cette valeur. Lorsque pwcValue vaut 0, la sortie PWC tombe à 0 lorsque le compteur atteint 0. L’impulsion globale ne dure alors que 500 µs, ce qui donne la position extrême anti-horaire. Lorsque pwcValue vaut 0x0FFF, la valeur maximale du registre de position 12 bits, 8192 (0x0FFF * 2) cycles à 0,25 µs de compteur ou 2048 µs s’écoulent avant que la sortie ne se retrouve à 0. L’impulsion globale dure 2548 µs, ce qui place le servo dans sa position extrême horaire. L’alternative finale de l’affectation conditionnelle fixe pwcOut à 1, cas qui correspond aux valeurs de compteur comprises entre 0 et la valeur de pwcValue. Entre ces deux positions extrêmes, le servo se place proportionnellement à la valeur de pwcValue, 0x800 correspond approximativement à la position centrale.

3.2.3 Mise en œuvre pratique du firmware sur la carte processeur

Le contrôleur de servos n’ayant plus de secret théorique, nous allons maintenant le mettre en œuvre sur une carte processeur Armadeus. Pour pouvoir mener à bien cette partie, il faut :

  • Disposer d’un système Armadeus APF9328 et APF9328 DevLight ou APF27 et APF27 Dev connecté via RS232 et Ethernet au système hôte avec un serveur TFTP configuré sur celui-ci, comme décrit en [29]. Kermit doit être installé et configuré, comme l’indique [30].
  • Que le SDK Armadeus version 3.3 soit installé sur le système hôte comme décrit en [31].
  • Que les images binaires générées par le SDK Armadeus soient installées sur le système cible, comme décrit en [32].
  • Que la suite ISE® WebPACK™ Design Software soit installée sur le système hôte, comme je l’ai décrit au chapitre 3.2.1.
  • Que l’interface d’adaptation de niveaux soit connectée à une carte APF9328 DevLight ou APF27 Dev et à des servos, comme indiqué au 3.1. Le nombre de servos connectés importe peu.

Une fois ces pré-requis réalisés, nous aurons à notre disposition un système embarqué et son environnement de développement complètement fonctionnels.

3.2.3.1 Synthèse du firmware FPGA du contrôleur de servos

La première phase de la mise en œuvre consiste à synthétiser le firmware à partir des fichiers sources VHDL et du fichier de contraintes ucf. Le résultat sera un fichier « bitstream » *.bit qui pourra être téléchargé dans le FPGA de la carte processeur grâce à U-Boot. Ce fichier, au format propriétaire Xilinx, contient toutes les informations requises par le FPGA pour qu’il puisse se configurer et avoir un comportement se conformant au code VHDL et aux contraintes du fichier ucf. L’outil qui génère de tels fichiers est bien sûr la suite ISE.

La synthèse du firmware commence par la création d’un nouveau projet dans ISE : File → New Project. Choisissez le nom de votre projet et un répertoire du même nom sera créé pour l’occasion tant les fichiers de travail générés sont nombreux. Choisissez HDL comme Top-level source type car tout est décrit en VHDL. Ensuite, les choix diffèrent en fonction du FPGA utilisé et donc de la carte Armadeus :

Pour une carte APF9328, choisissez :

  • Family : Spartan3 ;
  • Device : XC3S200 ;
  • Package : TQ144 ;
  • Speed : -4 ;
  • Top Level Source : HDL ;
  • Synthesis Tool : XST (VHDL/Verilog) ;
  • Simulator : ISim (VHDL/Verilog) ;
  • Preferred Language : VHDL ;
  • VHDL Source Analysis Standard : VHDL-93.

Pour une carte APF27, les paramètres suivants diffèrent :

– Family : Spartan3A and Spartan3AN ;

– Device : XC3S200A ;

– Package : FT256 ;

– Speed : -5.

Les paramètres Family, Device, Package et Speed sont des caractéristiques physiques du FPGA que le lecteur curieux pourra retrouver inscrites sur le boîtier du circuit intégré du FPGA. Il convient maintenant de télécharger les sources du projet à partir du site de l’auteur. Dans un terminal :

wget http://www.embedded-wire.org/fpga-servo-controller/fpga-servo-controller-00.00.01.tgz

Décompressez ensuite l’archive :

cd ~/sandbox

tar zxvf fpga-servo-controller-00.00.01.tgz

Retournez dans ISE pour inclure les fichiers sources dans le projet : Project → Add Source... Sélectionnez le répertoire ~/sandbox/fpga-servo-controller-00.00.01/fpgaFirmware et sélectionnez les fichiers SERVO_TOP.vhd, COUNTER.vhd, PWM.vhd et RESET.vhd. Le fichier de contraintes SERVO_APF9328.ucf devra être aussi inclus dans le cas d’une carte APF9328 et SERVO_APF27.ucf le sera pour une carte APF27.

Il ne reste plus qu’à lancer la synthèse : dans la vue Hierarchy, cliquez sur SERVO_TOP – structural. Apparaît alors dans la vue Processes, la liste des actions réalisables. Faites un clic droit sur Generate Programming File, puis séléctionnez Run dans le menu contextuel. La synthèse prend environ une minute à s’exécuter et un fichier SERVO_TOP.bit est généré dans le répertoire du projet. C’est le fichier de firmware du contrôleur de servos qui servira à la programmation du FPGA.

3.2.3.2 Installation du firmware FPGA sur le système cible

La mémoire Flash des cartes APF9328 et APF27 comporte cinq partitions, dont une est réservée à l’image binaire d’un firmware FPGA. L’installation de celui-ci consiste donc à flasher le fichier SERVO_TOP.bit dans cette partition. Lors de l’initialisation du système et en fonction de la valeur de la variable d’environnement firmware_autoload, U-Boot chargera le contenu de cette partition dans le FPGA avant de lancer le noyau Linux.

Comme sur nos petits téléphones Android et autres PSP, une mauvaise manipulation lors du flashage des images binaires peut « briquer » une carte processeur Armadeus, c’est-à-dire la rendre non opérationnelle. Par exemple, écrire une image de firmware FPGA dans la partition U-Boot ou charger un firmware FPGA destiné à un autre type de FPGA tout en ayant le chargement automatique activé produit ce désagrément. Cependant, n’ayez pas d’angoisse lors de ces opérations. Contrairement à la plupart des produits grand public, les systèmes Armadeus sont réellement et complètement ouverts. La mise en place d’un simple cavalier et une connexion série suffisent à se sortir de cette mauvaise passe [33] et évitent une fin funeste dans la benne à recycler (si on ne possède pas de sonde JTAG).

Pour flasher notre firmware dans la carte processeur, il faut que la carte processeur soit connectée au système hôte via une liaison série et Ethernet [29]. Les paramètres IP de la carte et Kermit [30] doivent être correctement configurés. Pour finir, un serveur TFTP doit être actif sur le système hôte [29]. Lancez Kermit dans un terminal et alimentez la carte processeur, puis interrompez le démarrage en appuyant sur une touche :

U-Boot 1.3.4 (Jan 31 2011 - 15:36:10) apf27 patch 2.1

I2C:   ready

DRAM: 128 MB

NAND: 256 MiB

In:    serial

Out:   serial

Err:   serial

nand_unlock: start: 000a0000, length: 267780096!

Hit any key to stop autoboot: 0

BIOS>

Copiez le fichier SERVO_TOP.bit dans le répertoire du serveur TFTP. Ensuite, lancez le téléchargement du fichier à partir du système cible via TFTP :

BIOS> tftpboot ${loadaddr} SERVO_TOP.bit

FEC ETHERNET: Link is up - 100/Full

TFTP from server 192.168.0.27; our IP address is 192.168.0.10

Filename 'SERVO_TOP.bit'.

Load address: 0xa0000000

Loading: ##############################

done

Bytes transferred = 149610 (2486a hex)

Maintenant que l’image du fichier est chargée en mémoire, son chargement est possible :

BIOS> fpga load 0 ${loadaddr} ${firmware_len}

Si U-Boot rend la main, c’est que le firmware est valide. Il peut donc être flashé :

BIOS> run flash_firmware

NAND erase: device 0 offset 0x100000, size 0x80000

OK

NAND write: device 0 offset 0x100000, size 0x2486a

149610 bytes written: OK

Flashing of Firmware succeed

Pour charger automatiquement au démarrage le firmware dans le FPGA :

BIOS> setenv firmware_autoload 1

BIOS> saveenv

Saving Environment to NAND...

Erasing Nand...

Erasing at 0xe0000 -- 100% complete.

Writing to Nand... done

Vous pouvez maintenant vérifier l’opération en réinitialisant la carte par un appui sur le bouton reset et en interrompant le démarrage. Le message supplémentaire :

Firmware successfully programmed

indique que le firmware est bien chargé automatiquement dans le FPGA.

Les premiers tests de fonctionnement du contrôleur de servos vont pouvoir avoir lieu. Même si ce n’est pas la finalité du projet, il est possible de dialoguer directement avec le firmware via les adresses physiques en exécutant des commandes U-Boot de lectures et d’écritures dans la mémoire. Dans les exemples donnés, les adresses physiques sont celles d’une carte APF27. Pour faire l’équivalent sur une carte APF9328, il suffit de remplacer les adresses 0xD60000XX par 0x120000XX. Tout d’abord, commençons par inspecter les registres du contrôleur de servos :

BIOS> md.w D6000000

d6000000: 7207 0004 0020 0000 0000 0000 0000 0000    .r.. ...........

d6000010: 0800 0800 0800 0800 0800 0800 0800 0800    ................

d6000020: 0800 0800 0800 0800 0800 0800 0800 0800    ................

d6000030: 0800 0800 0800 0800 0800 0800 0800 0800    ................

d6000040: 0800 0800 0800 0800 0800 0800 0800 0800    ................

La commande md.w (pour Memory Dump Word) affiche le contenu de la mémoire de l’adresse physique spécifiée sous la forme de mots de 16 bits. Voici quelques explications de ces valeurs cryptiques :

  • d6000000 7207 : Identifiant magique qui permet d’identifier le firmware par le driver Linux.
  • d6000002 0004 : Numéro de version du firmware.
  • d6000004 0020 : Nombre de servos gérés par le contrôleur, 32 servos en décimal.
  • d6000006 0000 : Registre de statut.
  • d6000008 0000 : Registre de commande.
  • d600000A 0000 : Partie basse du registre d’activation des servos regEnable pour les servos 0 à 15.
  • d600000C 0000 : Partie haute du registre d’activation des servos regEnable pour les servos 16 à 31.
  • d6000010 à d600004E 0800 : Registres de position des 32 servos ayant la valeur par défaut 0x800 correspondant approximativement à la position médiane.

BIOS> mw.w d600000A FFFF

Cette commande active les servos 0 à 15 et les place à leur position de consigne par défaut. Ce qui peut être observé par :

BIOS> md.w D6000000     

d6000000: 7207 0004 0020 0000 0000 ffff 0000 0000 .r.. ...........

Pour placer le servo 0 sur sa position maximale anti-horaire :

BIOS> mw.w d6000010 0000

Pour placer le servo 0 sur sa position maximale horaire :

BIOS> mw.w d6000010 0FFF

Si vous avez connecté des servos à l’interface et qu’ils sont alimentés, vous les entendrez sûrement forcer pour atteindre ces positions extrêmes qui ne sont généralement pas accessibles à cause des butées mécaniques présentes dans les servos. Ces positions ne sont pas à recommander en régime établi car le courant consommé est alors maximal. Le pilote Linux permettra, nous le verrons par la suite, de placer des butées logicielles afin de s’adapter à chaque servo particulier et à chaque dispositif mécanique demandant un débattement restreint.

La position courante des servos est visualisable :

BIOS> md.w D6000000     

d6000000: 7207 0004 0020 0000 0000 ffff 0000 0000    .r.. ...........

d6000010: 0fff 0800 0800 0800 0800 0800 0800 0800 ................

Pour finir cette séquence de dissection U-Boot, voici comme effectuer une remise à zéro à chaud :

BIOS> mw.w d6000008 0001

Effectivement, le contrôleur a retrouvé son état initial :

BIOS> md.w D6000000     

d6000000: 7207 0004 0020 0000 0000 0000 0000 0000 .r.. ...........

d6000010: 0800 0800 0800 0800 0800 0800 0800 0800 ................

d6000020: 0800 0800 0800 0800 0800 0800 0800 0800    ................

d6000030: 0800 0800 0800 0800 0800 0800 0800 0800    ................

d6000040: 0800 0800 0800 0800 0800 0800 0800 0800    ................

À ce stade, la partie matérielle du contrôleur de servo est complément implantée et fonctionnelle. Nous allons à présent retourner vers un domaine logiciel mais encore très proche du matériel : le pilote Linux.

Conclusion temporaire

Nous voici arrivés à la fin de la première partie. Tout le côté matériel du contrôleur de servos est opérationnel. Celui-ci comporte l’interface d’adaptation de niveau et le firmware FPGA. C’est un bon exemple de la puissance d’une architecture matérielle mixte basée sur un processeur générique associé à un FPGA : le périphérique spécifique n’existe pas, ce n’est pas un problème, nous le créons. Cette étape est indispensable, mais dans l’état notre nouveau périphérique est inutilisable sous notre système préféré, Linux. Comme un corps sans âme, il demeure inerte. La seconde partie de cet article traitera de l’âme artificielle de notre contrôleur de servos : ses logiciels. Le premier, le plus fondamental, est le pilote de périphérique Linux. Il est le plus proche du matériel et le plus complexe : nous aborderons les délices du code noyau. Ensuite viendra le serveur de commandes réseau. Il est à l’écoute des requêtes d’un client distant qui lui transmet des commandes dans un langage compréhensible. Pour terminer, une application graphique cliente sera présentée à titre d’exemple. Alors, en route pour la suite…

 

Sur le même sujet

La liberté jusqu’au cœur du processeur avec RISC-V

Magazine
Marque
Hackable
Numéro
31
|
Mois de parution
octobre 2019
|
Domaines
Résumé
RISC-V est un jeu d’instructions 32 bits libre, développé initialement par l’université de Berkeley. Ce jeu d’instructions (ISA pour Instruction Set Architecture) est maintenant soutenu par une fondation regroupant quasiment tous les grands noms de l’industrie informatique. Dans cet article, nous allons décrire succinctement le concept de RISC vs CISC, puis nous expliquerons les bases du jeu d’instructions avec un peu de code assembleur, enfin nous terminerons par une description de quelques émulateurs et processeurs RISC-V disponibles aujourd’hui sur le marché.

Démarrez avec MicroPython

Magazine
Marque
GNU/Linux Magazine
Numéro
228
|
Mois de parution
juillet 2019
|
Domaines
Résumé
Pour mettre sur pied une preuve de concept ou un prototype électronique, il faut habituellement choisir une carte de développement et ensuite acquérir les outils de développement logiciel du fabricant. Ces logiciels s’appuient généralement sur les langages bas niveau comme le C, dont la maîtrise n’est pas accessible à tout le monde. MicroPython est a été mis sur pied pour permettre l’utilisation d’un langage de programmation haut niveau (Python) sur microcontrôleur, pour créer de magnifiques applications d’IoT.

Contrôler vos modèles Lego au joypad à l'aide de BrickPi

Magazine
Marque
Hackable
Numéro
29
|
Mois de parution
avril 2019
|
Domaines
Résumé
Dexter Industries propose des cartes d’extension pour Raspberry Pi. Dans cet article, nous allons nous intéresser à la carte BrickPi permettant de piloter les moteurs et senseurs Lego MindStorms. Nous verrons comment piloter notre robot Lego à l’aide d’un joypad. Nous développerons notre programme grâce au langage Python.

Créez simplement votre périphérique matériel avec le langage C

Magazine
Marque
Hackable
Numéro
29
|
Mois de parution
avril 2019
|
Domaines
Résumé
Cet article présente la création et la mise en œuvre d’un périphérique matériel libre sous Linux embarqué pour la carte ZedBoard, utilisant un circuit FPGA Zynq. La synthèse de haut niveau HLS, à l’aide du langage C, sera utilisée ici pour la création du périphérique matériel.

Bus CAN : se lancer dans l'analyse des communications de votre véhicule

Magazine
Marque
MISC
HS n°
Numéro
19
|
Mois de parution
février 2019
|
Domaines
Résumé

Cet article a pour but de s’intéresser aux communications internes d’un véhicule. Il sera présenté comment réaliser sa propre interface CAN/USB basée sur un Arduino, puis sera décrite la manière d’analyser et d’injecter des paquets à l’aide d’outils d’analyse réseau classiques.

Par le même auteur

Qemu : comment émuler une nouvelle machine ? Cas de l’APF27

Magazine
Marque
GNU/Linux Magazine
Numéro
148
|
Mois de parution
avril 2012
|
Domaines
Résumé
Dans la première partie, nous avons abordé deux aspects fondamentaux de Qemu, la recompilation dynamique du code émulé et la gestion du temps. Cependant, nous sommes restés sur le banc des spectateurs. Bien que Qemu 1.0 supporte 27 machines ARM différentes, aucune n’est basée sur les SoC Freescale™ de la famille i.MX. Dans ce second volet, nous passerons à la pratique en créant « from scratch » le support de l’émulation d’une nouvelle machine construite autour d’un i.MX27 : la carte Armadeus APF27.

Qemu : Visite au cœur de l’émulateur

Magazine
Marque
GNU/Linux Magazine
Numéro
147
|
Mois de parution
mars 2012
|
Domaines
Résumé

Les machines virtuelles, par leurs avantages et grâce aux progrès des processeurs, sont devenues un domaine très en vogue actuellement, même si leurs débuts remontent à l’époque des Beatles. Elles couvrent un large secteur de l’informatique allant du plus ludique émulateur de Commode 64 jusqu’aux plus sérieux hyperviseurs au cœur d’énormes centres de calcul. Les techniques utilisées pour les faire fonctionner sont aussi variées, depuis l’émulation totale jusqu’à la paravirtualisation. Les logiciels existants sont nombreux et Qemu est l’un d’eux, bien connu dans le monde de l’embarqué. Mais comment fonctionne-t-il ? Et comment émuler son propre matériel ?

Contrôleur de servomoteurs FPGA sur plate-forme Armadeus : partie logicielle

Magazine
Marque
Open Silicium
Numéro
3
|
Mois de parution
juillet 2011
|
Domaines
Résumé

Dans la première partie, nous avons conçu un nouveau périphérique basé sur un FPGA : le contrôleur matériel de servomoteurs. Il est l’équivalent d’un circuit électronique capable d’une concurrence vraie tout en ayant la souplesse d’une conception logicielle.Comme tout périphérique, il nécessite un peu de logiciel pour être utilisable sous un système d’exploitation tel que Linux. Tout d’abord, nous aborderons le pilote de périphérique ou driver. Il est au sein du noyau Linux et assure la communication entre le matériel et l’espace utilisateur. Ensuite, nous verrons le serveur de commandes. Il reçoit les ordres d’un client distant et les exécute. Pour clore le sujet loin des arcanes du mode noyau, un exemple de client graphique Qt est présenté, qui permet de faire bouger les servomoteurs avec une souris.

Contrôleur de servomoteurs FPGA sur plate-forme Armadeus

Magazine
Marque
Open Silicium
Numéro
3
|
Mois de parution
juillet 2011
|
Domaines
Résumé

La robotique expérimentale utilise souvent comme actionneurs des servomoteurs de radiocommande en grand nombre. Leur pilotage demande la génération d’autant de signaux temporellement précis et synchrones.Même si les systèmes embarqués disposent aujourd’hui d’une puissance de calcul impressionnante, elle n’est pas suffisante pour produire des signaux de commandes ayant ces caractéristiques à cause des problèmes de latences du système d’exploitation.Seule une alternative matérielle peut y pallier. Le FPGA est la solution disponible aujourd’hui, polyvalente et facilement accessible.Le projet proposé ici permet de commander jusqu’à 32 servomoteurs de radiocommande avec un système embarqué Armadeus : la puissance d’un ARM9 sous GNU/Linux alliée à celle d’un FPGA.