Publié le 17 septembre 2016 - par

I2C, plus de 30 ans et toujours d’actualité.

figureI2C01Episode 1 – Dans le domaine des bus de transmission série (SPI, 1Wire et autres), le bus I2C est certainement le plus connu. Il se décline sous différentes appellations (I2C, SmBus, 2Wire ou TWI) et, sur le fond, ils se ressemblent tous à quelques variantes près (fréquences ou niveaux de tension). Ces variantes existent principalement pour échapper à la licence I2C et de la redevance associée.

NIVEAU_debutant

Introduction

Le bus I2C (Inter Integrated Circuit) a été créé dans le début des années 80 par la société Philips (actuellement I2C est pris en charge par NXP). On trouve des centaines de boîtiers différents avec une interface I2C.
On peut classer ces circuits en trois grandes familles (les applications grands publics, les applications professionnelles et les applications à usages généraux).

Quelques exemples de circuits disponibles dans ces domaines :

  • contrôles de tonalité, contrôles de volume, sélecteurs de sources,

  • convertisseurs A/D, convertisseurs D/A,

  • commandes d’amplificateur, fading, compact disc,

  • synthétiseurs de fréquences, PLL, circuits de FI,

  • décodeurs stéréo, RDS,

  • décodeurs PAL / SECAM / NTSC,

  • circuits de réception TV,

  • générateurs DTMF, générateurs de mélodie,

  • mémoires RAM et E2PROM,

  • expandeurs d’entrées / sorties,

  • commandes d’afficheurs LED, LCD, etc…

  • horloges temps réel,

  • etc…

Pour mener à bien notre expérimentation, il nous fallait des composants I2C et de préférence des circuits faciles à mettre en œuvre. Un petit tour dans l’atelier et dans le tiroir circuits intégrés I2C, il n’y avait que l’embarras du choix. Nous avons retenu les candidats suivants :

– une RAM (Random Access Memory) PCF8570,
– une E2PROM (
Electrically-Erasable Programmable Read-Only Memory) PCF8582,
– un capteur de température DS1621.

Vous pouvez utiliser tout ou partie des composants I2C (cela vous donne 7 possibilités).

Le Bus I2C

Nous allons pas vous présenter le BUS I2C, d’autres l’ont très bien fait. En effet, on trouve sur la toile des centaines de pages sur le sujet et il existe des ouvrages entiers consacrés au bus i2c.
Vous pourrez, par exemple, lire l’article WikiPedia, ou bien, l’article du site de Christian Tavernier.

Nota : Pourquoi le site de CT ?
Car c’est à travers ces articles de la presse électronique que j’aie découvert le monde des micro-processeurs (6800, 6809 et autres 68000 de Motorola). A l’époque le clavier était composé d’une vingtaine de boutons poussoirs et de six afficheurs 7 segments (2 pour les données et 4 pour les adresses) et le reste c’était de l’assembleur. Séquence nostalgie !

La figure ci-dessous représente le synoptique de notre base d’expérimentation.

figureI2C02 Figure 2 : Synoptique

Rappel :
Le bus i2c est un bus de type série, synchrone et half-duplex. Il est composé de trois fils, à savoir :
SDA (Serial Data Line), transmission des données dans les deux sens,
SCL (Serial Clock Line), signal d’horloge pour la synchronisation,
GND (Ground), il faut bien une référence.

Les informations transitent sur un même fil mais jamais en même temps.
Toutes les transmissions (écriture ou lecture) sont initiées par le maître et adressées à un esclave via sont adresse.

C’est un bus lent, à l’origine la fréquence de transmission était fixée à 100 kbits/s (standard mode). Depuis d’autres fréquences de fonctionnement sont apparues 400 kbits/s (fast mode), 1 Mbits/s (fast mode +), 3.4 Mbits/s (high-speed mode) et 5 Mbits/s (Ultra-fast mode). Le mode ultra-fast n’est plus bidirectionnel, le maître ne fait que des écritures.

Configurer le bus I2C

Généralités

En préparant cet article, nous avons consulté nos notes et des sites web pour définir les procédures de configuration du bus i2c. On constate que l’on trouve tout et même parfois n’importe quoi !
Pour clarifier la situation, nous avons utilisé une nouvelle installation de raspbian (2016-05-27-raspbian-jessie.img) sur notre Raspberry Pi et nous avons examiné les différentes étapes de l’installation. Après création de la carte SD, démarrage, paramétrages (extension du fichier système, localisation, fuseau horaire, clavier, région WiFi et accès réseau) et les re-démarrages correspondants, nous avons un système propre et vierge de toutes autres installations et mises à jour.

Si l’on examine les fichiers etc/modules et etc/modprobe.d/raspi-blacklist.conf, nous constatons que le premier contient la ligne :

i2c-dev

Le second est vide (pas d’interdiction de chargement de modules noyau).

En exécutant la commande console :

lsmod

Nous obtenons la liste des modules actifs du noyau :

Module   Size   Used by
...
i2c_dev  5859   0 
...

Le module i2c_dev gère les accès /dev/entries.

On peut obtenir plus d’informations sur le module par la commande :

modinfo i2c_dev

Activation I2C

Mode graphique

Pour activer l’interface i2c, il suffit de faire les opérations suivantes à la souris :

Menu → Préférences → Configuration du Raspberry Pi

Dans la fenêtre affichée, il faut se rendre dans l’onglet Interfaces et cliquer sur le bouton radio «Activé» de la ligne I2C puis sur le bouton Valider.

Pour terminer l’opération, il faut rebooter le Raspberry Pi.

Mode console

Pour activer l’interface i2c, il suffit de saisir dans la console Linux :

sudo raspi-config

Puis il faut sélectionner l’option 9 (Advanced Options) puis A6 (I2C) puis <Oui> et <Ok>. Pour sortir de l’application utilisée <Finish>.

Pour terminer l’opération, il faut rebooter le Raspberry Pi.

Contrôle

Si l’on examine les fichiers etc/modules et etc/modprobe.d/raspi-blacklist.conf, nous constatons que le premier contient la ligne :

i2c-dev

Le second est vide (pas d’interdiction de chargement de modules noyau).
Le contenu des deux fichiers n’a pas changé par rapport à la situation précédente.

En exécutant la commande console :

lsmod

Nous obtenons la liste des modules actifs du noyau :

Module       Size   Used by
...
i2c_bcm2708  4770   0
...
i2c_dev      5859 0 
...

Le module i2c_bcm2708 est le pilote de bas niveau du bus i2c.

On peut obtenir plus d’informations sur le module par la commande :

modinfo i2c_bcm2708

Si l’on regarde les messages du noyau avec la commande :

dmesg | grep i2c

On obtient les informations suivantes :

[ 5.580941] i2c /dev entries driver
[ 9.097842] bcm2708_i2c 20804000.i2c: BSC1 Controller at 0x20804000 (irq 77)
            (baudrate 100000)

On voit que le bus i2c est disponible à la fréquence standard de 100 kbits/s.

Une autre information utile est disponible par la commande :

ls -l /dev/i2c*

On obtient l’information suivante :

crw-rw---- 1 root i2c 89, 1 sept. 2 15:57 /dev/i2c-1

Dans la suite, on se servira du chiffre 1 de /dev/i2c-1 pour l’accès au bus i2c.

