Piduino par l’exemple

Comme piduino utilise la même API que Arduino, il vous suffit de vous rendre sur la référence Arduino, ou sur n’importe quel site expliquant les fonctions du langage Arduino pour en avoir une documentation.

Premier exemple, Blink !

Le dossier exemples contient des exemples du monde Arduino qui peuvent être utilisés directement avec piduino. La seule chose à ajouter par rapport à l’exemple correspondant Arduino est la ligne :

#include <Piduino.h>

Voici le code source de l’exemple Blink qui fait clignoter une led :

#include <Piduino.h> // all the magic is here ;-)

const int ledPin = 0; // Header Pin 11: GPIO17 for RPi, GPIOA0 for NanoPi

void setup() {
  // initialize digital pin ledPin as an output.
  pinMode (ledPin, OUTPUT);
}

void loop () {
  // Press Ctrl+C to abort ...
  digitalWrite (ledPin, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay (1000);                 // wait for a second
  digitalWrite (ledPin, LOW);   // turn the LED off by making the voltage LOW
  delay (1000);                 // wait for a second
}

Évidement, vous devez connaître le numéro de broche où vous avez connecté la led ! Ce nombre dépend de votre modèle de carte Pi, pour le savoir rapidement, vous pouvez taper la commande pido readall 1, qui nous donne, par exemple, l’affichage suivant sur un Raspberry Pi B:

                                    P1 (#1)
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
|     |     |     3.3V |      |   |  1 || 2  |   |      | 5V       |     |     |
|   2 |   8 |    GPIO2 |   IN | 1 |  3 || 4  |   |      | 5V       |     |     |
|   3 |   9 |    GPIO3 |   IN | 1 |  5 || 6  |   |      | GND      |     |     |
|   4 |   7 |    GPIO4 |   IN | 1 |  7 || 8  | 1 | ALT0 | TXD0     | 15  | 14  |
|     |     |      GND |      |   |  9 || 10 | 1 | ALT0 | RXD0     | 16  | 15  |
|  17 |   0 |   GPIO17 |   IN | 0 | 11 || 12 | 0 | IN   | GPIO18   | 1   | 18  |
|  27 |   2 |   GPIO27 |   IN | 0 | 13 || 14 |   |      | GND      |     |     |
|  22 |   3 |   GPIO22 |   IN | 0 | 15 || 16 | 0 | IN   | GPIO23   | 4   | 23  |
|     |     |     3.3V |      |   | 17 || 18 | 0 | IN   | GPIO24   | 5   | 24  |
|  10 |  12 |   GPIO10 |   IN | 0 | 19 || 20 |   |      | GND      |     |     |
|   9 |  13 |    GPIO9 |   IN | 0 | 21 || 22 | 0 | IN   | GPIO25   | 6   | 25  |
|  11 |  14 |   GPIO11 |   IN | 0 | 23 || 24 | 1 | IN   | GPIO8    | 10  | 8   |
|     |     |      GND |      |   | 25 || 26 | 1 | IN   | GPIO7    | 11  | 7   |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | V | Ph || Ph | V | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+---+----++----+---+------+----------+-----+-----+

La colonne iNo correspond au numéro ‘Arduino’, le numéro 0 pin correspond donc à la broche 11 du connecteur GPIO (GPIO17).

Pour compiler le programme blink sur la ligne de commande, vous devez taper le commander:

$ g++ -o blink blink.cpp $(pkg-config --cflags --libs piduino)

La dernière partie de la commande utilise pkg-config pour ajouter les options de construction à g ++ afin de compiler le programme correctement.

Vous pouvez ensuite exécuter le programme:

 $ sudo ./blink

sudo est nécessaire pour accéder à la zone de mémoire du GPIO. Vous pouvez activer le bit setuid pour éviter sudo à l’avenir:

$ sudo chmod u+s blink
$ ./blink

Pour disposer d’un environnement de développement plus convivial, il est conseillé d’utiliser Codelite, l’installation de piduino ajoute un modèle de programme pour piduino. Pour créer un nouveau programme piduino dans Codelite, il faut, une fois votre workspace créé, utiliser le menu Workspace/New Project et sélectionner le modèle Simple Executable (C++ piduino) :

Modèle de projet Codelite pour piduino

Dans Codelite, on peut non seulement compiler, mais aussi éditer et surtout déboguer le programme :

Débogage avec Codelite

Deuxième exemple

le deuxième exemple rtc_bq32k, utilise la bibliothèque Wire pour lire l’heure dans un circuit BQ32000 RTC.

Cela permet de découvrir 2 différences importantes entre une carte Arduino et une carte Pi :

  1. Tout d’abord, sur une carte Pi, l’interface homme-machine (écran et clavier) est fait sur la ligne de commande (la console !). Sur Arduino, le port série est utilisé.
  2. Sur une carte Pi, un programme peut se terminer pour donner la main à l’utilisateur. Sur Arduino, le programme ne s’arrête jamais (en fait, sur un système Linux, le programme du noyau ne s’arrête jamais non plus …)

Pour résoudre le premier problème, piduino définit un objet Console dont le l’utilisation est identique à l’objet Serial (c’est une classe dérivée de Stream).

Afin de permettre la compilation sur les deux plates-formes sans modifier le code source, nous pouvons ajouter au début du programme un bloc qui teste si la plate-forme cible est un système Unix/Linux (piduino), si c’est le cas, on inclut le fichier Piduino.h, sinon on définit un alias de Console qui correspond à Serial, c’est-à-dire que l’interface homme-machine est sur le port série.

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Wire.h>

void printBcdDigit (byte val, bool end = false) {
  val = (val / 16 * 10) + (val % 16); // BCD to DEC

  if (val < 10) {
    Console.write ('0'); // leading zero
  }
  if (end) {

    Console.println (val);
  }
  else {

    Console.print (val);
    Console.write (':');
  }
}

void setup() {

  Console.begin (115200);
  Wire.begin(); // Starting the i2c master
}

void loop() {

  Wire.beginTransmission (0x68); // start of the frame for the RTC at slave address 0x68
  Wire.write (0); // write the address of the register in the RTC, 0 first register
  Wire.endTransmission (false); // restart condition
  Wire.requestFrom (0x68, 3); // 3-byte read request

  if (Wire.available() == 3) { // if the 3 bytes have been read
    byte sec = Wire.read();
    byte min = Wire.read();
    byte hour = Wire.read() & 0x3F; // remove CENT_EN and CENT LSB bits

    // time display
    printBcdDigit (hour);
    printBcdDigit (min);
    printBcdDigit (sec, true);
  }
  exit (0); // exit the loop() function without ever coming back.
  // On Arduino, exit() performs an infinite loop as explained on
  // https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html
  // on a Pi board, exit () stops the program by returning the supplied value.
}

Pour résoudre le second problème, il est possible d’utiliser sur les 2 les plates-formes, la fonction exit() (définie dans le bibliothèque standard). Cette fonction, compatible avec les deux plates-formes, permet d’arrêter l’exécution la fonction loop().

Sur un système Unix/Linux, cela arrête le programme et revient à la ligne de commande, sur Arduino, cela effectue une boucle infinie (après avoir appelé le destructeur d’objets C++).

Configuration de piduino

piduino détecte le modèle de carte sur lequel il s’exécute au moment du lancement du programme qui l’utilise, ainsi si vous installez le même paquet Armbian libpiduino, en architecture armhf par exemple, sur une carte Nano Pi Neo et sur Orange Pi Zero, piduino détectera automatiquement le bon modèle de carte !

Comment fait-il ?

Il utilise les fichiers présents sur la machine hôte pour le savoir. Il commence par scruter les fichiers /etc/friendlyelec-release puis /etc/armbian-release à la recherche d’un champ BOARD lui indiquant la signature de la carte (tag), si il ne trouve rien, il scrute le fichier /proc/cpuinfo à la recherche d’un numéro de révision correspondant à une carte Raspberry Pi de la base de données, si il ne le trouve pas, il ne peut le deviner ! vous aurez donc une erreur.

Il est possible de forcer le choix du modèle de carte Pi en utilisant le fichier de configuration /etc/piduino.conf.

Cela peut être nécessaire lorsque le programme ne peut pas détecter la configuration de la carte. Par exemple, dans le cas du NanoPi Neo Core/Core2, nous pouvons indiquer que la carte est sur son shield, dans ce cas, l’affichage du connecteur par la commande pido readall sera adaptée.

Comme expliqué précédemment, la détection de modèle de carte Pi utilise deux méthodes:
* La première méthode, qui s’applique aux cartes Raspberry Pi, lit le Fichier /proc/cpuinfo pour obtenir le modèle de microprocesseur dans le champ Hardware et la version du matériel dans le champ Revision. Ce numéro de révision est comparé avec le base de données pour en déduire le modèle RaspberryPi.
* La deuxième méthode, qui s’applique aux cartes utilisant ArmBian, vient de la lecture /etc/armbian-release ou /etc/friendlyelec-release pour obtenir le modèle de carte dans BOARD. piduino compare cette signature avec la base de données pour en déduire le modèle RaspberryPi.

Dans le fichier de configuration /etc/piduino.conf, nous trouverons ces deux possibilités, qu’il faut renseigner (l’une ou l’autre, mais jamais les deux !).

Par exemple, si nous voulons indiquer que notre NanoPi Neo Core2 est installé sur son shield, nous mettrons la valeur du champ tag à nanopineocore2shield:

# PiDuino configuration file
connection_info="sqlite3:db=/usr/local/share/piduino/piduino.db"

# Allows you to force the board tag (Armbian)
# !! Be careful, this is at your own risk !!
# !! Forcing an incorrect value may destroy GPIO pins !!
tag="nanopineocore2shield"

# Allows forcing the revision of the board (Raspbian)
# !! Be careful, this is at your own risk !!
# !! Forcing an incorrect value may destroy GPIO pins !!
#revision=0xa02082

On peut constater que le fichier de configuration contient également l’adresse de la base de données à utiliser. La base de données est par défaut un fichier SQLite3 local, mais cette base de données peut être installée sur un serveur MySQL par exemple (pour le format de la ligne connection_info Voir la documentation de CPPDB).

Interface Homme-Machine pour boîtier sur rail DIN

Open-source-hardware-logo.png

Dans cet article nous allons nous intéresser au projet Hmi4DinBox. La partie matérielle de Hmi4DinBox a été conçue par 2 étudiants Lionel et Justine du BTS Systèmes Numériques, Option “Électronique et communication” du Lycée Rouvière de Toulon.

Hmi4DinBox est une interface homme-machine conçue pour être implémentée à l’avant d’un boîtier de rail DIN.

Voilà ce que ça donne une fois implantée dans le boîtier DIN :

et voilà le cahier des charges exprimé en SysML qui résume les spécifications de cette interface :

Requirement Diagram Hmi4DinBox

Caractéristiques

Ses caractéristiques principales sont les suivantes :

  • LCD 2×16 caractères avec rétro-éclairage (basé sur ST7032),
  • un bouton de navigation avec (4 directions + 1 bouton poussoir),
  • 5 leds,
  • piloté par un simple bus I2C et un signal binaire HIRQ qui indique qu’un ou plusieurs actions ont été effectuées sur le bouton de navigation (appui et relâchement de touche dans les 5 directions stockées dans une mémoire tampon),
  • alimenté par 5V ou 3.3V permettant de l’utiliser avec une carte Arduino ou Pi,
  • Interface de programmation USB compatible avec Arduino IDE,
  • Interface JTAG pour la programmation du chargeur de démarrage et le débogage du firmware.

Publié sous licence open-source hardware, il est composé de :

Comment connecter cette IHM à votre carte ?

L’IHM est connectée à votre carte par le connecteur J1 MOLEX KK254 à 5 broches situé à l’arrière du circuit imprimé.

Le brochage du connecteur J1 est le suivant:

# Nom Description
1 VCC Alimentation 5V ou 3,3V, protégée contre les surtensions et les inversions de polarité
2 SCL Horloge I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
3 SDA Données I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
4 HIRQ Indique qu’une ou plusieurs actions ont été effectuées sur le bouton de navigation
5 GND Masse

HIRQ est à l’état haut tant qu’il y a des actions non lues dans le tampon (capacité de 32 actions). Il passe à l’état bas quand toutes les actions ont été lues avec la méthode key ().

Comment installer la bibliothèque ?

Cette bibliothèque permet le contrôle côté client de Hmi4DinBox connecté par un bus I2C. Elle est destinée à être utilisée dans un environnement Arduino (également compatible avec le piduino sur des cartes Pi telles que Raspberry Pi ou NanoPi).

Pour installer la bibliothèque pour Arduino IDE, téléchargez simplement son fichier zip et l’intégrer dans votre croquis en fonction de explications sur le site Arduino.

Comment utiliser l’IHM dans votre programme?

Au début du fichier Hmi4DinBox.h, nous voyons la définition de constantes décrivant la solution matérielle:

#define LED1 0
#define LED2 1
#define LED3 2
#define LED4 3
#define LED5 4

Nous avons 5 leds, la numérotation commence à 0.

#define LED_RED LED1
#define LED_GREEN1 LED2
#define LED_GREEN2 LED3
#define LED_YELLOW1 LED4
#define LED_YELLOW2 LED5

Nous définissons des constantes plus explicites correspondant aux couleurs des leds : la led N°1 est rouge, les N°2 et 3 vertes…

Ensuite, on trouve les constantes pour la fonction bouton de navigation :

#define KUP 1
#define KDOWN 2
#define KLEFT 3
#define KRIGHT 4
#define KCENTER 5

Nous avons 5 actions (ou touches, keys) correspondant aux 4 directions du bouton de navigation et le bouton central.

Pour utiliser cette IHM dans un programme, vous devez déclarer une variable globale de type Hmi4DinBox:

Hmi4DinBox hmi;

Si on utilise le pavé de navigation, il est nécessaire lors de cette déclaration de spécifier le numéro de la broche Arduino utilisée par l’IHM pour indiquer que des actions sur le bouton sont dans le tampon interne (il faudra donc les lire …).

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

Ensuite, nous pouvons accéder :

  • à l’afficheur LCD grâce à hmi.lcd,
  • aux leds grâce à hmi.led,
  • au bouton de navigation grâce à hmi.keyb,
  • au rétroéclairage avec hmi.backlight.

Utilisation de l’écran LCD

La classe WireLcd est dérivée de la classe Arduino Print. Vous pouvez donc utiliser les fonctions “print(),println(),write()`:

int i = 5;
hmi.lcd.print("Helloworld ! ");
hmi.lcd.println(i++);
hmi.lcd.write('C');

Voici le croquis HelloWorld.ino :

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

Hmi4DinBox hmi;

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }
}

void loop() {
  static int counter = 0;

  // Write a piece of text on the first line...
  hmi.lcd.setCursor (0, 0); //LINE 1, ADDRESS 0
  hmi.lcd.print ("Hello World");

  // Write the counter on the second line...
  hmi.lcd.setCursor (1, 0);
  hmi.lcd.print (counter / 10, DEC);
  hmi.lcd.write ('.');
  hmi.lcd.print (counter % 10, DEC);
  hmi.lcd.write (' ');
  counter++;
  delay (500);
}

Comme on peut le voir ci-dessus, on fera,

Pour effacer l’écran:

hmi.lcd.clear();

Pour aller au début de la ligne 2:

hmi.lcd.setCursor (1, 0);

Pour revenir en haut à droite:

hmi.lcd.home();

Pour éteindre l’écran:

hmi.lcd.noDisplay();

Pour allumer l’écran:

hmi.lcd.display();

Pour activer le curseur:

hmi.lcd.cursor();

Pour désactiver le curseur:

hmi.lcd.noCursor();

Pour activer le curseur clignotant:

hmi.lcd.blink();

Pour désactiver le clignotement du curseur:

hmi.lcd.noBlink();

Le contraste de l’écran LCD peut être modifié, la valeur peut varier de 0 à 63:

hmi.lcd.setcontrast (24);

Le contraste peut également être lu et ajusté par rapport à la valeur actuelle:

byte c = hmi.lcd.getcontrast();
hmi.lcd.adjcontrast (-1); // decrement the contrast value by 1

Utilisation des leds

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

Hmi4DinBox hmi;
void printLed (int led);

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }
  hmi.lcd.cursor();
  hmi.lcd.blink();
  hmi.lcd.clear();
  hmi.lcd.print ("12345");
}

void loop() {

  // turn on all leds
  hmi.led.writeAll();
  // Pause
  delay (2000);

  // turn off all leds
  hmi.led.writeAll (0);
  // Pause
  delay (2000);

  // turn on the LEDs one by one.
  hmi.led.set (LED1);
  delay (500);
  hmi.led.set (LED2);
  delay (500);
  hmi.led.set (LED3);
  delay (500);
  hmi.led.set (LED4);
  delay (500);
  hmi.led.set (LED5);
  delay (500);
  // Pause
  delay (2000);

  // turn off the LEDs one by one with a for loop
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.clear (led);
    delay (500);
  }
  // Pause
  delay (2000);

  // turn on the LEDs one by with a for loop, using toggle and get value.
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.toggle (led);
    printLed (led);
    delay (500);
  }
  // Pause
  delay (2000);

  // turn off the LEDs one by with a for loop, using toggle() and get()
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.toggle (led);
    printLed (led);
    delay (500);
  }
  // Pause
  delay (2000);
}

void printLed (int led) {
  hmi.lcd.setCursor (1, led);
  if (hmi.led.get (led)) {
    hmi.lcd.write ('O');
  }
  else {

    hmi.lcd.write ('_');
  }
}

Comme on peut le voir dans le croquis LedDemo.ino, ci-dessus :

hmi.led.set (LED1);

permet d’allumer la LED1,

hmi.led.clear (LED1);

permet d’éteindre la LED1,

hmi.led.toggle (LED1);

permet de basculer l’état de LED1,

hmi.led.get (LED1);

permet de lire l’état de la LED1,

La fonction hmi.led.writeAll() vous permet de modifier toutes les leds en même temps. Par défaut, un appel à cette fonction sans paramètre active toutes les leds. Si on lui passe un paramètre, cela correspond à l’état des leds. Le bit 0 de ce paramètre est utilisé pour contrôler LED1, bit 1 pour LED2 …

Utilisation du bouton de navigation

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }

  hmi.lcd.clear();
  hmi.lcd.cursor();
  hmi.lcd.blink();
}

void loop() {
  static int column = 0;
  static int row = 0;

  if (hmi.keyb.available()) { // check if keys are available
    byte key = hmi.keyb.key(); // get the next key

    if (hmi.keyb.released()) { // this key was released ?

      hmi.lcd.write ('R'); // yes, print R
      hmi.led.clear(LED_GREEN1);
    }
    else {

      hmi.lcd.write ('P'); // no, print P
      hmi.led.set(LED_GREEN1);
    }

    hmi.lcd.print (key); // print the key

    // Move the cursor when you reach the end of the line.
    column += 2;
    if (column >= 16) { // end of line ?
      // yes
      if (++row > 1) { // end of screen ?
        // wait for reading last key and clear the LCD
        delay(500);
        row = 0;
        hmi.lcd.clear();
      }
      column = 0;
      hmi.lcd.setCursor (row, column);
    }

  }
}

Comme on peut le voir dans le croquis KeyboardDemo.ino, ci-dessus :

hmi.keyb.available ()

permet de vérifier si des actions sur le bouton sont disponibles pour la lecture,

key = hmi.keyb.key ();

permet de lire la prochaine action mémorisée,

hmi.keyb.released ()

vous permet de savoir si cette action est un relâchement (et donc pas un appui),

hmi.keyb.pressed ()

vous permet de savoir si cette action est un appui.

Utilisation du rétro-éclairage

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

byte bl;
void printBacklight ();

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }

  // read value stored in EEPROM.
  bl = hmi.backlight.read();

  // print this value
  hmi.lcd.clear();
  hmi.lcd.print ("Backlight");
  printBacklight();
}

void loop() {

  if (hmi.keyb.available()) { // check if keys are available

    if (hmi.keyb.pressed()) {
      byte key = hmi.keyb.key(); // get the next key

      if ( (key == KUP) || (key == KDOWN)) { // if key UP or DOWN

        if (key == KUP) {

          bl += 8; // UP for increase
        }
        else {

          bl -= 8;  // DOWN for decrease
        }

        // change value and print
        hmi.backlight.write (bl);
        printBacklight();
        hmi.led.toggle (LED_GREEN1);
      }
    }
  }
}

void printBacklight () {

  hmi.lcd.setCursor (0, 10);
  hmi.lcd.print (bl);
  hmi.lcd.print ("  ");
}

Comme on peut le voir dans le croquis BacklightDemo.ino ci-dessus,

hmi.backlight.write (bl);

permet de modifier la valeur du rétro-éclairage (entre 0 et 255), comme cette valeur est stockée dans la mémoire EEPROM de l’IHM, il est possible de lire la valeur actuelle grâce à:

bl = hmi.backlight.read ();

Utiliser Hmi4DinBox sur carte Pi

Dans cet article nous allons utiliser l’interface homme-machine Hmi4DinBox sur une carte Pi.

Après une installation ou une mise à jour de piduino, nous connecterons l’interface Hmi4DinBox à un carte Pi avant d’en effectuer le test à partir de la ligne de commande avec les outils du paquet i2c-tools.

Comme nous l’avons vu dans un article précédent cette interface est prévue pour être connectée à une carte Arduino ou Pi par un bus I2C. Elle est fournie avec une librairie Arduino que nous utiliserons dans un deuxième article pour réaliser des programmes accédant à l’interface Hmi4DinBox sur notre carte Nano Pi.

La carte Nano Pi utilisée est une carte Nano Pi Neo Core implantée sur un Mini Shield, commercialisée en France par GoTronic, mais on pourra utiliser n’importe quelle carte Pi compatible avec piduino.

hmi4dinbox-nanopi

Installer ou mettre à jour piduino

Si ce n’est pas encore fait, installer les paquets libpiduino-dev et piduino-utils comme expliqué dans l’article sur piduino.

Si vous avez déjà installer piduino, effectuez une mise à jour des paquets :

sudo apt update
sudo apt upgrade

Afin que pido affiche correctement le connecteur GPIO, il est nécessaire de modifier le fichier /etc/piduino.conf afin d’indiquer que la carte est implantée sur le Mini Shield:

sudo nano /etc/piduino.conf

Il faut retirer le # devant la ligne tag et mettre nanopineocoreshield entre les guillemets. Le fichier doit ressembler à ceci:

# PiDuino configuration file
connection_info="sqlite3:db=/usr/share/piduino/piduino.db"

# Allows you to force the board tag (Armbian)
# !! Be careful, this is at your own risk !!
# !! Forcing an incorrect value may destroy GPIO pins !!
tag="nanopineocoreshield"

# Allows forcing the revision of the board (Raspbian)
# !! Be careful, this is at your own risk !!
# !! Forcing an incorrect value may destroy GPIO pins !!
#revision=0xa02082

Relier l’interface Hmi4DinBox au Nano Pi

Fil de câblageAprès avoir arrêté la carte Nano Pi, nous allons relier l’interface Hmi4DinBox au connecteur GPIO grâce à des fils équipés de petits connecteurs (femelle-femelle) comme ci-contre.

Le brochage du connecteur J1 de l’interface Hmi4dinBox est le suivant:

# Nom Description
1 VCC Alimentation 5V ou 3,3V, protégée contre les surtensions et les inversions de polarité
2 SCL Horloge I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
3 SDA Données I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
4 HIRQ Indique qu’une ou plusieurs actions ont été effectuées sur le bouton de navigation
5 GND Masse

Nous pouvons voir le brochage du connecteur GPIO CON1 du Nano Pi grâce à pido :

pido readall 1
                                          CON1 (#1)
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
|     |     |     3.3V |      |      |   |  1 || 2  |   |      |      | 5V       |     |     |
|  12 |   8 |  I2C0SDA | ALT2 |  OFF |   |  3 || 4  |   |      |      | 5V       |     |     |
|  11 |   9 |  I2C0SCK | ALT2 |  OFF |   |  5 || 6  |   |      |      | GND      |     |     |
|  91 |   7 |  GPIOG11 |  OFF |  OFF |   |  7 || 8  |   | OFF  | ALT2 | UART1TX  | 15  | 86  |
|     |     |      GND |      |      |   |  9 || 10 |   | OFF  | ALT2 | UART1RX  | 16  | 87  |
|   0 |   0 |   GPIOA0 |  OFF |  OFF |   | 11 || 12 |   | OFF  | OFF  | GPIOA6   | 1   | 6   |
|   2 |   2 |   GPIOA2 |  OFF |  OFF |   | 13 || 14 |   |      |      | GND      |     |     |
|   3 |   3 |   GPIOA3 |  OFF |  OFF |   | 15 || 16 |   | OFF  | OFF  | GPIOG8   | 4   | 88  |
|     |     |     3.3V |      |      |   | 17 || 18 |   | OFF  | OFF  | GPIOG9   | 5   | 89  |
|  15 |  28 | SPI1MOSI | ALT2 |  OFF |   | 19 || 20 |   |      |      | GND      |     |     |
|  16 |  24 | SPI1MISO | ALT2 |  OFF |   | 21 || 22 |   | OFF  | OFF  | GPIOA1   | 6   | 1   |
|  14 |  29 |  SPI1CLK | ALT2 |  OFF |   | 23 || 24 |   | OFF  | ALT2 | SPI1CS   | 27  | 13  |
|     |     |      GND |      |      |   | 25 || 26 |   | OFF  | OFF  | GPIOA17  | 11  | 17  |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+

Voilà les connexions que nous devons réaliser :

J1NomCON1NomiNo
1VCC13.3V
2SCL5I2C0SCK9
3SDA3I2C0SDA8
4HIRQ15GPIOA33
5GND6GND

Après avoir vérifié, puis revérifié encore les 5 fils, démarrer le Nano Pi.

Tester l’interface Hmi4DinBox

Avant d’utiliser l’interface dans un programme, il est préférable d’en effectuer un test directement à partir de la ligne de commande.

Nous allons utiliser pour ce faire les programmes du paquet i2c-tools (à installer si nécessaire).

sudo apt install i2c-tools

Avant de “parler” à notre interface, il ne faut oublier que les lignes SDA et SCL sur un Nano Pi ne disposent pas de résistances de pull-up (ce n’est pas le cas sur Raspberry Pi). Il faut donc, soit ajouter une résistance de 1,8k reliée au 3,3V sur chaque signal, soit activer les résistances de pull-up sur les lignes iNo 8 et 9. C’est cette dernière solution que nous allons utiliser car elle est plus simple (mais la première solution avec des résistances externes devra être retenue dans un cadre industriel).

pido nous permet d’activer les résistances de pull-up :

pido pull 8 up
pido pull 9 up

Si vous ne savez pas pourquoi il faut des résistances de pull-up sur les lignes I2C, il est tant de se renseigner sur Wikipedia

Pour comprendre ce que nous allons faire, il faut regarder le diagramme d’exigences de l’interface Hmi4DinBox qui a été présenté dans un article précédent.

L’exigence Affichage LED (id=1.10.2) nous dit la chose suivante :

  • L’interface dispose de 5 leds T1-3mm.
  • L’accès à la fonction par le module hôte se fera par bus I2C à l’adresse 0111111.
  • Le registre 0 à cette adresse permettra de lire et modifier l’état des leds (1 bit par led). Le bit 0 correspond à la led rouge, le bit 1 à la verte1, le bit 2 à la verte2, le 3 à jaune1 et le 4 à jaune2).

L’exigence Affichage LCD (id=1.10.1) nous informe que l’afficheur de modèle ST7032i se trouve à l’adresse 0111110.

Utilisons i2cdetect sur le bus I2C 0 pour savoir si les 2 adresses, celle du LCD et l’autre utilisée pour les leds (et les autres fonctions) sont détectées :

sudo i2cdetect  -y 0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e 3f 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --                         

Bingo ! nous détectons 2 circuits sur le bus I2c :

  • un circuit à l’adresse hexadécimale 0x3e, 00111110 en binaire, qui correspond au LCD.
  • un circuit à l’adresse hexadécimale 0x3f, 00111111 en binaire, qui correspond aux leds (et les autres fonctions).

Nous allons écrire 0x01 dans le registre 0 à l’adresse 0x3f grâce à i2cset :

sudo i2cset -y 0 0x3f 0 0x01

Normalement cela allume la LED1, c’est à dire la rouge, pour allumer la LED2 on écrira 0x02, la LED3 0x04, la LED4 0x08, la LED5 0x10. On allumer plusieurs leds en effectuant une somme des valeurs, par exemple, 0x12 allume la LED2 et la LED5, 0x1F toutes les leds…

Pour éteindre les leds :

sudo i2cset -y 0 0x3f 0 0x00

Pour lire l’états des leds :

sudo i2cget -y 0 0x3f 0
0x12

L’exigence id=1.10.1.1 nous permet de savoir que le registre 1 à l’adresse 0x3f correspond au rétroéclairage, pour l’allumer à mi-puissance soit 128 :

sudo i2cset -y 0 0x3f 1 128

L’exigence id=1.10.3 nous permet de savoir que les actions effectuées par l’utilisateur sont accessibles par le registre 2 à l’adresse 0x3f et que le signal HIRQ, broche iNo 3 dans notre cas, est à l’état haut lorsque des actions sont disponibles. Commençons par passer la broche en entrée et lisons sa valeur avec pido :

pido mode 3 in
pido read 3
0

Cela veut dire qu’aucune action n’est mémorisé, effectuons un appui sur le bouton, puis relisons HIRQ :

pido read 3
1

Nous pouvons effectuer maintenant la lecture des actions avec i2cget en lisant le registre 2 à l’adresse 0x3f :

sudo i2cget -y 0 0x3f 2
0x05

D’après l’exigence id=1.10.3, cela correspond à un appui (bit 7 à 0) au centre.

Une nouvelle lecture de HIRQ, nous apprend qu’il y a encore des actions disponibles :

pido read 3
1

Qu’on peut lire :

sudo i2cget -y 0 0x3f 2
0x85

Cela correspond à un relâchement (bit 7 à 1 0x80) du centre (0x05). Une nouvelle lecture de HIRQ, nous apprend qu’il n’y a plus d’action disponible.

pido read 3
0

Le test en ligne de commande du LCD à l’adresse 0x3e est un peu plus complexe car il nécessite de se pencher sur le datasheet du ST7032i… Nous verrons sa programmation dans le prochain article… 🙂

piduino

Arduino sur cartes Pi, le meilleur des deux mondes !

piduino est une bibliothèque C++ pour cartes Pi qui permet d’utiliser les entrées-sorties comme GPIO, I2C, SPI, UART… avec une API aussi proche que possible du langage Arduino.
La description des cartes Pi utilise un modèle “Objet” stocké dans une base de données qui permet d’ajouter facilement de nouveaux modèles de cartes.

Actuellement, les modèles SoC pris en charge sont AllWinner H-Series et Broadcom BCM2708 à 2710 qui lui permet d’être utilisé sur Raspberry Pi et la plupart des Nano Pi, Orange Pi et Banana Pi.

Pour en savoir plus sur piduino, vous pouvez suivre la Wiki, mais si vous êtes pressé, passons à la version de démarrage rapide …

Guide de démarrage rapide

Le moyen le plus rapide et le plus sûr d’installer piduino sur Armbian est d’utiliser le dépôt APT de piduino.org, vous devriez donc procéder comme suit:

wget -O- http://www.piduino.org/piduino-key.asc | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.piduino.org stretch piduino'
sudo apt update
sudo apt install libpiduino-dev piduino-utils

Ce dépôt fournit les packages piduino pour les architectures armhf etarm64 (et la plupart des librairies et programmes disponibles sur le github de epsilonrt).
Dans les commandes ci-dessus, le dépôt est une distribution Debian Stretch, mais vous pouvez également choisir Ubuntu Xenial ou Bionic en remplaçant stretch par xenial ou bionic. Il peut être nécessaire d’installer le logiciel software-properties-common paquet pour add-apt-repository.

Pour Raspbian, vous devez faire un peu différent:

wget -O- http://www.piduino.org/piduino-key.asc | sudo apt-key add -
echo 'deb http://raspbian.piduino.org stretch piduino' | sudo tee /etc/apt/sources.list.d/piduino.list
sudo apt update
sudo apt installer libpiduino-dev piduino-utils

Le dépôt Raspbian fournit des packages piduino Stretch pour l’architecture armhf uniquement .

Si vous voulez construire piduino à partir de sources, vous pouvez suivre la Wiki.

Utilitaires

Une fois installé, vous devez exécuter ce qui suit sur la ligne de commande:

$ pinfo
Nom: NanoPi Core2 Mini Shield
Famille: NanoPi
ID de base de données: 40
Fabricant: Friendly ARM
Tag Conseil: nanopineocore2shield
SoC: H5 (Allwinner)
Mémoire: 1024 Mo
Identifiant GPIO: 9
Bus I2C: / dev / i2c-0
Bus SPI: /dev/spidev1.0
Ports série: / dev / ttyS1

Comme nous pouvons l’imaginer, dans l’exemple ci-dessus, nous sommes sur une NanoPi Neo Core2 connecté à un Mini Shield.

Pour lire l’état des broches du connecteur 1, exécutez la commande suivante sur la ligne de commande:

$ pido readall 1
                                          CON1 (#1)
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
|     |     |     3.3V |      |      |   |  1 || 2  |   |      |      | 5V       |     |     |
|  12 |   8 |  I2C0SDA | ALT2 |  OFF |   |  3 || 4  |   |      |      | 5V       |     |     |
|  11 |   9 |  I2C0SCK | ALT2 |  OFF |   |  5 || 6  |   |      |      | GND      |     |     |
|  91 |   7 |  GPIOG11 |  OFF |  OFF |   |  7 || 8  |   | OFF  | ALT2 | UART1TX  | 15  | 86  |
|     |     |      GND |      |      |   |  9 || 10 |   | OFF  | ALT2 | UART1RX  | 16  | 87  |
|   0 |   0 |   GPIOA0 |  OFF |  OFF |   | 11 || 12 |   | OFF  | OFF  | GPIOA6   | 1   | 6   |
|   2 |   2 |   GPIOA2 |  OFF |  OFF |   | 13 || 14 |   |      |      | GND      |     |     |
|   3 |   3 |   GPIOA3 |  OFF |  OFF |   | 15 || 16 |   | OFF  | ALT2 | UART1RTS | 4   | 88  |
|     |     |     3.3V |      |      |   | 17 || 18 |   | OFF  | ALT2 | UART1CTS | 5   | 89  |
|  15 |  28 | SPI1MOSI | ALT2 |  OFF |   | 19 || 20 |   |      |      | GND      |     |     |
|  16 |  24 | SPI1MISO | ALT2 |  OFF |   | 21 || 22 |   | OFF  | OFF  | GPIOA1   | 6   | 1   |
|  14 |  29 |  SPI1CLK | ALT2 |  OFF |   | 23 || 24 |   | OFF  | ALT2 | SPI1CS   | 27  | 13  |
|     |     |      GND |      |      |   | 25 || 26 |   | OFF  | OFF  | GPIOA17  | 11  | 17  |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+

pido etpinfo disposent de pages de man…, donc vous pouvez en savoir plus sur ces commandes grâce à :

$ man pido

Exemple Blink

Vous êtes prêt à faire de l’Arduino sur carte Pi ? Ok, allons-y !

Nous allons faire clignoter une led connectée avec une résistance à une broche GPIO. Voilà le code source de l’exemple, qui, à l’exception de la première ligne est identique à celui du tutoriel Arduino :

#include <Piduino.h> // all the magic is here ;-)

const int ledPin = 0; // Header Pin 11: GPIO17 for RPi, GPIOA0 for NanoPi

void setup() {
  // initialize digital pin ledPin as an output.
  pinMode (ledPin, OUTPUT);
}

void loop () {
  // Press Ctrl+C to abort ...
  digitalWrite (ledPin, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay (1000);                 // wait for a second
  digitalWrite (ledPin, LOW);   // turn the LED off by making the voltage LOW
  delay (1000);                 // wait for a second
}

Évidement, vous devez connaître le numéro de broche où vous avez connecté à la led ! Pour ce faire :

$ pido readall 1
                                          CON1 (#1)
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
|     |     |     3.3V |      |      |   |  1 || 2  |   |      |      | 5V       |     |     |
|  12 |   8 |  I2C0SDA | ALT2 |  OFF |   |  3 || 4  |   |      |      | 5V       |     |     |
|  11 |   9 |  I2C0SCK | ALT2 |  OFF |   |  5 || 6  |   |      |      | GND      |     |     |
|  91 |   7 |  GPIOG11 |  OFF |  OFF |   |  7 || 8  |   | OFF  | ALT2 | UART1TX  | 15  | 86  |
|     |     |      GND |      |      |   |  9 || 10 |   | OFF  | ALT2 | UART1RX  | 16  | 87  |
|   0 |   0 |   GPIOA0 |  OFF |  OFF |   | 11 || 12 |   | OFF  | OFF  | GPIOA6   | 1   | 6   |
|   2 |   2 |   GPIOA2 |  OFF |  OFF |   | 13 || 14 |   |      |      | GND      |     |     |
|   3 |   3 |   GPIOA3 |  OFF |  OFF |   | 15 || 16 |   | OFF  | OFF  | GPIOG8   | 4   | 88  |
|     |     |     3.3V |      |      |   | 17 || 18 |   | OFF  | OFF  | GPIOG9   | 5   | 89  |
|  22 |  12 |   GPIOC0 |  OFF |  OFF |   | 19 || 20 |   |      |      | GND      |     |     |
|  23 |  13 |   GPIOC1 |  OFF |  OFF |   | 21 || 22 |   | OFF  | OFF  | GPIOA1   | 6   | 1   |
|  24 |  14 |   GPIOC2 |  OFF |  OFF |   | 23 || 24 |   | UP   | OFF  | GPIOC3   | 10  | 25  |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+
| sOc | iNo |   Name   | Mode | Pull | V | Ph || Ph | V | Pull | Mode |   Name   | iNo | sOc |
+-----+-----+----------+------+------+---+----++----+---+------+------+----------+-----+-----+

La colonne iNo de ce tableau correspond au numéro ‘Arduino’, le numéro 0 pin correspond donc à la broche 11 du connecteur GPIO (GPIOA0 pour un Nano Pi).

Une fois le code source du programme, enregistré dans le fichier blink.cpp (pas d’extension .ino sous Pi !), vous pouvez construire, vous devez taper la commande:

$ g++ -o blink blink.cpp $(pkg-config --cflags --libs piduino)

Vous pouvez ensuite exécuter le programme:

$ sudo ./blink

sudo est nécessaire pour accéder à la zone mémoire du GPIO. Vous pouvez activer le bit setuid pour éviter sudo à l’avenir:

$ sudo chmod u+s blink
$ ./blink

Avec Codelite c’est plus facile et amusant, non ?

Débogage avec Codelite

Vous devriez lire le wiki sur les exemples pour en savoir plus …

Genèse du projet

piduino est né d’une question d’un de mes étudiants qui m’a demandé pourquoi la programmation des entrées-sorties sur NanoPi n’était pas aussi simple que sur Arduino.

piduino vise donc à répondre à ce besoin:

Une interface de programmation d’application (API) sur les cartes Pi aussi proche que possible de celle d’Arduino.

Cette API doit permettre l’utilisation de GPIO, port série, bus I2C et SPI… sur Raspberry Pi, Nano Pi, Orange Pi, Banana Pi, Beagle Board… comme sur une carte Arduino.

Que propose piduino ?

  • Une interface de programmation API identique à Arduino, à l’exeception du #include <Piduino.h> est ajouté au début du programme. Cela n’interdit pas d’offrir des extensions de l’API mais à condition de rester indépendant de la plate-forme et de ne pas rendre le code incompatible avec Arduino. Il est logique de penser que les utilisateurs qui souhaitent rester dans le monde Arduino utilisent le C++, piduino est destiné à ce cas d’utilisation. Néanmoins, certaines fonctions peuvent être utilisées en langage C (pinMode(), digitalWrite(), …).
  • La description des cartes Pi basée sur un modèle “Objet” stocké dans une base de données (SQLite par défaut), permettant à un utilisateur qui n’est pas forcément un hacker d’ajouter une nouvelle variante de carte Pi SANS programmation. Une variante désigne une carte équipée du même modèle de SoC avec une partie matérielle différente (connecteurs…).
  • Une conception objet en C++ avec une séparation claire de la partie spécifique à la plate-forme. La prise en charge de nouveaux SoC se résume à ajouter une partie “HAL” dans le répertoire src/arch. Les HAL actuellement disponibles sont les Soc AllWinner de la série H (cartes Nano Pi, Banana Pi, Orange Pi …) et les Broadcom de la famille BCM2835 à 37 (cartes Raspberry Pi).
  • Un utilitaire en ligne de commande de manipulation des signaux GPIO : pido
  • Un utilitaire en ligne de commande qui récupère les informations de la carte et le la base de données : pinfo
  • Un programme de gestion de la base de données de cartes Pi: pidbm (en développement…).

Remarque

Il existe déjà quelques projets qui permettent la programmation des entrées-sorties sur cartes embarquées, mais pour un seul modèle de carte Pi.

Le plus connu est probablement wiringPi. wiringPi est une solution prévue pour Raspberry Pi et même s’il y a versions dérivées pour d’autres cartes Pi, ces versions sont des fork “boiteux” de la version originale, d’un point de vue du génie logiciel.

Les raisons qui m’ont amené à ne pas choisir wiringPi sont les suivantes :

  • Même s’il y a une similitude avec la programmation Arduino, il y a des différences qui augmentent avec le temps (et qui perturbe les débutants).
  • wiringPi a été conçu en C pur, ce qui freine l’évolutivité et n’est pas très compatible avec Arduino (le langage Arduino est du C++ !). Il est impossible par exemple de compiler un programme Arduino faisant appel à la liaison série (HardwareSerial) ou aux bus I2c (Wire) et SPI…
  • wiringPi a été conçu pour le Raspberry Pi et son adaptation à d’autres cartes Pi est de plus en plus ingérable, au fur et à mesure de l’arrivée de nouveaux modèles de cartes Pi. Il suffit d’aller sur le site de ArmBian pour voir la multitude de modèles Pi !

Installation d’une RTC sur ArmBian

Sur les Pi Board comme le Raspberry Pi ou le Nano Pi, on ne dispose pas d’horloge temps réel (RTC pour Real Time Clock) capable de mémoriser la date et l’heure lorsqu’on coupe l’alimentation. Les concepteurs de ces cartes sont tout simplement partie du postulat que la carte sera reliée à une réseau disposant d’un serveur de temps NTP. Dans la plupart des cas c’est vrai, mais dans d’autres c’est fortement handicapant ! Cela est d’autant plus navrant, que les processeurs AllWinner H3 et H5 utilisés par les cartes Nano Pi disposent de RTC sur la puce du SoC ! Les concepteurs n’ont juste pas jugé utile de sortir sur une pastille la tension de backup de cette RTC ! La solution est donc d’ajouter un circuit intégré RTC généralement connecté à la carte par le bus I2c.

Le but de ce tutoriel est d’expliquer la procédure d’installation et de configuration d’un circuit RTC sur un système embarqué utilisant ArmBian.

Dans ce document nous utiliserons, pour l’exemple, une RTC Texas Instruments BQ32000 que nous relierons à une carte NanoPi Neo. La version de ArmBian utilisée est la Debian Stretch 5.41 (kernel 4.14.18).

NanoPi Neo

Les broches SDA et SCL de la RTC seront reliées aux broches correspondantes du bus I2c0 du connecteur GPIO (broche 3 et 5).

Vérification de la présence du driver de la RTC

Dans un premier temps, il faut vérifier la disponibilité du driver de la RTC BQ320000 sur le système :

$ sudo find / -name "*bq32*.ko"
/lib/modules/4.14.18-sunxi/kernel/drivers/rtc/rtc-bq32k.ko

Ici le driver est présent et s’appelle rtc-bq32k.ko. Si le fichier n’est pas présent, c’est qu’il n’a pas été compilé lors de la compilation du kernel 🙁 Dans ce cas, il faudra valider la compilation du driver correspondant et recompiler le système. Cette opération sort du contexte de ce document, mais il faut savoir que cela n’est pas très compliqué à condition de disposer d’un PC puissant sous Ubuntu Xenial ! Le site de ArmBian explique la procédure.

Chargement du driver au boot

Avec les noyaux modernes, le chargement des drivers se fait par l’intermédiaire de Device Tree. Device Tree que l’on peut traduire par “Arborescence matérielle” est une façon de décrire tout le matériel disponible sur un système.

Si ce nouveau système a facilité la vie des développeurs de Linux, il a sacrément compliqué celle des utilisateurs qui souhaitent juste ajouter une “chite” RTC sur leur PiBoard préférée. Disons le clairement : Device Tree c’est de la bonne veille “Usine à gaz” conçue par des informaticiens totalement crazy 😀

Heureusement les concepteurs de ArmBian ont choisi de faciliter la vie de leurs utilisateurs, mais il va falloir quand même se retrousser les manches et mettre les mains dans le cambouis !

La site ArmBian présente la procédure.

Ce qu’il faut savoir :

  • Une configuration matérielle est décrite par un fichier overlay d’extension .dtbo qui sont des fichiers binaires (dtbo pour device tree binary overlay)
  • Un fichier overlay est créé à l’aide du compilateur dtc à partir d’un fichier source .dts qui respecte la syntaxe fdt pour Flat Device Tree (et qui est loin d’être abordable pour le non-informaticien…)
  • Les fichiers overlay décrivant la configuration de base de la carte se trouvent dans /boot/dtb
  • Les fichiers overlay des circuits ajoutés se trouvent dans /boot/overlay-user

Choisir la configuration du driver

Chaque driver de circuit disposent d’options pouvant être ajoutées à l’overlay pour modifier son comportement. Ces options sont documentées directement dans les sources du kernel Sic ! Pour la BQ32000 et notre version de noyau c’est ici, voilà le fichier :

* TI BQ32000                I2C Serial Real-Time Clock

Required properties:
- compatible: Should contain "ti,bq32000".
- reg: I2C address for chip

Optional properties:
- trickle-resistor-ohms : Selected resistor for trickle charger
       Values usable are 1120 and 20180
       Should be given if trickle charger should be enabled
- trickle-diode-disable : Do not use internal trickle charger diode
       Should be given if internal trickle charger diode should be disabled
Example:
       bq32000: rtc@68 {
               compatible = "ti,bq32000";
               trickle-resistor-ohms = <1120>;
               reg = <0x68>;
       };

Comme on peut le voir au paragraphe 7.3.3 du datasheet, le BQ32000 peut être utilisé avec une pile ou un super condensateur (de plusieurs Farads !), afin de maintenir l’heure lorsque l’alimentation du Nano Pi est coupée. Dans le cas d’une pile, le circuit de charge (trickle charging) devra être désactivé, dans le cas de l’utilisation d’un super condensateur il doit être activé. C’est le sens des options :

  • trickle-diode-disable qui permet de désactiver le circuit de charge (TCH2 ouvert). Si cette option n’est pas indiquée, le circuit de charge est activé, ce qui peut conduire à l’explosion de la pile !
  • trickle-resistor-ohms qui permet de choisir la résistance du circuit de charge (TCFE ouvert ou fermé). Il sera nécessaire de consulter la documenation du super condensateur pour choisir la configuration correcte (résistance de 1120 ohms, la plupart du temps, cad TCFE ouvert)

Une explication des courants/temps de charge/décharge est présent sur le site de Maxim

Création du fichier source .dts

A partir des fichiers du dossier examples du dépôt sunxi-DT-overlays, on créée le fichier i2c-bq32000.dts suivant :

/dts-v1/;
/plugin/;

/ {
  compatible = "allwinner,sun4i-a10", "allwinner,sun7i-a20", "allwinner,sun8i-h3", "allwinner,sun50i-a64", "allwinner,sun50i-h5";

  /* 
   * Aliases can be used to set the external RTC as rtc0
   * Needs supplying the correct path to the I2C controller RTC is connected to,
   * this example is for I2C0 on H3
   * NOTE: setting time at boot by the kernel
   * may not work in some cases if the external RTC module is loaded too late
   */
  fragment@0 {
    target-path = "/aliases";
    __overlay__ {
      rtc0 = "/soc/i2c@01c2ac00/bq32000@68";
    };
  };

  fragment@1 {
    target = <&i2c0>;
    __overlay__ {
      #address-cells = <1>;
      #size-cells = <0>;
      bq32000@68 {
        compatible = "ti,bq32000";
        reg = <0x68>;
        trickle-diode-disable;
        status = "okay";
      };
    };
  };
};

L’option trickle-diode-disable; étant présente, on est dans le cas de l’utilisation d’une pile, si ce n’est pas le cas, il faudra la remplacer par trickle-resistor-ohms = <1120>; (ou 20180…).

Il faut noter que sur le NanoPi Neo, le bus I2c utilisé est le bus 0. L’adresse de son contrôleur est 0x01c2ac00. Si on utilise une autre carte et/ou un autre bus, il faudra rechercher cette adresse à l’aide de la commande :

$ sudo dtc -qq -I fs /proc/device-tree | grep -e '/soc/i2c@'

Dans le cas du NanoPi Neo cela nous donne à l’affichage :

i2c1 = "/soc/i2c@01c2b000";
i2c2 = "/soc/i2c@01c2b400";
i2c0 = "/soc/i2c@01c2ac00";
r_i2c = "/soc/i2c@01f02400";
i2c0 = "/soc/i2c@01c2ac00";

On voit que le SoC H3 du NanoPi Neo dispose de 3 bus I2c aux adresses 0x01c2ac00 pour le bus 0, 0x01c2b000 pour le bus 1 et 0x01c2b400 pour le bus 2.

Les lignes rtc0 = “/soc/i2c@… et target = <&i2c… devront alors être modifiées en conséquence.

Installation du fichier overlay .dtbo

Pour compiler les fichiers overlay, il faut installer les fichiers en-tête du kernel utilisé, cela se fait grâce à la commande :

$ sudo armbian-config

On choisit le menu Software puis Headers pour installer (attention si cela a déjà été effectuée cela désisnstalle les fichiers !). Faire Cancel et Cancel pour sortir.

Pour compiler et installer notre overlay, on utilise la commande :

$ sudo armbian-add-overlay i2c-bq32000.dts

Cela affiche le message suivant :

  Compiling the overlay
  Copying the compiled overlay file to /boot/overlay-user/
  Reboot is required to apply the changes

Un petit ls /boot/overlay-user nous permet de vérifier la précence du fichier .dtbo :

$ ls /boot/overlay-user
i2c-bq32000.dtbo

Vérifications de bon fonctionnement de la RTC

Après reboot, on peut vérifier le chargement du driver dans le journal du kernel :

$ dmesg | grep rtc
[    4.556601] sun6i-rtc 1f00000.rtc: rtc core: registered rtc-sun6i as rtc0
[    4.556613] sun6i-rtc 1f00000.rtc: RTC enabled
[    5.168535] bq32k 0-0068: rtc core: registered bq32k as rtc1

Si on ne trouve pas de trace du driver bk32k, il faudra observer les messages du kernel au démarrage du système sur la liaison série de debug (UART0).

Notre RTC a donc été connectée au fichier /dev/rtc1. Comme on peut le voir ci-dessus, la RTC du SoC a elle aussi été connectée au système par l’intermédiaire du fichier /dev/rtc0. Cette RTC n’ayant pas de tension de backup, il faudra la désactiver à l’étape suivante.

Dans un premier temps vérifions le fonctionnement de notre RTC BQ32000 en afffichant la date/heure système (NTP) et celle de notre RTC :

$ date && sudo hwclock -r -f /dev/rtc1
samedi 17 mars 2018, 16:26:02 (UTC+0100)
2018-03-17 16:24:47.160983+0100

Nous constatons que notre RTC fonctionne, mais qu’elle n’est pas à l’heure ! Pour un affichage simple de l’heure RTC, on effectue la commande

$ sudo hwclock -r -f /dev/rtc1

Pour mettre à l’heure notre RTC à partir de l’heure NTP :

$ sudo hwclock -w -f /dev/rtc1

Vérifions :

$ date && sudo hwclock -r -f /dev/rtc1
samedi 17 mars 2018, 16:30:48 (UTC+0100)
2018-03-17 16:30:47.162255+0100

Désactivation de la RTC du SoC H3

La RTC du SoC H3 doit être désactivée car le kernel va systématiquement la reconfigurer comme RTC par défaut (/dev/rtc) puisque son driver est compilé à l’intérieur du noyau !

Pour désactiver la RTC du SoC H3, il faut créer un fichier overlay…

Nous allons dans un premier temps, “décompiler” la configuration du NanoPi grâce à la commande :

$ sudo dtc -qq -I fs /proc/device-tree > nanopi-neo.dts

Cela créée le fichier source nanopi-neo.dts, une recherche avec Geany, nous permet de voir les partie qui concerne la RTC du SoC :

....
rtc = "/soc/rtc@01f00000";
....
rtc@01f00000 {
  compatible = "allwinner,sun6i-a31-rtc";
  interrupts = <0x0 0x28 0x4 0x0 0x29 0x4>;
  phandle = <0x69>;
  reg = <0x1f00000 0x54>;
  linux,phandle = <0x69>;
};
....

Le fichier source overlay rtc-disable.dts permettant de la désactiver est le suivant :

/dts-v1/;
/plugin/;

/ {
  compatible = "allwinner,sun4i-a10", "allwinner,sun7i-a20", "allwinner,sun8i-h3", "allwinner,sun50i-a64", "allwinner,sun50i-h5";

 fragment@0 {
   target = <&rtc>;

   __overlay__ {
     status = "disabled";
   };
 };
};

Une fois compilé et installé à l’aide de sudo armbian-add-overlay rtc-disable.dts et après redémarrage, on peut constater que cette rtc est effectivement désactivée :

rtc@01f00000 {
  compatible = "allwinner,sun6i-a31-rtc";
  status = "disabled";
  interrupts = <0x0 0x28 0x4 0x0 0x29 0x4>;
  phandle = <0x69>;
  reg = <0x1f00000 0x54>;
  linux,phandle = <0x69>;
};

Un **ls -l /dev/rtc***, nous permet de voir qu’il n’a plus qu’une seule rtc, la rtc0 :

lrwxrwxrwx 1 root root      4 nov.   3  2016 /dev/rtc -> rtc0
crw------- 1 root root 253, 0 nov.   3  2016 /dev/rtc0

Une recherche dans le journal du kernel nous permet de voir que c’est la BQ32000 :

$ dmesg | grep rtc
[    5.182244] bq32k 0-0068: rtc core: registered bq32k as rtc0

Un sudo hwclock -r nous permet de lire les informations de notre RTC :

2018-03-17 21:50:43.386528+0100

Mise à l’heure RTC au boot

Le circuit RTC est maintenant détecté et configuré par le noyau, mais ce n’est pas pour autant qu’il est utilisé par le système !

Nous allons créer un script qui sera lancé automatiquement par le démon de démarrage du système systemd. Cette étape est dérivée de celle présentée sur le site hackable.fr

Tout d’abord on créée un script shell qui utilise hwclock pour mettre à l’heure le système avec l’heure RTC, le fichier rtc-setup est le suivant :

#!/bin/sh
hwclock -s --utc
echo "System Time synced with RTC Time"

Puis on rend ce script exécutable :

$ chmod +x rtc-setup

On peut tester ce script et vérifier que l’heure système et bien la même que l’heure RTC :

$ sudo ./rtc-setup
$ date && sudo hwclock -r
samedi 17 mars 2018, 18:06:36 (UTC+0100)
2018-03-17 18:06:36.829015+0100

On créée ensuite le répertoire /usr/lib/systemd/scripts et on y copie notre script rtc-setup :

$ sudo mkdir -p /usr/lib/systemd/scripts
$ sudo cp rtc-setup /usr/lib/systemd/scripts

On créée ensuite un fichier service systemd pour automatiser le démarrage de rtc-setup au boot, le fichier rtc-init.service est le suivant :

[Unit]
Description=RTC Clock Setup and Time Sync
Before=cron.service

[Service]
Type=oneshot
ExecStart=/usr/lib/systemd/scripts/rtc-setup

[Install]
WantedBy=multi-user.target

On copie ce fichier dans le répertoire /etc/systemd/system :

$ sudo cp rtc-init.service /etc/systemd/system

Il ne reste plus qu’à valider le service rtc-init et à invalider le service fake-hwclock :

$ sudo systemctl enable rtc-init
$ sudo systemctl disable fake-hwclock

fake-hwclock est un service qui palie à l’absence de RTC sur un système embarqué, son rôle est d’écrire régulièrement dans un fichier l’heure système (généralement récupérée par NTP) et la restaurer au démarrage. Cela permet, en cas d’absence de réseau, d’avoir une heure cohérente mais complétement fausse.

Il est maintenant nécessaire de redémarrer et de vérifier que tout fonctionne :

$ sudo reboot
....
$ date && sudo hwclock -r

Retour à la configuration par défaut

Pour revenir à la configuration par défaut, si par exemple, on retire le circuit RTC, il suffit de dévalider le service rtc-init et de valider le service fake-hwclock :

$ sudo systemctl disable rtc-init
$ sudo systemctl enable fake-hwclock

Puis de redémarrer :

$ sudo reboot