Le bus I2C

Mise en œuvre avec le microcontrôleur PIC 16F876A

 

 

1- Présentation du bus I2C

 

Le bus I2C (Inter-Integrated Circuit) est un bus populaire développé par la société Philips dans les années 1980.

Le bus I2C est un bus série synchrone bifilaire :

 

Le bus I2C est contrôlé par un maître (c'est généralement un microcontrôleur, par exemple un PIC 16F876A).

Le bus I2C peut avoir plusieurs maîtres (on parle alors de mode multi-maîtres), mais à un instant donné, un seul maître contrôle le bus I2C.

Le bus I2C possède un ou plusieurs esclaves (un bus I2C sans esclaves ne sert à rien !).

 

Un esclave est identifié par une adresse unique (sur 7 bits ou parfois 10 bits)

Exemple d'esclaves :

N.B. Deux esclaves ne doivent pas avoir la même adresse.

N.B. Un maître n'a pas d'adresse.

 

Le dialogue se fait uniquement entre un maître et un esclave.

 

Le dialogue est toujours initié par le maître (condition Start) : le maître envoie sur le bus I2C l'adresse de l'esclave avec qui il veut communiquer.

Le dialogue est toujours terminé par le maître (condition Stop).

Le signal d'horloge (SCL) est généré par le maître.

La fréquence de l'horloge (f SCL) prend trois valeurs typiques :

N.B. Le PIC 16F876A peut travailler dans le mode High-speed, le DS1307 dans le mode standard et le DS1631 dans le mode Fast.

C'est l'élément le plus lent qui impose la fréquence de l'horloge du bus I2C : ici 100 kHz (DS1307).

 

Par définition, les données transitent de l'émetteur vers le récepteur.

Deux cas se présentent :

 

 

N.B. Des dispositifs sont prévus pour contrôler le bon déroulement du transfert (bit Acknowledge).

 

2 - Technologie du bus I2C : sorties de type drain ouvert (ou collecteur ouvert)

 

Les circuits connectés sur un bus I2C ont des sorties de type drain ouvert (ou collecteur ouvert), ce qui permet de faire un ET logique "câblé".

Deux résistances de pull-up sont bien sûr placées entre les lignes SDA et SCL et l'alimentation (VDD).

Quand le bus n'est pas utilisé, SDA et SCL sont au niveau haut (niveau de repos).

 

Quand une ligne (SDA ou SCL) est au repos (niveau 1), on peut la forcer à 0.

Quand une ligne (SDA ou SCL) est au niveau 0, on ne peut pas la forcer à 1.

Dans l'exemple ci-dessous, l'horloge SCL est au niveau 0 car c'est le PIC 16F876A (maître) qui force cette ligne à 0 :

N.B. Le DS1307 fonctionne toujours en esclave : ce n'est donc jamais lui qui agit sur l'état de l'horloge (SCL).

 

En savoir plus sur la configuration de sortie de type collecteur ouvert

 

3 - Exemple de communication entre un maître (PIC 16F876A) et un esclave

 

Le protocole de communication du bus I2C est décrit en détails dans le document de référence :

Spécifications complètes du bus I2C

 

Nous verrons l'exemple de l'esclave DS1307.

Le circuit Dallas DS1307 est une horloge temps réel (Real Time Clock), qui fournit secondes, minutes, heures, jours, dates, mois et années.

Les années bissextiles sont prises en compte (jusqu'en 2100).

Le DS1307 travaille dans le mode standard (fréquence d'horloge f SCL de 100 kHz).

L'adresse I2C (7 bits) du DS1307 est : 1 1 0 1 0 0 0 (adresse fixée par le constructeur => adresse non modifiable).

 

L'émetteur est le maître et le récepteur est l'esclave.

 

Le registre d'adresse 0x04 du DS1307 contient la date (cf. datasheet du DS1307).

Pour régler le calendrier au 27 du mois, il faut écrire la donnée 0x27 (codage BCD) dans le registre d'adresse 0x04 du DS1307.

Le bus I2C utilise le protocole suivant :

 

 

1) Pour initier le dialogue, le maître crée une condition Start

2) Le maître envoie l'adresse de l'esclave (1010100) suivi du bit 0 (bit Write)

3) L'esclave répond (accusé de réception : bit Acknowledge)

4) Le maître envoie l'adresse du registre (0x04) à écrire

5) L'esclave répond (accusé de réception : bit Acknowledge)

