Changes between Initial Version and Version 1 of IOC_T01


Ignore:
Timestamp:
Feb 4, 2021, 9:27:16 PM (4 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • IOC_T01

    v1 v1  
     1{{{#!protected
     2= 1. Préambule
     3
     4Au long de ce module, vous allez utiliser 4 nano-ordinateurs (!RaspberryPi 1B, !RaspberryPi 3B, Arduino nano et ESP32 TTGO-Lora32-Oled), quelques périphériques (LED, bouton, photorésistance, accéléromètre, etc.) et mettre en oeuvre plusieurs "briques technologiques" intervenant dans les systèmes d'objets communicants (HTTP, MQTT, sql, i2c, Arduino, etc.).
     5
     6Vous allez travailler en binôme. Le choix de votre binôme se fait à la première séance et est **définitif**.
     7Dans ce TP, vous allez écrire une application pour !RaspberryPi 1B afin de contrôler deux LEDs et un  bouton un bouton poussoir. Cette application aura trois threads POSIX, 1 par LED et 1 pour le bouton-poussoir.
     8Ce que vous allez devoir faire, c'est :
     9
     10* Compilation croisée via un compilateur déporté.
     11* Connexion à un système distant par ssh / scp.
     12* Manipulation directe des GPIO d'une !RaspberryPi en mode utilisateur.
     13* Écriture d'application multi-threadées simple.
     14
     15Vous écrirez un **compte-rendu au format markdown** (CR), dans lequel vous répondrez aux quelques questions du TME, vous mettrez les étapes de réalisation du TP, ainsi vous conserverez une trace et vous ajouterez tout ou partie de vos codes commentés (succinctement, mais proprement). Les comptes-rendus de TP doivent être suffisamment explicites pour que vous puissiez les réutiliser plus tard. Par exemple, vous allez devoir lire des documents externes et vous devrez référencer ces documents (mettre les liens) pour les retrouver. En fonction de vos connaissances de départ, vous pourrez être plus ou moins explicite sur tel ou tel point.
     16
     17* Le  document doit impérativement être nommé `ioc20_X_nom1_nom2.md` (où X est le numéro du TP: 1 pour le TP1, 2 pour le TP2, etc.)
     18* vos noms doivent figurer dans le document.
     19* Vous devez déposer votre CR sur un [https://docs.google.com/forms/d/e/1FAIpQLScZIBRnMg70TbUyXhWSYWOYBtOLdMlQFjM-rimTLEdrvvjKbA/viewform?usp=pp_url formulaire Google]
     20 
     21 '''Attention : je n'irai pas à la pêche, le non-respect des consignes pour les comptes-rendus sera noté 0 ! (vous êtes trop nombreux).'''
     22
     23
     24
     25= 2. Hello World! !RaspberryPi
     26
     27
     28
     29Le schéma ci-dessous représente la plateforme des cartes !RaspberryPi 1.
     30Pour exécuter votre programme sur une carte !RaspberryPi, vous devez vous connecter en `ssh` sur une des cartes !RaspberryPi en passant par le routeur peri. Le routeur peri a deux adresses: `132.227.71.43` du côté laboratoire et `192.168.1.1` de l'autre coté.  Le serveur de nom (DNS) du laboratoire a attribué le nom `peri` à l'adresse `132.227.71.43`. 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 IP sur ce réseau `192.168.1.20`).
     31Pour faire ça, le firmware du routeur a été remplacé par '''[[http://www.dd-wrt.com/site/index | 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 la carte !RaspberryPi d'adresse IP `192.168.1.X` port 22. Le port 22 est celui sur lequel écoute le serveur ssh.
     32
     33[[Image(htdocs:png/plateforme-IOC-raspberry.png, width=700px, nolink)]]
     34
     35Il n'y a qu'un seul compte utilisateur sur les cartes !RaspberryPi. Le nom de login est `pi` et le mot de passe est `raspberry` (ne le changez pas). Comme, il n'y a qu'un compte par carte et que vous allez devoir vous le partager, vous devez créer un répertoire à la racine avec '''votre nom de votre login''' sur le réseau enseignement accolé avec un underscore (_) 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). En principe, vous allez toujours utiliser la même !RaspberryPi, mais vous pouvez vous déplacer en cas de problème, cependant veillez à une répartition équitable.
     36
     37    '''Je vous demande de respecter cette convention'''[[BR]]
     38    '''afin que je puisse vous identifier facilement.'''
     39 
     40
     41Pour se connecter en ssh sur la carte n°`20` avec le login `pi`, il faut utiliser le port 62220 de l'adresses IP `peri`(132.227.71.43) et les paquets sont routés sur l'adresse 192.168.1.20 port 22, il faut taper (remarquez que c'est un `p` minuscule)
     42{{{#!sh
     43$ ssh -p 62220 pi@peri
     44pi@peri's password: raspberry
     45}}}
     46
     47Pour copier un fichier `file.x` depuis votre compte enseignement sur le carte n°`20` il faut taper (remarquez que c'est un `P` majuscule):
     48{{{#!sh
     49$ scp -P 62220 file.x pi@peri:almada_fomentin
     50pi@peri's password: raspberry
     51}}}
     52
     53Il est recommandé de ne pas laisser de fichiers source sur la carte SD d'une !RaspberryPi, car celle-ci peut être reformatée en cas de dysfonctionnement.
     54
     55 **Questions pour voir si vous avez compris**::
     56 1. Pourquoi passer par la redirection des ports ? (faites une réponse succincte)
     57 1. Pourquoi faut-il que vos fichiers soit dans un répertoire propre sur une !RaspberryPi ?
     58
     59
     60
     61= 3. Configuration des clés ssh
     62
     63
     64
     65Taper le mot de passe à chaque fois que l'on se logue et à chaque copie peut être pénible à la longue.
     66Pour é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 :
     67
     68{{{#!sh
     69ssh-keygen -t rsa
     70ssh-copy-id -i $HOME/.ssh/id_rsa.pub -p 622x pi@peri
     71ssh -p 622x pi@peri
     72}}}
     73
     74* Pour la première commande, vous devez taper 3 fois sur la touche entrée pour choisir les valeurs par défaut.
     75* 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).
     76* La troisième est là pour vérifier que vous n'avez plus besoin du mot de passe.
     77
     78
     79
     80= 4. Prise en mains des outils de développement: Hello World!
     81
     82
     83
     84La première étape consiste à vous familiariser avec les outils de
     85développement. Pour cela, vous allez développer un petit programme
     86de type "Hello World!" qui affiche une phrase sur la sortie standard
     87grâce à un printf.
     88Pour compiler votre programme, suivez les instructions suivantes :
     89
     90* 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.
     91  {{{#!sh
     92  source /users/enseig/franck/IOC/export_rpi_toolchain.sh
     93  }}}
     94
     95* Une fois le terminal configuré, vérifiez que le compilateur est accessible, si cette commande ne retourne rien, la configuration n'a pas fonctionné.
     96  {{{#!sh
     97  which bcm2708hardfp-gcc
     98  }}}
     99   
     100
     101Votre 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. 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 le SoC BCM2708 avec l'option hardfp activée (calcul flottant matériel). Il s'agit bien du SoC de la carte !RaspberryPi 1B.
     102
     103Maintenant, 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:
     104{{{#!make
     105cible: dépendances
     106        commande
     107}}}
     108Notez 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:
     109{{{#!make
     110helloworld.x: helloworld.c
     111        bcm2708hardfp-gcc -o $@ $< -O2 -static
     112}}}
     113
     114Ci-dessous, un `Makefile` un peu plus complexe qui se charge de la copie et qui utilise la règle de compilation implicite. Dans la suite, nous vous fournissons un nouveau Makefile que vous pourrez modifier.
     115
     116{{{#!make
     117CC=bcm2708hardfp-gcc
     118CFLAGS=-O2 -static
     119CARD=20
     120NAME=nom1-nom2
     121CFLAGS=-W -Wall -Wextra -Wfatal-errors -O2
     122APP=helloworld
     123
     124all: $(APP)
     125        scp -P 622$(CARD) $^ pi@peri:$(NAME)
     126
     127clean:
     128        rm $(APP)
     129}}}
     130
     131  **__Attention aux tabulations__**::
     132  Devant les commandes (scp et rm) vous devez mettre une tabulation. Si vous n'êtes pas familier avec les makefiles, consultez l'article
     133  de [https://fr.wikipedia.org/wiki/Make Wikipedia] ou de [http://gl.developpez.com/tutoriel/outil/makefile/ Developpez.com].
     134
     135  **__0ption -static__**::
     136  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.
     137
     138Vous devez donc :
     139* Créer un répertoire `helloworld` sur le PC de dev et vous y déplacer
     140* Créer un fichier C `helloworld.c` avec seulement la fonction `main()` qui affiche "Helloworld !" sur stdout.
     141* Créer un Makefile sur le modèle du dernier exemple en le modifiant.
     142* Crosscompiler et uploader l'exécutable sur votre !RaspberryPi
     143* Exécuter le programme.
     144
     145
     146
     147= 5. Contrôle de GPIO en sortie
     148
     149
     150
     151[[Image(htdocs:png/Raspberry-Pi-GPIO-Layout-Revision-2.png,500px,nolink)]]
     152
     153Dans 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.
     154
     155* 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.
     156{{{#!sh
     157cp -rp /users/enseig/franck/IOC/lab1 .
     158}}}
     159* Éditez le fichier blink0.c
     160  ATTENTION: ne changez pas les valeurs de GPIO_LED0, GPIO_LED1 et GPIO_BP, car vous risqueriez d'endommager la carte !RaspberryPi.
     161* Ensuite, compilez-le grâce au Makefile (**Vous devez l'adapter à votre carte et vos noms**) qui vous est fourni.
     162* Exécuter le avec `sudo` sans argument et changer la fréquence de clignotement en passant un argument.
     163{{{#!sh
     164sudo ./blink0
     165}}}
     166
     167
     168 **Questions sur le code de blink0.c **::
     169 1. A quoi correspond l'adresse `BCM2835_GPIO_BASE` ?
     170 1. Que représente la structure `struct gpio_s` ?
     171 1. Dans quel espace d'adressage est l'adresse `gpio_regs_virt` ?
     172 1. Dans la fonction `gpio_fsel()`, que contient la variable reg ?
     173 1. Dans la fonction `gpio_write()`, pourquoi écrire à deux adresses différentes en fonction de la valeur val ?
     174 1. Dans la fonction `gpio_mmap()`, à quoi correspondent les flags de `open()` ?
     175 1. Dans la fonction `gpio_mmap()`, commentez les arguments de `mmap()`.
     176 1. Que fait la fonction `delay()` ?
     177 1. Pourquoi doit-on utiliser `sudo`
     178
     179
     180
     181= 6. Contrôle de plusieurs GPIO en mode "sortie"
     182
     183
     184
     185Vous allez maintenant faire clignoter deux LEDs à des fréquences différentes et paramétrables sur la ligne de commande. Pour tous les exercices ci-après, vous devez changer votre Makefile en ajoutant les programmes à compiler.
     186
     187* Commencez par faire une copie du fichier `Blink0.c` en `blink0_pt.c`. Vous allez créer un thread Posix dans `blink0_pt.c` pour faire clignoter une led, c'est donc le même comportement que blink0 avec un pthread pour la partie `// Blink led ...` (à la fin de `main()`).
     188* Faites ensuite une une copie du fichier `Blink0_pt.c` en `blink01_pt.c`et faites clignoter les deux LEDs en utilisant deux pthreads.
     189
     190
     191
     192= 7. Lecture de la valeur d'une entrée GPIO
     193
     194
     195
     196Maintenant que vous maîtrisez le contrôle d'un GPIO en sortie, passons au mode "entrée".
     197
     198* Écrivez un programme `read_bp.c` qui configure la GPIO 18 en entrée (là où est connecté le bouton-poussoir) et qui affiche la valeur de ce GPIO dans une boucle infinie.
     199* On veut maintenant détecter l'appui sur le bouton-poussoir. Pour cela, reprenez le programme `blink01_pt.c` et faites une copie en `blink01_bp_pt.c`. Dans un pthread, vous allez lire la valeur du bouton périodiquement toutes les 20ms. L'appui ou le relâchement est présent quand deux valeurs lues successivement sont différentes. Quand le bouton n'est pas enfoncé, la valeur de la pin est à 1.
     200* Commencer par tester que vous savez détecter l'appui sans gérer les LEDs.
     201* Ensuite, modifier le programme pour faire fonctionner la led0 en télérupteur (un appui changer l'état de la LED) alors que la led1 continue de clignoter périodiquement.
     202
     203**Principe d'utilisation du changement d'état du bouton**
     204
     205Deux variables globales pour le passage de l'information entre la tâche (le thread) qui gère le bouton et détecte les événements d'appui et la tâche qui attend ces événements.
     206`BP_ON` et `BP_OFF` sont un peu des ''lignes d'interruption'' qui sont levées (mise à 1) par la tâche qui gère le bouton et baissées (acquittées, mise à 0) par la (ou les tâches) qui attendent après qu'elles aient vu le passage à 1.
     207
     208{{{#!c
     209BP_ON = 0   // mis à 1 si le bouton a été appuyé, mis à 0 quand la tâche qui attend l'appui a vu l'appui
     210BP_OFF = 0  // mis à 1 si le bouton a été relâché, mis à 0 quand la tâche qui attend le relâchement a vu le relâchement
     211}}}
     212
     213Pseudo-code de la tâche de détection du changement d'état du bouton
     214{{{#!c
     215val_prec = 1
     216val_nouv = 1
     217faire toujours
     218   attendre 20ms
     219   val_nouv <- valeur du BP
     220   si (val_prec != val_nouv) alors   // changement d'état
     221      si (val_nouv == 0) alors       // appui détecté
     222         BP_ON <- 1
     223      sinon                          // relâchement détecté
     224         BP_OFF <- 1
     225      fsi
     226      val_prec = val_nouv
     227   fsi
     228finfaire
     229}}}
     230
     231Pseudo-code d'une tâche qui attendrait à la fois l'appui et le relâchement du bouton. Notez que dans le cas du télérupteur, seul un des deux changements d'état est utilisé.
     232
     233{{{#!c
     234si BP_ON alors
     235   BP_ON <- 0                   // l'appui est un événement ponctuel
     236   comportement quand un appui est detecte
     237fsi
     238si BP_OFF alors
     239   BP_OFF <- 0                  // Le relâchement est un événement ponctuel
     240   comportement quand un relâchement est detecte
     241fsi
     242}}}
     243
     244
     245= TP3 : Pilotage d'un écran LCD en mode utilisateur et par un driver =
     246
     247
     248Objectifs:
     249* Fonctionnement d'un écran LCD et fonctions de base
     250* Ecriture d'un driver d'affichage
     251* Usage de IOCTL
     252* Affichage du monitoring système
     253
     254Pré-requis (Raspberry Pi):
     255* Outils de compilation croisée
     256* Configuration et utilisation des GPIO
     257
     258Ressources:
     259* newhaven display est le fabriquant du module LCD, c'est donc, en principe la référence.
     260  * Dans ce document ([[http://www.newhavendisplay.com/specs/NHD-0420DZ-FL-YBW.pdf | Datasheet du LCD du module]]), il est dit que le contrôleur du LCD est le ST7066U, mais, sauf preuve du contraire, la séquence
     261    d'initialisation du LCD décrite dans la documentation du [[http://www.newhavendisplay.com/app_notes/ST7066U.pdf | ST7006U]]
     262    ne fonctionne pas...
     263* HD44780 est le contrôleur historique de la majorité des LCD de ce genre. La procédure d'initialisation fonctionne.
     264  * [[http://en.wikipedia.org/wiki/Hitachi_HD44780_LCD_controller | LCD Display HD44780]]
     265    [[https://www.sparkfun.com/datasheets/LCD/HD44780.pdf | Datasheet du HD44780]]
     266* Le document suivant définit comment est adressé la mémoire "vidéo" du LCD.
     267  * [[http://web.alfredstate.edu/faculty/weimandn/lcd/lcd_addressing/lcd_addressing_index.html | Adressage de la mémoire interne de l'afficheur]]
     268
     269Prenez le temps de parcourir les documents sur les afficheurs avant de commencer le TP.
     270
     271Le code à modifier se trouve en pièces jointes de cette page [htdocs:docs/lcd/lcd_user.c lcd_user.c] et [htdocs:docs/lcd/Makefile Makefile]. Vous pourrez les copier dans un répertoire `TP4/lcd_user/`
     272
     273== 1. Configuration des GPIO pour le pilotage de l'écran ==
     274
     275L'écran LCD de type HD44780 dispose de plusieurs signaux de contrôle et de données. Les signaux de contrôle sont au nombre de 3: RS, RW et E. Les signaux de données sont au nombre de 4 ou 8 suivant le mode.
     276
     277Dans ce TP, nous utiliserons l'écran en mode 4-bit car la carte Raspberry Pi dispose d'un nombre limité de GPIO. De plus, le signal RW sera connecté directement à 0V (donc toujours en écriture) car nous n'allons gérer que les opérations d'écriture (Note: les GPIO de la carte Raspberry Pi fonctionnent en 3.3V, ils ne supportent pas les niveaux de tension 5V émis par l'afficheur, demandez-moi cela n'est pas clair).
     278
     279Les signaux de contrôle RS et E sont utilisés de la manière suivante:
     280* RS vaut 1 pour l'envoi d'une donnée (e.g. un caractère) et vaut 0 pour l'envoi d'une commande (instruction).
     281* E est un signal de validation; la valeur sur le bus de données (4 bits) est pris en compte à chaque front descendant de ce signal.
     282
     283Voici le mapping des GPIO pour les différents signaux:
     284
     285||=   Signal LCD  =||=     GPIO     =||
     286||       RS        ||       7        ||
     287||       E         ||       27       ||
     288|| D4, D5, D6, D7  || 22, 23, 24, 25 ||
     289
     290Dans la documentation de l'afficheur, on peut savoir :
     291* Comment faut-il configurer les GPIOs pour les différents signaux de l'afficheur LCD ?
     292* Comment écrire des valeurs vers le LCD ?
     293* Quelles valeurs doivent être envoyées vers l'afficheur pour réaliser l'initialisation ?
     294* Comment demander l'affichage d'un caractère ?
     295* Comment envoyer des commandes telles que : l'effacement de l'écran, le déplacement du curseur, etc. ?
     296
     297En particulier, page 11 de la documentation ([[http://www.newhavendisplay.com/specs/NHD-0420DZ-FL-YBW.pdf | Datasheet du LCD de la plateforme]]) se trouve du pseudo-code pour l'usage de l'afficheur.
     298* P1 est un registre dont l'état est recopié sur les broches D0 à D7 (bit7 de P1 sur D7, bit6 sur D6, etc. jusqu'à D4).
     299* D_I est un registre dont l'état est recopié sur RS qui indique si on envoie un caractère (D) ou une instruction (I).
     300* R_W est un registre dont l'état est recopié sur RW
     301* Delay() est une attente en microsecondes.
     302
     303{{{#!c
     3044-bit Initialization:
     305/**********************************************************/
     306void command(char i)
     307{
     308  P1 = i;                       //put data on output Port
     309  D_I =0;                       //D/I=LOW : send instruction
     310  R_W =0;                       //R/W=LOW : Write       
     311  Nybble();                     //Send lower 4 bits
     312  i = i<<4;                     //Shift over by 4 bits
     313  P1 = i;                       //put data on output Port
     314  Nybble();                     //Send upper 4 bits
     315}
     316/**********************************************************/
     317void write(char i)
     318{
     319  P1 = i;                       //put data on output Port
     320  D_I =1;                       //D/I=HIGH : send data
     321  R_W =0;                       //R/W=LOW : Write   
     322  Nybble();                     //Clock lower 4 bits
     323  i = i<<4;                     //Shift over by 4 bits
     324  P1 = i;                       //put data on output Port
     325  Nybble();                     //Clock upper 4 bits
     326}
     327/**********************************************************/
     328void Nybble()
     329{
     330  E = 1;
     331  Delay(1);                     //enable pulse width  >= 300ns
     332  E = 0;                        //Clock enable: falling edge
     333}
     334/**********************************************************/
     335void init()
     336{
     337  P1 = 0;
     338  P3 = 0;
     339  Delay(100);                   //Wait >40 msec after power is applied
     340  P1 = 0x30;                    //put 0x30 on the output port
     341  Delay(30);                    //must wait 5ms, busy flag not available
     342  Nybble();                     //command 0x30 = Wake up 
     343  Delay(10);                    //must wait 160us, busy flag not available
     344  Nybble();                     //command 0x30 = Wake up #2
     345  Delay(10);                    //must wait 160us, busy flag not available
     346  Nybble();                     //command 0x30 = Wake up #3
     347  Delay(10);                    //can check busy flag now instead of delay
     348  P1= 0x20;                     //put 0x20 on the output port
     349  Nybble();                     //Function set: 4-bit interface
     350  command(0x28);                //Function set: 4-bit/2-line
     351  command(0x10);                //Set cursor
     352  command(0x0F);                //Display ON; Blinking cursor
     353  command(0x06);                //Entry Mode set
     354}
     355/**********************************************************/
     356}}}
     357
     358== 2. Fonctionnement de l'écran et fonctions de base ==
     359
     360La prise en compte de la donnée est réalisée lors d'un front descendant du signal E.
     361Pour créer un front descendant:
     362* on place la donnée,
     363* puis le signal E est mis à 1 pendant 1µs.
     364
     365[[Image(htdocs:png/command_lcd.png, width=900px, nolink)]]
     366
     367Nous utilisons l'afficheur LCD en mode 4 bits. Or, les commandes et les données sont transmises sur 8 bits ou 1 octet. Ainsi, toutes les commandes et toutes les données sont transmises en deux étapes: les 4 bits de poids fort et ensuite les 4 bits de poids faible.
     368
     369Nous avons toutes les fonctions dont nous avons besoin. Maintenant regardons d'un peu plus près la phase d'initialisation de l'afficheur LCD. Au démarrage, l'afficheur est dans un mode non défini (8 bits ou 4 bits). Il faut donc le forcer en mode 4 bits.
     370
     371Vous êtes prêt à tester votre code et vérifier qu'il affiche correctement une chaine de caractère.
     372erry Pi.
     373
     374Il peut être utile de manipuler la position du curseur pour choisir où placer les caractères sur l'afficheur.
     375
     376Pour cela, l'afficheur dispose de trois instructions: Cursor home, Display clear et Set DDRAM address. La dernière instruction est relative à la mémoire interne de l'afficheur (Display Data RAM).
     377
     378La mémoire DDRAM est construite de la manière suivante:
     379||0x00 ..... Ligne 1 ..... 0x13||
     380||0x40 ..... Ligne 2 ..... 0x53||
     381||0x14 ..... Ligne 3 ..... 0x27||
     382||0x54 ..... Ligne 4 ..... 0x67||
     383
     384On souhaites utiliser toutes les lignes
     385
     386|| **Questions:**\\1. Répondez aux questions qui se trouve dans le code [attachment:lcd_user.c lcd_user.c] \\2. Ecrire une fonction lcd_set_cursor qui positionne le curseur aux coordonnées (x,y) avec x la colonne, y la ligne.\\3. Ecrire un programme qui affiche la chaine passée en paramètre sur chacune des lignes de l'afficheur.
     387
     388== 4. Driver pour le LCD ==
     389
     390* Reprenez le pilote que vous aviez écrit pour les leds et modifiez la commande `write` de telle sorte que si vous appelez votre device /dev/lcd_xy (où xy sont les initiales de votre binôme), vous puissiez écrire.
     391{{{#!bash
     392$ echo "Bonjour" > /dev/lcd_xy
     393}}}
     394 Vous allez devoir remplacer usleep() par udelay() et ajouter un `#include <asm/delay.h>` au début du code de votre module (merci Arthur).
     395 Vous devez (en principe) allouer de la mémoire noyau pour ce driver et utiliser la fonction `copy_from_user(unsigned long dest, unsigned long src, unsigned long len);` pour le message à écrire.
     396
     397* ajouter la gestion de quelques caractères de contrôle [https://theasciicode.com.ar/ascii-control-characters/backspace-ascii-code-8.html ASCII], tels que LF ('\n') ou BS.
     398
     399* Ajoutez la gestion de l'appel système unlocked_ioctl() pour
     400  * effacer l'écran : LCDIOCT_CLEAR
     401  * position le curseur : LCDIOCT_SETXY (vous pouvez coder les coordonnées dans l'argument).
     402
     403= 8. Compte rendu
     404
     405
     406
     407Vous devez rendre un compte-rendu ioc20_X_nom1_nom2.zip avec
     408* ioc21_X_nom1_nom2.md contenant la description des expériences et les réponses aux questions
     409* 2 répertoires avec vos sources ''correctement'' commentées (juste le nécessaire).
     410  * `helloword` avec `helloworld.c` et `Makefile``
     411  * `lab1`avec `Makefile`, `blink0.c`, `blink0_pt.c`, `blink01_pt.c`, `read_bp.c`, et `blink01_bp_pt.c`.
     412
     413