Compléments

Vous pouvez ajouter ou retirer des modules du noyau avec les commandes :

sudo modprobe -a [nom_module]
sudo modprobe -r [nom_module]

(a comme add et r comme remove).
Ces modifications restent actives tant que le système n’a pas été redémarré ou éteint.

La liste des modules à charger au démarrage se trouve dans le fichier /etc/modules. Chaque ligne représente un module à charger, sauf les lignes vides ou les lignes précédées par un #.
Attention : les modules sont chargés dans l’ordre dans lequel ils sont listés.

Il existe également une liste de modules que le noyau n’a pas le droit de charger qui sont listés dans le fichier /etc/modprobe.d/blacklist.

Nota :
Si l’on explore, un peu, la liste des fichiers de Linux, on constate que pour activer ou désactiver l’interface i2c, il suffit de modifier le fichier /boot/config.txt.

...
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
...
dtparam=i2c_arm=on   #active l'interface i2c
dtparam=i2c_arm=off  #désactive l'interface i2c

Il faudra rebooter pour que la modification soit prise en compte. Cette procédure est une alternative à la modification via l’interface graphique ou via sudo raspi-config.

On pourra également changer la fréquence du bus i2c en ajoutant dans /boot/config.txt la ligne ci-dessous :

...
# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=on
dtparam=i2c_baudrate=400000
#dtparam=i2s=on
#dtparam=spi=on
...

Avec cette modification, la fréquence du bus devient 400 kbits/s.

Outils

Pour utiliser notre interface i2c, il nous faut des outils que l’on peut installer par les commandes suivantes :

sudo apt-get update
sudo apt-get install i2c-tools

On a, maintenant, à notre disposition des commandes pour explorer le bus i2c.

i2cdetect

Syntaxe :

i2cdetect
Usage: i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
       i2cdetect -F I2CBUS
       i2cdetect -l
  I2CBUS is an integer or an I2C bus name
  If provided, FIRST and LAST limit the probing range.

La première commande que nous utiliserons est i2cdetect avec la syntaxe i2cdetect -y 1.

Pour plus de détail sur la commande, vous pouvez consulter la page i2cdetect

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

La commande n’a pas détecter d’adresses i2c sur le bus, ce qui est normal, car nous n’avons pas implanter de composants pour l’instant. On constate, également, que la commande n’a pas balayé l’ensemble des adresses du bus (0x03 à 0x77).

En effet, la norme i2c prévoit que certaines adresses sont réservées :
0x00 à 0x02 : appel général, CBUS et autres bus
0x78 à 0x7B : adressage sur 10 bits
0x7C à 0x7F : device ID

Nous disposons ainsi de 117 adresses utilisables pour notre bus i2c donc conforme avec notre plage de balayage de i2cdetect..

Nota :
L’adresse 0x03 (utilisation future) devrait également faire partie des adresses réservées.
Les adresses 0x4 à 0x07 sont utilisées par des composants hautes vitesses.

Si l’on veut scruter l’ensemble des adresses, il faudra utiliser la commande i2cdetect -a 1.

pi@raspberrypi:~ $ i2cdetect -a 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x00-0x7f.
Continue? [Y/n] 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 

Avertissement : Certaines options de i2cdetec sont à utiliser avec précautions.

On peut, également, obtenir la liste des bus disponibles par i2cdetect -l

pi@rasp10:~ $ i2cdetect -l
i2c-1 i2c         20804000.i2c                   I2C adapter

Ou, sur les fonctionnalités implémentées par i2cdetect -F 1.

pi@rasp10:~ $ i2cdetect -F 1
Functionalities implemented by /dev/i2c-1:
I2C                              yes
SMBus Quick Command              yes
SMBus Send Byte                  yes
SMBus Receive Byte               yes
SMBus Write Byte                 yes
SMBus Read Byte                  yes
SMBus Write Word                 yes
SMBus Read Word                  yes
SMBus Process Call               yes
SMBus Block Write                yes
SMBus Block Read                 no
SMBus Block Process Call         no
SMBus PEC                        yes
I2C Block Write                  yes
I2C Block Read                   yes

Mais i2cdetect n’est pas la seule commande disponible, elle est venue avec quelques copines. Nous n’utiliserons que les commandes ci-dessous pour nos tests.

i2cdump

Affiche le contenu d’une zone.

Syntaxe :

i2cdump
Usage: i2cdump [-f] [-y] [-r first-last] I2CBUS ADDRESS [MODE [BANK [BANKREG]]]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x03 - 0x77)
  MODE is one of:
    b (byte, default)
    w (word)
    W (word on even register addresses)
    s (SMBus block)
    i (I2C block)
    c (consecutive byte)
    Append p for SMBus PEC

Pour plus de détail sur la commande, vous pouvez consulter la page i2cdump

i2cget

Lecture d’une adresse.

Syntaxe :

i2cget
Usage: i2cget [-f] [-y] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
   I2CBUS is an integer or an I2C bus name
   ADDRESS is an integer (0x03 - 0x77)
   MODE is one of:
     b (read byte data, default)
     w (read word data)
     c (write byte/read byte)
     Append p for SMBus PEC

Pour plus de détail sur la commande, vous pouvez consulter la page i2cget

i2cset

Ecriture à une adresse.

Syntaxe :

i2cset
Usage: i2cset [-f] [-y] [-m MASK] [-r] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE] ... [MODE]
  I2CBUS is an integer or an I2C bus name
  ADDRESS is an integer (0x03 - 0x77)
  MODE is one of:
    c (byte, no value)
    b (byte data, default)
    w (word data)
    i (I2C block data)
    s (SMBus block data)
    Append p for SMBus PEC

Pour plus de détail sur la commande, vous pouvez consulter la page i2cset

Base d’expérimentation

Schéma électronique

figurei2c03Nous avons un schéma un peu plus touffu que les fois précédentes mais rien de bien compliqué.
Toutes les pins SDA et SCL des composants i2c sont reliées à la broche SDA ou SCL de notre Raspberry PI. L’alimentation est également prélevée sur la carte et pour finir toutes les broches gnd sont reliées entre-elles.

Remarque 1 : Les deux résistances de soutirage, entre l’alimentation et les lignes SDA et SCL (ET câblé), nécessaires au fonctionnement du bus ne sont pas prévues sur le schéma car implantées sur la carte Raspberry Pi.

Remarque 2 : Pour éviter d’avoir des phénomènes d’oscillations avec les circuits intégrés numériques, il est préférable de prévoir un condensateur de découplage (100 nF) sur la broche d’alimentation (câblage à faire au plus près du circuit intégré).

Câblage

figurei2c04Pour réaliser notre base d’expérimentation, il nous faut :
– un Raspberry Pi,
– un cobbler avec sa nappe,
– une plaque de prototypage rapide,
– un PCF8570 (RAM),
– un PCF 8582 (E2PROM),
– un DS 1621 (température),
– trois condensateurs 100 nF,
– des fils de liaison mâle/mâle.

Nota : Pour le câblage des adresses des circuits i2c, il faut consulter la documentation technique des composants. Les trois fiches techniques (datasheets) sont jointes dans le zip des sources.

Tests

Il n’y a pas de secret pour exploiter nos composants connectés au bus i2c, il faudra lire et relire des fiches techniques (FT).