6) Le maître envoie la donnée (0x27) à écrire

7) L'esclave écrit la donnée puis envoie un accusé de réception (bit Acknowledge)

8) Le maître termine le dialogue avec une condition Stop

Le bus I2C est maintenant libre (SCL = 1, SDA = 1 : niveaux de repos).

 

C'est simple, non ?

Un peu moins évident, le chronogramme correspondant des lignes SDA et SCL :

 

Plus précisément, la condition Start est définie par la transition de la ligne SDA du niveau haut au niveau bas, SCL étant au niveau haut.

C'est bien sûr le maître qui force la ligne SDA au niveau bas :

C'est exactement la même chose avec la condition Repeated Start :

 

La condition Stop est définie par la transition de la ligne SDA du niveau bas au niveau haut, SCL étant au niveau haut.

C'est bien sûr le maître qui agit sur la ligne SDA :

 

En dehors de ces trois conditions, la ligne SDA ne peut changer de niveau que lorsque SCL est au niveau bas :

 

La donnée présente sur la ligne SDA doit être stable sur l'intervalle de temps où SCL est au niveau haut.

C'est l'émetteur qui agit sur la ligne SDA, et c'est évidemment le maître qui génère le signal d'horloge SCL.

N.B. Les données sont transmises octet par octet (1 octet = 8 bits)

 

Le protocole est le même que pour la transmission d'un bit de donnée.

2 cas :

- bit Acknowledge (master to slave)

Le bit Acknowledge est généré par le maître (le maître force la ligne SDA au niveau 0).

- bit Acknowledge (slave to master)

Le bit Acknowledge est généré par un esclave (l'esclave force la ligne SDA au niveau 0).

 

 

Le protocole est le même que pour la transmission d'un bit de donnée.

Le bit Not Acknowledge est toujours généré par le maître (le maître ramène la ligne SDA au niveau 1).

 

 

 

Vous remarquerez qu'il faut 9 bits pour transmettre un octet (8 bits de l'octet et le bit ACK).

Avec une fréquence d'horloge f SCL = 100 kHz, le transfert de 3 octets dure environ :

3 octets * 9 bits / 100 000 Hz = 270 µs

 

L'émetteur est l'esclave et le récepteur est le maître.

Les registres d'adresses 0x00 à 0x06 du DS1307 contiennent respectivement les secondes, minutes, heures, jours, dates, mois et années (cf. datasheet du DS1307).

Voici comment lire, d'une seule traite, le contenu des registres d'adresses 0x00 à 0x06 du DS1307 :

 

Chronogramme correspondant :

 

 

1) Pour initier le dialogue, le maître crée une condition Start

2) Le maître envoie l'adresse de l'esclave (1010100) suivi du bit 0 (bit Write)

3) L'esclave répond (accusé de réception : bit Acknowledge)

4) Le maître envoie l'adresse du registre (0x00) à lire

5) L'esclave répond (accusé de réception : bit Acknowledge)

6) Le maître émet une condition Repeated Start

7) Le maître envoie l'adresse de l'esclave (1010100) suivi du bit 1 (bit Read)

8) L'esclave répond (accusé de réception : bit Acknowledge)

9) L'esclave envoie le contenu du registre d'adresse 0x00 au maître

10) Le maître répond (accusé de réception : bit Acknowledge)

11) L'esclave envoie le contenu du registre d'adresse 0x01 (automatiquement incrémenté) au maître

12) Le maître répond (accusé de réception : bit Acknowledge)

13) L'esclave envoie le contenu du registre d'adresse 0x02 (automatiquement incrémenté) au maître

14) Le maître répond (accusé de réception : bit Acknowledge)

...

21) L'esclave envoie le contenu du registre d'adresse 0x06 (automatiquement incrémenté) au maître

22) Le maître répond (accusé de réception : bit Not Acknowledge)

23) Le maître termine le dialogue avec une condition Stop

Le bus I2C est maintenant libre (SCL = 1, SDA = 1 : niveaux de repos).

 

Le contenu du registre d'adresse 0x00 du DS1307 est 0x06 (codage BCD : 06 secondes).

Le contenu du registre d'adresse 0x01 est 0x56 (c'est-à-dire 56 minutes).

Le contenu du registre d'adresse 0x02 est 0x09 (c'est-à-dire 09 heures).

Le contenu du registre d'adresse 0x03 est 0x03 (c'est-à-dire Mardi).

