Main

Apprendre : Accueil | Exemples | Fondements? | Bidouillage? | Liens?

Interfacer une EEPROM Série utilisant le protocole de communication SPI


Description

Dans ce tutoriel, vous allez apprendre comment interfacer votre carte Arduino avec une mémoire externe EEPROM série type AT25HP512 qui utilise le protocole de communication série SPI (Interface Série pour Périphérique). Les mémoires EEPROM en circuit intégré sont très pratiques pour le stockage de données, et les étapes que nous présenterons ici pour implémenter la communication SPI pourront être modifiées pour être utilisées avec la plupart des autres composants SPI. Noter que le microcontrôleur de la carte Arduino contient une EEPROM interne qui peut être utilisée directement (voir la librairie EEPROM ) : ce tutoriel ne vous sera utile que si vous avez besoin de davantage de mémoire que ce dont dispose déjà la carte Arduino. Pour savoir de combien de mémoire dispose votre carte Arduino, voir ici : Les différents types de mémoires de la carte Arduino

Matériel nécessaire

  • Une carte Arduino
  • une mémoire EEPROM Série AT25HP512 (ou équivalent)
  • des straps

Introduction au protocole de communication SPI ( Serial Peripheral Interface )

Le protocole de communication SPI est un protocole de communication série d'envoi de donnée synchronisé utilisé par les microcontrôleurs pour communiquer avec un ou plusieurs périphériques rapidement sur de courtes distances. Ce protocole peut aussi être utilisé entre deux microcontrôleurs.

Avec une connexion SPI, il y a toujours un composant maître (habituellement un microcontrôleur) qui contrôle les composants périphériques. Typiquement, il y a 3 lignes communes à tous les éléments :

  • La ligne "Master In Slave Out" (MISO) : la ligne utilisée pour envoyer les données depuis le composant esclave vers le composant maître,
  • La ligne "Master Out Slave In" (MOSI) : la ligne utilisée pour envoyer les données depuis le composant maître vers le(s) composant(s) esclave(s),
  • La ligne "Serial Clock" (SCK) : L'impulsion d'horloge qui synchronise l'émission des données par le composant maître

Une ligne supplémentaire est également utilisée :

  • la ligne "Slave Select pin" : disponible sur chaque élément dont le composant maître peut disposer pour activer et désactiver des éléments voulus ou éviter de mauvaises communications dues au bruit de la ligne.

Le point difficile avec les communications SPI est que le standard est perdu et chaque composant l'implémente de façon légèrement différente. Ceci veut dire que vous devrez faire attention à la fiche technique des composants utilisés quand vous écrirez votre code de communication. Généralement, il y a trois modes de transmission, numéroté de 0 à 3. Ces modes contrôlent la façon dont les données sont émises ou reçues sur le front montant ou descendant du signal d'horloge, et la façon dont l'horloge est prise en compte quand elle est au niveau BAS ou HAUT.

Tous les paramétrages de la communication SPI sont déterminées pour les cartes Arduino par le registre de contrôle SPI (SPCR). Un registre est juste un octet de la mémoire du microcontrôleur qui peut être lu ou écrit. Les registres ont généralement trois fonctions : contrôle, donnée et état.

Les registres de contrôle fixe les paramètres de contrôle pour toute une variété de fonctionnalité du micro-contrôleur. Habituellement, chaque bit dans un registre de contrôle agit sur un paramètre précis, tel que la vitesse ou la polarité.

Les registres de donnée contiennent simplement des octets. Par exemple, le registre de donnée SPI (SPDR) contient l'octet qui doit être envoyé sur la ligne MOSI et les données qui viennent d'être reçues par la ligne MISO.

Les registres d'état change leur état en fonction de conditions variées liées au fonctionnement du micro-contrôleur. Par exemple, le 7ème bit du registre d'état (SPSR) est mis à 1 quand une valeur est émises ou reçu par le module SPI du micro-contrôleur.

Le registre de contrôle SPI (SPCR) a 8 bits, chaque bit contrôlant un paramètre particulier du module SPI.

Le registre de contrôle SPCR

Bit76543210
NomSPIESPEDORDMSTRCPOLCPHASPR1SPR0

avec :

  • SPIE (Enable Interrupt SPI bit) : Activation de l'interruption SPI si égal à 1
  • SPE (Enable SPI) : Active le module SPI si égal à 1
  • DORD : Envoie les données en commençant par le bit de poids faible si égal à 1, par le bit de poids fort si égal à 0.
  • MSTR : Configure l'Arduino en mode MAÎTRE si égal à 1, en mode ESCLAVE si égal à 0
  • CPOL : Configure l'impulsion d'horloge inactive au niveau HAUt si égal à 1, inactive au niveau BAS si égal 0
  • CPHA : Valide les données sur le front descendant si vaut 1, sur le front montant si vaut 0
  • SPR1 et SPR0 : Configure la vitesse de communication : 00 est le plus rapide (Fosc/4= 4MHz), 11 est le plus lent (250KHz)

