Manual

do

Maker

.

com

EEPROM externa para Arduino

EEPROM externa para Arduino

O Arduino tem espaço mais que suficiente para a maioria dos projetos que você possa vir a fazer, mas às vezes pode ser necessário um espaço extra de armazenamento. Daí você precisa guardar alguma coisa mínima e lá vem "aquela" mão de obra pra colocar um SD para guardar poucos dados; muita corrente, processo lento, etc. Bem, também às vezes, tudo o que você precisa é de uma EEPROM externa - uma memória rápida e que não vai utilizar recursos de sua board; não mais que o I²C, que você poderá inclusive compartilhar com mais 126 dispositivos no mesmo barramento.

ATN24C

Essas memórias EEPROM podem ser encontradas em diversos tamanhos. Nesse artigo vamos discorrer sobre a AT24C02, que possúi 2K (256x8) de memória. Isso é mais do que o próprio Arduino oferece!

Endereçamento

Dispositivos I²C são indereçáveis. No caso desse módulo, à esquerda você vê os endereçadores A0, A1 e A2. Você pode também controlar esses endereços por software, mas para não ter problema e facilitar as coisas, melhor usar os jumpers mesmo.

Como saber os endereços

Para simplificar ao máximo, simplesmente conecte seu módulo ao Arduino e use o scan da biblioteca Wire. Depois você pode ir desligando, trocando o endereço e refazendo o scan, mas para utilizar agora você pode simplesmente utilizar o endereço padrão que nele está, apenas faça o primeiro scan (como será exemplificado).

Até 8 EEPROMs desse modelo podem ser endereçadas no mesmo barramento.

Write Protected

Em alguns casos você poderá desejar manter de forma permanente seus dados gravados na EEPROM para algum projeto, por exemplo. Para evitar a escrita acidental, a EEPROM tem um pino WP (Write Protect). Quando conectado ao ground, a leitura e escrita serão permitidas. De outro modo, ligando-o ao VCC a EEPROM estará em modo protegido.

Absolute Maximum Ratings

Sempre olhe o datasheet e nunca deixa de olhar essa tabela. Todos os limites para não queimar o componente em questão estão descritos em Absolute Maximum Ratings. Dessa tabela, devemos nos ater à tensão, que com o ground correspondente tem uma tolerância de -1 à 7V nos pinos e 6.25V em operação. Isso significa que você pode trabalhar em níveis lógicos de 3.3V ou 5V sem maiores preocupações.

Datasheet

O datasheet desse modelo de EEPROM pode ser pego nesse link.

Wiring

Não tem segredo. Nesse artigo utilizaremos o Arduino Nano, assim teremos um conjunto minimalista, podendo ser alocado em qualquer espaço.

Primeiramente, localize o SDA e o SCL na board:

nano_pinout.webp

Conecte SDA do módulo EEPROM ao SDA do Arduino Nano. Do mesmo modo, conecte SCL ao SCL do Arduino Nano. Em seguida, conecte GND e VCC do Nano ao módulo EEPROM.

 

Baixar biblioteca e testar os códigos

A bilioteca mais simples pode ser baixada daqui. O código é extremamente básico e funcional, gostei muito para um teste imediato. No sketch original, apenas mude o endereço I²C da EEPROM e o tamanho da EEPROM:

#define BUSADDRESS  0x50      
#define EEPROMSIZE  2048     //AT24C16 2048byte

AH_24Cxx ic_eeprom = AH_24Cxx(AT24C02, BUSADDRESS);

E o código ficou assim:

/********************************************************
**  Download from:                                     **
**  http://www.arduino-projekte.de                     **
**                                                     **
**  Based on Code from:				       **
**  http://arduino.cc/playground/                      **
**                                                     **
**  Released into the public domain.                   **
********************************************************/

#include <AH_24Cxx.h>
#include <Wire.h> 

