CONFIGURANDO CORRETAMENTE OS REGISTRADORES DOS MICROCONTROLADORES AVR - parte 4

USO DOS REGISTRADORES EECR, EEARH/L e EEDR – CONTROLE DA MEMÓRIA EEPROM INTERNA




MEMÓRIAS TIPO ROM E RAM

Muitos usuários ainda não compreenderam como os vários tipos de memórias se comportam e conseqüentemente como deve ser feito o seu correto uso.

Uma memória tipo ROM (Read Only Memory) é uma memória que permite apenas a sua leitura. Geralmente a sua escrita é feita durante a sua produção ou ainda posteriormente, em tempo de desenvolvimento. E o que “é escrito não é perdido” quando retirada a alimentação da memória. Devido a essa característica este tipo de memória recebe a designação de “memória não volátil”. Um exemplo que pode ser dado é a área de memória dedicada ao programa nos microcontroladores. Apesar da grande maioria hoje possuir tecnologia FLASH, o que permite a sua re-gravação, a sua escrita é feita em tempo de desenvolvimento para ser posteriormente apenas “lida” (execução do programa). Assim, a área de programa de um microcontrolador pode ser considerada também um tipo de memória ROM.

Já uma memória do tipo RAM (Randon Access Memory) permite sua escrita e leitura em qualquer tempo e seu uso é feito principalmente em tempo de execução. Este tipo de memória é muito utilizada pelo programa para guardar variáveis que terão seus valores alterados constantemente por cálculos e outros. Ela também é utilizada como memória “temporária” já que seu acesso é sempre mais rápido se comparado aos tipos ROM's. Porém sua principal característica é a perda dos dados quando retirada a alimentação. Esta característica faz com este tipo de memória receba a designação de “memória volátil”.

Assim, uma memória ROM é toda aquela que é utilizada apenas para leitura em tempo de execução e é não volátil, enquanto que uma memória RAM pode ser lida e/ou escrita em tempo de execução e é volátil.

Obs.: É importante compreender que tudo o que foi dito até o momento refere-se aos tipos de memória e não aos tipos de dispositivos. Por isso pode-se comparar uma memória FLASH, em tempo de execução, a uma memória ROM.


MEMÓRIA EEPROM

Uma memória tipo EEPROM (Eletrically Erasable Programmable – Read Only Access) é na uma memória do tipo ROM, também não volátil, mas que pode ser escrita também em tempo de execução, se necessário. E hoje em dias a grande maioria das EEPROM's não requerem tensões muito altas para serem escritas e ou apagadas. Atualmente existem versões que trabalham com 5VDC e até 3,3V (também conhecidas como Low Voltage). Desta forma é possível apagar/escrever um byte ou conjunto deles em tempo de execução.

Uma outra característica muito importante deste tipo de memória é que ela pode ser lida infinitas vezes, mas só pode ser escrita um determinado número de vezes. Geralmente são os fabricantes que determinam o ciclo de escrita destas memórias, mas ela é da ordem de 100.000 a 1.000.000 de vezes.

Um outro detalhe também muito importante é que em uma memória EEPROM pode-se ler, escrever ou apagar um único espaço de memória. Não há nenhum tipo de controle quanto a “blocos” ou “bancos” (espaços contínuos de memória, maiores que um byte). Fazendo uma comparação bastante simplória, seu uso seria então exatamente igual a de uma memória RAM, porem com a caracerística não volátil da memória ROM.

A memória EEPROM é utilizada sempre que se faz necessário guardar um determinado dado (ou conjunto deles) que não poderá ser perdido quando for retirada a alimentação da memória, e este deverá ser alterado posteriormente num próximo período de execução.


A MEMÓRIA EEPROM INTERNA DOS MICROCONTROLADORES AVR

A família de microcontroladores AVR AT90S, ATtiny e ATMEGA AVR da ATMEL possui em seu interior uma memória EEPROM com tamanho que pode variar de 64B (64 bytes) a 4 kB (4096 bytes), dependendo do modelo. No ATtiny11 e AT90S1200 o tamanho disponível da memória EEPROM é de 64B, enquanto que nas versões ATMEGA128 e ATMEGA2560 é de 4 kB. Porém seu uso é sempre similar e tudo o que for descrito para um modelo da família poderá ser aplicado a outro sem maiores problemas.


REGISTRADORES EECR, EEARH/L E EEDR

Uma grande vantagem do uso de uma memória interna ao microcontrolador está no seu controle e velocidade de acesso, já que não é necessário utilizar um barramento externo para controle, leitura ou escrita.