Le contenu du registre d'adresse 0x04 est 0x20 (c'est-à-dire 20 ème jour du mois).

Le contenu du registre d'adresse 0x05 est 0x05 (c'est-à-dire mois de mai).

Le contenu du registre d'adresse 0x06 est 0x08 (c'est-à-dire année 2008).

(Mardi 20 mai 2008, 9 heures 56 minutes et 6 secondes)

 

4 - Le module MSSP du PIC 16F876A, programmé en langage assembleur

 

Le bus I2C est géré par le module MSSP (Master Synchronous serial port).

Le module MSSP utilise 6 registres pour la gestion du bus I2C :

• MSSP Control Register (SSPCON) en banque 0

• MSSP Control Register 2 (SSPCON2) en banque 1

• MSSP Status Register (SSPSTAT) en banque 1

• Serial Receive / Transmit Buffer Register (SSPBUF) en banque 0

• MSSP Shift Register (SSPSR) (ce registre n'est pas directement accessible)

• MSSP Address Register (SSPADD) en banque 1

 

4-1- Configuration du module MSSP en mode I2C maître

 

Le PIC 16F876A peut être configuré en maître ou en esclave.

Le PIC 16F876A peut fonctionner jusqu'à 1 MHz (mode High-Speed).

On peut, bien sûr, travailler plus lentement (mode standard 100 kHz ou mode Fast 400 kHz).

 

On se placera dans le cas courant suivant :

 

 

On se place dans l'environnement MPLAB IDE, en langage assembleur.

 

a) On commence par configurer les broches RC3/SCK/SCL et RC4/SDI/SDA en entrées (c'est comme ça).

Ces deux broches se trouvent dans le port C.

 

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf TRISC , 3 ; broche RC3/SCK/SCL configurée en entrée

bsf TRISC , 4 ; broche RC4/SDI/SDA configurée en entrée

 

b) Configuration du bit SMP du registre SSPSTAT (banque 1)

La valeur du bit SMP dépend de la fréquence de l'horloge du bus I2C (f SCL) choisie :

 

bsf SSPSTAT , SMP ; SMP = 1 : fréquence de l'horloge du bus I2C = 100 kHz

ou

bsf SSPSTAT , SMP ; SMP = 1 : fréquence de l'horloge du bus I2C = 1 MHz

ou

bcf SSPSTAT , SMP ; SMP = 0 : fréquence de l'horloge du bus I2C = 400 kHz

 

c) Configuration du bit CKE du registre SSPSTAT (banque 1)

Le bit CKE doit être mis à 0.

 

bcf SSPSTAT , CKE ; CKE = 0 : Disable SMBus specific inputs

 

d) Initialisation de la valeur du registre SSPADD (banque 1)

 

f SCL = FOSC/(4 * (SSPADD + 1))

ou

(SSPADD) = (FOSC / (4*f SCL)) - 1

 

Avec un PIC 16F876A travaillant à 20 MHz (oscillateur à quartz externe de 20 MHz) et une fréquence d'horloge I2C de 100 kHz, cela donne :

 

(SSPADD) = (20 000 000 / (4*100 000)) - 1 = .49 = 0x31

 

movlw 0x31

movwf SSPADD ; (SSPADD) = (FOSC / (4*f SCL)) - 1 = 0x31

 

e) Configuration du registre SSPCON (banque 0)

 

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

movlw B'00101000'

movwf SSPCON

; bit 5 (SSPEN) = 1 : enable the MSSP

; bit 3 (SSPM3) = 1

; bit 2 (SSPM2) = 0

; bit 1 (SSPM1) = 0

; bit 0 (SSPM0) = 0

; (SSPM3 : SSPM0) = (1000) : MSSP configuré dans le mode I2C maître

 

En résumé, avec un PIC 16F876A travaillant à 20 MHz et une fréquence d'horloge I2C de 100 kHz, la séquence d'initialisation du module MSSP donne :

 

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf TRISC , 3 ; broche RC3/SCK/SCL configurée en entrée

bsf TRISC , 4 ; broche RC4/SDI/SDA configurée en entrée

bsf SSPSTAT , SMP ; SMP = 1 : fréquence de l'horloge du bus I2C = 100 kHz

bcf SSPSTAT , CKE ; CKE = 0 : Disable SMBus specific inputs

movlw 0x31

movwf SSPADD ; (SSPADD) = (FOSC / (4*f SCL)) - 1 = 0x31

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

movlw B'00101000'

movwf SSPCON

