Skyduino:~#
Articles
arduino, programmation, python, tutoriel

[Tutoriel Arduino] Communication par SPI (hard et soft) et par I2C

Bonjour tout le monde !

Aujourd’hui je vous est préparé un petit tutoriel qui va allier électronique et programmation arduino.

Le but de ce tutoriel est de comprendre comment communiquer par SPI et par I2C avec des périphériques externe, le tout avec une carte arduino.

Pour les curieux :
La page wikipédia sur le bus I2C
La page wikipédia sur le bus SPI

Voici la liste des composants nécessaire pour chaque partie du tutoriels :
1ere partie, Horloge I2C + afficheur 7 segments :
1x carte arduino (uno pour moi),
1x PCF8575 + support cms -> DIL,
2x résistances de 10K,
2x réseau de résistances de 1K (!! réseau DIL (dual in line) pas SIL (single in line)),
2x afficheurs 7 segments à anode commune (CA), par exemple SA52-11EWA ou un HDSP-5501,
1x module RTC sur base de DS1307 (exemple : module RTC de sparkfun, adafruit, ...),
1x condensateur de 100nF (optionnel),
et beaucoup de fils (~40) !

Schéma : Format JPG

2eme partie, SPI Hardware :
1x carte arduino (uno pour moi),
1x "Rotary Encoder LED Ring" de Mayhew Labs,
1x Protoshield + breadbard de sparkfun,
et 8 fils.

Schéma : voir vidéo

3eme partie, SPI software, shiftOut :
1x carte arduino (uno pour moi),
1x 74HC595,
2x résistances de 10K,
1x condensateur de 100nF (optionnel),
1x réseau de résistances de 1K (SIL),
8 x led rouge 5mm,
et une 20aine de fils.

Schéma et détails : Schéma arduino.cc

4eme partie, SPI software, shiftIn :
1x carte arduino (uno pour moi),
1x CD4021,
1x condensateur de 100nF (optionnel),
1x réseau de résistances de 1K (SIL),
1x "mini switch" à 8 interrupteurs,
et une 20aine de fils.

Schéma et détails : Schéma arduino.cc

Les liens vers la référence des librairies et fonctions « importante » de ce tutoriel :
Librairie Wire (I2C)
Librairie SPI
Référence de ShiftOut
Référence de ShiftIn

Et pour finir bien évidement le tutoriel :

Pour ceux qui est des différents sketch de ce tutoriel :

Le sketch de la partie I2C :

// Inclusion de la librairie I2C
#include <Wire.h>

// Adresse I2C du module RTC
#define DS1307_ADDRESS 0x68
// Adresse I2C du PCF8575
#define PCF8575_ADDRESS 0x20

// Conversion BCD -> decimal
byte bcdToDec(byte val)  {
  return ((val/16*10) + (val%16));
}

// Tableau de correspondance chiffre -> 7-segments
byte digits[10] = {
  B10000001 ,
  B11101101 ,
  B01000011 ,
  B01001001 ,
  B00101101 ,
  B00011001 ,
  B00010001 ,
  B11001101 ,
  B00000001 ,
  B00001101 
};

void setup()
{
  Wire.begin(); // Configuration du bus I2C
}

void loop()
{
  byte secondes = recupSecondes(); // Récupération des secondes depuis le module RTC
  afficheNombre(secondes); // Affichage sur le double 7-segments
  delay(1000); // Attente de 1s
}

// Retourne le nomre de secondes de l'heure courante stocké dans le module RTC
int recupSecondes() 
{
  Wire.beginTransmission(DS1307_ADDRESS); // Engage la communication
  Wire.send(0); // Démande l'adresse 0 (-> secondes)
  Wire.endTransmission(); // Termine la communication

  Wire.requestFrom(DS1307_ADDRESS, 1); // Demande une réponse de 1 octet en provenance du module RTC
  return bcdToDec(Wire.receive()); // Converti le résultat (en bcd) en décimal (base 10)
}

// Affiche un nombre décimal de 0 à 99 sur le double afficheur 7-segments
void afficheNombre(byte nombre)
{
  byte x = nombre % 10; // chiffre des unités
  byte y = nombre / 10; // chiffre des dizaines
  Wire.beginTransmission(PCF8575_ADDRESS); // Engage la communication
  Wire.send(digits[x]); // Envoie le 1er octet (bits 0 ~ 8 )             
  Wire.send(digits[y]); // Envoie le 2eme octet (bits 9 ~ 16)
  Wire.endTransmission(); // Termine la communication   
}

+ Bonus le script python pour générer les valeurs des digits :

byteOrder = "gfabedc."
digits = [ "abcdef" , "bc" , "abged" , "abcdg" , "bcfg" , "afgcd" , "acdefg" , "abc" , "abcdefg" , "abcfg" ]

print "byte digits[10] = {"
for digit in digits:
  nstr = "B"
  for letter in byteOrder:
    if letter in digit:
      nstr +=  "0"
    else:
      nstr +=  "1"
  print nstr,","
print "};"

Le sketch de la partie SPI hardware :

// Inclusion de la librairie SPI hardware
#include <SPI.h>

// Tableau d'effets lumineux (ici un simple scroll avec un led)
unsigned int effect[16] = {1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};

void setup()
{
  SPI.begin(); // Configuration du port SPI hardware
}

void loop()
{
  for(byte i = 0 ; i < 16 ; i++) // Pour chacune des 16 leds
  {
    unsigned int n = effect[i]; // Récupération de "l'image" correspondant à i dans la tableau des effets lumineux 
    SPI.transfer(n & 0xFF); // Envoi du LSB (1er octet, bits 0 ~ 8 )
    SPI.transfer((n & 0xFF00) >> 8); // Envoi du MSB (2eme octet, bits 9 ~ 16)
    delay(250); // Avec un petit effet K2000 ce sera plus marant =P
  }
}

