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