; bit 5 (SSPEN) = 1 : enable the MSSP

; bit 3 (SSPM3) = 1

; bit 2 (SSPM2) = 0

; bit 1 (SSPM1) = 0

; bit 0 (SSPM0) = 0

; (SSPM3 : SSPM0) = (1000) : MSSP configuré dans le mode I2C maître

 

4-2- Routines de communication du bus I2C

 

Pour lancer une opération Start (routine I2C_start), il suffit simplement de mettre à 1 le bit SEN (bit 0 du registre SSPCON2, banque 1).

Au préalable, on vérifie que le bus I2C est libre (routine I2C_idle).

Si ce n'est pas le cas, on attend qu'il le soit (routine I2C_idle).

 

Code en assembleur de ma routine I2C_start :

 

I2C_start

call I2C_idle ; on attend que le bus I2C soit libre

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf SSPCON2 , SEN ; SEN = 1 (lancement d'une opération Start)

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

Vous aurez noté que la routine I2C_start appelle la routine I2C_idle.

La routine I2C_idle teste les 6 bits suivants :

Le bus I2C est prêt si ces 6 bits sont au niveau 0.

 

I2C_idle

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

I2C_idle0

btfsc SSPCON2 , ACKEN

goto I2C_idle0 ; le bit ACKEN n'est pas nul

btfsc SSPCON2 , RCEN

goto I2C_idle0 ; le bit RCEN n'est pas nul

btfsc SSPCON2 , PEN

goto I2C_idle0 ; le bit PEN n'est pas nul

btfsc SSPCON2 , RSEN

goto I2C_idle0 ; le bit RSEN n'est pas nul

btfsc SSPCON2 , SEN

goto I2C_idle0 ; le bit SEN n'est pas nul

btfsc SSPSTAT , R_W

goto I2C_idle0 ; le bit R_W n'est pas nul

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-2-2- Ecriture d'un octet (émetteur PIC 16F876A)

 

Il suffit simplement d'écrire l'octet dans le registre buffer SSPBUF (banque 0).

 

I2C_write

call I2C_idle ; on attend que le bus I2C soit prêt

movwf SSPBUF

return

 

Il faut placer l'octet à écrire dans l'accumulateur (W) avant de lancer la routine I2C_write :

movlw B'XXXXXXXX' ; (XXXXXXXX) sont les 8 bits à écrire, MSB en premier

call I2C_write

 

4-2-3- Acknowledge (esclave vers maître)

 

Après l'écriture d'un octet dans l'esclave, on vérifie que l'esclave renvoit un bit Acknowledge (accusé de réception).

Pour cela, il suffit de lire le bit ACKSTAT (bit 6 du registre SSPCON2, banque 1).

Si le bit ACKSTAT est au niveau 0, tout va bien.

Si le bit ACKSTAT est au niveau 1, cela veut dire que l'esclave n'est pas encore prêt (par exemple, une écriture dans une EEPROM 24xx64 prendra plusieurs millisecondes).

Dans ce cas, on attend le temps qu'il faut ...

 

I2C_ACK_slave_to_master

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

I2C_ACK_slave_to_master0

btfsc SSPCON2 , ACKSTAT

goto I2C_ACK_slave_to_master0 ; on attend la réception du bit ACK

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-2-4- Condition Repeated Start

 

Pour lancer une opération Repeated Start, il suffit simplement de mettre à 1 le bit RSEN (bit 1 du registre SSPCON2, banque 1).

 

I2C_Repeated_Start

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf SSPCON2 , RSEN ; RSEN = 1 (lancement d'une opération Repeated Start)

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-2-5- Lecture d'un octet (récepteur PIC 16F876A)

 

On commence par lancer la réception en mettant à 1 le bit RCEN (bit 3 du registre SSPCON2, banque 1).

Quand la réception est finie, le bit RCEN est automatiquement mis à 0.

L'octet reçu se trouve dans le registre buffer SSPBUF (banque 0).

 

I2C_read

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf SSPCON2 , RCEN ; on lance la réception

I2C_read0

btfsc SSPCON2 , RCEN

goto I2C_read0 ; on attend la fin de la réception

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

movf SSPBUF , W

return

 

Après avoir lancer la routine I2C_read, l'octet lu est disponible dans l'accumulateur (W) :

 

call I2C_read

movwf REGISTRE ; les 8 bits lus sont transférés dans un registre d'usage général

 

4-2-6- Acknowledge (maître vers esclave)

 

On commence par placer à 0 le bit ACKDT (bit 5 du registre SSPCON2, banque 1).

Pour lancer une opération Acknowledge, il suffit simplement de mettre à 1 le bit ACKEN (bit 4 du registre SSPCON2, banque 1) :

 

I2C_ACK_master_to_slave

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bcf SSPCON2 , ACKDT ; ACKDT = 0

bsf SSPCON2 , ACKEN ; ACKEN = 1 (lancement d'une opération Acknowledge)

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-2-7- Not Acknowledge (maître vers esclave)

 

On commence par placer à 1 le bit ACKDT (bit 5 du registre SSPCON2, banque 1).

Pour lancer une opération Not Acknowledge, il suffit simplement de mettre à 1 le bit ACKEN (bit 4 du registre SSPCON2, banque 1) :

 

I2C_NOACK

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf SSPCON2 , ACKDT ; ACKDT = 1

bsf SSPCON2 , ACKEN ; ACKEN = 1 (lancement d'une opération Not Acknowledge)

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-2-8- Condition Stop

 

Pour lancer une opération Stop, il suffit simplement de mettre à 1 le bit PEN (bit 2 du registre SSPCON2, banque 1).

 

I2C_stop

call I2C_idle ; on attend que le bus I2C soit prêt

bsf STATUS , RP0 ; passage en banque 1

bcf STATUS , RP1 ; passage en banque 1

bsf SSPCON2 , PEN ; PEN = 1 (lancement d'une opération Stop)

bcf STATUS , RP0 ; passage en banque 0

bcf STATUS , RP1 ; passage en banque 0

return

 

4-3- Exemples de programmes

 

4-3-1- Exemple n°1 : Ecriture d'un DS1307

 

On reprend l'exemple du paragraphe 3 :

 

call I2C_start

 

movlw B'11010000' ; 8 bits à écrire (7 bits d'adresse + bit 0 Write)

call I2C_write

call I2C_ACK_slave_to_master

 

movlw B'00000100' ; 8 bits à écrire

call I2C_write

call I2C_ACK_slave_to_master

 

movlw B'00100111' ; 8 bits à écrire

call I2C_write

call I2C_ACK_slave_to_master

 

call I2C_stop

 

4-3-2- Exemple n°2 : Lecture d'un DS1307

 

On reprend l'exemple du paragraphe 3 :

 

call I2C_start

 

movlw B'11010000' ; 8 bits à écrire (7 bits d'adresse + bit 0 Write)

call I2C_write

call I2C_ACK_slave_to_master

 

movlw B'00000000' ; 8 bits à écrire

call I2C_write

call I2C_ACK_slave_to_master

 

call I2C_Repeated_Start

 

movlw B'11010001' ; 8 bits à écrire (7 bits d'adresse + bit 1 Read)

call I2C_write

call I2C_ACK_slave_to_master

 

call I2C_read

movwf DATA0 ; les 8 bits lus sont transférés dans le registre d'usage général DATA0

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA1 ; les 8 bits lus sont transférés dans le registre d'usage général DATA1

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA2 ; les 8 bits lus sont transférés dans le registre d'usage général DATA2

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA3 ; les 8 bits lus sont transférés dans le registre d'usage général DATA3

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA4 ; les 8 bits lus sont transférés dans le registre d'usage général DATA4

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA5 ; les 8 bits lus sont transférés dans le registre d'usage général DATA5

 

call I2C_ACK_master_to_slave

call I2C_read

movwf DATA6 ; les 8 bits lus sont transférés dans le registre d'usage général DATA6

 

call I2C_NOACK

 

call I2C_stop

 

4-4- Exemples de projets complets

5 - Le module SSP du PIC 16F88

 

Le module SSP (Synchronous serial port) du PIC 16F88 gère le bus I2C, mais seulement en mode esclave.

C'est la principale différence avec le module MSSP (Master Synchronous serial port) du PIC 16F87X, qui gère également (au sens hardware) le mode maître et le mode multi-maîtres.

Cependant, le module SSP du PIC 16F88 permet d'implanter de manière logicielle le mode I2C maître ou multi-maîtres.

Vous trouverez toutes les informations utiles sur le site de Microchip :

 

6- Bibliographie

 

Site de Philips

 

Le cours de Bigonoff

Site de Microchip

Site de Maxim / Dallas semiconductor

Christian Tavernier

 

© Fabrice Sincère ; Révision 1.9