Para utilizar a memória interna EEPROM nos microcontroladores AVR é necessário compreender o funcionamento de alguns registradores importantes no seu controle. São eles:


  • Registrador EECR (Registro de controle do microcontrolador para memória);

  • Registrador EEAR(H/L) (Registro para acesso a um endereço na memória – 1 ou 2 bytes);

  • Registro EEDR (Registro para escrita/leitura do dado na memória).



REGISTRADOR EECR

Na tabela a seguir é dada a descrição do registrador EECR – EEPROM Control Register, a posição de cada um dos bit's de controle, o valor inicial dos mesmos durante o reset do microcontrolador e a descrição de suas funções.

Tabela – Registrador EECR

Bit

7

6

5

4

3

2

1

0

-

-

-

-

EERIE

EEMWE

EEWE

EERE

Leitura/

Escrita

R

R

R

R

R/W

R/W

R/W

R/W

Valor

Inicial

0

0

0

0

0

0

X

0


EERIE – EEPROM Ready Interrupt Enable

0 – desabilita INT que avisa sobre EEPROM pronta;
1 – habilita INT que avisa sobre EEPROM pronta.

Obs.: Se EEWE for igual a “0”, e EERIE igual a “1”, INT gerada initerruptamente.


EEMWE – EEPROM Master Write Enable

0 – desabilita escrita na EEPROM;
1 – habilita escrita na EEPROM.


EEWE – EEPROM Write Enable

0 – não executa a escrita do dado no endereço especificado;
1 – executa a escrita do dado no endereço especificado.

Obs.: Os outros bit's (4 a 7) são reservados e lidos sempre como “0”.



REGISTRADORES EEARH E EEARL

A tabela abaixo mostra a descrição dos registradores EEAR(H) e EEAR(L) – EEPROM Address Register HIGH and LOW e estes podem ser tratados apenas como EEAR (16 bits) se o compilador for para Linguagem C. Compiladores Assembler não permitem este tipo de abreviação e você deverá utilizar os registradores de maneira independente EEARH e EEARL. 

A função deste registrador é conter a posição de memória (endereço) onde o dado será escrito ou lido. O mesmo possui 9 bits o que permite endereçar até 512 posições na memória (de 0 a 511) para a versão ATMEGA16, por exemplo. Para as versões com até 4 kBytes são 12 bits o que perminte endereçar 4096 posições de memória (0 a 4095).

Tabela – Registros EEARL e EEARH

Bit High

15

14

13

12

11

10

9

8

-

-

-

-

-

-

-

EEAR8

Leitura/

Escrita

R

R

R

R

R

R

R

R/W

Valor

Inicial

0

0

0

0

0

0

0

x










Bit Low

7

6

5

4

3

2

1

0

EEAR7

EEAR6

EEAR5

EEAR4

EEAR3

EEAR2

EEAR1

EEAR0

Leitura/

Escrita

R/W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

Valor

Inicial

x

X

x

x

x

x

x

X

Obs.: Nos microcontroladores AVR com memória EEPROM inferior a 512 Bytes este registrador tem apenas 8 bits e recebe o nome de EEAR apenas e assim deverá ser usado, tanto em C como em Assembler.


REGISTRADOR EEDR

Abaixo na tabela, é dada a descrição do registrador EEDR – EEPROM Data Register. A função deste registrador é conter o byte a ser escrito ou receber o byte lido de uma posição qualquer de memória (endereço) da EEPROM.

Tabela – Registro EEDR

Bit High

7

6

5

4

3

2

1

0

MSB







LSB

Leitura/

Escrita

R/W

R/W

R/W

R/W

R/W

R/W

R/W

R/W

Valor

Inicial

0

0

0

0

0

0

0

0



EXEMPLOS - ROTINAS DE CONFIGURAÇÃO E USO

Agora que já foram descritos os registradores, vamos exemplificar a sua configuração e uso para acesso a memória EEPROM interna nos microcontroladores AVR.

A primeira coisa a fazer é determinar se você deseja ou não usar a interrupção da memória EEPROM, já que está é a única configuração que se faz necessária e se for assim você deve apenas “setar” o bit EERIE e manter EEWE em “0”, para gerar a interrupção de maneira constante.

Esta interrupção atua da seguinte maneira: toda vez que for solicitada um procedimento de escrita em um determinado endereço e a memória o fizer, uma interrupção será ativada para avisar o usuário que a mesma está pronta para um próximo processo. Esse tipo de uso é importante caso o programa tenha outros processamentos mais importantes a fazer do que aguardar a escrita na memória EEPROM, já que ela consome alguns ciclos de máquina. Porém, geralmente “processamentos mais importantes” devem ser feitos sempre que possível por interrupção e sendo assim, aguardar o processamento da escrita (durante alguns poucos ciclos) não afetará em nada o desempenho do programa. Se você escolher utilizar a interrupção através da ativação do bit EERIE deverá ativar também o bit “I” no registrador SREG (Status REG) que habilita o uso de qualquer interrupção no AVR.

