AT24C256 – I2C e o módulo EEPROM AT24C256

Ola. Neste post vamos rever o módulo AT24C256, desta vez voltado para o I2C. No post anterior eu falei de maneira geral sobre o I2C, e que para trabalharmos com os diversos dispositivos temos que recorrer ao datasheet do mesmo. Pois bem, abaixo o link para download do PDF e a imagem do módulo que eu adquiri no Ebay.

Datasheet AT24C256 ou Download do Site da Atmel 

AT24C256

Lembrando que este post é uma análise voltado para I2C do sketch que usamos aqui neste post, se não viram antes, vejam agora para acompanhar a explicação.

Na página 1 do datasheet temos as características do chip, e as mais interessantes são:

  • Memória organizada como 32.768 posições de 8 bits
  • Pino de proteção de escrita (WP na imagem acima)
  • 64-byte Page Write Mode (podemos escrever até 64 bytes em uma única operação)
  • Tempo de escrita máximo de 5ms
  • Suporta um milhão de operações de escrita
  • Armazena as informações por até 40 anos
  • Esquema de endereçamento permite até 4 AT24C256 conectados no barramento I2C

AT24C256 endereço I2C

Ainda na página 1, Table 1, vemos os pinos do chip e podemos ver que o módulo acima possui os conectores para os mesmo. Na pagina 2 do datasheet temos a descrição dos pinos. De interesse para nós são os pinos usados para o endereçamento, e o pino WP. Quando WP esta conectado ao GND, como na imagem acima, permite operações de escrita e leitura. Caso mudemos o jumper de posição, ligando WP ao Vcc, o chip permite apenas a leitura.

Quanto aos pinos de endereçamento, bem, perdi muitas horas com uma divergência entre o módulo acima e o chip AT24C256. Se notarem no datasheet, o chip possui apenas os pinos A0 e A1 para endereçamento, e o módulo acima possui A0, A1 e A2. Pode parecer ridículo, mas eu demorei uma eternidade até perceber que o A2 do módulo acima não esta conectado a nada, tanto faz mudar o jumper, tirar o jumpar, queimar, martelar, etc…

Continuando, na página 10 do datasheet temos o endereçamento do dispositivo. Na página 11, figura 7, temos os bits do endereçamento junto com os pinos A1 e A0. Sempre que temos um pino conectado ao GND, temos o valor 0, e se o pino estiver ligado ao Vcc, temos o valor 1. Com isso podemos ter as seguintes variações de endereço:

  • 10100 0 0
  • 10100 0 1
  • 10100 1 0
  • 10100 1 1

Usem essa calculadora online para converter de binário para hexadecimal, e verão que cada variação acima produz os seguintes endereços: 50, 51, 52 e 53. Então, manipulando os pinos A1 e A0, podemos ter 4 endereços distintos para o At24C256, podendo então utilizar até 4 EEPROMs no mesmo barramento I2C.

Uma coisa interessante para notar, é que este chip especificamente permite a escolha de 4 endereços de forma manual, apenas alterando jumpers. Alguns dispositivos tem apenas um endereço fixo, de modo que não podemos alterar, e outros dispositivos permitem que estipulemos via software qual o endereço que ele utilizará. A questão é sempre recorrer ao datasheet para saber esses detalhes.

AT24C256 – Operações de Escrita e Leitura

Analisar a operação de escrita do AT24C256 é interessante, pois ele tem uma pequena diferença do procedimento que vimos no post anterior.

Wire.write(X); espera que o valor de X seja um byte, certo? Pois bem, considerem que o chip AT24C256 possui 32.768 posições de memória. Com apenas um byte é impossível endereçar alêm de 256 posições, então vamos precisar de um tipo de variável que suporte endereçar todas as posições de memória. Vejamos o tipo int, que suporta de -32.768 até 32.768, perfeito não é? Pois então será ele que usaremos, mas antes temos que ver como adaptar o int para o tipo byte, que é o valor aceito por Wire.write();

Um valor do tipo int é composto por 16 bits. Utilizando a calculadora online indicada anteriormente, ou uma de sua preferência, faça a conversão do valor 31.333 para binário. O resultado deverá ser o seguinte:

01111010  01100101

Reparem que temos dois grupos de 8 bits, ou um byte. Na verdade o primeiro grupo tem 7 bits, mas podemos completar ele sem problemas com um 0 a esquerda (0 a esquerda não muda nada). Se convertermos cada um dos grupos para decimal, teremos os valores 122 e 101, e estes dois valores estão entre a faixa de válidos para o tipo byte.

Resumindo, já que não podemos mandar o endereço de memória diretamente, já que é um int, mandamos dois bytes, sempre na ordem primeiro/segundo grupo de bits.

Então quer dizer que sempre que precisar separar um valor do tipo int em dois valores do tipo byte eu vou ter que ficar fazendo conta? Não, o Arduino faz isso para você. A mais simples é a seguinte:

int end_memoria = 31333;

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

void loop() {
  byte byte_alto = highByte(end_memoria);
  byte byte_baixo = lowByte(end_memoria);
  Serial.print(byte_alto);
  Serial.print(" ");
  Serial.println(byte_baixo);
  delay(10000);
}

Façam upload do código acima, e verifiquem que o valor mostrado na serial é exatamente 122 e 101.

Outra forma de obter o mesmo resultado é utilizando operadores Bit Shift e BitWise. Não vou entrar em detalhes sobre Bit Shift e BitWise, mas eles fazem a mesma coisa que highByte() e lowByte(). O motivo de mostrá-lo aqui é que vocês vão encontrar estes operadores em muitos exemplos pela net. Abaixo exemplo:

int end_memoria = 31333;

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

void loop() {
  byte byte_alto = (end_memoria >> 8);
  byte byte_baixo = (end_memoria & 0xFF);
  Serial.print(byte_alto);
  Serial.print(" ");
  Serial.println(byte_baixo);
  delay(10000);
}

Façam upload, e verifiquem que o comportamento é o mesmo.

Vamos ver no datasheet no dispositivo como fazer escritas e leituras.

Escrita

Na página 10 temos o procedimento de escrita. Um byte(8 bits) de endereço do dispositivo, seguindo por 2 bytes de endereço de memória, e por fim um byte de dados. Se observarem no sketch utilizando o AT24C256, enviamos exatamente cada byte solicitado. Abaixo o trecho de código:

void i2c_eeprom_escrita(int end_eeprom, unsigned int end_memoria, byte valor)
{
  Wire.beginTransmission(end_eeprom);
  Wire.write((int)(end_memoria >> 8));
  Wire.write((int)(end_memoria & 0xFF));
  Wire.write(valor);
  Wire.endTransmission();
}

Algumas observações: Na escrita o ultimo bit do endereço do dispositivo é 0, indicando uma escrita; alem disso, o datasheet fala sobre “page write”, que é uma divisão lógica da memória em 512 páginas de 64 bytes cada (512×64=32768), o que permite a escrita de 64 bytes consecutivos em uma única operação.

Leitura

Na página 11 temos o procedimento para leitura. O datasheet especifica que enviemos um byte de endereço do dispositivo, com o ultimo bit sendo 1(operação de leitura). A valor retornado é o valor da ultima posição de memória conhecida, por isto damos um write no endereço de memória que queremos ler. Abaixo um trecho de código mostrando:

char i2c_eeprom_leitura(int end_eeprom, unsigned int end_memoria)
{
  Wire.beginTransmission(end_eeprom);
  Wire.write((int)(end_memoria >> 8));
  Wire.write((int)(end_memoria & 0xFF));
  Wire.endTransmission();
  Wire.requestFrom(end_eeprom,1);
  byte dado;
  if (Wire.available()) {
    dado=Wire.read();
  }
  Wire.endTransmission();
  return dado;
}

Ufa, teoria é um saco… 😉

Bem pessoal, como alguns perceberam eu fiquei muito tempo sem postar, e perdi um pouco “a mão” de escrever.

Se não tiver ficado claro, por favor perguntem a vontade que será um prazer responder e tentar melhor. Já estou preparando um sketch utilizando 5 dispositivos I2C, e creio que será interessante.

 

Abs a todos.

 

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

20 − dezesseis =