Cela signifie que pour écrire un code pour un nouveau composant SPI, vous devez déterminer plusieurs choses et configurer en conséquence le registre SPCR :

  • Les données seront transmises avec le bit de poids fort (MSB) ou de poids faible (LSB) en premier ?
  • Est-ce que l'horloge sera inactive au niveau HAUT ou au niveau BAS ?
  • Les données seront-elles validées sur le front montant ou le front descendant des impulsions d'horloge ?
  • A quelle vitesse le module SPI doit-il fonctionner ?

Une fois que vous avez configuré correctement le registre de contrôle SPI (SPCR), vous devrez évaluer de quelle durée de pause vous aurez besoin entre les instructions et vous serez alors prêt à commencer.

Maintenant que vous avez un petite idée de la façon dont le module SPI travaille, regardons plus en détail le fonctionnement de l'EEPROM externe.

Présentation de l'EEPROM série

La mémoire EEPROM série AT25HP512 est une mémoire EEPROM série de 65 536 octets. Elle supporte les mode SPI 0 et 3, fonctionne jusqu'à 10Mhz à 5V et peut fonctionner à des vitesses plus lentes à 1.8V. Cette mémoire est organisée en 512 pages de 128 octets chacune. Il n'est possible d'écrire que 128 octets à la fois, mais il est possible de lire 1 à 128 octets à la fois. Ce composant offre divers degrés de protection en écriture et une broche HOLD, mais nous ne parlerons pas de ceci dans ce tutoriel.

Le composant est activé en mettant la broche Chip Select (CS) au niveau bas. Les instructions sont envoyées en tant que codes opérationnels de 8 bits (opcodes) et sont transmis sur le front montant de l'impulsion d'horloge. Cela prend environ 10 millisecondes à la mémoire EEPROM pour écrire une page (128 octets) de donnée, et donc une pause de 10 ms devra suivre chaque routine d'écriture en EEPROM.

Voir également : Datasheet de l'eeprom spi AT25HP512

Préparation de la plaque d'essai.

Insérer la mémoire AT25HP512 sur une plaque d'essai. Connecter le +5V et le 0V de la plaque d'essai au +5V et au 0V de la carte Arduino. Connecter les broches 3,7 et 8 de l'EEPROM au 5V et la broche 4 au 0V.

Sur la photo, les connexions +5v sont rouges, et les connexions au 0V sont noires.

Connecter les broches de l'EEPROM de la façon suivante :

  • broche 1 (Chip Select - CS ) à la broche 10 de l'Arduino
  • broche 2 (MISO) à la broche 12 de l'Arduino
  • broche 5 (MOSI) à la broche 11 de l'Arduino
  • broche 6 (Sérial Clock - SCK) à la broche 13 de l'Arduino

Les connexions ont les couleurs suivantes : SS en blanc, MISO en jaune, MOSI en bleu et SCK en vert.

Programmation de l'Arduino.

A présent, nous allons écrire le code pour activer les communication SPI entre l'EEPROM externe et l'Arduino. Dans la routine setup, ce programme remplit 128 octets, soit une page de l'EEPROM avec des données. Dans la fonction loop, le programme lit les données, un octet à la fois et envoie l'octet lu vers la connexion série de la carte Arduino pour affichage sur l'ordinateur. Nous allons avancer progressivement avec des petits morceaux de code.

La première étape consiste à configurer les directives du pre-processeur. Les directives du pré-processeur sont exécutée avant que la compilation ne commence. Elle commence avec un # et ne se termine pas par ; (voir par exemple #define).

Nous allons définir les broches que nous allons utiliser pour notre communication série : DATAOUT, DATAIN, SPICLOCK et SLAVESELECT. Puis nous définiront nos opcodes pour l'EEPROM. Les opcodes sont des commandes de contrôle de l'EEPROM.


#define DATAOUT 11//MOSI
#define DATAIN  12//MISO 
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss

//opcodes
#define WREN  6
#define WRDI  4
#define RDSR  5
#define WRSR  1
#define READ  3
#define WRITE 2 

Puis nous définissons les variables globales que nous allons utiliser dans le programme. Noter la variable char buffer[] : c'est un tableau de 128 octet que nous utiliserons pour stocker les données à écrire dans l'EEPROM :


