Ir ao conteúdo
  • Comunicados

    • Gabriel Torres

      Seja um moderador do Clube do Hardware!   12-02-2016

      Prezados membros do Clube do Hardware, Está aberto o processo de seleção de novos moderadores para diversos setores ou áreas do Clube do Hardware. Os requisitos são:   Pelo menos 500 posts e um ano de cadastro; Boa frequência de participação; Ser respeitoso, cordial e educado com os demais membros; Ter bom nível de português; Ter razoável conhecimento da área em que pretende atuar; Saber trabalhar em equipe (com os moderadores, coordenadores e administradores).   Os interessados deverão enviar uma mensagem privada para o usuário @Equipe Clube do Hardware com o título "Candidato a moderador". A mensagem deverá conter respostas às perguntas abaixo:   Qual o seu nome completo? Qual sua data de nascimento? Qual sua formação/profissão? Já atuou como moderador em algo outro fórum, se sim, qual? De forma sucinta, explique o porquê de querer ser moderador do fórum e conte-nos um pouco sobre você.   OBS: Não se trata de função remunerada. Todos que fazem parte do staff são voluntários.
    • DiF

      Poste seus códigos corretamente!   21-05-2016

      Prezados membros do Fórum do Clube do Hardware, O Fórum oferece um recurso chamado CODE, onde o ícone no painel do editor é  <>     O uso deste recurso é  imprescindível para uma melhor leitura, manter a organização, diferenciar de texto comum e principalmente evitar que os compiladores e IDEs acusem erro ao colar um código copiado daqui. Portanto convido-lhes para ler as instruções de como usar este recurso CODE neste tópico:  
wBB

Memória externa 24C64 com I2C

Recommended Posts

wBB    16

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);

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
test man*~    99

Tipo, são 64kbits (8kbytes - 8k x 8bits) e 32kbits (4kbytes - 4k x 8bits) por isso os endereços podem ser de 12 ou 13 bits dependendo da eeprom que você vai usar... 24c32 - 32kbits e 24c64 - 64kbits... Estou no celular daí nem da pra entender o código =D

Compartilhar este post


Link para o post
Compartilhar em outros sites
wBB    16
  • Autor do tópico
  • 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?

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    test man*~    99
    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 :)

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    Isadora Ferraz    1.587

    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

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    wBB    16
  • Autor do tópico
  • Vixi!!!! Estou me sentindo um *****! Vocês tem razão, eu estava vendo o o datasheet da memória que eu estou usando corretamente, mas estava pensando em bytes mesmo e não em bits, como vocês citaram. Gente, me desculpem por favor!

    Obrigado @test man*~ e@Isadora Ferraz

    Editado por wBB
    • Curtir 2

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    wBB    16
  • Autor do tópico
  • Postado (editado)

    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;
    }

     

    Editado por wBB
    • Curtir 1

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    Isadora Ferraz    1.587

    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.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    wBB    16
  • Autor do tópico
  • 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?

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    Isadora Ferraz    1.587

    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

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    wBB    16
  • Autor do tópico
  • Ok! Obrigado Isadora.

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
    wBB    16
  • Autor do tópico
  • 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); 
    }

     

     

     

     

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Crie uma conta ou entre para comentar

    Você precisar ser um membro para fazer um comentário






    Sobre o Clube do Hardware

    No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações 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

    ×