LA LIAISON SPI
Une liaison SPI (Serial Peripheral Interface) est une liaison série synchrone créée par Motorola et qui fonctionne en full duplex (les deux circuits peuvent communiquer en même temps sur le même bus) . Comme pour la liaison I2C, la communication est réalisée selon un schéma maître-esclaves, où le maître s'occupe totalement de la communication. Plusieurs esclaves peuvent être reliés au même bus et la sélection du destinataire se fait par une ligne appelée Slave Select (SS).
Le bus SPI utilise quatre signaux logiques :
-
SCLK : Serial Clock, Horloge (généré par le maître).
-
MOSI : Master Output, Slave Input (généré par le maître).
-
MISO : Master Input, Slave Output (généré par l'esclave).
-
SS : Slave Select, Actif à l'état bas (généré par le maître).
La communication sur le bus est orchestrée de la manière suivante :
-
Le maître sélectionne l'esclave avec lequel il souhaite communiquer en mettant un niveau bas sur la ligne SS correspondante.
-
Le maître génère le signal d'horloge en fonction des capacités de l'esclave
-
A chaque coup d'horloge, le maître et l'esclave s'échangent un bit sur les lignes MOSI et MISO selon le principe ci-dessous.
L'Arduino Uno possède une liaison SPI (SCLK : broche numérique N°13, MISO : broche numérique N°12, MOSI : broche numérique N°11 et SS : broche numérique N°10 et autres si nous avons plusieurs composants esclaves) et une bibliothèque SPI() permet de gérer les échanges d'informations entre la carte Arduino et les circuits SPI connectés. Cette bibliothèque est installée de base dans l'IDE Arduino. Ses principales fonctions sont :
-
SPI.begin() : initialise la communication sur le bus.
-
SPI.setBitOrder(sens) : définit le premier bit qui est transmis où sens peut être LSBFIRST si le bit de poids faible est transmis en premier ou MSBFIRST si bit de poids fort est transmis en premier.
-
SPI.setClockDivider(diviseur) : définit la fréquence d'horloge où diviseur est un multiple de 2 et peut prendre les valeurs SPI_CLOCK_DIV2 à SPI_CLOCK_DIV128.
-
SPI.setDataMode(mode) : définit le mode de fonctionnement du bus où mode peut prendre les valeurs SPI_MODE0 à SPI_MODE3.
-
SPI.transfer(octet) : envoie l'octet sur la ligne MOSI ou reçoit l'octet sur la ligne MISO.
-
SPI.end() : arrête la communication.
-
Lorsque les 8 bit ont été transmis, le maître arrête l'horloge et refait passer la ligne SS au niveau haut.
Il est possible de choisir le type d’horloge grâce à une combinaison de 2 bit :
-
CPOL (Clock POLarity) qui détermine le niveau logique de la ligne SCLK au repos.
-
CPHA (Clock PHAse) qui détermine le front sur lequel la donnée est modifiée et le front sur lequel la donnée va être lue.
Nous avons ainsi 4 modes de fonctionnement :
-
mode 0 : CPOL=0 et CPHA=0.
-
mode 1 : CPOL=0 et CPHA=1.
-
mode 2 : CPOL=1 et CPHA=0.
-
mode 3 : CPOL=1 et CPHA=1.
Une nouvelle fois, comme pour la fréquence d'horloge, c'est l'esclave qui impose au maître le mode de fonctionnement.
Exemple :
Nous souhaitons afficher dans le moniteur série, les valeurs X et Y d'un joystick Pmod et allumer les led lorsque les deux boutons poussoirs sont actifs. La documentation du module se trouve ici.
/* Pmod_JSTK est un programme qui permet d'afficher les valeurs X et Y d'un joystick dans le moniteur série */
#define CS 10 // affectation de la broche CS
#include <SPI.h> // appel de la bibliothèque
int i;
byte recu[6]; // stockage des données du module
int X;
int Y;
int led=128;
void setup()
{
Serial.begin(9600); // initialisation de la liaison série
SPI.begin(); // initialisation du port SPI
SPI.setDataMode(SPI_MODE0); // configuration de la liaison SPI en mode 0
SPI.setClockDivider(SPI_CLOCK_DIV16); // configuration de l'horloge à 1MHz
pinMode(CS, OUTPUT);
}
void loop()
{
digitalWrite(CS, LOW); // activation de la ligne CS
delayMicroseconds(15); // voir doc: pause de 15us après l'activation de la ligne CS
for (i=0;i<5;i=i+1)
{
recu[i] = SPI.transfer(led); // envoi de 5 données pour récupérer les données du module, les led sont éteintes
delayMicroseconds(10); // voir doc: pause de 10us après chaque envoi
}
digitalWrite(CS, HIGH); // désactivation de la ligne CS
X = recu[0]; // X a un format de 10 bit
X |= (recu[1] << 8);
Y = recu[2]; // Y a un format de 10 bit
Y |= (recu[3] << 8);
for (i=0;i<5;i=i+1) // écriture dans le moniteur série
{
Serial.print("i");
Serial.print(i);
Serial.print("=");
Serial.print(recu[i]);
Serial.print('\t'); // tabulation
}
Serial.print("X=");
Serial.print(X);
Serial.print('\t'); // tabulation
Serial.print("Y=");
Serial.println(Y);
delay(10);
switch (recu[4])
{
case 2: // BTN1 actif
led=129;
break;
case 4: // BTN2 actif
led=130;
break;
case 6: // BTN1 et BTN2 actifs
led=131;
break;
default:
led=128;
break;
}
}