byte eeprom_output_data;
byte eeprom_input_data=0;
byte clr;
int address=0;
//data buffer
char buffer [128]; 

Tout d'abord nous allons initialiser notre communication série, configurer les broches en entrées et en sortie et mettre la ligne SLAVESELECT au niveau HAUT pour démarrer. Ceci désélectionne la mémoire et évite les mauvaises transmissions de messages dues au bruit des lignes :


void setup()
{
  Serial.begin(9600);

  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device 

Maintenant nous allons initialiser le registre de contrôle SPI (SPCR) avec la valeur binaire 01010000. Dans le registre de contrôle, chaque bit aune fonction différente (voir ci-dessus) :

  • SPIE (Enable Interrupt SPI bit) =0 : Inactivation de l'interruption SPI
  • SPE (Enable SPI) =1 : Active le module SPI
  • DORD = 0 : Envoie les données en commençant par le bit de poids fort
  • MSTR =1 : Configure l'Arduino en mode MAÎTRE
  • CPOL= 0 : Configure l'impulsion d'horloge inactive au niveau BAS
  • CPHA = 0 : Valide les données sur le front sur le front montant
  • SPR1 =0 et SPR0 = 0: Configure la vitesse de communication à 00 = le plus rapide (Fosc/4= 4MHz)

Après avoir configuré notre registre de contrôle, nous lisons le registre d'état SPI et le registre de donnée SPI dans la variable poubelle clr pour effacer toutes données liées aux exécutions précédentes.


  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 rate (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10); 

Ici, nous remplissons notre tableau de données avec des nombres et envoyons une instruction d'écriture à l'EEPROM. L'EEPROM DOIT ETRE ACTIVEE EN ECRITURE avant toute instruction d'écriture. Pour envoyer l'instruction, nous mettons la ligne SLAVESELECT au niveau bas, pour activer la mémoire EEPROM externe, puis nous envoyons l'intruction en utilisant la fonction spi-transfer (voir ci-dessous). Noter que nous utilisons l'opcode WRENque nous avons défini au début du programme. Puis nous remettons le ligne SLAVESELECT à nouveau au niveau HAUT :


  //fill buffer with data
  fill_buffer();
  //fill eeprom w/ buffer
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(WREN); //write enable
  digitalWrite(SLAVESELECT,HIGH);

A présent, nous mettons la ligne SLAVESELECT au niveau BAS pour sélectionner la mémoire EEPROM externe après une brève pause. Nous envoyons une instruction WRITE pour indiquer à l'EEPROM que nous allons envoyer une donnée à mettre en mémoire. Nous envoyons l'adresse 16 bits sur 2 octets pour commencer l'écriture, le bit de poids fort en premier. Puis nous envoyons nos 128 octets de données depuis notre tableau buffer, un octet après l'autre, sans pause. Finalement, nous remettons la ligne SLAVESELECT à l'état HAUT pour désactiver la mémoire puis faisons une pause pour permettre à l'EEPROM d'écrire les données :


  delay(10);
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(WRITE); //write instruction
  address=0;
  spi_transfer((char)(address>>8));   //send MSByte address first
  spi_transfer((char)(address));      //send LSByte address
  //write 128 bytes
  for (int I=0;I<128;I++)
  {
    spi_transfer(buffer[I]); //write data byte
  }
  digitalWrite(SLAVESELECT,HIGH); //release chip
  //wait for eeprom to finish writing
  delay(3000); 

Nous terminons la fonction setup en envoyons le mot "hi" suivi d'un saut de ligne via la connexion série vers l'ordinateur. De cette façon, si nos données envoyées pour affichage ont une drôle d'allure, nous saurons que ce n'est pas un caprice du port série :


  Serial.print('h',BYTE);
  Serial.print('i',BYTE);
  Serial.print('\n',BYTE);//debug
  delay(1000);
}

Dans notre boucle principale loop, nous lisons juste un octet à la fois depuis l'EEPROM et nous l'affichons sur le port série vers l'ordinateur. Nous ajoutons un saut de ligne et une pause pour plus de lisibilité. A chaque passage, la boucle incrément l'adresse de l'EEPROM à lire. Quand l'adresse arrive à 128, nous la réinitialisons à 0 car nous avons rempli uniquement 128 adresses de l'EEPROM avec des données.


void loop()
{
  eeprom_output_data = read_eeprom(address);
  Serial.print(eeprom_output_data,DEC);
  Serial.print('\n',BYTE);
  address++;
  delay(500); //pause for readability
} 

La fonction fill_buffer rempli simplement notre tableau de données avec les nombres 0 à 127 à chaque index du tableau. Cette fonction peut être aisément modifiée pour remplir le tableau avec des données significatives pour votre application.