Em meus exemplos não utilizaremos a interrupção, pois como dito, anteriormente, na maioria dos casos a espera de alguns ciclos para a realização da escrita na EEPROM não causará perda de performance do programa.


LENDO A MEMÓRIA EEPROM DO AVR

Para ler na memória EEPROM interna do AVR você deve apenas se certificar que não há nenhuma escrita em andamento, inserir no registrador EEAR o endereço a ser lido e “setar” o bit EEWE. Quando isso for feito, o dado presente no endereço desejado será inserido no registrador EEDR. Veja abaixo um exemplo.

Função para leitura de um byte na memória EEPROM do AVR

//********************************************************************************
// read_Byte_EEPROM - lê um byte na EEPROM
//
// Entradas - endereço a ser lido (inteiro)
// Saídas - dado lido (byte)
//********************************************************************************

byte read_Byte_eeprom(uint16 end){

    if (end > TAM_EEPROM)  //se tamanho da EEPROM excedido, retorna 0x00
        return 0x00;

    while(EECR & (1<<EEWE))    //verifica se escrita em andamento
        ;                      //aguarda atéliberar

    EEAR = end;            //carrega registro com endereço a ser lido
    EECR |= (1<<EERE);     //seta bit no registro de controle p/ habilitar leitura
    return EEDR;           //retorna dado lido

}


A função acima demonstra o que foi dito anteriormente e traz ainda uma implementação. Logo no primeiro teste condicional (if) é testado o tamanho da EEPROM. Isso é muito importante, pois permite que a mesma função seja utilizada com diferentes versões do AVR, independente do tamanho da sua memória. Basta que você insira no começo de seu programa uma diretriz de pré-compilação definindo o tamanho máximo da memória EEPROM. Veja a seguir como fazê-lo:


#define TAM_EEPROM 512 //define 512 bytes máximos para a EEPROM

Obs.: O teste do tamanho da EEPROM é opcional e pode ser retirado da função sem maiores problemas, caso você assim deseje.

Nas linhas seguintes, um laço é implementado para verificar se há uma escrita em andamento (repare que é testado se o bit EEWE, no registrador EECR, é igual a zero). Isto pode parecer bobagem, mas caso você tenha que implementar um procedimento de escrita em uma outra sub-rotina qualquer e que possa ser ativada por uma interrupção, este laço será importante já que evitará uma “colisão” de um dado de entrada com um de saída.

Logo abaixo, nas linhas da função, o endereço que se deseja ler é colocado no registrador de endereços (EEAR) e então o bit EERE, no registrador EECR, é “setado”. Isso habilita a leitura na EEPROM e coloca no registrador EEDR o dado presente no endereço inserido no registrador EEAR.


ESCREVENDO NA MEMÓRIA EEPROM DO AVR

O processo de escrita na EEPROM é bastante similar ao processo de leitura. Veja um exemplo abaixo.

Função para escrita de um byte na memória EEPROM do AVR

//********************************************************************************
// write_B_EEPROM - escreve um byte na EEPROM
//
// Entradas - endereço (inteiro) e dado (byte) a ser escrito
// Saídas - nenhuma
//********************************************************************************

void write_B_eeprom(byte dado, uint16 end){

    if (end > TAM_EEPROM)                 //se tamanho da EEPROM excedido, retorna sem gravar
        return;

    while(EECR & (1<<EEWE))               //verifica se escrita em andamento
        ;                                 //aguarda atéliberar

    EEAR = end;                           //carrega registro com endereço
    EEDR = dado;                          //carrega registro com dado a ser escrito
    EECR |= (1<<EEMWE);                   //habilita lógica de escrita
    EECR |= (1<<EEWE);                    //habilita a escrita na EEPROM

}

Note que mais uma vez a função testa o tamanho da EEPROM, e se há alguma escrita em andamento. Somente quando o bit EEWE, no registrador EECR, for igual a zero é que o laço é finalizado. As diferenças básicas entre as rotinas de leitura e escrita começam nas linhas abaixo ao laço. Agora os registradores de endereço (EEAR) deve conter o endereço onde se deseja “guardar” o dado e o registrador (EEDR) o dado a ser escrito. Então basta “setar” os bits de controle de habilitação (EEMWE) e escrita (EEWE), nesta ordem. Com isso feito a rotina retorna para onde foi chamada e o AVR se encarrega de realizar a escrita, consumindo em background os ciclos de máquina necessários para tal.


