wiki:IOC20_T01

Version 5 (modified by franck, 5 years ago) (diff)

--

1. Préambule

Au long de ce module, vous allez utiliser 4 nano-ordinateurs :

  • RaspberryPi !1B
  • RaspberryPi 3B
  • Arduino nano
  • Module ESP32 TTGO-Lora32-Oled

Dans ce TP, vous allez :

  • écrire une application pour la RaspberryPi 1B pour faire clignoter deux LEDs selon un motif que vous choisirez et également de récupérer les informations d'un bouton poussoir.

À la fin de ce TP, vous devriez avoir acquis les compétences suivantes:

  • Compilation croisée via un compilateur déporté
  • Contrôle distant de système embarqué par liaison réseau (SSH)
  • Manipulation d'un périphérique GPIO et de ses registres "memory mappés"
  • Écriture d’une librairie C pour la manipulation des GPIO.

Vous écrirez un CR au format markdown, dans lequel vous répondrez aux quelques questions du TME, vous mettrez les étapes de configurations de votre compte pour réaliser le TP, ainsi vous conserverez une trace et vous ajouterez vos codes commentés (succinctement mais proprement).

Contraintes pour le compte-rendu :

  • Le document doit impérativement être nommé ioc_X_nom1_nom2.md (où X est le numéro du TP: 1 pour le TP1, 2 pour le TP2, etc.)
  • vos noms doivent figurer dans le document.
  • Vous devez envoyer le document par mail à franck.wajsburt@… avec le sujet: [IOC20] CR TP1 nom1 nom2

Je n'irai pas à la pêche, le non-respect des consignes pour les comptes-rendus sera noté 0 ! (vous êtes trop nombreux).

2. Hello World! RaspberryPi 1

2.1. Accès aux cartes RaspberryPi

Le schéma ci-dessous représente la plateforme des cartes RaspberryPi 1. Pour exécuter votre programme sur une carte RaspberryPi, vous devez vous connecter en ssh sur une carte RaspberryPi en passant par le routeur peri. Le routeur peri a deux adresses: 132.227.71.43 du coté laboratoire et 192.168.1.1 de l'autre coté. Le serveur de nom (DNS) du laboratoire a attribué l'adresse 132.227.71.43 le nom peri. Le routeur peri crée un sous-réseau (192.168.1.x) où sont connectées les cartes RaspberryPi. Les cartes sont numérotées de 20 à 26. Le routeur a été configuré pour reconnaître les adresses MAC des cartes et leur attribuer une adresse IP fixe. La carte n°X (X allant de 20 à 26) reçoit l'adresse IP 192.168.1.X (par exemple la carte n°20 a comme adresse sur ce réseau 192.168.1.20). Pour faire ça, le firmware du routeur a été remplacé par dd-wrt qui permet ce type de configuration. Le routeur a été également été configuré pour que tous les paquets entrants sur le port 62200+X de l'adresse peri soient routés vers l'adresse 192.168.1.X port 22. Le port 22 est celui sur lequel écoute le serveur ssh.

plateforme-IOC-raspberry.png

Il n'y a qu'un seul compte utilisateur sur une carte RaspberryPi. Le nom de login est pi et le mot de passe est raspberry. Comme, il n'y a qu'un compte par carte et que vous allez devoir vous les partager, vous devez créer un répertoire à la racine avec votre nom de votre login sur le réseau enseignement accolé avec un tiret avec le nom de login de votre binôme. Par exemple, deux personnes en binôme, dont les noms de login sont respectivement almada et fomentin vont créer un répertoire almada-fomentin (en minuscule).

Je vous demande de respecter cette convention
afin que je puisse vous identifier facilement.

