Ir ao conteúdo
  • Cadastre-se

Memória externa 24C64 com I2C


Posts recomendados

Pessoal, por favor, uma ajudinha na teoria para entender o endereçamento de memória EEPROM. Estou usando uma memória externa 24C64 Atmel e, de acordo com o datasheet (AQUI), os endereços de memória são de 12/13bits. A comunicação é feita por I2C.

 

DÚVIDAS:

1) O que querem dizer esse "12/13bits" indicados no datasheet? em que situação se usam 13bits?

2) Considerando que esta memória é de 64KB, como se consegue apontar para todas as memórias disponíveis se com 12 bits de endereçamento só conseguimos representar 4096 posições?

3) Neste meu código abaixo como eu poderia escrever no endereço de memória 0xF618?

 

Neste meu teste escrevi o caracter "b" na memória 0x0F03 e funcionou OK (fiz a leitura em outra parte). Os quatro bits mais significativos do byte mais significativo são sempre 0000 de acordo com o datasheet, restando os 12bits para endereçamento.

  // Velocidade indicada no datasheet 100KHz ou 400KHz (em 5Vcc)
  I2C1_Init(400000);
  I2C1_Start();
  // Endereço do device de memória 
  // OBS.: pinos de endereçamento A2 e A1 aterrados; A0 em VCC
  I2C1_Wr(0xA2);
  // Endereço de escrita: 0x0F03
  // Byte MAIS significativo do endereço de escrita
  I2C1_Wr(0b00001111); // 0x0F
  // Byte MENOS significativo do endereço de escrita
  I2C1_Wr(0b00000011); // 0x03
  // Dado enviado para escrita
  I2C1_Wr('b');
  I2C1_Stop();
  Delay_ms(10);

 

Link para o comentário
Compartilhar em outros sites

sim, esta parte da divisão das memórias eu entendi. O que não entendi é como fazer referência a todos os endereços de memória, principalmente aqueles mais altos, já que os quatro bits mais significativos do byte mais significativo (que aponta para o endereço a ser escrito) é sempre "0000". Portanto, são 8 x 8K, ok... mas com 12 bits eu só consigo acessar os primeiros 8K... e os outros 7 x 8K?

Link para o comentário
Compartilhar em outros sites

39 minutos atrás, wBB disse:

 (qu Portanto, são 8 x 8K, ok... mas com 12 bits eu só consigo acessar os primeiros 8K... e os outros 7 x 8K?

 

Outros 7 :confused: Como assim ? São 8kBytes (capacidade total para a 24C64) assim os 13 bits são suficientes para endereçar a memória inteira.

 

64 k bits = 8 k bytes ou eu estou comendo mosca? Hehe :)

Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Sobre os bits é relativamente simples uai: 64K bits= 8K bytes pois cada byte tem 8 bits e se lembrar da tabuada 8 vezes 8 = .... acho que @test man*~ está certo kk.

9 horas atrás, wBB disse:

os quatro bits mais significativos do byte mais significativo (que aponta para o endereço a ser escrito) é sempre "0000"

Penso que você está a fazer confusão. Com 12 bits p.ex. não há necessidade de se usar os 4 mais significativos do byte "mais significativo" . A totalidade dos 8 menos sigs sim é usada mas do mais sigs bastam 4... 8lsb+4lsb=...?

Agora se fosse uma 24c512 =64k bytes aí sim usaria todos os 16bits = 2 bytes = msB´s e lsB´s

Link para o comentário
Compartilhar em outros sites

Pessoal, caso sirva para alguém já fica registrado. Se alguma coisa estiver errada, por favor me corrijam. 

Seguem as funções de leitura e escrita de bytes individuais e em blocos via I2C para a memória 24C64.

 

OBS.: Esses "delay_ms(10)" não são legais existirem, pois travam o processamento de outras coisas. Foram inseridos apenas para postar o código e devem ser substituídos por alternativas que não  bloqueiem o processamento.

 