MAIS ALGUMAS DICAS

Você deve ter percebido que é bem simples lidar com a memória EEPROM do AVR. As rotinas passadas permitem a leitura e/ou escrita de um byte. Porém em alguns casos você terá a necessidade de ler e escrever um inteiro com 16 bits ou mesmo um inteiro longo com 32 bits. Nestes casos basta utilizar as funções apresentadas, com chamada indireta por uma outra função. Veja nos box abaixo exemplos de leitura e escrita, respectivamente, para um inteiro de 16 bits.

Função para leitura de um inteiro de 16 bits (2 bytes) na memória EEPROM do AVR

//********************************************************************************
// read_I_EEPROM - lê um inteiro na EEPROM (dois bytes)
//
// Entradas - endereço inicial (inteiro)
// Saídas - dado lido (inteiro)
//********************************************************************************

uint16 read_I_eeprom(uint16 end){

    uint16 aux1 = 0;                         //variável auxiliar
    aux1 = (read_B_eeprom(end) & 0xFFFF);    //aux1 = xxxx xxxx EEEE EEEE
    aux1 = aux1 << 8;                        //aux1 = EEEE EEEE xxxx xxxx
    aux1 |= (read_B_eeprom(end+1) & 0xFF);   //aux1 = EEEE EEEE EEEE EEEE
    return aux1;                             //retorna inteiro (dois bytes lidos)

}

Função para escrita de um inteiro de 16 bits (2 bytes) na memória EEPROM do AVR

//********************************************************************************
// write_I_EEPROM - escreve um inteiro na EEPROM (dois bytes)
//
// Entradas - endereço (inteiro) e dado (inteiro) a ser escrito
// Saídas - nenhuma
//********************************************************************************

void write_I_eeprom(uint16 dado, uint16 end){

    write_B_eeprom(((dado>>8)&0xFFFF), end); //escreve parte MSB
    write_B_eeprom((dado&0xFF), end+1);   //escreve parte LSB uma posição á frente

}

Seguindo o mesmo raciocínio você poderá implementar funções para ler/escrever um inteiro longo com 32 bits, uma string com “n” caracteres, etc. Cada um poderá aplicando um pouco de lógica de programação chegar as suas próprias soluções formando, assim, uma biblioteca bastante diversa para o uso da memória EEPROM do AVR.


ALGUNS CUIDADOS

Você deve ter em mente que a memória EEPROM do AVR está interna ao mesmo e sendo assim depende da alimentação do microcontrolador. Uma corrupção dos dados pode ocorrer durante um longo período de VCC low (alimentação abaixo do tolerável) para o microcontrolador. Para evitar problemas com corrupção de dados na EEPROM, ative o BOD (Brown Out Detection). Isto permitirá que o microcontrolador mantenha-se em reset até que a alimentação volte ao normal.

Uma outra saída é ativar o modo sleep sempre que for detectado um longo período de VCC low. Assim evita-se a perda de dados por corrupção. Em alguns casos é melhor deixar de “gravar” os dados do que “gravá-los” com erro. Desta forma se, durante uma verificação dos dados, for notado que o equipamento deixou de gravá-los, será mais fácil detectar o problema. Caso contrário o projetista irá buscar o problema em outro ponto do circuito, como canais AD's e outros, que nada têm haver com o erro.


CONCLUSÃO

A cada parte apresentada mais informações sobre o microcontrolador AVR foram acrescentadas. Em breve trarei outras informações sobre o controle dos registradores e conseqüentemente o uso dos seus periféricos. Desta vez, vou me concentrar na parte de comunicação. Aguarde!


Datasheets recomendados:

ATtiny11/12 - http://www.atmel.com/dyn/resources/prod_documents/doc1006.pdf
ATMEGA16 - http://www.atmel.com/dyn/resources/prod_documents/doc2466.pdf
ATMEGA128 - http://www.atmel.com/dyn/resources/prod_documents/doc2467.pdf
ATMEGA2560 - http://www.atmel.com/dyn/resources/prod_documents/doc2549.pdf



Este artigo foi publicado, com minha autorização, na revista Eletrônica Total nrº 133 de Outubro de 2008.



Copyright deste conteúdo reservado para Márcio José Soares e protegido pela Lei de Direitos Autorais LEI N° 9.610, de 19 de Fevereiro de 1998. É estritamente proibida a reprodução total ou parcial do conteúdo desta página em outros pontos da internet, livros ou outros tipos de publicações comerciais ou não, sem a prévia autorização por escrito do autor.