void fill_buffer()
{
  for (int I=0;I<128;I++)
  {
    buffer[I]=I;
  }
} 

La fonction spi-transfer charge les données dans le registre de transmission du module SPI, et démarre ainsi la transmission SPI. Elle regarde ensuite l'état d'un bit du registre d'état, SPIF, pour détecter lorsque la transmission est complète en utilisant un bit de masque. Une explicatino des bits masques peut être trouvée ici. Puis la fonction renvoie toute données reçue de l'EEPROM qui est présente dans le registre de données.


char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait for the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

La fonction read_eeprom nous permet de lire les données depuis l'EEPROM. Tout d'abord, nous mettons la ligne SLAVESELECT au niveau bas pour activer la mémoire EEPROM. Puis nous transmettons une instruction READ, suivie des 16 bits de l'adresse que nous souhaitons lire, le bit de poids fort en premier. Ensuite nous envoyons un octet factice à l'EEPROM pour la fonction d'envoi des données. Finalement nous remettons la ligne SLAVESELECT au niveau HAUT pour désactiver la mémoire externe après avoir lu un octet, et on renvoie la valeur. Si nous voulons lire plusieurs octets à la fois nous devrons laisser la broche SLAVESELECT au niveau BAS tant que nous répèterons la ligne data=spi-transfer(0xFF); jusqu'à 128 fois pour la lecture d'une page complète.


byte read_eeprom(int EEPROM_address)
{
  //READ EEPROM
  int data;
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(READ); //transmit read opcode
  spi_transfer((char)(EEPROM_address>>8));   //send MSByte address first
  spi_transfer((char)(EEPROM_address));      //send LSByte address
  data = spi_transfer(0xFF); //get data byte
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
  return data;
} 

Le programme entier

Pour un copier/coller facilité, voici le programme entier.


#define DATAOUT 11//MOSI
#define DATAIN  12//MISO 
#define SPICLOCK  13//sck
#define SLAVESELECT 10//ss

//opcodes
#define WREN  6
#define WRDI  4
#define RDSR  5
#define WRSR  1
#define READ  3
#define WRITE 2

byte eeprom_output_data;
byte eeprom_input_data=0;
byte clr;
int address=0;
//data buffer
char buffer [128];

void fill_buffer()
{
  for (int I=0;I<128;I++)
  {
    buffer[I]=I;
  }
}

char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

void setup()
{
  Serial.begin(9600);

  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SLAVESELECT,OUTPUT);
  digitalWrite(SLAVESELECT,HIGH); //disable device
  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 rate (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10);
  //fill buffer with data
  fill_buffer();
  //fill eeprom w/ buffer
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(WREN); //write enable
  digitalWrite(SLAVESELECT,HIGH);
  delay(10);
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(WRITE); //write instruction
  address=0;
  spi_transfer((char)(address>>8));   //send MSByte address first
  spi_transfer((char)(address));      //send LSByte address
  //write 128 bytes
  for (int I=0;I<128;I++)
  {
    spi_transfer(buffer[I]); //write data byte
  }
  digitalWrite(SLAVESELECT,HIGH); //release chip
  //wait for eeprom to finish writing
  delay(3000);
  Serial.print('h',BYTE);
  Serial.print('i',BYTE);
  Serial.print('\n',BYTE);//debug
  delay(1000);
}

byte read_eeprom(int EEPROM_address)
{
  //READ EEPROM
  int data;
  digitalWrite(SLAVESELECT,LOW);
  spi_transfer(READ); //transmit read opcode
  spi_transfer((char)(EEPROM_address>>8));   //send MSByte address first
  spi_transfer((char)(EEPROM_address));      //send LSByte address
  data = spi_transfer(0xFF); //get data byte
  digitalWrite(SLAVESELECT,HIGH); //release chip, signal end transfer
  return data;
}

void loop()
{
  eeprom_output_data = read_eeprom(address);
  Serial.print(eeprom_output_data,DEC);
  Serial.print('\n',BYTE);
  address++;
  if (address == 128)
    address = 0;
  delay(500); //pause for readability
}

Credits : code et tutoriel par Heather Dewey-Hagborg, photos par Thomas Dexter

Commentaires utilisateurs


Page d'accueil de la référence Arduino en français

Corrections, suggestions et nouvelle documentation peuvent être postées sur le Forum Arduino.

La présente traduction française commentée a été réalisée par Xavier HINAULT (2010) (www.mon-club-elec.fr) et est sous licence Creative Commons Attribution-ShareAlike 3.0.

Traduction par X. HINAULT de la page originale : http://www.arduino.cc/en/Tutorial/SPIEEPROM