Changes between Version 1 and Version 2 of SujetTP6-2018


Ignore:
Timestamp:
Apr 6, 2018, 8:17:55 AM (7 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • SujetTP6-2018

    v1 v2  
    1 {{{#!protected
    2 = Programmation Arduino & communication NRF24L01
     1
     2= Programmation Arduino
    33
    44== Objectifs de la séance
     
    10104. Communication point-à-point NRF24L01
    1111
    12 == Remarques ==
    13 
    14 Vous devez utiliser la version d'arduino qui se trouve :
     12== Préambule ==
     13
     14Vous devez utiliser la version d'arduino qui se trouve (je vous conseille d'ajouter le chemin `/opt/arduino-1.6.8` dans la variable PATH dans le `.bashrc`.
    1515{{{#!bash
    1616> /opt/arduino-1.6.8/arduino
     
    2222- [http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.ReferenceMaxi Langage Arduino]
    2323
    24 == Objectif ==
    25 
    26 Le but de cette séance est de faire de la programmation sur Arduino en utilisant 3 entrées-sorties :
     24Lors de cette séance, nous allons programmer sur Arduino en utilisant :
    2725- La LED présente sur le module.
    2826- L'écran OLED
     27- Le capteur de lumière (cherchez-le :-)
     28- Le capteur sonore.
    2929- Le port série qui relie le module et le PC.
    30 
    31 Nous voulons aussi expérimenter la programmation "multi-tâches". Les guillemets expriment le fait qu'il s'agit plus d'une structure du programme que d'une vraie programmation multi-tâches dans la mesure où il n'y a pas de système d'exploitation. Toutefois, les "tâches" seront programmées de sorte à être indépendantes de leur contexte d'utilisation et elles seront "ordonnancées" par la fonction loop().
     30- Le port SPI qui relie le module au NRF
     31
     32== Démarrage
     33
     34La première chose est d'exécuter l'exemple `blink``
     35- Brancher le module Arduino avec le câble USB
     36- lancer : `/opt/arduino-1.6.8/arduino &`
     37- Sélectionner : `Tools -> Boards -> Arduino Nano`
     38- Sélectionner : `Tools -> Processor -> ATmega328`
     39- Sélectionner : `Tools -> Ports -> /dev/ttyUSB0 ou /dev/ttyUSB1`
     40- Charger le programme Blink : `File -> Exemples -> 0.1 Basics -> Blink`
     41- Cliquer sur l'icône en forme de V pour Compiler
     42- Cliquer sur l'icône en forme de -> pour uploader
     43- En bas de la fenêtre un message vous indique la réussite de la compilation et de l'upload.
     44- La led doit clignoter sur le module
     45
     46Changer la fréquence et tester.
    3247
    3348== Exécution ''multi-tâches'' ==
    3449
    35 Nous allons donc voir comment il est possible de programmer des applications multi-tâches coopératives dans l'environnement Arduino sans pour autant disposee des services d'un OS. Le code a été volontairement simplifié à l'extrême afin de bien comprendre le principe. ICI, toute l'application sera dans un seul fichier et nous n'allons pas utiliser la programmation objet pour ne pas complexifier (mais nous le ferons plus tard).
    36 
    37 Chaque tâche est représentée par une fonction qui code son comportement.
    38 Dans l'environnement Arduino, la fonction loop() s'exécute en boucle, c'est elle qui va séquencer l'exécution des tâches.
    39 La fonction loop() demande donc l'exécution des tâches à tour de rôle.
     50Il est possible de programmer des applications multi-tâches coopératives dans l'environnement Arduino sans pour autant dispose des services d'un OS. Le code a été volontairement simplifié à l'extrême afin de bien comprendre le principe. ICI, toute l'application sera dans un seul fichier et nous n'allons pas utiliser la programmation objet pour ne pas complexifier.
     51
     52Chaque tâche est représentée par une fonction `loop_Tache()` qui code son comportement.
     53Dans l'environnement Arduino, la fonction `loop()` s'exécute en boucle, c'est elle qui va séquencer l'exécution des fonction `loop_Tache()`.
     54La fonction `loop()` demande donc l'exécution des fonction `loop_Tache()` à tour de rôle.
    4055Les tâches n'ont pas le droit de conserver le processeur sinon cela crée un blocage du système.
     56Cela signifie qu'il est interdit de faire des boucles d'attente d'un événement/
    4157La structure générale d'une tâche est la suivante :
    4258
    4359{{{#!c
    44 void Tache(arguments) {
    45    // test de la condition d'exécution
     60void loop_Tache(arguments) {
     61   // test de la condition d'exécution, si absent on SORT
    4662   if (evement_attendu_absent) return;
    4763   // code de la tache
     
    5066}}}
    5167
    52 Pour les tâches périodiques, nous pouvons écrire une fonction qui exploite un timer interne du processeur qui s'incrémente chaque microseconde. Cette fonction nommée `waitFor(int timer, unsigned long period)` prend deux paramètres `timer` et `period`. Le premier un numéro de timer (il en faudra autant que de tâches périodiques). Le second est une période en microsecondes.
     68Dans le cas plus général, les tâches ont un contexte d'exécution représenté par une variable globale sous forme d'une structure. Cette structure est passée en argument de la tâche. Cette structure doit le plus souvent être initialisée. L'initialisation est faite par une seconde fonction `setup_Tache()`, laquelle doit être appelée par la fonction `setup()`.
     69
     70Pour les tâches périodiques (elles sont fréquentes), nous pouvons écrire une fonction qui exploite un timer interne du processeur qui s'incrémente chaque microseconde. Cette fonction nommée `waitFor(int timer, unsigned long period)` prend deux paramètres `timer` et `period`. Le premier un numéro de timer (il en faudra autant que de tâches périodiques). Le second est une période en microsecondes.
    5371
    5472`wairFor()` peut être appelée aussi souvent que nécessaire, elle rend 1 une seule fois par période (second paramètre).
    5573Si elle n'est pas appelée pendant longtemps alors elle rend le nombre de périodes qui se sont écoulées.
    5674
    57 Dans l'application suivante nous avons deux tâches périodiques `Led()` et `Mess()`.
     75Dans l'application suivante nous avons deux tâches périodiques `Led` et `Mess`.
    5876La première fait clignoter une led dont le numéro est passé en paramètre à 5Hz.
    5977La seconde affiche bonjour à une fois par seconde.
     78
    6079{{{#!c
    6180// --------------------------------------------------------------------------------------------------------------------
     
    84103}
    85104
    86 void Led(int timer, long period, byte led) {
    87   static byte val = 0;                                    // valeur à mettre sur la led
    88   if (!waitFor(timer,period)) return;                     // sort s'il y a moins d'une période écoulée
    89   digitalWrite(led,val);                                  // ecriture
    90   val = 1 - val;                                          // changement d'état
    91 }
    92 
    93 void Mess(int timer, long period, const char * mess) {
    94   if (!(waitFor(timer,period))) return;                   // sort s'il y a moins d'une période écoulée
    95   Serial.println(mess);                                   // affichage du message
    96 }
     105//--------- définition de la tache Led
     106
     107typedef struct Led_st {
     108  int timer;                                              // numéro de timer utilisé par WaitFor
     109  int period;                                             // periode de clignotement
     110  int pin;                                                // numéro de la broche sur laquelle est la LED
     111  int etat;                                               // etat interne de la led
     112} Led_t ;
     113
     114void setup_Led( Led_t * ctx, int timer, long period, byte pin) {
     115  ctx->timer = timer;
     116  ctx->period = period;
     117  ctx->pin = pin;
     118  ctx->etat = 0;
     119  pinMode(pin,OUTPUT);
     120  digitalWrite(pin, ctx->etat);
     121}
     122
     123void loop_Led(Led_t * ctx) {
     124  if (!waitFor(ctx->timer, ctx->period)) return;          // sort s'il y a moins d'une période écoulée
     125  digitalWrite(ctx->pin,ctx->etat);                       // ecriture
     126  ctx->etat = 1 - ctx->etat;                              // changement d'état
     127}
     128
     129//--------- definition de la tache Mess
     130
     131typedef struct Mess_st {
     132  int timer;                                              // numéro de timer utilisé par WaitFor
     133  int period;                                             // periode d'affichage
     134  char mess[20];
     135} Mess_t ;
     136
     137void setup_Mess(Mess_t * ctx, int timer, long period, const char * mess) {
     138  ctx->timer = timer;
     139  ctx->period = period;
     140  strcpy(ctx->mess, mess);
     141  Serial.begin(9600);                                     // initialisation du débit de la liaison série
     142}
     143
     144void loop_Mess(Mess_t *ctx) {
     145  if (!(waitFor(ctx->timer,ctx->period))) return;         // sort s'il y a moins d'une période écoulée
     146  Serial.println(ctx->mess);                              // affichage du message
     147}
     148
     149//--------- Déclaration des tâches
     150
     151Led_t led1;
     152Mess_t mess1;
     153
     154//--------- Setup et Loop
    97155
    98156void setup() {
    99   pinMode(13,OUTPUT);                                     // initialisation de la direction de la broche
    100   Serial.begin(115200);                                   // initialisation du débit de la liaison série
     157  setup_Led(&Led1, 0, 100000, 13);                        // Led est exécutée toutes les 100ms
     158  setup_Mess(&Mess1, 1, 1000000, "bonjour");              // Mess est exécutée toutes les secondes
    101159}
    102160
    103161void loop() {
    104   Led (0,100000,13);                                      // Led est exécutée toutes les 100ms
    105   Mess(1,1000000,"bonjour");                              // Mess est exécutée toutes les secondes
    106 }
    107 }}}
     162  loop_Led(&Led1);                                       
     163  loop_Mess(&Mess1);
     164}
     165                                     
     166}}}
     167
     168**Questions**
     169- Que contient le tableau `waitForTimer[]`` ?
     170- Dans quel cas la fonction `waitFor()` peut rendre 2 ?
     171- Modifier le programme initial pour afficher "Salut" et plus de "bonjour" toutes les 1.5 secondes sans changer le comportement existant.
    108172
    109173== Utilisation de l'écran ==
     
    116180- Vous pouvez exécuter l'exemple proposé dans la bibliothèque. Cette bibliothèque fonctionne pour plusieurs
    117181  types modèles. Vous allez choisir le bon exemple : 128x32 I2C.
     182- Pour ajouter une bibliothèque Arduino, vous devez simplement télécharger le .zip et importer directement le
     183  .zip en sélectionnant le menu `Sketch -> include Library -> Add ZIP Library`
     184- Pour tester la librairie rendez-vous dans `File -> Exemples -> Adafruit SSD1306 -> ssd1306_128x32_i2c`.
     185  Il s'agit d'un programme qui teste les fonctionnalité de l'écran et de la bibliothèque graphique.
    118186
    119187**Questions**
    120 - Que contient le tableau `waitForTimer[]`` ?
    121 - Dans quel cas la fonction `waitFor() peut rendre 2 ?
    122 - Quel est le numéro de l'abonné I2C de l'écran ?
    123 - Modifier le programme initial pour afficher "bonjour" sur l'Oled toutes les 2 secondes sans changer le comportement existant.
     188
     189- Extraire de ce code, ce qui est nécessaire pour juste afficher un compteur qui s'incrémente toutes des 1 seconde sur l'écran OLED.
    124190
    125191== Communications inter-tâches ==
     
    137203} mb0 = {.state = EMPTY};
    138204
    139 void T1(&mb) {
     205void loop_T1(&mb) {
    140206  if (mb->state != EMPTY) return; // attend que la mailbox soit vide
    141207  mb->val = 42;
     
    143209}
    144210
    145 void T2(&mb) {
     211void loop_T2(&mb) {
    146212  if (mb->state != FULL) return; // attend que la mailbox soit pleine
    147213  // usage de mb->val
     
    152218**Questions**
    153219
    154 - Vous allez reprendre le programme ci-dessus qui fait clignoter la LED et afficher un message périodique sur l'écran Oled et le transformer de sorte à modifier son comportement par des commandes envoyé par l'utilisateur depuis le clavier du PC.
    155 
    156 - Le programme est en attente d'un commande pour contrôler l'état de la LED et le message affiché sur l'écran Oled. Les commandes pourraient être :
    157   - A : met la lED en clignotement.
    158   - E : éteint la LED.
    159   - P message : change le message affiché sur l'écran.
    160 
    161   Le langage est volontairement simple pour que l'interprétation de la commande soit simple.
    162   Commencez par les commandes de la LED
    163 
    164   Quand cela marche ajouter la commande de message.
    165 
    166 - Pour la tâche qui lit le clavier vous avez deux possibilités. Un tâche "normale" qui est ordonnancée par la fonction loop() ou une tâche dont l'exécution est déclenchée par l'arrivée d'un caractère. En effet, l'arrivée d'un caractère déclenche une interruption, il est possible de programmer une routine d'interruption ISR (Interrupt Service Routine). Faites une tâche "normale" et tenter de faire une tâche ISR.
    167 
    168 
    169 
    170 
    171 
    172 
    173 
    174 
    175 
    176 
    177 == Récupération de la bibliothèque du NRF24L01 ==
    178 
    179 Si nous voulons continuer à cross compiler, il faut installer la librairie qui va permettre de contrôler le module NRF24L01. Il existe plusieurs librairies. Celle choisie à le mérite d'être disponible dans l'environnement !RaspberryPi et !Arduino. C'est-à-dire que lorsque vous aurez compris comment l'utiliser avec la !RaspberryPi, le passage sur Arduino sera facile.
     220- Ajouter une tâche qui lit toutes les 0,5 seconde le port analogique 15 (par `analogRead()`) sur lequel se
     221  trouve la photo-résistance et qui sort sa valeur dans une boite à lettre.
     222- Mofifier la tâche Led pour que la fréquence de clignotement soit inversement proportionnel à la lumière reçue
     223  (moins il y a de lumière plus elle clignote vite). La tâche Led devra donc se brancher sur la boite à lettre.
     224
     225◊{{{#!protected
     226= Utilisation du module NRG24L01
     227
     228
     229== Récupération de la bibliothèque du NRF24L01
     230
     231Si nous voulons continuer à cross compiler, il faut installer la librairie qui va permettre de contrôler le module NRF24L01. Il existe plusieurs librairies. Celle choisie à le mérite d'être disponible dans l'environnement !RaspberryPi et Arduino. C'est-à-dire que lorsque vous aurez compris comment l'utiliser avec la !RaspberryPi, le passage sur Arduino sera facile.
    180232
    181233 * Aller sur le site https://github.com/tmrh20/RF24
     
    203255 * [https://github.com/TMRh20/RF24 Repository API TMRh20/RF24]
    204256
    205 == Communication entre le capteur et la base ==
     257== Premier usage : lecture d'un capteur
    206258
    207259La documentation de la bibliothèque est [http://tmrh20.github.io/RF24/classRF24.html ici] dont voici un résumé :
     
    218270* `void         openReadingPipe (uint8_t number, const uint8_t *address)`
    219271
    220 - **sensor** (sur l'Arduino)
     272
     273- **sensor** (emetteur)
     274  Le programme c-
    221275{{{#!c
    222276#include <SPI.h>
     
    226280RF24 radio(9,10); // radio(CE,CS)
    227281
    228 byte addresses[][6] = {"0Node"};
     282byte addresses[][6] = {"0XXXX"};
    229283
    230284void setup() {
     
    242296  Serial.println(F("Now sending !"));
    243297
    244   unsigned long start_time = millis();                             // Take the time, and send it.  This will block until complete
     298  unsigned long start_time = millis();            // Take the time, and send it.  This will block until complete
    245299  if (!radio.write( &start_time, sizeof(unsigned long) )){
    246300     Serial.println(F("failed!"));