As memórias EEProm são memórias não voláteis (isto é, mantém o conteúdo mesmo sem alimentação) que podem ser lidas e gravadas posição a posição (a gravação das memórias Flash requer o apagamento de páginas ou blocos com várias posições).
A 24C256 é parte de uma família de memórias EEProm com capacidade ente 2Kbits (24C02) e 512Kbits (24C512). Estas memórias estão organizadas em bytes, a 24C02 possui 256 posições, a 24C256 32K e a 24C512 64K.
A comunicação entre o microcontrolador e a memória é serial, usando o protocolo I2C. Esta comunicação utiliza dois sinais: SCL (clock) e SDA (dados) e permite a ligação de vários dispositivos slave (escravo) a um master (mestre) através de um barramento (ou "varal").
O sinal SCL é sempre gerado pelo mestre. Já o sinal SDA é bi-direcional, podendo ser controlado pelo mestre ou pelo escravo. Do ponto de vista elétrico são usadas saídas open-colector, que podem ser colocadas em zero (terra) pelos dispositivos. Resistores de pull-up são necessários para manter os sinais em um quando nenhuma saída está ativada. Normalmente o sinal SDA só muda quando o sinal SCL está em zero. Duas violações desta regra caracterizam o início (start) e o fim (stop) de uma comunicação.
As figuras abaixo mostram como são comandadas as leituras e escritas na EEProm. O slave address é o endereço do chip de memória, parte dele é fixo e parte é definida através de pinos do integrado (para permitir colocar vários chips iguais no mesmo barramento). O bit de ACK é sempre gerado pelo slave e indica que foi recebido um byte como sucesso. Na leitura os bits marcados como DATA são gerados pelo slave. Todos os demais bits (incluindo start, stop e NOACK) são gerados pelo mestre.
O protocolo I2C pode ser implementado com entradas e saídas digitais, mas é bastante trabalhoso. Felizmente o MSP430 possui um periférico chamado USI que cuida de uma boa parte disso (o start e stop requerem um pouquinho de trabalho).
Mas chega de teoria. O circuito que vamos montar é bem simples. Vamos aproveitar os resistores de pull-up internos do MSP430, portanto basta o 24C256 e alguns fios. A placa Launchpad possui um LED que pode ser conectado ao pino P1.6, que é também o SCL, é necessário abrir o jumper correspondente.
As rotinas de leitura e escrita e um pequeno programa de teste ficam assim:
#include <msp430.h>
typedef unsigned char byte;
typedef unsigned int word;
// definições dos pinos de E/S no port 1
#define I2C_SDA 0x80
#define I2C_SCL 0x40
#define LED 0x01
// Endereçamento da EEProm
#define ADDR 0xA0
// Valor para gerar interrupções cada 250us c/ clock de 1MHz
#define TEMPO_250us 249 // (250us * 1MHz)-1
// variáveis globais
static volatile unsigned int contDelay;
// rotinas locais
static void EEProm_Write (word ender, byte dado);
static byte EEProm_Read (word ender);
static byte I2C_TxByte (byte b);
static byte I2C_RxByte ();
static void I2C_TxStop ();
static void I2C_TxStart ();
static void Delay (unsigned int ms);
int main( void )
{
word i;
byte dado;
// Desliga Watchdog
WDTCTL = WDTPW + WDTSSEL + WDTHOLD;
// Programa entradas e saídas
P1SEL = 0;
P1DIR = 0xFF; // Todo mundo é saída
P1OUT = I2C_SCL | I2C_SDA;
P1REN = I2C_SCL | I2C_SDA; // Ativa resistores de pull up
// Alimentação já deve estar estável, vamos ligar o DCO
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
// Programa USI
// Clock = SMCLK/4 = 1/4 MHZ = 250 KHz, ativo low
USICKCTL = USIDIV1 | USISSEL1 | USICKPL | USISWRST;
// Habilitar SCL e SDA, MSB first, Master
USICTL0 = USIPE7 | USIPE6 | USIMST;
// Modo I2C
USICTL1 = USII2C;
// 8 bit shift register
USICNT = 0;
// Libera USI para operação
USICKCTL &= ~USISWRST;
// Programar a interrupção de tempo real p/ cada 250 us
TACCR0 = TEMPO_250us;
TACTL = TASSEL_2 + MC_1 + TAIE; // SMCLK, up mode, interrupt
_BIS_SR (GIE);
// Grava dados de teste
for (i = 0; i < 255; i++)
{
EEProm_Write (i, (byte) i);
Delay (10);
}
for (i = 0; i < 7; i++)
{
EEProm_Write (1 << (i+8), (byte) i);
Delay (10);
}
// Testa continuamente a leitura
while (1)
{
for (i = 0; i < 255; i++)
{
dado = EEProm_Read (i);
if (dado != i)
{
P1OUT |= LED;
while (1)
;
}
}
for (i = 0; i < 7; i++)
{
dado = EEProm_Read (1 << (i+8));
if (dado != i)
{
P1OUT |= LED;
while (1)
;
}
}
Delay (5);
}
}
static void EEProm_Write (word ender, byte dado)
{
I2C_TxStart();
I2C_TxByte (ADDR);
I2C_TxByte ((byte) (ender >> 8));
I2C_TxByte ((byte) (ender & 0xFF));
I2C_TxByte (dado);
I2C_TxStop();
}
static byte EEProm_Read (word ender)
{
byte dado;
I2C_TxStart();
I2C_TxByte (ADDR);
I2C_TxByte ((byte) (ender >> 8));
I2C_TxByte ((byte) (ender & 0xFF));
I2C_TxStart();
I2C_TxByte (ADDR | 1);
dado = I2C_RxByte ();
I2C_TxStop();
return dado;
}
// Transmite um byte pelo I2C
static byte I2C_TxByte (byte b)
{
USISRL = b;
USICTL0 |= USIOE;
USICNT = 8;
while ((USICTL1 & USIIFG) == 0)
;
USICTL0 &= ~USIOE;
USICNT = 1;
while ((USICTL1 & USIIFG) == 0)
;
return (USISRL & 1) == 0;
}
// Recebe um byte pelo I2C
static byte I2C_RxByte ()
{
byte dado;
USICTL0 &= ~USIOE;
USICNT = 8;
while ((USICTL1 & USIIFG) == 0)
;
dado = USISRL;
USISRL = 0xFF;
USICTL0 |= USIOE;
USICNT = 1;
while ((USICTL1 & USIIFG) == 0)
;
return dado;
}
// Transmite Start
static void I2C_TxStart ()
{
USICTL0 |= USIOE;
USISRL = 0xFF;
USICNT = 1;
while ((USICTL1 & USIIFG) == 0)
;
USISRL = 0;
USICTL0 |= USIGE;
USICTL0 &= ~USIGE;
}
// Transmite Stop
static void I2C_TxStop ()
{
USICTL0 |= USIOE;
USISRL = 0;
USICNT = 1;
while ((USICTL1 & USIIFG) == 0)
;
USISRL = 0xFF;
USICTL0 |= USIGE;
USICTL0 &= ~(USIGE | USIOE);
}
// Aguarda ms milisegundos
// 1 <= ms <= 16383
static void Delay (unsigned int ms)
{
_BIC_SR (GIE);
contDelay = ms << 2;
_BIS_SR (GIE);
while (contDelay != 0)
;
}
// Tratamento da interrupção do Timer A
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A_TO(void)
{
switch (TAIV)
{
case 10: // overflow
if (contDelay)
contDelay--;
break;
}
}
O programa de teste grava valores conhecidos em algumas posições da EEProm e depois fica continuamente testando-os, em caso de erro o LED ligado ao P1.0 é aceso.No próximo post vamos ver uma outra opção de EEProm.





Nenhum comentário:
Postar um comentário