Dernière mise à jour :
24/07/2011
Présentation
Cette page présente les principes de base pour
écrire ou lire des données sur un bus I2C. Le nombre de
périphériques I2C existants est très grand (convertisseurs A/N,
tuners, amplis BF, horloges, mémoires, etc) et tous ne seront évidement
pas
abordés ici. Le but de cet article est juste de faire un rapide tours
du propriétaire de ce bus au nom mystérieux. Des exemples pratiques
seront donnés par la suite :
Exemple
avec horloge temps réel RTC PCF8583
Exemple
avec expandeur de port PCF8574
Exemple
avec convertisseur A/N et N/A PCF8591
Exemple
avec mémoire EEProm 24C02, 24C04 et 24C16
Présentation du bus I2C
I2C =
Inter
Integrated
Circuit - bus
appelée également "interface deux fils".
La
liaison I2C est une liaison série de type bidirectionnelle
"Half-duplex" sur laquelle on peut raccorder en même temps plusieurs
élements qui répondent au standard I2C, selon le principe d'un réseau
parallèle. Sur ce réseau, un des élements est désigné comme maître et
tous les autres sont désignés comme esclaves. L'écriture ou la
lecture de données sur un bus I2C s'effectue de
façon synchrone avec deux fils
seulements, quelque soit la quantité d'informations (nombre de bits ou
d'octets) à transmettre. Ces deux fils (lignes) s'appellent SDA et SCL :
- SCL = Serial
CLock =
ligne d'horloge qui sert au cadencement des données à transmettre
- SDA = Serial
DAta =
ligne de données qui sert de support pour les données à transmettre.
Remarque
: attention quand on parle de liaison deux fils : il existe forcement
un troisième fil qui est la masse et qui sert de référence de
potentiel. Ce troisième fil de référence est souvent sous-entendu et
n'apparait pas forcement sur les schémas électroniques, où les divers
points d'alimentation (Masse et VDD) des circuits intégrés sont souvent
commun.
Le
synoptique qui suit montre un exemple d'un réseau I2C dans lequel un
élement maître peut s'adresser à trois élements esclaves différents (le
nombre d'esclaves peut être différent de 3 : 1 ou 5 ou 8 par exemple).
La
transposition de ce synoptique fonctionnel dans le monde réel
pourrait déboucher sur le schéma électronique suivant (c'est un exemple
entre autres) :
Echange
des données
On
peut envoyer des
données tout comme on peut en recevoir, mais sur un seul et
même fil de
liaison de données SDA.
Il ne s'agit donc pas d'une liaison de type "Full duplex" dans laquelle
on aurait un premier fil pour l'envoi de données et un second fil pour
la réception des données. La
différentiation émission / réception se fait au niveau logiciel et
au niveau matériel, puisqu'il faut bien qu'une ligne d'entrée / sortie
soit configurée dans le bon sens selon la direction à faire prendre aux
données (cela est vrai pour le maître et les esclaves). Dans la
pratique, et grâce aux routines de lecture / écriture
sur bus I2C, nous verrons que tout ce qui touche à la configuration
matérielle est réduite à sa plus simple expression, et qu'il n'y a rien
besoin de faire de particulier (tout est fait dans les coulisses). La
gestion de la communication se fait par un seul des élements du bus
I2C, qui est celui qu a été désigné comme maître. Les autres
éléments branchés
sur le bus - les esclaves - se contentent d'obéir au maître.
C'est le maitre qui génère et fournit les signaux d'horloge transmis
sur la ligne SCL, et c'est également lui qui gère l'envoi des données
sur la ligne SDA. A noter que la ligne SCL n'est pas le siège permanent
de changement d'états logiques, les signaux d'horloges ne sont transmis
que quand il doit y avoir des échanges avec au moins un périphérique.
On peut dire que la ligne SCL indique à l'esclave comment "interprêter"
les données transmises sur la ligne SDA. L'esclave est relativement
"neutre" dans la gestion d'envoi des données, mais il fait tout de même
savoir au maître s'il reçoit bien les données qu'il est censé
recevoir. Pour cela, l'esclave produit un signal dit d'acquitement ACK
(pour Aknowledge). L'absence de signal d'acquitement (si toutes les
données n'ont pas été reçues) se traduit par un "message" de type NACK
(No Acknowledge). Le signal ACK est produit par la mise à l'état
logique bas (0) de la ligne SDA par l'esclave, alors que le signal NACK
est reconnu par l'état logique haut (1) de la ligne SDA (état logique
maintenu grâce à la présence de la résistance de rappel au +Valim -
pullup, résistance R2 sur le schéma précédent).
Types de messages
Les messages que l'on envoie à un périphérique I2C peuvent grosso-modo
être classés dans deux catégories :
- les messages qui indiquent ce que le composant doit faire -
par exemple
faire passer une sortie logique à l'état logique haut, ou stocker une
valeur dans une case mémoire d'une EEProm. Ce type de message n'attend
pas de réponse particulière, si ce n'est le signal d'acquitement qui
confirme que tout s'est bien passé côté communication. On
peut vérifier que l'ordre a été exécuté
mais cette vérification n'est pas obligatoire.
- les messages qui
impliquent une réponse - par exemple lecture de plusieurs
entrées
logiques ou
analogiques ou lecture d'une valeur dans une case mémoire d'une EEProm.
Dans ce cas le maître envoie un message et l'esclave donne sa
réponse.
Comme les lignes SDA et SCL font
référence à deux lignes d'entrée / sortie du PIC et que généralement
ces dernières doivent être
configurées
en entrée ou en sortie,
on pourrait penser qu'il est nécessaire de spécifier dès le début si
les lignes SDA et SCL doivent être configurées en entrée ou en sortie.
Dans les faits, la configuration (orientation) de ces deux lignes est
assurée dès le départ grâce à la routine
d'initialisation I2C1_Init, et les changements de direction
sont
automatiquement gérés dans les routines de lecture ou d'écriture. Il
est donc inutile d'en rajouter à ce
propos dans les lignes de votre programme (cela sera vu au
moment
des exemples pratiques).
Adressage
Un bus I2C est conçu pour
pouvoir y connecter plusieurs périphériques de façon
simultanée, c'est ce que l'on a vu avec le schéma précédent. Pour
que les dialogues puissent rester digeste sur cet unique bus, il faut
que les périphériques ne causent pas tous en même temps.
Imaginez
une réunion de 10 personnes où tout le monde prend la parole en même
temps, ça fait du bruit et on ne comprend rien. Chaque périphérique
doit donc être adressable de façon individuelle, et quand on en
questionne un il est hors de question qu'un autre réponde à sa place.
Pour cela, chaque périphérique dispose d'une adresse unique, et ne
répond que si les question qu'il reçoit intègre un numéro d'adresse qui
correspond à son adresse propre. S'il s'agit d'une autre adresse (celle
de son voisin par exemple), il se contente d'écouter mais ne prend pas
la parole car il est bien élevé et qu'il a compris que ce n'était pas à
son tour de se manifester. Ce point est facile à comprendre, mais il
faut bien s'en souvenir car une erreur commune consiste à
envoyer des messages sur le bus avec une adresse erronnée, et on se
demande pourquoi le circuit auquel on s'adresse ne répond pas. Dans
le schéma d'exemple montré ci-avant, les trois périphériques PCF8574,
PCF8583 et 24C02 possèdent chacun une adresse différente.
Nombre d'octets transmis
Un message complet peut comporter
plusieurs octets dont le nombre exact dépend du composant
périphérique
I2C utilisé. Le premier octet correspond toujours à
l'adresse de l'esclave avec lequel on souhaite communiquer. A noter que
l'adresse n'est pas codée sur 8 bits mais sur 7 bits seulement. Le
huitième bit permet de définir si on souhaite se positionner en mode
lecture (READ, valeur du bit à 1) ou en mode écriture (WRITE, valeur du
bit à 0). Le second octet à transmettre dépend du type de périphérique
I2C auquel on s'adresse. Il peut s'agir d'un octet de contrôle, tout
comme il peut s'agir d'un octet de donnée. Le
graphe qui suit montre un exemple de données transitant sur la ligne
SDA, au rythme des impulsions
d'horloge présentes sur la ligne SCL (les
échanges de données ont été "provoqués" par un PIC maître communiquant
avec un expandeur de port PCF8574).
Chronologie d'une communication
Comme
la liaison est de type sérielle, les données qui se présentent sous
forme d'octet sont transmises bit après bit. Pour un octet, il y a huit
informations binaires à transmettre à la queue-leu-leu.
- Au repos,
c'est à dire quand il n'y a aucun échange de données sur la liaison
I2C, les deux lignes SCL et SCA sont à l'état logique haut.
- Pour
démarrer la communication, le maître fait passer la ligne
SDA à
l'état logique bas, tout en laissant la ligne SCL à l'état logique
haut. Cette première information constitue ce qu'on appelle le bit de
START.
- Le maître fait ensuite passer la ligne SCL à l'état bas,
puis applique sur la ligne SDA le bit de poids fort (MSB) du
premier octet à
transmettre (il y en a toujours au moins 2).
- Le maître indique à l'esclave que l'information
présente sur la ligne SDA peut être prise en compte, en faisant passer
la ligne SCL à l'état haut. A ce moment, l'exclave prend cette valeur
et la garde en mémoire, en attendant les valeurs suivantes.
- Le maître refait passer la ligne SCL à l'état bas,
puis applique sur la
ligne SDA le deuxième bit de l'octet à transmettre (celui qui suit
celui de poids fort transmis juste avant).
- Le maître indique à l'esclave que cette
nouvelle information présente sur la ligne
SDA peut être prise en compte, en faisant passer la ligne SCL à l'état
haut. A ce moment, l'exclave prend cette seconde valeur et la garde en
mémoire.
Ce procédé se déroule de façon répétée pour chaque bit de l'octet à
transmettre, soit huit fois.
Une
fois les huit bits de l'octet transmis, la ligne SDA, qui était
jusqu'alors configurée en sortie logique, est configurée en entrée
logique par le maître. Ce dernier surveille dès cet instant l'état de
la ligne SDA. Si la transmission des données s'est déroulée sans
erreur, l'esclave positionne la ligne SDA à l'état logique bas (ACK) et
la suite des opérations n'est pas compromise. Si l'esclave ne fait rien
et que la ligne SDA reste à l'état logique haut, c'est qu'une erreur
s'est produite. Dans un tel cas de figure, on considère qu'il y a fin
de transmission, il est inutile de continuer à envoyer le reste des
données. La fin de transmission est initiée par le maître par un bit de
STOP, qui correspond à un passage à l'état haut de la ligne SDA
(repassée en mode sortie côté maître) alors que la ligne SCL est
toujours à l'état logique haut.
Exemple pratique de communication I²C
Le
graphe que l'on a vu précédement et qui est rappelé
ci-après montre un exemple de données transitant sur la ligne
SDA, au rythme des impulsions
d'horloge présentes sur la ligne SCL et "provoqués" par un PIC
maître communiquant avec un expandeur de port PCF8574.
Le code logiciel MikroPascal qui a été mis en oeuvre pour établir cet
échange de données est visible ci-après.
procedure Init_I2C_materiel;
begin
I2C1_Init(100000);
end.
procedure PCF8574_Read;
begin
I2C1_Start; // signal START
I2C1_Wr($41); // addresse lecture PCF8574
Values := I2C1_Rd(0); // lecture port PCF8574
I2C1_Stop; // signal STOP
end;
procedure PCF8574_Write;
begin
I2C1_Start; // signal START
I2C1_Wr($70); // addresse ecriture PCF8574A
I2C1_Wr(Values); // ecriture valeurs
I2C1_Stop; // signal STOP
end;
Remarque
: dans
ce cas précis, le fait d'aller lire ou écrire des données dans
le PCF8574 conduit à la circulation d'une même quantité de données. Sur
les deux procédures de lecture et d'écriture ci-avant, une
seule
(écriture ou lecture) a conduit aux données visibles sur le graphe.
Initilisation
La
routine d'initialisation (Init_I2C_materiel) a été appelée une seule
fois au tout début du programme, il n'est pas nécessaire de la répeter
ensuite. Elle est indispensable, la
liaison I2C ne fonctionnera pas si elle n'est pas correctement
préparée.
Lecture et écriture
Pour la lecture
(procédure PCF8574_Read), deux octets sont transmis. Le premier octet
est envoyé par le maître (le PIC) et correspondant à l'adresse de base
d'un PCF8574. Le second octet est envoyé par l'esclave, qui répond au
maître.
Pour l'écriture
(procédure PCF8574_Write), deux octets sont également véhiculés, mais
cette fois dans un sens unique : c'est le maître (le PIC) qui envoie
les deux octets l'un après l'autre en direction de l'esclave. Le
premier octet correspondant à l'adresse d'écriture d'un
PCF8574A. Le
second octet correspond à la valeur à écrire dans le PCF8574A.
Notez
comme l'écriture de ces procédures est simple par rapport à ce qu'il
faudrait faire si on voulait exploiter le bus I2C à partir de rien,
c'est à dire en modifiant "à la mano" les états logiques des deux
lignes SCL et SDA.
Là, pour l'écriture dans le PCF8574A, on suit une procédure de
dialogue tout à fait standard :
- on envoie le signal START avec la procédure
I2C1_Start;
- on envoie l'octet d'adresse avec la procédure I2C1_Wr(add);
- on envoie l'octet de données avec la procédure I2C1_Wr(val);
- on envoie le signal STOP avec la procédure
I2C1_Stop.
Et pour la lecture depuis le PCF8574, même principe :
- on envoie le signal START avec la procédure
I2C1_Start;
- on envoie l'octet d'adresse avec la procédure I2C1_Wr(add);
- on récupère l'octet de donnée avec la procédure I2C1_Rd(0);
- on envoie le signal STOP avec la procédure
I2C1_Stop.
I2C matériel ou I2C logiciel ?
Les
routines liées au mode de communication du bus I2C peuvent
s'appuyer sur des lignes physiques dédiées du PIC, c'est à dire sur des
lignes pour
lesquelles une partie du matériel inclus dans le PIC est
conçu pour
fonctionner de façon optimale avec le protocole I2C. Ces lignes,
comme elles sont dédiées, se
trouvent à un endroit précis, sur un port précis. Par exemple sur le
PIC 18F2420, les deux lignes SCL et SDA se trouvent respectivement sur
les lignes RC3/SCL et RC4/SDA. On ne trouve pas de telles lignes
dédiées sur les PIC 16F84 ou 16F628, mais cela n'empêche pas de mettre
en oeuvre un bus I2C avec ces derniers. Seulement dans ce cas, il faut
utiliser des routines I2C qui intègrent les "émulations
logicielles"
qui remplacent la partie matériel manquante. Comme les fonctions
émulées fonctionnent moins
rapidement, certaines précautions doivent être prises, mais pour les
besoins courants cela suffit généralement. D'un point de vue écriture
logicielle, cela fonctionne de la même façon, il suffit juste de
choisir les
routines adéquates.
Exemple de code pour un PIC 18F2420 possédant un module MSSP
/ I2C
Le
code ci-dessous permet d'initialiser le mode de communication I2C, en
utilisant le module MSSP dans le "mode" I2C. Cette ligne est
indispensable avant de commencer toute action sur le bus I2C.
procedure Init_I2C_materiel;
begin
I2C1_Init(100000);
end.
A partir de cet instant, la communication peut s'établir sur
le bus I2C, avec les données sur la ligne SDA et les signaux d'horloge
sur la ligne SCL.
Exemple de code pour un PIC 16F628A ne possédant pas de
module MSSP / I2C
Le
code ci-dessous permet d'initialiser le mode de communication I2C, qui
peut être assuré même en l'absence d'un module de communication série
MSSP implémenté de façon matérielle dans le PIC.
// définition des broches du PIC à utiliser
var
Soft_I2C_Scl: sbit at RB0_bit;
Soft_I2C_Sda: sbit at RB4_bit;
Soft_I2C_Scl_Direction: sbit at TRISB0_bit;
Soft_I2C_Sda_Direction: sbit at TRISB1_bit;
procedure Init_I2C_logiciel;
begin
Soft_I2C_Init(100000);
end.
A partir de cet instant, la communication peut s'établir sur
le bus I2C, avec les données sur la broche d'entrée / sortie du PIC qui
a été choisie comme ligne SDA et les signaux
d'horloge sur la
broche
d'entrée / sortie du PIC qui a été choisie
comme ligne SCL.
L'utilisation des routines "soft" est un peu plus contraignante que
l'utilisation des routines "hard". Il faut en effet :
- désactiver les interruptions éventuellement mises en place
pendant qu'ont lieu les communications I2C, et les rétablir après.
- tenir compte du fait que les routines "soft" sont de
type bloquantes et qu'elles ne se terminent que quand la ligne
SCL
est repassée à l'état logique haut.