Le sketch de la partie ShiftOut :

#define CS_PIN 10
#define DS_PIN 11
#define CLK_PIN 12

void setup()
{
  Serial.begin(9600); // Configuration du port série
  pinMode(CS_PIN, OUTPUT); // broche CS -> Chip Enable
  pinMode(DS_PIN, OUTPUT); // broche DS -> Data Serial
  pinMode(CLK_PIN, OUTPUT); // Broche CLK -> Clock
}

void loop()
{
  if(Serial.available() > 2) // Attente de 3 caractéres sur le port série
  {
    int nombre = (Serial.read() - '0') * 100; // Récupération du nombre en décimal sur 3 chiffres
    nombre += (Serial.read() - '0') * 10;
    nombre += Serial.read() - '0';
    nombre &= 255; // Protection contre le nombre >255 qui pourrait entrainer un bug de shiftOut
    
    digitalWrite(CS_PIN, LOW); // On sélectionne le 74HC595 -> en attente d'un octet
    shiftOut(DS_PIN, CLK_PIN, MSBFIRST, (byte)nombre); // On envoi l'octet (cast en byte pour forcer l'envoi d'un seul octet)
    digitalWrite(CS_PIN, HIGH); // On déselectionne le 74HC595 -> Affichage de l'octet
  }
}

Le sketch de la partie ShiftIn :

#define CS_PIN 10
#define DS_PIN 11
#define CLK_PIN 12

void setup()
{
  Serial.begin(9600); // Configuration du port série
  pinMode(CS_PIN, OUTPUT); // broche CS -> Chip Enable
  pinMode(DS_PIN, INPUT); // broche DS -> Data Serial
  pinMode(CLK_PIN, OUTPUT); // Broche CLK -> Clock
}

void loop()
{
  digitalWrite(CS_PIN, HIGH); // On place CS à HIGH pour collecter les données paralléles de Q0 à Q7
  delayMicroseconds(20);
  digitalWrite(CS_PIN, LOW); // A partir de maintenant le CD021 devrait avoir collecté les états de Q0 à Q7
  
  byte n = shiftIn(DS_PIN, CLK_PIN, MSBFIRST); // On récupére l'octet depuis le CD4021
  
  // Affichage des valeurs des deux encodeurs
  Serial.print("V1 & V2 :"); 
  Serial.println(n, BIN); // Bug bit 8 ?
  delay(1000);
}

+ Bonus le sketch SPI – Read :

#include <SPI.h>

void setup()
{
  Serial.begin(9600); // Configuration du port série
  SPI.begin(); // Configuration du port SPI
  SPI.setClockDivider(SPI_CLOCK_DIV128);
  pinMode(10, OUTPUT);
}

void loop()
{
  digitalWrite(10, LOW);
  byte n = SPI.transfer(0); // On récupére l'octet depuis le CD4021
  digitalWrite(10, HIGH);
  
  // Affichage des valeurs des deux encodeurs
  Serial.print("V1 & V2 :"); 
  Serial.println(n, BIN);
  delay(1000);
}
Advertisements

Discussion

5 réflexions sur “[Tutoriel Arduino] Communication par SPI (hard et soft) et par I2C

  1. Je suis débutant (de chez débutant).
    Merci pour ce tuto bien sympathique et bien ACCESSIBLE.
    J’ai compris pas mal de chose (et pas seulement sur le I2c et le SPI). Bookmarké !

    Publié par Joe | 10 mai 2014, 1 h 05 min
  2. Bonjour, super tuto, à la base ce n’est pas exactement ce que je cherchais, mais grâce à vous, j’ai pu avancé sur une autre partie d’un de mes projets.
    Juste une question au cas oui vous auriez l’info sous la main, vous parlez de possibilité de montage en cascade pour les PCF8575 et CD4021, mais je n’ai pas encore trouvé de schéma de câblage, auriez vous ça.

    En tous cas bravo

    Publié par Flo | 16 octobre 2014, 22 h 46 min
    • >> vous parlez de possibilité de montage en cascade pour les PCF8575 et CD4021, mais je n’ai pas encore trouvé de schéma de câblage, auriez vous ça.

      Pour les PCF8575 c’est assez simple, un bus I2C c’est deux fils communs à tout les modules (SDA et SCL).
      Ce qui fait la différence entre deux modules c’est l’adresse du dit module.

      Pour le PCF8575 en particulier, il est possible de choisir l’adresse de chaque module au moyen des broches A0, A1 et A2.
      Au total il est possible d’avoir huit PCF8575 sur un même bus I2C (mais pas plus).

      Pour le CD4021 c’est différent, il faut faire une cascade (« daisy chain » en anglais) : http://lucidtronix.com/system/attached_files/425/original/daisy_chained_schematic.png?1366148689

      Publié par Skywodd | 20 octobre 2014, 14 h 54 min
  3. Franchement, super tuto, je m’étais pas mal documenté sur le SPI et sur l’i2c mais je n’ai jamais rien trouvé d’aussi simplement expliqué.

    Publié par Lao | 8 avril 2015, 14 h 43 min

Rétroliens/Pings

  1. Pingback: Arduino | Pearltrees - 10 décembre 2015

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Skyduino devient Carnet du Maker

Le site Carnet du Maker remplace désormais le blog Skyduino pour tout ce qui touche à l'Arduino, l'informatique et au DIY.