/*------------------------------------------------------------------------------
Descrição       : Comunicação I2C entre microcontrolador e memória externa
Microcontrolador: PIC18F46K80
Memória         : Atmel 24C64
IDE             : MikroC for PIC 6.6.3
Programador     : Microchip PICkit 3
------------------------------------------------------------------------------*/

// -------------- PARÂMETROS: CONSTANTES E DEFINES DE SISTEMA ------------------

// Espaço total da memória 24C64 (em bytes)
#define MEMORY_LENGTH_24C64    0x2000
// Tamanho da paginação de memória (em bytes)
#define PAGE_BLOCK_WIDTH_24C64 32


// ------------------------ RESPOSTAS DE FUNÇÕES -------------------------------
#define ERROR_MEMORY_OVERFLOW   -1
#define ERROR_UNKNOW             0
#define SUCCESS_EXEC             1

/*==============================================================================
function  : signed char writeChar_24C64
Descrição : Escreve um caracter em um endereço de memória
Parâmetros:   deviceAddr: Endereço do dispositivo no barramento I2C
              dataAddr  : Endereço da memória a ser escrita
              data      : Dado a ser escrito na memória
Retorno   : ERROR_UNKNOW: Erro de escrita
            SUCCESS_EXEC: Valor escrito com sucesso
==============================================================================*/
signed char writeChar_24C64(char deviceAddr, unsigned int dataAddr, char dat) {
    char aux = 0;

    I2C1_Start();
    if (I2C1_Wr(deviceAddr)) return ERROR_UNKNOW;
    aux = (dataAddr >> 8) & 0x1F;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    aux = dataAddr & 0x00FF;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    if (I2C1_Wr(dat)) return ERROR_UNKNOW;
    I2C1_Stop();

    delay_ms(10);
    
    return SUCCESS_EXEC;
}


/*==============================================================================
function  : signed char writeBlock_24C64
Descrição : Escreve um bloco de dados a partir de um endereço de memória
Parâmetros:   deviceAddr : Endereço do dispositivo no barramento I2C
              dataAddr   : Endereço da memória a ser escrita
              blockLength: Número de caracteres a serem escritos
              *datBlock  : Ponteiro para o bloco de dados a ser escrito na memória.
Retorno:    ERROR_MEMORY_OVERFLOW: Estouro de memória
            ERROR_UNKNOW         : Erro de escrita
            SUCCESS_EXEC         : Memórias escritas com sucesso
            
--------------------------------------------------------------------------------
EXEMPLO DE USO:

    char i= 0;
    char txt[100];
    char ler[100];
    char val[4];

    writeBlock_24C64(0xA2, 0x0F0F, sizeof(txt), txt);
    readBlock_24C64(0xA2, 0x0F0F, sizeof(ler), &ler);

    for (i = 0; i < sizeof(ler); i++ ) {
        ByteToStr(ler[i], val);
        UART1_Write_Text(val);
    }
            
==============================================================================*/
signed char writeBlock_24C64(char deviceAddr, unsigned int dataAddr, unsigned int blockLength, char *datBlock) {
    char aux;
    unsigned int i, count;

    // Testa se o o espaço disponível para gravar dados é suficiente
    // e se existe pelo menos 1 byte a ser salvo na memória
    if ((blockLength >= (MEMORY_LENGTH_24C64 - dataAddr)) || (blockLength <= 0)) return ERROR_MEMORY_OVERFLOW;

    // Inicia o primeiro bloco
    I2C1_Start();
    if (I2C1_Wr(deviceAddr)) return ERROR_UNKNOW;
    aux = (dataAddr >> 8) & 0x1F;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    aux = dataAddr & 0x00FF;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;

    i     = 0;
    count = 1;
    while (i < blockLength) {
        // Escreve o dado propriamente e avança para o próximo caracter
        if (I2C1_Wr(*datBlock)) return ERROR_UNKNOW;
        datBlock++;
        i++;

        // Verifica a quebra de pagina de memória
        if (i / (count * PAGE_BLOCK_WIDTH_24C64)) {
            // Finaliza a escrita do bloco corrente
            I2C1_Stop();
            count++;

            delay_ms(10);

            // Restarta o próximo bloco de dados para escrita
            I2C1_Start();
            if (I2C1_Wr(deviceAddr)) return ERROR_UNKNOW;
            aux = ((dataAddr + i) >> 8) & 0x1F;
            if (I2C1_Wr(aux)) return ERROR_UNKNOW;
            aux = (dataAddr + i) & 0x00FF;
            if (I2C1_Wr(aux)) return ERROR_UNKNOW;
        }
    }

    I2C1_Stop();
    delay_ms(10);

    return SUCCESS_EXEC;
}