Ainsi, pour se connecter en ssh sur la carte n°20 avec le login pi, il faut taper (remarquez que c'est un p minuscule):

$ ssh -p 62220 pi@peri
pi@peri's password: raspberry 

Pour copier un fichier file.x depuis votre compte enseignement sur le carte n°20 il faut taper (remarquez que c'est un P majuscule):

$ scp -P 62220 file.x pi@peri:almada-fomentin
pi@peri's password: raspberry 

Il est recommandé de ne pas laisser de fichiers source sur la carte SD, car celle-ci peut être reformatée en cas de dysfonctionnement.

2.2. Configuration des clés ssh

Taper le mot de passe à chaque fois que l'on se logue et à chaque copie peut être pénible à la longue. Pour éviter cela, il faut donner à votre RaspberryPi la clé publique du protocole de chiffrement utilisé pour la connexion. Vous allez utiliser les commandes suivantes sur le PC développement :

ssh-keygen -t rsa
ssh-copy-id -i $HOME/.ssh/id_rsa.pub -p 622x pi@peri
ssh -p 622x pi@peri
  • Pour la première commande, vous devez taper 3 fois sur la touche entrée pour choisir les valeurs par défaut.
  • Pour la deuxième commande, vous sélectionnez la bonne carte (en remplaçant x par le bon chiffre) et vous tapez le mot de passe raspberry (ce sera la dernière fois).
  • La troisième est là pour vérifier que vous n'avez plus besoin du mot de passe.

2.3. Prise en mains des outils de développement: Hello World!

La première étape consiste à vous familiariser avec les outils de développement. Pour cela, vous allez développer un petit programme de type "Hello World!" qui affiche une phrase sur la sortie standard grâce à un printf. Pour compiler votre programme, suivez les instructions suivantes :

  • Tout d'abord, configurez votre terminal pour utiliser le compilateur croisé. Ajoutez dans votre $HOME/.bashrc (sur le PC de développement) la ligne suivante et ouvrez un nouveau terminal.
    source /users/enseig/franck/peri/export_rpi_toolchain.sh
    
  • Une fois le terminal configuré, vérifiez que le compilateur est accessible, si cette commande ne retourne rien, la configuration n'a pas fonctionné.
    which bcm2708hardfp-gcc
    

Votre suite d'outils (toolchain) contient tous les outils nécessaires pour la compilation, l'édition des liens et la manipulation de binaires pour la carte RaspberryPi. Et tous ces outils sont préfixés par la même chaîne de caractères: bcm2708hardfp-. Il s'agit donc d'un compilateur pour un SoC BCM2708 avec l'option hardfp activée (calcul flottant matériel). Il s'agit bien du SoC de la carte RaspberryPi?.

Maintenant, pour compiler un programme C vers un binaire qui puisse s'exécuter sur la carte RaspberryPi?, il vous faut écrire un Makefile pour plus de facilité. Pour cela, suivez la syntaxe de base des Makefile:

cible: dépendances
    commande

Notez bien que l'indentation de la seconde ligne doit OBLIGATOIREMENT être une tabulation et non une suite d'espaces.

Vous pourrez donc par exemple, écrire la règle de Makefile suivante:

helloworld.x: helloworld.c
    bcm2708hardfp-gcc -o $@ $< -O2 -static

Ci-dessous, un Makefile un peu plus complexe qui se charge de la copie et qui utilise la règle de compilation implicite (juste par curiosité). Dans la suite, nous vous fournissons un nouveau Makefile que vous pourrez modifier.

CC=bcm2708hardfp-gcc
CFLAGS=-O2 -static
CARD=20
NAME=nom1-nom2
CFLAGS=-W -Wall -Wextra -Wfatal-errors -O2
APP=helloworld

all: $(APP)
    scp -P 622$(CARD) $^ pi@peri:$(NAME)

clean:
    rm $(APP)
Attention aux tabulations
Devant les commandes (scp et rm) vous devez mettre une tabulation. Si vous n'êtes pas familier avec les makefiles, consultez l'article de Wikipedia ou de Developpez.com.
0ption -static
L'option "-static" est utilisée par l'éditeur de lien. Elle est importante ici, car la librairie C du compilateur croisé n'est pas tout à fait identique à la librairie C sur la carte RaspberryPi. Ajouter "-static" à la ligne de compilation permet de créer un binaire qui contient en plus les fonctions de la librairie C utilisée par votre programme. Ceci permettra à celui-ci de ne pas essayer d'utiliser des fonctions de la librairie C installée sur la carte qui, sinon, aurait été chargée dynamiquement.

2.2. Contrôle de GPIO en sortie

Raspberry-Pi-GPIO-Layout-Revision-2.png

Dans cet exercice, on vous propose de manipuler une pin du GPIO en mode "sortie" pour contrôler le clignotement d'une LED à une fréquence donnée.

  • Avant de commencer à écrire du code, déterminez quelles sont les différentes étapes nécessaires depuis l'initialisation jusqu'à l'envoi des valeurs 0 ou 1 sur le GPIO.
  • Récupérer le répertoire lab1, exécuter la commande suivante dans un terminal et dans le dossier que vous souhaitez pour contenir votre code pour ce premier TP.
    cp -rp /users/enseig/franck/peri/lab1 .
    
  • Éditez le fichier lab1.c et ajoutez le code que vous pensez nécessaire à la réalisation de cet exercice.
    ATTENTION: ne changez pas les valeurs de GPIO_LED0, car vous risqueriez d'endommager la carte RaspberryPi.
  • Ensuite, compilez-le grâce au Makefile (Vous devez l'adapter) qui vous est fourni. Qu'observez-vous ? Essayez de changer la fréquence de clignotement.

2.3. Contrôle de plusieurs GPIO en mode "sortie"

Refaites le même exercice que précédemment, mais cette fois-ci configurez plusieurs GPIO en sorties.

  • La LED0 est connectée sur le GPIO 4, la LED1 est connectée sur le GPIO 17. Faites clignoter les deux LEDs.
  • Avant d'exécuter votre programme, vérifiez parce que le matériel est fragile, pour pouvez faire valider votre code avec le chargé de TP.

Vous devez faire clignoter les deux LEDs a des fréquences différentes et, si possible, parametrable par des arguments en ligne de commandes. Je vous conseille de créer une thread par LED

2.4. Lecture de la valeur d'une entrée GPIO

Maintenant que vous maîtrisez le contrôle d'un GPIO en sortie, passons au mode "entrée". Écrivez un programme qui configure la GPIO 18 en entrée. Vous devez afficher la valeur de ce GPIO dans une boucle infinie.

  • Une fois réalisé, compilez votre programme comme précédemment à l'aide du Makefile fourni.
  • Ensuite, copiez votre programme sur la carte comme indiqué dans l'exercice précédent.

On veut détecter l'appui sur le bouton poussoir. Pour cela, vous allez lire sa valeur périodiquement toutes les 20ms. L'appui ou le relâchement correspond est présent quand deux valeurs lues successivement sont différentes. Quand le bouton n'est pas enfoncé, la valeur de la broche est à 1. La structure du programme suggéré.

val_prec = 1
val_nouv = 1
BP_ON = 0
BP_OFF = 0
faire toujours:
   attendre 20ms
   val_nouv <- valeur du BP
   si (val_prec != val_nouv) ET (val_nouv == 0) (appui est detecte) alors BP_ON <- 1
   si un relachement est detecte alors BP_OFF <- 1
   si BP_ON :
      BP_ON <- 0                   // l'appui est un evenement ponctuel
      comportement quand un appui est detecte
   si BP_OFF :
      BP_OFF <- 0                  // Le relachemet est un évènement ponctuel 
      comportement quand un relachement est detecte

Commencer par tester que vous savez détecter l'appui sans gérer les LEDs.

Ensuite, faite un programme qui fait clignoter deux fois plus vite les LEDs lorsqu'on appuit sur le bouton une fois, et qui refait clignoter les LEDs normalement au nouvel appui.

2.5. Réalisation d'une petite librairie

Écrire les fonctions suivantes et fabriquer une fonction libgpio.a. Vous devez vous même, définir les prototypes des fonctions et écrire le Makefile pour fabriquer une library et linker votre programme avec. Si vous avez des difficultés, Google existe :-)) !

Indications : Une bibliothèque de fonction est en principe une archive de fichiers objet (.o). Dans notre cas, c'est un peu particulier, nous n'auront qu'un seul fichier objet à l'intérieur. Cette bibliothèque reçoit l'adresse de base physique des registres GPIO. Elle utilise des variables globale statique (local au fichier) pour stocker des états (file descriptor par exemple).

  • int gpio_init(void)
    • Ouverture et mapping
    • L'adresse de base des registres GPIO dans l'espace d'adressage physique est supposée connue, mais nous verrons que l'on peut savoir où se trouve les GPIO en lisant des variables exposées par le noyau.
    • Rend 0 si pas d'erreur; -1 en cas d'erreur.
  • int gpio_setup (int gpio, int direction)
    • Setup des broches en INPUT ou OUTPUT
    • gpio : un numéro de broche légal
    • direction : 0 = INPUT, 1 = OUTPUT
    • Rend 0 si pas d'erreur; -1 en cas d'erreur.
  • int gpio_read (int gpio, int * val)
    • Lecture d'une broche
    • gpio : un numéro de broche légal
    • * val : pointeur vers un buffer qui contiendra la valeur lue
    • Rend 0 si pas d'erreur; -1 en cas d'erreur
  • int gpio_write (int gpio, int val) : écriture d'une broche
    • Ecriture d'une broche
    • gpio : un numéro de broche légal
    • val : valeur à écrire
    • Rend 0 si pas d'erreur; -1 en cas d'erreur