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;
- }
- }
No próximo post vamos ver uma outra opção de EEProm.
Nenhum comentário:
Postar um comentário