/*==============================================================================
function  : signed char readChar_24C64
Descrição : Lê um caracter em um endereço de memória
Parâmetros:   deviceAddr: Endereço do dispositivo no barramento I2C
              dataAddr  : Endereço da memória a ser lida
              *dat      : Ponteiro para o valor lido na memória
Retorno   : ERROR_UNKNOW: Erro de leitura
            SUCCESS_EXEC: Valor lido com sucesso

--------------------------------------------------------------------------------
EXEMPLO DE USO:
    char data = 0;
    readChar_24C64(0xA2, 0x00ff, &dat);
==============================================================================*/
signed char readChar_24C64(char deviceAddr, unsigned int dataAddr, char *dat) {
    char aux;

    I2C1_Start();
    if (I2C1_Wr(deviceAddr)) return ERROR_UNKNOW;
    aux = (dataAddr >> 8) & 0x1F;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    aux = dataAddr & 0x00FF;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    I2C1_Repeated_Start();
    aux = deviceAddr | 0x01;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;

    *dat = I2C1_Rd(0u);
    I2C1_Stop();

    delay_ms(10);

    return SUCCESS_EXEC;
}

/*==============================================================================
function  : signed char readBlock_24C64
Descrição : Lê um bloco de dados a partir de um endereço de memória
Parâmetros:   deviceAddr : Endereço do dispositivo no barramento I2C
              dataAddr   : Primeiro endereço de memória a ser lida
              blockLength: Número de caracteres a serem lidos
              *datBlock  : Ponteiro para endereço de início das memórias lidas
Retorno:    ERROR_MEMORY_OVERFLOW: Estouro de memória
            ERROR_UNKNOW         : Erro de leitura
            SUCCESS_EXEC         : Memórias lidas com sucesso
==============================================================================*/
signed char readBlock_24C64(char deviceAddr, unsigned int dataAddr, unsigned int blockLength, char *datBlock) {
    char aux;
    unsigned int i;

    if ((blockLength >= (MEMORY_LENGTH_24C64 - dataAddr)) || (blockLength <= 0)) return ERROR_MEMORY_OVERFLOW;

    I2C1_Start();

    if (I2C1_Wr(deviceAddr)) return ERROR_UNKNOW;
    aux = (dataAddr >> 8) & 0x1F;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
    aux = dataAddr & 0x00FF;
    if (I2C1_Wr(aux)) return ERROR_UNKNOW;
        
    for (i = 0; i < blockLength; i++) {
        I2C1_Repeated_Start();
        aux = deviceAddr | 0x01;
        if (I2C1_Wr(aux)) return ERROR_UNKNOW;
        
        *datBlock = I2C1_Rd(0u);
        datBlock++;
    }
    
    I2C1_Stop();
    delay_ms(10);

    return SUCCESS_EXEC;
}

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • Membro VIP

Tua iniciativa é válida sim. Não menciona qual mc usastes pois é 100% sw mas sim é válida. Pra não ficar só na crítica, analise o fonte abaixo pra familia pic. Note-se que é 100% hw sem uso de biblioteca.

 

Caro leitor ...se desejares, há de dar uma olhadela no datasheet do mc pra conhecer/conferir os registros/bits e suas funções nas entranhas do mc. Como sempre, a ideia é (tentar) forçar-te a por a mão na massa e não te deixar refém de libs prontas.

