Le
bus est construit sur 2 fils, chaque terminal est connecté sur ces 2
équipotentielles au travers de circuits en collecteur ouvert, avec une
résistance de charge. Ce n'est pas un bus différentiel, la charge
capacité du bus influe directement sur les performances, ce n'est donc
pas un bus destiné à courir sur des longueurs importantes.
Les
drivers open drain sont donnés pour absorber 3mA max, à 0,4V,
la valeur de la résistance de charge ne sera pas inférieure à
Rp=5/3=1.66 kO.
Le temps de montée maximal dicte la valeur maximale de la
résistance Rp. Le temps de montée 0%--70% est égal à
1,20*RC. Les graphiques ci dessous donnent les valeurs de Rp selon la capacité.
Donc en final, utiliser des résistances Rp de 2,2 kO semble un bon choix
Valeur de Rp, selon la capacité du bus,pour le mode standard(Trise < 1µs) |
Valeur de Rp, selon la capacité du bus,pour le mode rapide(Trise < 0.3µs) |
Des résistances séries (330Ohms typique, <500Ohms) peuvent être montées sur les lignes afin de protéger les circuits des surtensions que les lignes peuvent capter, surtout dans un environnement propice comme une télévision à écran cathodique, ou lorsque que le bus est un peu long.
Les changements d'état sur le bus de données SDA se font toujours
lorsque le signal d'horloge SCL est au niveau bas SAUF pour marquer les conditions
de START ou de STOP.
Les communications sont toujours à l'initiative d'un maître. Chaque
échange débute par une condition START, et se termine par une
condition STOP. Le premier octet transmis par le maître après le
"start" est l'adresse du terminal esclave adressé. Si l'esclave
se reconnaît, il accuse réception de son adresse, et dès
lors, il est seul sur le bus avec le maître.
Il existe 3 types d'échange :
Extensions :
Il existe plusieurs vitesses du bus I2C :
Les circuits standards et fast mode, utilisent la même technologie de
bus avec un pull up passif.
Le high speed demande des circuits spéciaux, avec un pull up actif, et
ne sera certainement pas simulable avec un µcontrôleur.
Transmission d'un START, et en même temps, on vérifie que les lignes répondent bien
TxmtStart
SETB _SDA ; set SDA high
SETB _SCL ; clock is high
JNB _SDA,I2CERR1 ; SDA stuck @0 or bus not free
JNB _SCL,I2CERR2 ; SCL stuck @0 or bus not free
CALL Delay40uSec ; Tsu:sta>4,0µs entre SCL=1 et SDA=0
CLR _SDA ; give a falling edge on SDA while clock is high
CALL Delay47uSec ; Thd:sta>4,7µs entre SDA=0 et SCL=0
JB _SDA,I2CERR3 ; SDA stuck @1 or uC problem
CLR _SCL
JB _SCL,I2CERR4 ; SCL stuck @1 or uC problem
RETLW 0 ; retour sans erreur
Transmission d'un STOP, on suppose ici que SCL=0 en entrant dans la routine
TxmtStop
; CLR _SCL ; normallement SCL est déjà à 0 !!! sinon on fait un start ici
CLR _SDA ; set SDA low
CALL Delay47uSec ; tLOW>4,7µS
SETB _SCL ; Clock is pulled up
CALL Delay40uSec ; Tsu:sto>4,0µs entre SCL=1 et SDA=1
SETB _SDA ; give a rising edge on SDA while CLOCK is high
CALL Delay47uSec ; c'est pas dans la spec, par sécurité
RETLW 0 ; retour sans erreur
Ecriture d'un bit sur le bus
TxmtBit
MOV _SDA,C ; SDA=Carry
CALL Delay47uSec ; tLOW>4,7µS, Tsu:dat>0,25µS
SETB _SCL
; test sda=Carry, sinon ---> I2CERR6
JNB _SCL,I2CERR5 ; SCL stuck @0 or bus not free
CALL Delay40uSec ; tHIGH>4,0µS
CLR _SCL
RETLW 0 ; retour sans erreur
Lecture d'un bit sur le bus ; on fait un vote majoritaire pour éviter les parasites ; SDA est supposé à UN
RcvBit
CALL Delay47uSec ; tLOW>4,7µS
SETB _SCL ; SCL=1, data sent by slave
CALL Delay40uSec ; tHIGH>4,0µS
JNB _SCL,I2CERR7 ; SCL stuck @0 or bus not free
CLRF rcvSDA1 ; 3 échantillonages de _SDA, et on compte le nombre de UN
BTFSC _SDA
INCF rcvSDA1,F
BTFSC _SDA
INCF rcvSDA1,F
BTFSC _SDA
INCF rcvSDA1,F
CLR _SCL ; SCL=0
MOVLW 2
SUBWF rcvSDA1,W ; W=rcvSDA1-2, Cy=1 si rcvSDA1=2 ou 3, c'est le bit reçu
RETLW 0
Attention à la profondeur de la pile nécessaire
à chaîner toutes les routines I2C. On arrive très rapidement
à des niveaux de plus de 4 qui doivent être maîtrisés
dans un environnement avec des interruptions.
Ici, nous avons une profondeur de 4, le dernier niveau étant traité
par macro. Cela ne laisse que 4 niveaux pour l'appelant et les interruptions.
Cette solution augmente la taille du code.
Séquence START-Add-NACK sur l'adresse D0h. Chaque bit dure 25µs. |
Séquence START-Add-ACK sur l'adresse A0h |
Tout échange I2C peut être résumé par une écriture suivie d'une lecture, avec un start répété.
START | Send address+W | Rec ack | Send bytes (W) | Rec ack | |
START | Send address+R | Rec ack | Receive bytes (R) | Send ack/Nack | STOP |
Il suffit donc de créer une routine générale avec 5 parametres en entrée :
L'appel est des plus simple :
set R0,R1,R2,R3,R4
call I2CXCH
jnb I2COK,error
Cette routine s'appuie sur quelques routines de base : TxmitBit, TxmtStart, RcvBit...
Les délais sont traités par macro, au moment de l'assemblage, en donnant en parametre la fréquence quartz.
Les erreurs possibles pendant l'echange sont résumées dans ce tableau
START | Send address+W | Rec ack | Send bytes (W) | Rec ack | |
01, 02, 03, 04 | 05, 06 | 07, 20 | 0D, 0E | 0F, 40 | |
START | Send address+R | Rec ack | Receive bytes (R) | Send ack/Nack | STOP |
11, 12, 13, 14 | 15, 16 | 17, 60 | 1F | pas d'erreur | pas d'erreur |
A la sortie de cette routine, on dispose d'un octet status qui donne l'erreur.
bits 7&6 |
bits 6&5 |
bits 4&3 |
bits 2&1&0 |
80h RESET-SDA line note released after 9 clocks 88h STOP-SDA line not released after recover |
20h WRITE-no ack while addressing |
+00h WRITE-bus error while addressing |
x1h START-SDA stuck @0 or bus not free x2h* START-SCL stuck @0 or bus not free x3h START-SDA stuck @1 or uC problem x4h* START-SCL stuck @1 or uC problem x5h* WRITE BIT/ACK-SCL not released after 3uS x6h WRITE BIT/ACK-SDA line not correct x7h* READ BIT/ACK-SCL not released after 3uS |
(*) seulement si le test SCL est autorisé
Considérons que SCL est reçue par un entrée ordinaire,
sans possibilité de sortie, et SDA est reçu sur une entrée
avec un open drain associé (RA4 sur un PIC).
Pour gérer le protocole, il faut pouvoir renvoyer Ack sur un adresse
reçue correcte, et appliquer le niveau ZERO sur SDA, en moins de Thigh+Tvd;dat
soit 7,5µs après le front montant de SCL. En faisant cela en 15
à 20 instructions, il faut une horloge à plus de 10MHz.
Au démarrage SCL est en entrée, et SDA en sortie avec SDA=1.
Pour un esclave, on peut considérer qu'on a plusieurs états stables
Mode=1 |
|
Mode=2 |
faudrait pouvoir détecter les
stop ici |
Mode=3 en write · |
|
Mode=3 en read · |
à continuer...