#define AT24C01  0
#define AT24C02  1
#define AT24C04  2
#define AT24C08  3
#define AT24C16  4
#define AT24C32  5
#define AT24C64  6
#define AT24C128 8
#define AT24C256 9
//Initialisation

int data;

#define BUSADDRESS  0x50      
#define EEPROMSIZE  2048     //AT24C16 2048byte

AH_24Cxx ic_eeprom = AH_24Cxx(AT24C02, BUSADDRESS);

void setup(){
 Serial.begin(9600);
 Wire.begin();
 Serial.println("Write data");
 for(int i = 0; i < 10; i++) {
   data = i;
   ic_eeprom.write_byte(i,data);
   delay(100);
 }
}

void loop(){
  Serial.println("Read data");
  for (int i=0;i<EEPROMSIZE;i++){
    Serial.print("pos. ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(ic_eeprom.read_byte(i));
    delay(1000);
  }
}

O resultado:

eeprom_reading-267x300.webp

Se precisar fazer um scan para encontrar esse (ou qualquer outro) dispositivo I²C, eu recomendo esse código:

//
//    FILE: MultiSpeedI2CScanner.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.04
// PURPOSE: I2C scanner @different speeds
//    DATE: 2013-11-05
//     URL: http://forum.arduino.cc/index.php?topic=197360
//
// Released to the public domain
//

#include <Wire.h>
#include <Arduino.h>

// scans devices from 50 to 800KHz I2C speeds.
// lower than 50 is not possible
// DS3231 RTC works on 800 KHz. TWBR = 2; (?)
long speed[] = { 
  50, 100, 200, 250, 400, 500, 800 };
const int speeds = sizeof(speed)/sizeof(speed[0]);

// DELAY BETWEEN TESTS
#define RESTORE_LATENCY  5    // for delay between tests of found devices.
bool delayFlag = false;

// MINIMIZE OUTPUT
bool printAll = true;
bool header = true;

// STATE MACHINE
enum states {
  STOP, ONCE, CONT, HELP };
states state = STOP;

uint32_t startScan;
uint32_t stopScan;

void setup() 
{
  Serial.begin(115200);
  Wire.begin();
  displayHelp();
}


void loop() 
{
  switch (getCommand())
  {
  case 's': 
    state = ONCE; 
    break;
  case 'c': 
    state = CONT; 
    break;
  case 'd': 
    delayFlag = !delayFlag;
    Serial.print(F("<delay="));
    Serial.println(delayFlag?F("5>"):F("0>"));
    break;
  case 'e': 
    // eeprom test TODO
    break;
  case 'h': 
    header = !header;
    Serial.print(F("<header="));
    Serial.println(header?F("yes>"):F("no>"));
    break;
  case '?': 
    state = HELP; 
    break;
  case 'p': 
    printAll = !printAll;
    Serial.print(F("<print="));
    Serial.println(printAll?F("all>"):F("found>"));
    break;
  case 'q': 
    state = HELP; 
    break;
  default:
    break;
  }

  switch(state)
  {
  case ONCE: 
    I2Cscan(); 
    state = HELP;
    break;
  case CONT:
    I2Cscan();
    delay(1000);
    break;    
  case HELP:
    displayHelp();
    state = STOP;
    break;
  case STOP:
    break;
  default: // ignore all non commands
    break;
  }
}

char getCommand()
{
  char c = '\0';
  if (Serial.available())
  {
    c = Serial.read();
  }
  return c;
}

void displayHelp()
{
  Serial.println(F("\nArduino I2C Scanner - 0.1.03\n"));
  Serial.println(F("\ts = single scan"));
  Serial.println(F("\tc = continuous scan - 1 second delay"));
  Serial.println(F("\tq = quit continuous scan"));
  Serial.println(F("\td = toggle latency delay between successful tests."));
  Serial.println(F("\tp = toggle printAll - printFound."));
  Serial.println(F("\th = toggle header - noHeader."));
  Serial.println(F("\t? = help - this page"));
  Serial.println();
}


void I2Cscan()
{
  startScan = millis();
  uint8_t count = 0;

  if (header)
  {
    Serial.print(F("TIME\tDEC\tHEX\t"));
    for (uint8_t s = 0; s < speeds; s++)
    {
      Serial.print(F("\t"));
      Serial.print(speed[s]);
    }
    Serial.println(F("\t[KHz]"));
    for (uint8_t s = 0; s < speeds + 5; s++)
    {
      Serial.print(F("--------"));
    }
    Serial.println();
  }

  // TEST
  // 0.1.04: tests only address range 8..120
  // --------------------------------------------
  // Address  R/W Bit Description
  // 0000 000   0 General call address
  // 0000 000   1 START byte
  // 0000 001   X CBUS address
  // 0000 010   X reserved - different bus format
  // 0000 011   X reserved - future purposes
  // 0000 1XX   X High Speed master code
  // 1111 1XX   X reserved - future purposes
  // 1111 0XX   X 10-bit slave addressing
  for (uint8_t address = 8; address < 120; address++)
  {
    bool printLine = printAll;
    bool found[speeds];
    bool fnd = false;

    for (uint8_t s = 0; s < speeds ; s++)
    {
      TWBR = (F_CPU/(speed[s]*1000) - 16)/2;
      Wire.beginTransmission (address);
      found[s] = (Wire.endTransmission () == 0);
      fnd |= found[s];
      // give device 5 millis
      if (fnd && delayFlag) delay(RESTORE_LATENCY);
    }

    if (fnd) count++;
    printLine |= fnd;

    if (printLine)
    {
      Serial.print(millis());
      Serial.print(F("\t"));
      Serial.print(address, DEC);
      Serial.print(F("\t0x"));
      Serial.print(address, HEX);
      Serial.print(F("\t"));

      for (uint8_t s = 0; s < speeds ; s++)
      {
        Serial.print(F("\t"));
        Serial.print(found[s]? F("V"):F("."));
      }
      Serial.println();
    }
  }

  stopScan = millis();
  if (header)
  {
    Serial.println();
    Serial.print(count);
    Serial.print(F(" devices found in "));
    Serial.print(stopScan - startScan);
    Serial.println(F(" milliseconds."));
  }
}

Selecionando a opção 's' no menu a seguir, você terá um single scan. Na posição que o dispositivo for encontrado, as velocidades e endereços serão preenchidos com 'V'.

Em suma, tudo o que você precisa fazer é ler e escrever inteiros de 8 bits. Se quiser guardar strings, variáveis de inicialização ou coisas assim, também é possível. Basta lembrar que um char é um tipo de inteiro, de modo que se você gravar um char 'a' em um endereço, lá estará o valor 97 decimal ou 0x61 hexadecimal. Como só podemos gravar 1 Byte por endereço, basta alocar uma string (um array de char, não um objeto) em uma região da memória EEPROM gerenciada por você.

Onde comprar

O módulo EEPROM pode ser adquirido na UsinaInfo.

Utilizei o Nano, afinal a tendência é o minimalismo e convenhamos que o UNO já está mais do que manjado. O Nano você pega aqui.

E para um momento mais intenso de tentação, recomendo dar uma olhada na categora "Arduino" da UsinaInfo clicando aqui mesmo. Às vezes você pode precisar de mais recursos, certo?

Gostou do expansor do meu Arduino Nano? É uma mão na roda para prototipar! Dê uma olhada aqui, caso tenha interesse.

...e na continuação

Claro, haverá continuação para dar mais algumas ideias de como utilizar a EEPROM caso ainda não lhe tenha ficado claro. Mas por agora, creio que seja o suficiente para motivá-lo, hum? E vamos para os próximos artigos com materiais da UsinaInfo!

Inscreva-se no nosso canal Manual do Maker no YouTube.

Também estamos no Instagram.

Nome do Autor

Djames Suhanko

Autor do blog "Do bit Ao Byte / Manual do Maker".

Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.