/******************************************************************************************/
void i2c_waitForIdle()
{
while ((SSPCON2 & 0x1F) | RW ); // wait for idle and not writing (PIC18Fxxx)
//while ((SSPCON2 & 0x1F) | STAT_RW ); // wait for idle and not writing (PIC16Fxxx)
}
/******************************************************************************************/
void i2c_start()
{
i2c_waitForIdle();
SEN=1;
}
/******************************************************************************************/
void i2c_repStart()
{
i2c_waitForIdle();
RSEN=1;
}
/******************************************************************************************/
void i2c_stop()
{
i2c_waitForIdle();
PEN=1;
}
/******************************************************************************************/
int i2c_read( unsigned char ack )
{
 unsigned char i2cReadData;
 i2c_waitForIdle();
 RCEN=1;
 i2c_waitForIdle();
 i2cReadData = SSPBUF;
 i2c_waitForIdle();
 if (ack)
        {
        ACKDT=0;
        }
 else
        {
        ACKDT=1;
        }
ACKEN=1;               // send acknowledge sequence
return i2cReadData;
}

Ah sim, permiti-me divulgar apenas a função de leitura e suprimi a inicialização  do hw.

Link para o comentário
Compartilhar em outros sites

Tem razão... editei o post e fiz a inclusão de algumas informações adicionais.

 

Aproveitando, eu alterei aqui no meu código esses delays de 10ms (que são pedidos no datasheet da memória), por um contador externo gerado por um timer do microcontrolador. Fiz isso com objetivo de evitar que o processamento fique travado devido aos delays, tal como indiquei em minha observação lá em cima. Aparentemente funcionou bem... Mas já vi em outras libs que o pessoal coloca na net, que muito não utilizam esse delay. Eu tentei deixar sem aqui e deu problema.

 

Você sabe se são mesmo necessários os delays e se essa forma que usei, por contador de tempo com timer, é a forma correta de evitar o problema de bloqueio de processamento? Ou devo usar outra forma?

Link para o comentário
Compartilhar em outros sites

  • Membro VIP

De  fato delay não trava. Ele é  apenas uma "perda de tempo". você pode manter o delay recomendado sim sem problema da forma como desejar.

Pra evitar delays, a dica é verificar os bits do hw como p.ex. o

while ((SSPCON2 & 0x1F) | RW );

 

Também pra não travar caso haja problema com o hw externo - tipo a memória não grava mais ou pifou - , a dica é fazer um timeout. Algo como
 

unsigned int tmout=10000;//p.ex.
while (((SSPCON2 & 0x1F) | RW )&&tmout--);

ou até mesmo decrementar o tmout na interrupção

tmout=10000;
while (((SSPCON2 & 0x1F) | RW )&&tmout);

 

pra você saber o quanto de tempo EXATO o sistema vai esperar o status do i2c.

Portanto pra um código mais enxuto e otimizado, , o macete é usar todos os recursos que o hw do mc lhe deixa a disposição

Link para o comentário
Compartilhar em outros sites

Encontrei um problema na função de escrita em bloco "writeBlock_24C64" e não estou conseguindo resolver. Nas demais funções de escrita e leitura está tudo funcionando corretamente.

 

PROBLEMA:

Considerando um bloco de 100 bytes por exemplo, quando começo a escrever a partir da memória ZERO (0x0000), os dados são salvos corretamente. Quando mando escrever a partir de qualquer outra memória maior que zero, os primeiros 10 ou 15 bytes (sem nenhuma regra) são salvos corretamente e os demais ficam desorganizados ou a ordem pula uma sequência.. enfim, fica bagunçado!

 

Alguém por me dar uma luz?!

 

Estou usando a função tal como no exemplo de uso que indiquei na própria função:

char i= 0; 
char txt[100]; 
char ler[100]; 
char val[4]; 

writeBlock_24C64(0xA2, 0x0F0F, sizeof(txt), txt); // com "&txt" funciona igual
readBlock_24C64(0xA2, 0x0F0F, sizeof(ler), ler); 

for (i = 0; i < sizeof(ler); i++ ) { 
    ByteToStr(ler[i], val); 
    UART1_Write_Text(val); 
}

 

 

 

 

Link para o comentário
Compartilhar em outros sites

Visitante
Este tópico está impedido de receber novas respostas.

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...