Nota :
Il ne faut jamais se contenter de la première page de la datasheet car souvent cela ressemble plus à un publicité, pas toujours très réaliste, qu’à un document technique. Mais c’est juste mon avis !

Composants

DS1621

Le DS1621 est un thermomètre digital et un thermostat permettant des mesures de température dans la plage -55°C et +125°C avec une précision de mesure de 0.5°C (on peut améliorer l’affichage par calcul).
L’alimentation électrique peut se faire dans la plage 2.7V à 5.5V ce qui est dans la plage d’utilisation de notre carte (3.3V).
Les fréquences d’utilisation sont de 100 kHz (standard mode) ou 400 kHz (fast mode) voir FT page 15.
L’adresse sur 7 bits du composant est 1001A2A1A0 soit 0x48 à 0x4F (FT page 8).

Nota :
Pour l’exploitation du composant, on utilise une adresse sur 8 bits sous la forme 1001A2A1A0S (S=0 pour l’écriture ou S=1 pour la lecture).

Information : La fiche technique donne également la procédure et les chronogrammes i2c pour sa mise en œuvre .

PCF8570

Le PCF8570 est une mémoire volatile (RAM) de 256 octets (8 bits).
L’alimentation électrique se fait dans la plage 2.5V et 6.0V.
La fréquence d’utilisation se limite au standard mode, c’est à dire 100 kHz (FT page 10).
L’adresse sur 7 bits du composant est 1010A2A1A0 soit 0x50 à 0x57 (FT page 12).

Information : La fiche technique donne également la procédure et les chronogrammes i2c pour sa mise en œuvre

PCF8582

Le PCF8582 est une mémoire non volatile effaçable et programmable électriquement (EEPROM ou E2PROM) de 256 octets (8 bits).
L’alimentation électrique se fait dans la plage 2.5V et 6.0V.
La fréquence d’utilisation se limite au standard mode, c’est à dire 100 kHz (FT page 11).
L’adresse sur 7 bits du composant est 1010A2A1A0 soit 0x50 à 0x57 (FT page 7).

Avertissement :
Le nombre de cycle écriture / effacement est limité à 1.000.000 de cycle (valeur théorique). Cela peut paraître énorme mais dans une boucle infinie cette limite peut être atteinte très rapidement. Alors attention !

Information :La fiche technique donne également la procédure et les chronogrammes i2c pour sa mise en œuvre.

Remarque : Le PCF8570 et PCF8582 ont la même plage d’adressage

i2cdetect

La commande i2cdetect -y 1 nous donne :

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

La recherche nous donne trois adresses de composants en hexadécimal 0x48, 0x50 et 0x51. Mais qui est qui ?

Pour le DS1621, nous avons pour A2 = 0 (grd), A1 = 0 et A0 = 0 ce qui nous donne une adresse 0b1001000 = 0x48.
Pour le PCF8570, nous avons pour A2 = 0, A1 = 0 et A0 = 0 ce qui nous donne une adresse 0b1010000 = 0x50.
Pour le PCF8582, nous avons pour A2 = 0, A1 = 0 et A0 = 1 (+3.3V) ce qui nous donne une adresse 0b1010001 = 0x51.

Si l’on fait la même manipulation en fixant la vitesse du bus à 400 kbits/sec, nous obtenons le résultat suivant :

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

Notre mémoire E2PROM (ad : 0x50) n’est plus détectée. Malgré une vitesse de 400 kbits/s notre RAM est toujours détectée. Par contre, les écritures et les lectures de la RAM s’avèrent être des plus aléatoires.
Pour le capteur de température (DS1621) tout est normal car prévu pour fonctionner à cette vitesse.

i2cdump

La commande i2cdump permet d’afficher le contenu mémoire du composant.

PCF8570

Pour afficher l’ensemble des données de notre RAM, on utilise la commande :

i2cdump -y 1 0x50 b

Le paramètre b signifie que l’affichage se fait sous la forme d’octets.

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 00 00 04 00 06 00 00 00 82 00 40 00 80 00 04 00 ..?.?...?.@.?.?.
10: 80 00 00 04 00 00 02 00 00 00 00 00 00 01 00 01 ?..?..?......?.?
20: 10 88 00 00 00 00 00 00 00 08 42 00 10 00 00 00 ??.......?B.?...
30: 00 09 00 02 00 80 00 04 00 41 00 00 08 00 00 08 .?.?.?.?.A..?..?
40: 00 00 80 00 00 08 00 00 00 00 00 10 00 00 00 80 ..?..?.....?...?
50: 80 02 00 80 02 00 00 02 00 00 20 00 00 10 09 80 ??.??..?.. ..???
60: 20 10 20 00 00 00 00 00 00 00 00 00 80 00 00 01 ? .........?..?
70: 00 00 10 20 10 00 00 8c 00 00 20 01 00 10 00 00 ..? ?..?.. ?.?..
80: 00 04 02 00 00 01 04 00 00 00 01 00 10 00 00 08 .??..??...?.?..?
90: 50 04 00 20 30 00 08 00 00 01 01 00 00 02 00 80 P?. 0.?..??..?.?
a0: 00 00 10 00 10 00 00 04 20 01 02 00 08 00 00 00 ..?.?..? ??.?...
b0: 02 00 00 00 00 00 00 01 00 02 00 00 28 00 00 00 ?......?.?..(...
c0: 40 00 00 00 00 a0 00 00 80 20 00 00 99 00 08 00 @....?..? ..?.?.
d0: 10 20 80 00 02 00 00 00 00 10 40 08 00 00 00 10 ? ?.?....?@?...?
e0: 00 00 00 02 00 80 00 08 00 00 00 08 30 28 00 10 ...?.?.?...?0(.?
f0: 04 00 02 00 22 00 00 80 80 00 00 00 00 20 00 00 ?.?."..??.... ..

A la mise sous tension, le contenu de la RAM est complètement aléatoire.

Pour afficher l’ensemble des données de notre RAM au format word (2 octets), on utilise la commande :

i2cdump -y 1 0x50 w

Le paramètre w signifie que l’affichage se fait sous la forme de word (2 octets).

     0,8  1,9  2,a  3,b  4,c  5,d  6,e  7,f
00: 0000 0400 0004 0600 0006 0000 0000 8200 
08: 0082 4000 0040 8000 0080 0400 0004 8000 
10: 0080 0000 0400 0004 0000 0200 0002 0000 
18: 0000 0000 0000 0000 0100 0001 0100 1001 
20: 8810 0088 0000 0000 0000 0000 0000 0000 
28: 0800 4208 0042 1000 0010 0000 0000 0000 
30: 0900 0009 0200 0002 8000 0080 0400 0004 
38: 4100 0041 0000 0800 0008 0000 0800 0008 
40: 0000 8000 0080 0000 0800 0008 0000 0000 
48: 0000 0000 1000 0010 0000 0000 8000 8080 
50: 0280 0002 8000 0280 0002 0000 0200 0002 
58: 0000 2000 0020 0000 1000 0910 8009 2080 
60: 1020 2010 0020 0000 0000 0000 0000 0000 
68: 0000 0000 0000 8000 0080 0000 0100 0001 
70: 0000 1000 2010 1020 0010 0000 8c00 008c 
78: 0000 2000 0120 0001 1000 0010 0000 0000 
80: 0400 0204 0002 0000 0100 0401 0004 0000 
88: 0000 0100 0001 1000 0010 0000 0800 5008 
90: 0450 0004 2000 3020 0030 0800 0008 0000 
98: 0100 0101 0001 0000 0200 0002 8000 0080 
a0: 0000 1000 0010 1000 0010 0000 0400 2004 
a8: 0120 0201 0002 0800 0008 0000 0000 0200 
b0: 0002 0000 0000 0000 0000 0000 0100 0001 
b8: 0200 0002 0000 2800 0028 0000 0000 4000 
c0: 0040 0000 0000 0000 a000 00a0 0000 8000 
c8: 2080 0020 0000 9900 0099 0800 0008 1000 
d0: 2010 8020 0080 0200 0002 0000 0000 0000 
d8: 1000 4010 0840 0008 0000 0000 1000 0010 
e0: 0000 0000 0200 0002 8000 0080 0800 0008 
e8: 0000 0000 0800 3008 2830 0028 1000 0410 
f0: 0004 0200 0002 2200 0022 0000 8000 8080 
f8: 0080 0000 0000 0000 2000 0020 0000 0000

On peut également afficher une partie du plan mémoire avec la commande :

i2cdump -y -r 0x06-0x2b 1 0x50 b
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 00 00 82 00 40 00 80 00 04 00                   ..?.@.?.?.
10: 80 00 00 04 00 00 02 00 00 00 00 00 00 01 00 01 ?..?..?......?.?
20: 10 88 00 00 00 00 00 00 00 08 42 00             ??.......?B.

PCF8582

Pour afficher l’ensemble des données de notre EEPROM, on utilise la commande :

i2cdump -y 1 0x51 b

Le paramètre b signifie que l’affichage se fait sous la forme d’octets.

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................

Nota :
Une mémoire EPROM ou EEPROM est considérée comme vierge lorsque tous ces octets sont à 0xff (tous les bits sont à 1).

DS1621

La même commande pour le capteur de température, vous donnera n’importe quoi car elle n’a pas de signification physique.

i2cset

La commande i2cset permet d’écrire une donnée vers le composant.

PCF8570

Pour écrire une données (octet) 0x46 (caractère F en ascii) à l’adresse mémoire 0x00 du composant 0x50, on utilise la commande :

i2cset -y 1 0x50 0x00 0x46 b

On peut vérifier le résultat avec de l’écriture avec la commande :

i2cdump -y -r 0x00-0x0f 1 0x50 b

Affichage de la plage 0x00 à 0x0f de notre RAM.

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 46 00 04 00 06 00 00 00 82 00 40 00 80 00 04 00 F.?.?...?.@.?.?.

On retrouve bien notre F dans le dump dans les colonnes de droite.

On peut utiliser d’autres bases de numérotation :

i2cset -y 1 0x50 0x01 0162 b

Ecrit en octal à l’adresse 0x01.

i2cset -y 1 0x50 0x02 97 b

Ecrit en décimal à l’adresse 0x02.

De même, il est possible d’écrire plusieurs octets à la suite avec la commande :

i2cset -y 1 0x50 0x03 0x6d 0142 111 0x69 0x73 0x65 0x33 0x31 i

Attention : Il y a un mélange de décimal, d’octal et d’hexadécimal.

Cette commande envoie les octets 0x6d, 0142, 111, 0x69, 0x73, 0x65, 0x33 et 0x31 à partir de l’adresse mémoire 0x03 de la RAM (ad : 0x50). Le caractère i précise que l’on transmet un bloc de données selon le format i2c.
Ce type d’écriture de données est limité à 8 octets.

Une dernière écriture avec la commande :

i2cset -y 1 0x50 0x0b 0x34 b

On peut vérifier le résultat avec i2cdump -y -r 0x00-0x0f 1 0x50 b (affiche la plage 0x00 à 0x0f de notre RAM).

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 46 72 61 6d 62 6f 69 73 65 33 31 34 80 00 04 00 Framboise314?.?.

Avec toutes ces manipulations, nous venons d’écrire Framboise314 (en ascii) dans notre mémoire RAM.

Il est possible d’écrire directement des données sur 2 octets (word au sens i2c) par la commande :

i2cset -y 1 0x50 0x10 0x1680 w
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 46 72 61 6d 62 6f 69 73 65 33 31 34 80 00 04 00 Framboise314?.?.
10: 80 16 00 04 00 00 02 00 00 00 00 00 00 01 00 01 ??.?..?......?.?

On retrouve nos données aux adresses 0x11 et 0x10

Remarque : Si l’on coupe l’alimentation de la RAM toutes les données seront perdues.

Nota :
Vous pouvez stocker en mémoire des valeurs numériques (int, short int ou float) mais il vous faudra décoder la valeur et faire une sauvegarde des différents octets qui composent la valeur. De même, il faudra recomposer les valeurs numériques à partir de ces octets.

PCF8582

Il suffit de faire les mêmes opérations pour l’EEPROM que celles de la RAM en changeant l’adresse 0x50 par 0x51.
On aura finalement dans l’EEPROM les données suivantes :

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f 0123456789abcdef
00: 46 72 61 6d 62 6f 69 73 65 20 33 31 34 ff ff ff Framboise 314...
10: 80 16 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ??..............

Par contre, une coupure de l’alimentation ne fera pas perdre les informations.

DS1621

Avant d’envoyer des données vers le DS1621 (thermomètre + thermostat), il faut savoir quoi et où ?
La documentation technique viendra de nouveau à notre secours (page 10) et vous trouverez également page 9 les chronogrammes des différentes lectures / écritures correspondant au DS1621.

Les différents registres

Vous trouverez les différents registres avec leurs descriptions page 10 de la documentation. Les commandes dont nous aurons besoin sont les suivantes :

Red Temperature (ad : 0xaa) : Registre à lecture seule. Il contient le résultat de la dernière conversion de température. (utilisé dans le paragraphe suivant)
Access Config (ad : 0xac) : Registre à lecture / écriture. Il définit les conditions de fonctionnement et de contrôle du circuit.
La signification de l’octet à joindre à cette adresse est décrite page 5 de la documentation techniques.
Le seul bit qui nous intéresse est le bit0 ou lsb qui permet la conversion de température en continue ou en coup par coup (0=continue et 1=coup par coup).

Pour nos tests, nous fixerons cet octet de configuration à 0x00.

Read Counter (ad : 0xa8) : Registre à lecture seule. Il lit la valeur de Count_Remain. Cette valeur est utilisée dans le calcul de la température donnant une approximation supérieur au 1/2 degré.
Read Slope (ad : 0xa9) : Registre à lecture seule. Il lit la valeur de Count_Per_C. Cette valeur est utilisée dans le calcul de la température donnant une approximation supérieur au 1/2 degré.
Start Convert T (ad:0xee) : Registre à lecture /écriture. Il déclenche la conversion de la température.

Le temps de conversion de la température est de 750 ms (page 15).

Information : Dans notre application, nous n’utiliserons pas la fonction thermostat.

Commandes

Pour configurer notre thermomètre, il suffit d’envoyer la commande :

i2cset -y 1 0x48 0xac 0x00 b

On écrit 0x00 (mesure en continue) dans le registre 0xac (accès configuration) à l’adresse du composant 0x48.

Pour démarrer les mesure :

i2cset -y 1 0x48 0xee

Nous exploiterons la lecture des données du capteur de température dans le chapitre suivant.

i2cget

La commande i2cget permet de lire une donnée du composant.

DS1621

Comme nous venons de mettre en service notre capteur de température, il ne nous reste plus qu’à lire les données. On obtient la valeur de la température par la commande :

i2cget – y 1 0x48 0xaa w

Un exemple de réponse du DS1621 :

pi@rasp10:~/dev/sagaI2C_01/bash $ i2cget -y 1 0x48 0xaa w
0x8013

A quoi correspondant cette donnée ?
Comme d’habitude nous trouvons la réponse dans la documentation technique page 4. La température est codée sur deux octets. L’octet de poids fort représente la température entière et l’octet de poids faible la température décimale.

0x8013: 0x13 octet poids fort et 0x80 octet poids faible
  Octet poids fort                 Octet poids faible
S  2⁶ 2⁵ 2⁴ 2³ 2² 2¹ 2⁰       2-¹ 2-2 2-3 2-4 2-5 2-6 2-7 2-8
0  0  0  1  0  0  1  1         1   0   0   0   0   0   0   0
^
Bit de signe 0 = positif ou 1 = négatif

Température entière  = 2⁴ + 2¹ + 2⁰ = 19°C 
Température décimale = 2-¹ = 0.5°C 
Température lue = 19.5°C

Si S = 1, les bits 2⁶ à 2⁰ de l’octet de poids fort représentent la valeur de la température en complément à 2.
Le complément à 2 correspond au complément à 1 + 1.

Exemple :

0x00ff: 0xff octet poids fort et 0x00 octet poids faible
Octet poids fort = 11111111
Complément à 1   = 00000000
Complément à 2   = 00000000 + 00000001 = 00000001
Soit -1°C

Autres lectures de température :
Température positive

i2cget – y 1 0x48 0xaa w   #température
i2cget – y 1 0x48 0xa8 b   #Count_Remain
i2cget – y 1 0x48 0xa9 b   #Count_Per_C

Résultats des lectures :

0X8012   #Température 18.5°C
0x07     #Count_Remain soit 7 en décimal
0x10     #Count_Per_C sit 16 en décimal

Avec Count_Remain et Count_Per_C, on peut obtenir température calculée plus fine par la formule :

                                                        (Count_Per_C - Count_Remain)
Température calculée = température entière lue - 0.25 + ----------------------------
                                                                Count_Per_C

Soit une température calculée de 18.3125°C. Tous les chiffres inférieures au dixième de degré n’ont pas de signification physique.
Pour ces mesures, nous retiendrons les valeurs suivantes :

Température lue      = 18.5°C
Température calculée = 18.3°C

Température négative

i2cget – y 1 0x48 0xaa w #température
i2cget – y 1 0x48 0xa8 b #Count_Remain
i2cget – y 1 0x48 0xa9 b #Count_Per_C

Résultats des lectures :

0X80e2   #Température -29.5°C
0x01     #Count_Remain soit 1 en décimal
0x10     #Count_Per_C sit 16 en décimal
Température lue      = -29.5°C
Température calculée = -29.3°C

PCF8570

Avec tout ce que l’on a vu précédemment, nous avons oublié nos deux mémoires.
Pour lire un octet dans la RAM, on peut utiliser la commande :

i2cget -y 1 0x50 0x00 b

Ce qui nous donne :

0x46   #F de Framboise314

PCF8582

Pour lire un octet dans l’EEPROM, on peut utiliser la commande :

i2cget -y 1 0x51 0x00 b

Ce qui nous donne :

0x46   #F de Framboise314

Commentaire

Avec tout ce que nous venons de voir et si vous disposez de la datasheet du composant, vous pouvez exploiter tous les périphériques i2c existants.

I2C et bash

Pour illustrer tout ce que nous venons de voir, nous vous proposons un script bash qui lit la température du DS1621 toutes les secondes et affiche dans la console la température lue ainsi que la température calculée.

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano i2c01.sh

Cette commande ouvre un fichier teste vide appelé i2c01.sh (pour la sauvegarde faire ctrl-o et pour sortir ctrl-x).
Le contenu du programme et ses commentaires sont représentés ci-dessous.

#!/bin/bash
#Programme lecture capteur de température DS1621 via bus i2c
#nom programme       : i2c01.sh
#logiciel            : bash
#cible               : raspberry Pi
#date de création    : 06/09/2016
#date de mise à jour : 08/09/2016
#version             : 1.0
#auteur              : icarePetibles
#référence           :
#Remarques           : Pour fonctionner, il faut être sous super utilisateur
#                      Il faut rendre le fichier exécutable avec
#                      sudo chmod +x i2c01.sh
#                      exécution : ./i2c01.sh
#
ATTENTE=1                                           #attente entre 2 mesures
                                                    #temps de conversion du DS1621
                                                    #750 ms
LC_NUMERIC=C                                        #définit le séparateur décimal
                                                    #en . au lieu de , car printf
                                                    #utilise les paramètres régionaux
#Procédure de netoyage et sortie
cleanup()                                           #sortie
{
    echo
    echo Fin script                                 #message de fin
    exit                                            #sortie
}

#programme principal
echo Début du script                                #IHM
echo Mesure de température avec capteur DS1621      #IHM
echo ctrl-c pour sortir de la boucle                #IHM
echo

#Setup pin et direction - Cature Control-C SIGHUP SIGKILL
trap "cleanup" SIGHUP SIGINT SIGTERM                #capture SIGNAL et lance la
                                                    #la procédure cleanup
#initialisation DS1621
i2cset -y 1 0x48 0xac 0x00                          #configuration
i2cset -y 1 0x48 0xee                               #démarre la converssion
#traitement des mesures
while true                                          #boucle infinie
    do                                              #faire
#lecure des registres du DS1621
        lecture=`i2cget -y 1 0x48 0xaa w`           #lecture température
        lecture=$(($lecture))                       #pour permettre le calcul
        counter=`i2cget -y 1 0x48 0xa8 b`           #lecture Count_Remain
        counter=$(($counter))                       #pour les calcul
        slope=`i2cget -y 1 0x48 0xa9 b`             #lecture Count_Per_C
        slope=$(($slope))                           #pour le calcul
        corr=$(echo "scale=3; -0.25+($slope/1-$counter/1)/($slope/1)" | bc -l)
                                                    #correction pour le calcul
#octets de poids faible et valeur décimale de la température
        lsb=$((($lecture>>8)/128))                  #extraction bit 2^7
        if [ $lsb -eq 1 ]                           #si bit poids fort = 1
        then
            decimal=0.5                             #partie décimale = 0.5°C
        else
            decimal=0.0                             #partie décimale = 0.0°C
        fi
#octet de poids fort, test >0 ou <0 et valeur entière de la température
        entier=$(($lecture&0x00ff))                 #pour le calcul
        signe=$(($entier/128))                      #extraction bit 2^7
        if [ $signe -eq 1 ]                         #si = 1 alors température <0
        then
            entier=$(($entier^0xff))                #complément à 1 par xor
            entier=$(echo "scale=0; ($entier+1)*(-1)" | bc -l)
                                                    #complément à 2
        fi
        temp_lue=$(echo "scale=1; $entier/1+$decimal/1" | bc -l)
                                                    #calcul température lue
#calcul la valeur de la température à partir de |temp_lue|, counter et slope
        if [ $entier -eq 0 ]                        #température = 0
        then
            temp_cal=0                              #init
            temp_cal=$(($temp_cal))                 #pour le calcul
            temp_cal=${temp_cal/.*}                 #conversion float to int
        else                                        #température <> 0
            if [ $lecture -eq 33023 ]               #cas particulier de 0x80FF
            then                                    #pb de float to int
                temp_cal=0                          #init
                temp_cal=$(($temp_cal))             #pour le calcul
                temp_cal=${temp_cal%.*}             #float to int
            else                                    #tous les autres cas
                temp_cal=${temp_lue%.*}             #valeur entière de temp_lue
            fi
        fi
        temp_cal=$(echo "scale=1; $temp_cal/1+$corr/1" | bc -l)
                                                    #température corrigée par calcul
#affichage des données
        printf "Température lue      : %.1f °C\n" $temp_lue
        printf "Température calculée : %.1f °C\n" $temp_cal
                                                    #nécessaire pour afficher
                                                    #correctement les nbres entre
                                                    #-0.9 et 0.9
        echo                                        #saut de ligne
        sleep $ATTENTE                              #attente
    done                                            #fin do
#Fin du script

Les commentaires du script sont auto-suffisants à la compréhension.

Exécution du programme

Pour exécuter ce script bash, il faut le rendre exécutable avec la commande :

sudo chmod +x i2c01.sh

Et pour l’exécuter :

./i2c01.sh

Pour sortir de la boucle de mesure, il suffit de faire ctrl-c au clavier.

Quelques copies d’écrans

figurei2c05 figurei2c06

Un peu d’habillage

Tous les programmes de la série « saga » affichaient leurs résultats dans la console Linux en mode texte. Ce type d’affichage n’est pas très « sexy », pour remédier à cette situation nous allons mettre en œuvre tput qui est disponible par défaut dans la distribution Raspbian.

tput

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano i2c06.sh

Cette commande ouvre un fichier teste vide appelé i2c06.sh (pour la sauvegarde faire ctrl-o et pour sortir ctrl-x).
Le contenu du programme et ses commentaires sont représentés ci-dessous.

#!/bin/bash
#Programme lecture capteur de température DS1621 via bus i2c
#nom programme       : i2c06.sh
#logiciel            : bash
#cible               : raspberry Pi
#date de création    : 06/09/2016
#date de mise à jour : 08/09/2016
#version             : 1.0
#auteur              : icarePetibles
#référence           :
#Remarques           : Pour fonctionner, il faut être sous super utilisateur
#                      Il faut rendre le fichier exécutable avec
#                      sudo chmod +x i2c06.sh
#                      exécution : ./i2c06.sh
#
echo -e "\e[8;12;40t"                               #fenêtre terminal 12x40
ATTENTE=1                                           #attente entre 2 mesures
                                                    #temps de conversion du DS1621
                                                    #750 ms
#
LC_NUMERIC=C                                        #définit le séparateur décimal
                                                    # en . au lieu de , car printf
                                                    #utilise les paramètres régionaux
#affichage écran
tput smcup                                          #sauve screen
clear                                               #efface écran
tput civis                                          #curseur invisible
tput cup 1 5                                        #curseur lig 1 col 6
tput setaf 4                                        #écrire en bleu
tput bold                                           #écrire en gras
echo "MESURE TEMPERATURE AVEC DS1621"               #IHM
tput sgr0                                           #annule attributs
tput cup 4 5                                        #lig 4 col 5
echo -e "Température lue      : \c"                 #IHM
tput cup 5 5                                        #lig 5 col 5
echo -e "Température calculée : \c"                 #IHM
tput cup 8 10                                       #lig 8 col 10
echo -e "Tapez \c"                                  #IHM
tput setaf 5                                        #écrire en magnenta
tput bold                                           #écrire en gras
echo -e "q\c"                                       #IHM
tput sgr0                                           #annule les attributs
tput setaf 0                                        #écrire en noir
echo -e " pour quitter\c"                           #IHM
#initialisation DS1621
i2cset -y 1 0x48 0xac 0x00                          #configuration
i2cset -y 1 0x48 0xee                               #démarre la converssion
#traitement des mesures
while true                                          #boucle infinie
    do                                              #faire
#lecure des registres du DS1621
        lecture=`i2cget -y 1 0x48 0xaa w`           #lecture température
        lecture=$(($lecture))                       #pour permettre le calcul
        counter=`i2cget -y 1 0x48 0xa8 b`           #lecture Count_Remain
        counter=$(($counter))                       #pour le calcul
        slope=`i2cget -y 1 0x48 0xa9 b`             #lecture Count_Per_C
        slope=$(($slope))                           #pour le calcul
        corr=$(echo "scale=3; -0.25+($slope/1-$counter/1)/($slope/1)" | bc -l)
                                                    #correction pour le calcul
#octets de poids faible et valeur décimale de la température
        lsb=$((($lecture>>8)/128))                  #pour le calcul
        if [ $lsb -eq 1 ]                           #si bit poids fort = 1
        then
            decimal=0.5                             #partie décimale = 0.5°C
        else
            decimal=0.0                             #partie décimale = 0.0°C
        fi
#octet de poids fort, test >0 ou <0 et valeur entière de la température
        entier=$(($lecture&0x00ff))                 #pour le calcul
        signe=$(($entier/128))                      #extraction bit 2^7
        if [ $signe -eq 1 ]                         #si = 1 alors température <0
        then
            entier=$(($entier^0xff))                #complément à 1 par xor
            entier=$(echo "scale=0; ($entier+1)*(-1)" | bc -l)
                                                    #complément à 2
        fi
        temp_lue=$(echo "scale=1; $entier/1+$decimal/1" | bc -l)
                                                    #calcul température lue
#calcul la valeur de la température à partir de |temp_lue|, counter et slope
        if [ $entier -eq 0 ]                        #température = 0
        then
            temp_cal=0                              #init
            temp_cal=$(($trmp_cal))                 #pour le calcul
            temp_cal=${temp_cal/.*}                 #conversion float to int
        else                                        #température <> 0
            if [ $lecture -eq 33023 ]               #cas particulier de 0x80FF
            then                                    #pb de float to int
                temp_cal=0                          #init
                temp_cal=$(($temp_cal))             #pour le calcul
                temp_cal=${temp_cal%.*}             #float to int
            else                                    #tous les autres cas
                temp_cal=${temp_lue%.*}             #valeur entière de temp_lue
            fi
        fi
        temp_cal=$(echo "scale=1; $temp_cal/1+$corr/1" | bc -l)
                                                    #température corrigée par calcul
        tput cup 4 28                               #lig 4 col 28
        tput setaf 1                                #écrire en rouge
        tput bold                                   #gras
        printf '%.1f' $temp_lue                     #affiche temp_lue avec format
                                                    #nécessaire pour afficher
                                                    #correctement les nbres entre
                                                    #-0.9 et 0.9
        tput sgr0                                   #annule attributs
        tput el                                     #efface jusqu'à la fin de ligne
        tput cup 4 33                               #curseur lig 4 col 33
        echo -e " °C\c"                             #IHM
        tput cup 5 28                               #lig 5 col 28
        tput setab 7                                #fond blanc
        printf "%.1f" $temp_cal                     #affiche temp_cal avec format
        tput sgr0                                   #annule attributs
        tput el                                     #efface jusqu'à la fin de ligne
        tput cup 5 33                               #curseur lig 5 col 33
        echo -e " °C\c"                             #IHM
        tput cup 8 0                                #curseur lig 8 col 0
        read -n 1 -t $ATTENTE -s saisie             #saisie clavier 1 carc, time out
#             ^    ^           ^                     et non visible
#             |    |           n'affiche pas le caractère saisie
#             |    time out en cas de non saisie, utiliser pour l'attente entre
#             |    2 mesures du DS1621
#             saisie 1 caractère
        if [ "$saisie" = "q" ]                      #si q
        then
            break                                   #sortie boucle infinie
        fi
    done                                            #fin do
echo
tput cnorm                                          #curseur visible
tput rmcup                                          #restaure screen
echo -e "\e[8;25;90t"                               #taille fenêtre 25x90
                                                    #à adapter à votre taille de
                                                    #fenêtre terminal console
echo                                                #saut de ligne
#Fin du script

Le programme de lecture et de calcul est le même que dans l’exemple précédent, les modifications portent uniquement sur l’affichage.

Exécution

Pour exécuter ce script bash, il faut le rendre exécutable avec la commande :

sudo chmod +x i2c06.sh

Et pour l’exécuter :

./i2c06.sh

Pour sortir de la boucle de mesure, il suffit de faire q au clavier.

Quelques copies d’écrans

figurei2c07 figurei2c08

Ne juger pas le côté design des fenêtres, mais elles montrent juste quelques possibilités de gestion semi-graphique de tput. Pour plus d’informations sur le programme tput, une recherche sur la toile avec votre butineur favori vous fournira toutes les options possibles.

tput et banner

Une autre possibilité d’affichage de nos résultats de mesure peut se faire par l’utilisation du logiciel banner. Banner affiche le texte passé en paramètre sous forme de bannière.
Le logiciel banner n’est pas présent dans la distribution Raspbian. Dans cette distribution, il s’appelle sysvbanner et pour l’installer il faut faire :

sudo apt-get update
sudo apt-get install sysvbanner

Programme

Pour saisir ce programme, il faut saisir dans la console :

nano i2c08.sh

Cette commande ouvre un fichier teste vide appelé i2c08.sh (pour la sauvegarde faire ctrl-o et pour sortir ctrl-x).
Le contenu du programme et ses commentaires sont représentés ci-dessous.

#!/bin/bash
#Programme lecture capteur de température DS1621 via bus i2c
#nom programme       : i2c08.sh
#logiciel            : bash
#cible               : raspberry Pi
#date de création    : 06/09/2016
#date de mise à jour : 08/09/2016
#version             : 1.0
#auteur              : icarePetibles
#référence           :
#Remarques           : Pour fonctionner, il faut être sous super utilisateur
#                      Il faut rendre le fichier exécutable avec
#                      sudo chmod +x i2c08.sh
#                      exécution : ./i2c08.sh
#
echo -e "\e[8;25;90t"                               #taille écran 25x90
ATTENTE=10                                          #attente entre 2 mesures
                                                    #temps de conversion du DS1621
                                                    #750 ms
LC_NUMERIC=C                                        #définit le séparateur décimal
                                                    #en . au lieu de , car printf
                                                    #utilise les paramètres régionaux
mesure(){                                           #lecture data DS1621
#lecure des registres du DS1621
        lecture=`i2cget -y 1 0x48 0xaa w`           #lecture température
        lecture=$(($lecture))                       #pour permettre le calcul
        counter=`i2cget -y 1 0x48 0xa8 b`           #lecture Count_Remain
        counter=$(($counter))                       #pour les calcul
        slope=`i2cget -y 1 0x48 0xa9 b`             #lecture Count_Per_C
        slope=$(($slope))                           #pour le calcul
        corr=$(echo "scale=3; -0.25+($slope/1-$counter/1)/($slope/1)" | bc -l)
                                                    #correction pour le calcul
#octets de poids faible et valeur décimale de la température
        lsb=$((($lecture>>8)/128))                  #extraction bit 2^7
        if [ $lsb -eq 1 ]                           #si bit poids fort = 1
        then
            decimal=0.5                             #partie décimale = 0.5°C
        else
            decimal=0.0                             #partie décimale = 0.0°C
        fi
#octet de poids fort, test >0 ou <0 et valeur entière de la température
        entier=$(($lecture&0x00ff))                 #pour le calcul
        signe=$(($entier/128))                      #extraction bit 2^7
        if [ $signe -eq 1 ]                         #si = 1 alors température <0
        then
            entier=$(($entier^0xff))                #complément à 1 par xor
            entier=$(echo "scale=0; ($entier+1)*(-1)" | bc -l)
                                                    #complément à 2
        fi
        temp_lue=$(echo "scale=1; $entier/1+$decimal/1" | bc -l)
                                                    #calcul température lue
#calcul la valeur de la température à partir de |temp_lue|, counter et slope
        if [ $entier -eq 0 ]                        #température = 0
        then
            temp_cal=0                              #init
            temp_cal=$(($temp_cal))                 #pour le calcul
            temp_cal=${temp_cal/.*}                 #conversion float to int
        else                                        #température <> 0
            if [ $lecture -eq 33023 ]               #cas particulier de 0x80FF
            then                                    #pb de float to int
                temp_cal=0                          #init
                temp_cal=$(($temp_cal))             #pour le calcul
                temp_cal=${temp_cal%.*}             #float to int
            else                                    #tous les autres cas
                temp_cal=${temp_lue%.*}             #valeur entière de temp_lue
            fi
        fi
        temp_cal=$(echo "scale=1; $temp_cal/1+$corr/1" | bc -l)
                                                    #température corrigée par calcul
}

BG_BLUE="$(tput setab 4)"               #commande couleur arrière plan
FG_WHITE="$(tput setaf 7)"              #commande couleur écriture

#lit la taille de la fenêtre terminal
terminal_size(){
    terminal_cols="$(tput cols)"        #nbre col fenêtre terminal
    terminal_rows="$(tput lines)"       #nbre lig fenêtre terminal
}

#calcul la taille de la bannière (banner)
banner_size(){
    banner_cols=0                       #initialisation
    banner rows=0                       #initialisation
    while read
    do
        [[ ${#REPLY} -gt $banner_cols ]] && banner_cols=${#REPLY}
#               ^     ^                      REPLY = contenu de read si l'on
#               |     >(supérieur)           affecte pas la valeur saisie à une
#               nbre de caractères de REPLY  variable
        ((++banner_rows))               #incrémente banner_rows
    done < <(banner "100.0^C")          #structure type de la température
#        ^ ^
#        | envoie résultat du processus
#        redirection
}

#affiche la température à l'écran
display_temp(){
    local row=$temp_row                 #variable locale
    while read
    do
        tput cup $row $temp_col         #coordonnées du curseur
        echo -n "$REPLY"                #affiche le contenu de REPLY
#             ^
#             sans retour à la ligne
        ((++row))                       #incrémente row
    done < <(banner "$temp_ban")        #différentes lignes de la température
#        ^ ^
#        | envoie résultat du processus
#        redirection
}

#efface l'écran par remplissage de blancs
clear_screen(){
    tput home                           #curseur position 0, 0 (haut gauche)
    echo -n "$blank_screen"             #rempli l'écran avec des spaces
}
#calcul de la colonne d'affichage
cal_coord(){
    if [[ ${#temp_ban} -eq 7 ]]         #si nbre carac de température = 7
    then
        temp_col=$(($temp_colB))        #valeur pour la structure type
        tput cup $temp_row $temp_col    #position curseur
    elif [[ ${#temp_ban} -eq 6 ]]       #si nbre carac de température = 6
    then
        temp_col=$(($temp_colB + 4))    #correction car < à la tructure de base
        tput cup $temp_row $temp_col    #position curseur
    elif [[ ${#temp_ban} -eq 5 ]]       #si nbre carac de température = 5
    then
        temp_col=$(($temp_colB + 8))    #correction car < à la structure de base
        tput cup $temp_row $temp_col    #position curseur
    fi                                  #ces corrections permettent d'avoir un
}                                       #affichage centré

#sortie de la boucle infinie par ctrl-c
trap 'tput sgr0; tput cnorm; tput rmcup || clear; exit 0' SIGINT
#           ^           ^           ^        ^              ^
#           |           |           |        efface écran   Ctrl-c
#           |           |           restaure contenu écran
#           |           curseur normal
#           désactive tous les attributs

#sauve le contenu de l'écran et rend le curseur invisible
tput smcup; tput civis
#      ^           ^
#      |           curseur invisible
#      sauve contenu écran
terminal_size                           #lit taille fenêtre terminal
banner_size                             #calcul taille banner
temp_row=$(((terminal_rows - banner_rows) / 2))
                                        #centrage pour les lignes
temp_colB=$(((terminal_cols - banner_cols) / 2))
                                        #centrage pour les colonnes
blank_screen=''                         #création variable
for ((i=0; i < (terminal_cols * terminal_rows); ++i))
do
    blank_screen+=" "                   #espace pour remplir l'écran
done

echo -n ${BG_BLUE}${FG_WHITE}           #fixe couleurs avant et arrière plan

#programme principal
#initialisation DS1621
i2cset -y 1 0x48 0xac 0x00                          #configuration
i2cset -y 1 0x48 0xee                               #démarre la converssion
#boucle infinie
while true
    do
        mesure                                      #lit et calcul les températures
        clear_screen                                #efface écran
        tput cup 1 38                               #curseur lig 1 col 38
        echo -n "Capteur DS1621"                    #IHM
        temp_ban=$(printf "%.1f^C" $temp_cal)       #température à afficher en
                                                    #bannière via banner
        cal_coord                                   #position bannière
        display_temp                                #affiche la température
                                                    #calculée en bannière
        tput cup 23 1
        printf "Température mesurée : %.1f°C" $temp_lue
        tput cup 23 69
        echo -n "<ctrl-c pour sortir>"
        sleep $ATTENTE                              #attente
    done                                            #fin do
#Fin du script

Le programme de lecture et de calcul est le même que dans l’exemple précédent mais il est regroupé dans un sous-programme mesure(). Certaines subtilités utilisées dans le script sont commentées dans le programme.

Exécution

Pour exécuter ce script bash, il faut le rendre exécutable avec la commande :

sudo chmod +x i2c08.sh

Et pour l’exécuter :

./i2c08.sh

Pour sortir de la boucle de mesure, il suffit de faire ctrl-c au clavier.

Quelques copies d’écrans

figurei2c09 figurei2c10

Banner n’est pas en mesure d’afficher le signe ° (il n’est pas dans les caractères de base du code ASCII), nous avons utilisé le symbole ^ pour palier à ce petit défaut. Il existe d’autres logiciels similaires à banner mais nous n’avons pas fait de tests avec ceux-ci.

Conclusion

figure13Nous arrivons à la fin de l’épisode 1 de la saga I2C qui portait sur la découverte du bus I2C, son activation ainsi que la mise en œuvre d’un capteur de température, d’une RAM et d’une EEPROM. La mise en forme de l’affichage sous forme semi-graphique nous a permis d’avoir une présentation plus agréable des résultats.
Le prochain épisode portera sur l’utilisation du capteur DS1621 sous une forme similaire mais avec les langages Python et C/C++.

A tous présents et à venir. Salut !

Sources

Vous pouvez télécharger les sources et la documentation :
http://psl.ibidouille.net/Images_forum/raspberryPi/sagaI2C_01.zip

Liens

Bus I2C
Manuel de l’utilisateur i2c

Autres articles de la série saga

Saga Blink – saison 1
Saga Blink – saison 2
Saga Push Button

Share Button

11 réflexions au sujet de « I2C, plus de 30 ans et toujours d’actualité. »

  1. msg

    Bonjour,

    Pas de PCF8574 ou PCF8574A dans le tirroir ?
    Dommage .

    Ça aurait intéressé certains pour faire des interfaces IO et éviter de faire des bétises sur ports direct du GPIO .

    Répondre
  2. msg

    Dans la description du DS1621 , il y a un bug .

    « Température entière = 2⁴ + 2¹ + 2¹ = 19°C »
    Il y a deux fois 2¹ , ce qui vaudrait 20°c et pas 19°C.

    Il faut lire : « Température entière = 2⁴ + 2¹ + 2⁰ = 19°C »

    Répondre
  3. Dodutils

    Rhâââ tous les exemples sont en Bash, beaucoup de gens ignorent qu’on peut créer de véritables programmes en shell script avec l’énorme avantage qu’il est présent en standard sur toutes les machines Linux 🙂

    Répondre
    1. Patrice SEIBEL Auteur de l’article

      Bonjour,
      J’attends la livraison du BME280 pour faire des tests.
      Et sans oublier les horloges RTC (DS3232M par exemple)
      [edit] Dans liste todo, il y a aussi un arduino comme composant i2c

      Répondre
  4. SJ

    Cela me rappel le temps chez Philips et la conception d’interface PC/I2C.
    Chaque famille de composant I2C a une adresse propre, c’est pour cela qu’NXP (ex Phlips Semiconductor) centralise tout ce petit monde pour éviter des conflits d’adresse.

    Répondre
    1. François MOCQ

      Bonjour Stéphane
      Pareil pour moi 🙂 j’avais préparé un cours en liaison avec Philips
      à partir d’une carte interface I2C sur PC et une carte multifonctions que j’avais développée…
      mais c’était il y a… très longtemps
      cordialement
      François

      Répondre
  5. Dodutils

    Je viens de tester le BME280 et vu qu’il faut appliquer quelques calculs pour obtenir les bonnes valeurs j’ai directement testé avec le script d’exemple récupéré sur Adafruit et ça marche nickel :

    root@RASPI-3B:~# python BME280_example.py
    Timestamp = 100691.000
    Temp = 19.666 deg C
    Pressure = 1016.77 hPa
    Humidity = 66.59 %

    Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Complétez ce captcha SVP * Time limit is exhausted. Please reload CAPTCHA.