MEMÓRIA EEPROM
PROGRAMAÇÃO C PARA MICROCONTROLADORES
8051 E PIC
PALAVRAS-CHAVE
Microcontrolador 8051, Microcontrolador PIC 16F877A, Memória EEPROM E2PROM
MEMORIA EEPROM
Nesta unidade de estudos vamos conhecer o funcionamento e a aplicação da memória EEPROM necessária para salvar informações antes do circuito ser desligado. Este componente tem como finalidade salvar e recuperar valor de variáveis, senhas, configurações do sistema, etc.
As variáveis são alocadas na memória RAM do microcontrolador e quando o sistema é desligado esses dados de execução são perdidos. Muitas aplicações não podem perder os dados de execução, isto é, o sistema precisa retomar o funcionamento a partir do ponto em que estava antes da queda de energia, com a mesma senha, os mesmos ajustes, por exemplo. Por isso, a memória EEPROM se torna alternativa para reter os dados do sistema e recupera-la, mesmo que o sistema permaneça desligado por anos.
METODOLOGIA
Serão analisadas as características técnicas da EEPROM 24C02, uma memória externa ao 8051, bem como o ciclo de vida útil e a estrutura de armazenamento organizada em índices ou posições de memória. Para comunicação entre o 8051 e a EEPROM será aplicado o protocolo de comunicação I2C abordado em seus aspectos gerais e a biblioteca i2c.h proverá operações de leitura e escrita na memória cujos dados poderão ser conferidos no mapa de memória em nível de simulação. Para salvar dados maiores do que 1 Byte a variável será quebrada em partes menores para tornar o seu armazenamento possível.
A abordagem desta unidade de estudos utiliza a programação C para o microcontrolador AT89S52, da família MCS-51 ou popularmente chamada de “8051” de modo que as informações salvas na EEPROM 24C02 sejam exibidas em display LCD 16x2.
Depois a atividade é realizada com a programação C para o microcontrolador PIC16F877A da família PIC 16F que dispõe de memória EEPROM interna.
RECURSOS
Para desenvolver e experimentar este material é necessário um computador ou notebook com sistema operacional Windows ou posterior e baixar o pacote de programas disponível em www.u8051.com.br o qual contém as seguintes ferramentas de desenvolvimento: Compilador 8051, Compilador PIC, Software de simulação Proteus-ISIS.
AVALIAÇÃO DE DESEMPENHO
A secção ATIVIDADES PROPOSTAS apresenta um conjunto de exercícios de dificuldade incremental a fim de avaliar o seu entendimento a cerca desta unidade de estudos.
RESULTADOS ESPERADOS
Ao concluir o desenvolvimento das atividades propostas, você deverá ser capaz de utilizar a memória EEPROM para salvar dados nestes modelos de microcontroladores.
MEMÓRIA EEPROM
1. A MEMÓRIA EEPROM 24C02
As memórias EEPROM (Electrically-Erasable Programable Read- Only Memory) são geralmente utilizadas em projetos que é necessário salvar informações em tempo de execução (running time) do sistema após o seu desligamento, como por exemplo:
contagens, parâmetros do sistema, senhas, entre outros dados.
Figura 1: Memoria EEPROM Figura 2: Memoria Pinagem EEPROM
Nesta unidade de estudos utilizaremos a memória EEPROM 24C02 com espaço de armazenamento de 256 Bytes. As memórias da família 24Cxx têm vida útil de mais de um milhão de ciclos de escrita e leitura. Cada ciclo de escrita ocorre ao salvar um dado, já o de leitura acontece quando buscamos o dado salvo na memória.
1.2
ARMAZENAMENTO DE DADOS
A memória EEPROM armazena dados de 8 bits, que na linguagem C é o tipo char. A exemplo do modelo 24C02, são 256 (0 a 255) espaços de armazenamento de 1 byte (8 bits). Cada espaço é um índice ou endereço de memória. Podemos então dizer que a memória 24C02 têm 256 índices de memória.
A tabela 1 ilustra os índices da memória EEPROM.
0 1 2 3
4 5 6 7
(dado: 25)
8 9 ... 255
Tabela 1: Espaços de memória identificados por Indice ou endereço
Quando desejamos escrever um dado, ou seja, salvar uma variável na EEPROM, devemos especificar o índice em que o dado será salvo. Por exemplo, na tabela 1 o valor 25 foi salvo no índice 7 da EEPROM. Essa informação fica retida no chip mesmo quando ele for desligado, e segundo a fabricante ATMEL, o tempo de armazenamento é 100 anos.
Já para ler um dado, isto é, recuperar uma informação salva, devemos especificar o mesmo índice em que a informação foi armazenada. Por exemplo, ao ler o índice 7 da tabela 1 a EEPROM fornecerá o valor “25”. Tanto as operações de escrita quando de leitura diminuem 1 ciclo de vida do chip no total de 1 milhão de acessos.
1.3
COMUNICAÇÃO I
2C
O protocolo de comunicação I2C foi orginalmente desenvolvido pela Phillips que utiliza apenas duas vias para comunicação entre componentes que têm suportam ao protocolo. As vias são:
SDA (serial data) e SCL (serial clock).
Cada componente I2C tem seu ID especificado pelo fabricante.
A EEPROM 24C02 têm ID 160, enquanto que o RTC DS1307 tem ID 208. É possível “pendurar” vários dispositivos I2C nas linhas SDA e SCL (ou barramento I2C) como demonstrado na figura 3.
Observe que os dispositivos conectados ao barramento I2C estão conectados em paralelo, então o ID é que distingue qual componente está realizando a comunicação. O código a seguir demonstra a leitura do índice 7 na EEPROM 24C02:
#define EEPROM 160 char cont;
cont=read_i2c(EEPROM, 7);
Após execução destes comandos char cont receberá o valor 25 se considerarmos a tabela 1. A função read_i2c conecta ao dispositivo escolhido (EEPROM) e executa a operação de leitura do índice determinado.
A seguir temos o código que salva a variável char cont no índice 7 da EEPROM:
#define EEPROM 160 char cont=26;
save_i2c(EEPROM, 7, cont);
A função save_i2c conecta ao dispositivo escolhido (EEPROM) e executa a operação de escrita no índice determinado. Por isso, o valor 26 será salvo no índice 7 da memória, substituindo o valor anterior.
1.3
A BIBLIOTECA i2C.H
É encontrada na pasta 8051\SDCC\INCLUDE que implementa as seguintes definições e funções do protocolo de comunicação I2C:
Comando Efeito
#define scl Px_y Define o I/O do 8051 conectado à linha SCL
#define sda Px_y Define o I/O do 8051 conectado à linha SDA save_i2c(ID, índice,
variável); Salva variável no índice do ID escolhido variável=read_i2c(ID,
índice); Busca dado salvo no índice do ID escolhido Tabela 2: Funções da biblioteca i2ch
1.4
ESQUEMÁTICO 8051 E EEPROM 24C02 COM DISPLAY LCD
O circuito da figura 4 apresenta a conexão entre o microcontrolador e a memória. O Display LCD será utilizado para exibir as operações de leitura e escrita na EEPROM.
Este esquema foi desenhado no software Proteus-ISIS versão 7 ou posterior utilizando os seguintes componentes:
Referência Componente
AT89C52 Microcontrolador AT89C52 24C02C Memória EEPROM 24C02 LM016L Display LCD 16x2 BUTTON Botões AVANCAR e SOMAR Fig. 3: Dois
dispositivos conectados na rede I2C
ID 160 ID 208
Barramento I2C
Fig. 4: Conexão do microcontrolador e a EEPROM
índice
MEMÓRIA EEPROM // Programa 1
Uma versão portátil deste simulador pode ser executada a partir da pasta 8051\ISIS77\BIN\ISIS.EXE
Figura 5: Executando o Simulador ISIS
A biblioteca i2.h implementa o protocolo I2C no 8051 através dos I/O’s escolhidos no comando #define que deve constar antes da biblioteca i2c.h. Já a comunicação com o LCD é fornecida na biblioteca lcd.h. Você verá a declaração destas bibliotecas na seguinte ordem:
#include<at89x52.h>
#include<lcd.h>
#define sda P2_6
#define scl P2_7
#include<i2c.h>
1.5
PROGRAMAÇÃO
A seguir temos a programação para o circuito da figura 4: ela permite ajustar os parâmetros de um temporizador: horário de ligar e o horário de desligar um dispositivo elétrico. Na figura 6 temos um exemplo de execução o programa.
#include<at89x52.h>
#include<lcd.h>
#define AVANCAR P3_2
#define SOMAR P3_3
#define EEPROM 160
#define sda P2_6
#define scl P2_7
#include<i2c.h>
bit b1, b2;
char horaL, minL, horaD, minD, pos;
void atualizaLCD();
void atualizaCursor();
void leMemoria();
//******************************
void main() { leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1) {
if(SOMAR==0 && b1==0) {
b1=1;
if(pos==0) {
horaL++;
save_i2c(EEPROM, 0, horaL);
}
if(pos==1) minL++;
atualizaLCD();
}
if(SOMAR==1) b1=0;
if(AVANCAR==0 && b2==0) {
b2=1;
pos++;
if(pos>1) pos=0;
atualizaCursor();
}
if(AVANCAR==1) b2=0;
}
} //******************************
void atualizaLCD() { lcd_cursor(0);
lcd_gotoxy(1,1);
lcd_puts("LIG ");
lcd_putchar(horaL/10+48);
lcd_putchar(horaL%10+48);
lcd_putchar(':');
lcd_putchar(minL/10+48);
lcd_putchar(minL%10+48);
lcd_gotoxy(2,1);
lcd_puts("DES ");
lcd_putchar(horaD/10+48);
lcd_putchar(horaD%10+48);
lcd_putchar(':');
lcd_putchar(minD/10+48);
lcd_putchar(minD%10+48);
atualizaCursor();
} //******************************
void atualizaCursor()
{ if(pos==0) lcd_gotoxy(1,6);
if(pos==1) lcd_gotoxy(1,9);
lcd_cursor(1);
} //******************************
void leMemoria()
{ horaL=read_i2c(EEPROM, 0);
}
Você pode revisar a unidade de estudos DISPLAY LCD para entender as instruções lcd_init. lcd_gotoxy, lcd_putchar, lcd_puts e lcd_cursor, por isso, essas instruções não serão aqui detalhadas. Nosso foco será a EEPROM.
As variáveis horaL, minL, horaD e horaL armazenam o horário de início e fim da temporização, sendo horas até o limite de 23 e minutos até 59. Por isso, o tipo char tendo alcance de 0 até 255 atende bem à essa aplicação, além de ser também o tipo de dado que pode ser salvo e lido no índice da memória EEPROM.
Observe que na função main ocorre um “salto” para a função leMemoria. A função read_i2c (ou read_eeprom no PIC) busca na EEPROM o dado salvo no índice 0 e atribui à variável char horaL.
Executado o último comando da função leMemoria ocorre então o
“retorno” para a função main em que as demais funções são executadas. A variável char pos é referência ao item que o usuário está ajustando na tela, conforme a figura 7.
Por isso, o programa toma como referência a variável pos para posicionar o cursor piscante, bem como determinar qual variável deve ser somada que leva a dedução da tabela 4.
Valor de
pos Variável alterada no
botão SOMAR Posição do cursor pos==0 horaL (hora ligar) (1,6) pos==1 minL (minutos ligar) (1,9) pos==2 horaD (hora desligar) (?,?) pos==3 mind (minutos desligar) (?,?) Tabela 4: Variável pos é referência de ajustes e de posição do cursor
1.6 EVENTO CLIQUE PARA SOMAR
O programa 1 usa-se a lógica do evento pressionar-e-soltar o botão para evitar a repetição. A variável b1 registra o instante em que o botão SOMAR é pressionado. Por exemplo, quando SOMAR é pressionado, a variável horaL é aumenta uma unidade (+1) e o seu valor é salvo no índice 0 da EEPROM, pelo comando save_i2c (ou write_eeprom no PIC) e então b1 recebe 1. Não importa agora que SOMAR ainda esteja pressionado, pois o teste b1==0 é falso e não ocorre mais a repetição dos comandos deste if. Porém, ao soltar este botão, a variável b1 retorna a 0, podendo então, registrar um novo clique.
Observe que o valor de horaL é salvo no índice 0 e que a função leMemória, busca o valor salvo neste mesmo índice. Cada variável deve ser salva e lida em seu índice exclusivo. Para salvar a variável minL, poderíamos especificar o índice 1 por exemplo.
1.7 VISUALIZANDO OS DADOS DA EEPROM Durante a simulação é possível
visualizar os dados armazenados na memória EEPROM. Para isso, basta pausar a simulação e acessar o menu Debug>I2C Memory Internal Memory.
1.8 APAGANDO OS DADOS DA EEPROM Para limpar o conteúdo da EEPROM encerre simulação e exclua o componente 24C02C do esquemático. Em seguida adicione o componente novamente e refaça as conexões da figura 4.
1.9
VETORES EM C
Um vetor é uma variável indexada, ou seja, comporta várias informações do tipo de dado em que o vetor é declarado.
Considere a seguinte declaração:
char num[11]={5,1,9,9,6,0,2,6,8,9,7};
O vetor mensagem têm 11 índices e cada um armazena um dado do tipo char. Assim temos que o vetor tenha os índices 0 ao 11 conforme o exemplo ao lado.
As variáveis indexadas armazenam valores do mesmo tipo. Esses dados armazenados são denominados itens do vetor.
//Fig. 6: Tela do programa 1
pos=0 pos=1
pos=2 pos=3 Fig. 7
Fig. 8: Visualizando os dados da EEPROM
num[0]=5;
num[1]=1;
num[2]=9;
num[3]=9;
num[4]=6;
num[5]=0;
num[6]=2;
num[7]=6;
num[8]=8;
num[9]=9;
num[10]=7
MEMÓRIA EEPROM
// Programa 3 1.10
SALVANDO NUMERO DE
TELEFONE E NOME (8051)
O programa 2 faz uso do vetor num que armazena o número telefônico de 9 dígitos e do vetor nome para armazenar o nome de quem atende no respectivo número.
A figura 9 ilustra a tela de execução do programa.
#include<at89x52.h>
#include<lcd.h>
#define AVANCAR P3_2
#define SOMAR P3_3
#define EEPROM 160
#define sda P2_6
#define scl P2_7
#include<i2c.h>
bit b1, b2;
char pos, num[8]={5,1,9,9,6,0,2,6};
char nome[4]={'C','A','S','A'};
void atualizaLCD();
void atualizaCursor();
void leMemoria();
//******************************
void main(){
leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1){
if(SOMAR==0 && b1==0){
b1=1;
if(pos<8){
num[pos]++;
save_i2c(EEPROM, pos, num[pos]);
} else{
nome[pos-8]++;
if(nome[pos-8]>'E') nome[pos-8]='A';
save_i2c(EEPROM, pos, num[pos-8]);
}
atualizaLCD();
}
if(SOMAR==1) b1=0;
if(AVANCAR==0 && b2==0){
b2=1;
pos++;
if(pos>11) pos=0;
atualizaCursor();
}
if(AVANCAR==1) b2=0;
}
} //******************************
void atualizaLCD(){
char i;
lcd_cursor(0);
lcd_gotoxy(1,1);
for(i=0;i<8;i++) lcd_putchar(num[i]+48);
lcd_gotoxy(2,1);
for(i=0;i<4;i++) lcd_putchar(nome[i]);
atualizaCursor();
} //******************************
void atualizaCursor(){
if(pos<8)lcd_gotoxy(1,pos+1);
else lcd_gotoxy(2,pos-7);
lcd_cursor(1);
} //******************************
void leMemoria(){
//comandos para ler os dados da EEPROM }
Vamos analisar o funcionamento do Programa 2 considerando que você já observou a abordagem do Programa 1. Ao todo são 11 dígitos do telefone e 4 dígitos do nome, totalizando a navegação entre 15 campos de dados no LCD, tendo como referência a variável pos. Quando pos está entre 0 e 10, o botão SOMAR aumenta o valor do vetor num[pos]. Isto, é, se pos for 0 então num[0], que contém o valor 5 será somado e terá agora 6. Se pos estiver acima de 10, então o vetor nome é que deve ser alterado. Mas observe que nome tem apenas 4 índices, logo, nome[11] é inválido. Por isso, temos nome[pos-11]++. O comando for(i=0;i<8;i++) lcd_putchar(num[i]); mostra todos os 8 índices do vetor num no LCD, enquanto que for(i=0;i<4;i++) lcd_putchar(nome[i]); exibe o nome.
Lembre-se de que escolhemos armazenar apenas letras no vetor nome. Aqui os caracteres válidos são: A, B, C, D, E por que aplicamos o filtro if(nome[pos-8]>'E') nome[pos-8]='A';
2
SALVANDO TIPO DE DADO INT
Vimos que a memória EEPROM armazena dados de 8 bits (1 Byte), o tipo char em C. Mas existem aplicações em que se faz necessário salvar variáveis do tipo int que tem o tamanho de 2 Bytes, ou seja, o dobro do tipo char. Por isso, é necessário quebrar a variável int em partes que resultem em duas variáveis do tipo char. Para ilustrar considere a variável int cont.
int cont=300;
O valor máximo de 1 Byte é 255. Então o valor 300 não pode ser armazenado em um único índice da EEPROM. É necessário
“quebrar” a variável int em duas variáveis char com a operação de divisão “/” e módulo “%” que obtém o resto da divisão.
cont=300;
cont/256; → 300/256 = 1,17 cont%256; → 300%256 = 44
Para salvar cont/256 no índice 0 e cont%256 no índice 1 aplicamos os seguintes comandos:
save_i2c(EEPROM, 0, cont/256);
save_i2c(EEPROM, 1, cont%256);
Teremos os seguintes dados na memória:
0 1 (01) 1 44 (2C) 2 3
Tabela 5: valor 300 salvo dos índices 0 e 1 da EEPROM
Para recuperar o dado é preciso ler e “unir” os valores destes mesmos índices aplicando multiplicação “*” e soma “+”.
0 1 (01) 1 44 (2C) 2 3
Sendo 255 o valor máximo de cada índice, temos que o valor máximo armazenável com 2 índices seja:
O programa 3 utiliza os índices 0 e 1 da EEPROM para salvar a variável int cont.
#include<at89x52.h>
#include<lcd.h>
#define SOMAR P3_3
#define EEPROM 160
#define sda P2_6
#define scl P2_7
#include<i2c.h>
bit b1;
int cont=12345;
void atualizaLCD();
void exibeInt(int valor);
void atualizaCursor();
void leMemoria();
//******************************
void main(){
leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1){
if(SOMAR==0 && b1==0) {
b1=1;
cont++;
save_i2c(EEPROM, 0, cont/256);
save_i2c(EEPROM, 1, cont%256);
atualizaLCD();
}
if(SOMAR==1) b1=0;
}
} //******************************
void atualizaLCD() { lcd_cursor(0);
lcd_gotoxy(1,1);
exibeInt(cont);
atualizaCursor();
} //******************************
void exibeInt(int valor) { lcd_putchar(valor/10000+48);
lcd_putchar(valor%10000/1000+48);
lcd_putchar(valor/100%10+48);
lcd_putchar(valor/10%10+48);
lcd_putchar(valor%10+48);
} //******************************
void atualizaCursor() { lcd_gotoxy(1,5);
lcd_cursor(1);
} //******************************
void leMemoria(){
unsigned char parte2;
cont=(read_i2c(EEPROM, 0)*256);
parte2=read_i2c(EEPROM, 1);
cont=cont+parte2;
}
0 255 (FF) 1 255 (FF) 2 3
Fig. 9: Tela do programa 2
// Programa 2
x 256 +
256 + 44 = 300
x 256 +
25280 + 255 = 65535
MEMÓRIA EEPROM
TIPO DE DADO INT E UNSIGNED INT (8051) O tipo de dado int suporta faixa de valores negativos e positivos.
O tipo unsigned int opera somente com a faixa de valores positiva. Na tabela a seguir temos um comparativo de alcance destes dois tipos de dados:
Tipo de dado Alcance de armazenamento
int -32768 a +32767
unsigned int 0 a 65535
Tabela 8: Diferença entre o tipo de dado int e unsigned int
A função exibeInt(tipo de dado) recebe como parâmetro o valor a ser exibido no LCD. O tipo de dado declarado na função deve ser do mesmo tipo de dado da variável que desejamos exibir.
Considere:
char cont=100;
O parâmetro da função deverá ser: exibeInt(char variável) unsigned char cont=200;
Parâmetro da função: exibeInt(unsigned char variável) int cont=20000;
O parâmetro da função deverá ser: exibeInt(int variável) unsigned int cont=60000;
Parâmetro da função: exibeInt(unsigned int variável) long int cont=90000;
Parâmetro da função: exibeInt(long int variável) Ao ler uma posição de memória você dever armazenar o dado numa variável que tenha essa capacidade de armazenamento.
Por exemplo, a variável parte2 do exemplo abaixo deverá ser do tipo int parte2:
int parte2=0;
parte2=(read_i2c(EEPROM, 0)*256);
FLAG CARRYOUT
Quando tentamos executar uma operação de soma cujo resultado seja maior do que a capacidade de armazenamento da variável de destino, ocorre o “vai um” ou carryout flag (CY).
Esta unidade de estudos não mergulha neste nível de segurança para validação de operações com variáveis, mas caso nos sistemas críticos e sem tolerância a falhas, é importante tratar essa flag após cada operação com variáveis para identificar se ela comportou o valor atribuído. O trecho a seguir soma a variável cont e o comando seguinte verifica se a flag CY está em 0, detectando erro na operação.
cont++;
if(CY==0) {
lcd_gotoxy(2,1);
lcd_puts("Carry Out Flag");
}
Considere int cont=32767, se ocorrer uma operação de adição cont++ o evento carryout será ativado e a flag CY recebe 0, por que o tipo de dado int não tem espaço suficiente para alocar o valor 32768. Um modo simples de evitar o evento carryout é verificar a flag CY.
ATIVIDADES PROPOSTAS 1
▪ Desenhar o esquemático da figura 4 no software simulador ISIS;
▪ Digitar e compilar o programa 1 no software JFE;
▪ Simular o programa 1 no software ISIS;
▪ Ampliar os ajustes das horas e minutos de todos os 4 campos da figura 7, limitando as horas e minutos em valores válidos.
▪ Salvar e recuperar os 4 campos de ajuste de tempo;
2
▪ Digitar e compilar o programa 2 no software JFE;
▪ Simular o programa 2 em conjunto com o esquemático da figura 4;
▪ Simular o programa 6 em conjunto com o esquemático da figura 2;
▪ Os dígitos válidos do telefone devem ser somente 0 a 9;
▪ Ampliar o número de telefone para 11 dígitos;
▪ Ampliar o tamanho do nome de 4 para 8 letras que contemplem somente o alfabeto maiúsculo;
▪ Salvar e recuperar todas as informações personalizáveis do usuário (telefone e nome);
3
▪ Digitar e compilar o programa 3 no software JFE;
▪ Simular o programa 3 em conjunto com o esquemático da figura 4;
▪ Acrescentar a tecla zera conectada em P3.4;
▪ Alterar o contador para suportar contagens até 50000, bem como salvar essa faixa de valores;
▪ Desafio supremo: exibir e salvar contagem até 99999 utilizando a variável long int cont. Dica: você usará os valores /65536, /256%256 para salvar o dado em 3 posições da memória EEPROM.
3. A MEMÓRIA EEPROM DO PIC
O microcontrolador PIC16F877A dispõe de EEPROM interna com capacidade de 256 Bytes. Isto significa que não precisamos conectar uma EEPROM externa para salvar os dados a exemplo do que ocorre no 8051. É claro que existem variações do 8051 que dispõe de EEPROM bem como há modelos do PIC que não tem essa memória interna.
3.1
ARMAZENAMENTO DE DADOS
A memória EEPROM armazena dados de 8 bits, que na linguagem C é o tipo char. No PIC16F877A são 256 (0 a 255) espaços de armazenamento com 1 byte (8 bits) cada. Um espaço é um índice ou endereço de memória. Podemos então dizer que este PIC tem 256 índices de memória.
A tabela 9 ilustra os índices da memória EEPROM do PIC.
0 1 2 3
4 5 6 7
(dado: 25)
8 9 ... 255
Tabela 9: Espaços de memória identificados por Indice ou endereço
Quando desejamos escrever um dado, ou seja, salvar uma variável na EEPROM, devemos especificar o índice em que o dado será salvo. Por exemplo, na tabela 9 o valor 25 foi salvo no índice 7 da EEPROM. Essa informação fica retida no chip mesmo quando ele for desligado, e segundo a fabricante MICROCHIP, o tempo de armazenamento é mais de 40 anos.
Já para recuperar a informação salva, devemos especificar o mesmo índice em que a informação foi armazenada. Por exemplo, ao ler o índice 7 da tabela 1 a EEPROM fornecerá o valor “25”. Tanto as operações de escrita quando de leitura diminuem 1 ciclo de vida do chip no total de 1 milhão de acessos.
3.2
FUNÇÕES DE ACESSO DA EEPROM INTERNA DO PIC
O código seguir demonstra a leitura do índice 7 na EEPROM do PIC:
char cont;
cont=read_eeprom(7);
A função read_eeprom executa a operação de leitura do índice determinado. No exemplo acima ocorreu a leitura do índice 7 e a variável char cont receberá o valor 25 se considerarmos a tabela 9.
A seguir temos o código que salva a variável char cont no índice 7 da EEPROM:
char cont=26;
write_eeprom(7, cont);
A função write_eeprom executa a operação de escrita no índice determinado. Por isso, após a execução destes comandos, o valor 26 será salvo no índice 7 da memória, substituindo o valor anterior.
Temos no PIC-C Compiler as seguintes funções de acesso à EEPROM do PIC:
índice índice
MEMÓRIA EEPROM
Engenharia Curso de microcontrolador online pic 8051 programação jfe pic c compiler projeto s microgenios gravador cu scopic aeci sp progisp u sbasp atmega Arduino faculdade unisinos feevale.br pucrs ufrgs uninter faccat ita unisanta unoeste anhanguera u sp unesp feec feelt ufrj pucminas unitaumaua ime unopar fam ufsc pucpr fei fatec uninove ead uniceug Anchieta uceff ufpel rogercom ufal edu br fpb up unifacs fatecpr feitep univap fen/ufg famen dombosco utfpr cefet uni pifam senai impacta unilasalle uva uc s.br unifor upe.br unig.br ifrs.edu.br
// Programa 4
Comando Efeito
write_eeprom(índice, variável); Salva variável no índice variável=read_eeprom(índice); Busca dado
salvo no índice Tabela 10: Funções de acesso a EEPROM
3.3
ESQUEMÁTICO
PIC E DISPLAY LCD O circuito da figura 10 apresenta o microcontrolador conectado ao display LCD para que na tela sejam exibidas as operações de leitura e escrita na EEPROM.Figura 10: Conectando o display LCD 16x2 ao microcontrolador PIC16F877A
Observe que quando os I/O’s do PIC são utilizados para leitura de sensores de contato (as teclas), os I/O’s utilizados devem ser configurados como input através da função set_tris. Por isso, esses I/O’s ficam em coletor aberto ou tristate, necessitando de resistores pull up R1 e R2 para definir o estado lógico 1 destas
“entradas” de teclas. Veja a tabela 12.
Este esquema foi desenhado no software Proteus-ISIS versão 7 ou posterior utilizando os seguintes componentes:
Referência Componente
PIC16F877A Microcontrolador PIC16F877A LM016L Display LCD 16x2
BUTTON Botões AVANCAR e SOMAR
RES Resistor 4K7
Uma versão portátil deste simulador pode ser executada a partir da pasta 8051\ISIS77\BIN\ISIS.EXE
Figura 11: Executando o Simulador ISIS
A biblioteca lcd.h comunica com o PIC através do PORT B, mas esta definição pode ser editada dentro da biblioteca se necessário. Para incluir o protocolo de comunicação com o LCD você verá a declaração da biblioteca lcd.h na seguinte ordem:
#include<16F877A.h>
#fuses nowdt, nobrownout, xt, noput, noprotect
#use delay(clock=4000000)
#include<lcd.h>
3.4
PROGRAMAÇÃO
A seguir temos a programação para o circuito da figura 10: ela permite ajustar os parâmetros de um temporizador: horário de ligar e o horário de desligar um dispositivo elétrico. Na figura 12 temos um exemplo de execução o programa.
#include<16F877A.h>
#fuses nowdt, nobrownout, xt, noput, noprotect
#use fast_io(C)
#use delay(clock=4000000)
#include<lcd.h>
#define AVANCAR PIN_C1
#define SOMAR PIN_C0 int1 b1,b2;
signed char horaL, minL, horaD, minD, pos;
void atualizaLCD();
void atualizaCursor();
void leMemoria();
//******************************
void main() {
set_tris_C(0b00000111);
leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1) {
if(input(SOMAR)==0 && b1==0) {
b1=1;
if(pos==0) {
horaL++;
write_eeprom(0, horaL);
}
if(pos==1) minL++;
atualizaLCD();
}
if(input(SOMAR)==1) b1=0;
if(input(AVANCAR)==0 && b2==0) {
b2=1;
pos++;
if(pos>1) pos=0;
atualizaCursor();
}
if(input(AVANCAR)==1) b2=0;
} }
//******************************
void atualizaLCD() {
lcd_cursor(0);
lcd_gotoxy(1,1);
lcd_putc("LIG ");
lcd_putc(horaL/10+48);
lcd_putc(horaL%10+48);
lcd_putc(':');
lcd_putc(minL/10+48);
lcd_putc(minL%10+48);
lcd_gotoxy(2,1);
lcd_putc("DES ");
lcd_putc(horaD/10+48);
lcd_putc(horaD%10+48);
lcd_putc(':');
lcd_putc(minD/10+48);
lcd_putc(minD%10+48);
atualizaCursor();
}
//******************************
void atualizaCursor() {
if(pos==0) lcd_gotoxy(1,6);
if(pos==1) lcd_gotoxy(1,9);
lcd_cursor(1);
}
//******************************
void leMemoria() {
horaL=read_eeprom(0);
}
O funcionamento do programa 4 está detalhado no item 1.5, mas o compilador aqui utilizado para o PIC é o PIC-C Compiler que implementa algumas funções para manipular os I/O’s do microcontrolador. São elas:
set_tris_IO(0bXXXXXXX); //IO é o PORT (A,B,C,D ou E) No PIC você deve especificar os I/O’s que serão utilizados como entrada de dados (leitura de botões). A instrução set_tris define a direção dos dados sendo 0=Output e 1=Input.
Comando em binário literal: set_tris_C(0b00000111);
Efeito: C7, C6, C5 e C4 são saídas. C3, C2, C1 e C0 são entradas
I/O C7 C6 C5 C4 C3 C2 C1 C0
Val 0 0 0 0 0 1 1 1
Direção Saída Saída Saída Saída Saída Entr Entr Entr Tabela 12: Definido a direção dos dados do PORT C
//Fig. 12: Tela do programa 4
MEMÓRIA EEPROM
// Programa 6 A função input(IO) lê o nível lógico encontrado no I/O
especificado. A diretiva #use fast_io(IO) especifica que o programador irá decidir quais I/O’s serão entrada com o comando set_tris(IO).
3.5
SALVANDO NUMERO DE TELEFONE E NOME (PIC)
O programa 5 faz uso do vetor num que armazena o número telefônico de 9 dígitos e do vetor nome para armazenar o nome de quem atende no respectivo número.
A figura 13 ilustra a tela de execução do programa.
#include<16F877A.h>
#fuses nowdt, nobrownout, xt, noput, noprotect
#use fast_io(C)
#use delay(clock=4000000)
#include<lcd.h>
#define AVANCAR PIN_C1
#define SOMAR PIN_C0 int1 b1,b2;
char pos, num[8]={5,1,9,9,6,0,2,6};
char nome[4]={'C','A','S','A'};
void atualizaLCD();
void atualizaCursor();
void leMemoria();
//******************************
void main(){
leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1){
if(input(SOMAR)==0 && b1==0) { b1=1;
if(pos<8){
num[pos]++;
write_eeprom(pos, num[pos]);
} else{
nome[pos-8]++;
if(nome[pos-8]>'E') nome[pos-8]='A';
write_eeprom(pos, num[pos-8]);
}
atualizaLCD();
}
if(input(SOMAR)==1) b1=0;
if(input(AVANCAR)==0 && b2==0){
b2=1;
pos++;
if(pos>11) pos=0;
atualizaCursor();
}
if(input(AVANCAR)==1) b2=0;
} }
//******************************
void atualizaLCD(){
char i;
lcd_cursor(0);
lcd_gotoxy(1,1);
for(i=0;i<8;i++) lcd_putc(num[i]+48);
lcd_gotoxy(2,1);
for(i=0;i<4;i++) lcd_putc(nome[i]);
atualizaCursor();
}
//******************************
void atualizaCursor(){
if(pos<8)lcd_gotoxy(1,pos+1);
else lcd_gotoxy(2,pos-7);
lcd_cursor(1);
}
//******************************
void leMemoria(){
//comandos para ler os dados da EEPROM }
TIPO DE DADO INT16 E SIGNED INT (PIC) O tipo de dado signed int16 ou signed long suporta faixa de valores negativos e positivos. O tipo int16 ou long opera somente com a faixa de valores positiva. Na tabela a seguir temos um comparativo de alcance destes dois tipos de dados:
Tipo de dado Alcance de armazenamento signed int16 ou
signed long -32768 a +32767 int16 ou
long 0 a 65535
Tabela 13: Diferença entre o tipos de dado int16 e signed int16
2
SALVANDO TIPO DE DADO INT16
A EEPROM armazena dados de 8 bits (1 Byte), o tipo char em C.
Mas existem aplicações em que se faz necessário salvar variáveis do tipo int16 que tem o tamanho de 2 Bytes, ou seja, o dobro do tipo char. Por isso, é necessário quebrar a variável int16 em duas partes que resultem em duas variáveis do tipo char. Para ilustrar considere a variável int16 cont.
int16 cont=300;
O valor máximo de 1 Byte é 255. Então o valor 300 não pode ser armazenado em um único índice da EEPROM. É necessário
“quebrar” a variável int16 em duas variáveis char com a operação de divisão “/” e módulo “%” que obtém o resto da divisão.
cont=300;
cont/256; → 300/256 = 1,17 cont%256; → 300%256 = 44
Para salvar cont/256 no índice 0 e cont%256 no índice 1 aplicamos os seguintes comandos:
write_eeprom(0, cont/256);
write_eeprom(1, cont%256);
Teremos os seguintes dados na memória:
0 1 (01) 1 44 (2C) 2 3
Tabela 5: valor 300 salvo dos índices 0 e 1 da EEPROM
Para recuperar o dado é preciso ler e “unir” os valores destes mesmos índices aplicando multiplicação “*” e soma “+”.
0 1 (01) 1 44 (2C) 2 3
O programa 6 utiliza os índices 0 e 1 da EEPROM para salvar a variável int16 cont.
#include<16F877A.h>
#fuses nowdt, nobrownout, xt, noput, noprotect
#use fast_io(C)
#use delay(clock=4000000)
#include<lcd.h>
#define SOMAR PIN_C0 int1 b1;
int16 cont=12345;
void atualizaLCD();
void exibeInt(int16 valor);
void atualizaCursor();
void leMemoria();
//******************************
void main(){
leMemoria();
lcd_init();
atualizaLCD();
atualizaCursor();
while(1){
if(input(SOMAR)==0 && b1==0) { b1=1;
cont++;
write_eeprom(0, cont/256);
write_eeprom(1, cont%256);
atualizaLCD();
}
if(input(SOMAR)==1) b1=0;
} }
//******************************
void atualizaLCD(){
lcd_cursor(0);
lcd_gotoxy(1,1);
exibeInt(cont);
atualizaCursor();
}
//******************************
void exibeInt(int16 valor){
lcd_putc(valor/10000+48);
lcd_putc(valor%10000/1000+48);
lcd_putc(valor/100%10+48);
lcd_putc(valor/10%10+48);
lcd_putc(valor%10+48);
} //******************************
void atualizaCursor(){
lcd_gotoxy(1,5);
lcd_cursor(1);
}
//******************************
void leMemoria(){
unsigned char parte2;
cont=(read_eeprom(0)*256);
parte2=read_eeprom(1);
cont=cont+parte2;
} Fig. 13: Tela do programa 5
x 256 +
256 + 44 = 300
MEMÓRIA EEPROM A função exibeInt(tipo de dado) recebe como parâmetro o valor
a ser exibido no LCD. O tipo de dado declarado na função deve ser do mesmo tipo de dado da variável que desejamos exibir.
Considere:
char cont=200;
Então o parâmetro da função deverá ser:
exibeInt(char variável) signed char cont=100;
Então o parâmetro da função deverá ser:
exibeInt(signed char variável) signed int16 cont=20000;
O parâmetro deverá ser: exibeInt(signed int16 variável) int16 cont=60000;
O parâmetro da função deverá ser: exibeInt(int16 variável) int32 cont=90000;
Então o parâmetro da função deverá ser:
exibeInt(long int32 variável)
ATIVIDADES PROPOSTAS 1
▪ Desenhar o esquemático da figura 10 no software simulador ISIS;
▪ Digitar e compilar o programa 1 no software PIC-C Compiler;
▪ Simular o programa 4 no software ISIS;
▪ Ampliar os ajustes das horas e minutos de todos os 4 campos da figura 12, limitando as horas e minutos em valores válidos.
▪ Salvar e recuperar os 4 campos de ajuste de tempo;
2
▪ Digitar e compilar o programa 4 no software PIC-C Compiler;
▪ Simular o programa 4 em conjunto com o esquemático da figura 10;
▪ Simular o programa 5 em conjunto com o esquemático da figura 10;
▪ Os dígitos válidos do telefone devem ser somente 0 a 9;
▪ Ampliar o número de telefone para 11 dígitos;
▪ Ampliar o tamanho do nome de 4 para 8 letras que contemplem somente o alfabeto maiúsculo;
▪ Salvar e recuperar todas as informações personalizáveis do usuário (telefone e nome);
3
▪ Digitar e compilar o programa 6 no software JFE;
▪ Simular o programa 6 em conjunto com o esquemático da figura 10;
▪ Acrescentar a tecla zera conectada no PORT C2;
▪ Alterar o contador para suportar contagens até 50000, bem como salvar essa faixa de valores na EEPROM;
▪ Desafio supremo: exibir e salvar contagem até 99999 utilizando a variável long int16 cont;
CONCLUSÃO
Analisamos as características técnicas da EEPROM 24C02 e a estrutura de armazenamento organizada em índices ou posições de memória. Para comunicação entre o 8051 e a EEPROM utilizou-se o protocolo de comunicação I2C com a biblioteca i2c.h para ler e escrever dados, os quais puderam visualizados no mapa de memória em nível de simulação. Para salvar dados maiores do que 1 Byte, utilizou-se um algoritmo para quebrar a variável em partes menores para tornar o seu armazenamento possível. Com a EEPROM externa no 8051 ou a interna do PIC foi possível salvar e recuperar as informações, mesmo após o circuito ser desligado.
LICENÇA DE USO DESTE MATERIAL
Todas as informações apresentadas funcionam em nível de simulação de software. Você pode baixa-las no site www.u8051.com.br e utiliza-las de forma parcial ou integral e livremente como material didático.
Documento atualizado em 21/02/2022 16:59 Prof. Cristian M.G ([email protected])
www.u8051.com.br
ANEXOS:
BIBLIOTECA i2C.H
Nesta página você encontra a biblioteca i2c.h para o microcontrolador 8051 para implementar a comunicação entre o microcontrolador a EEPROM 24C02.
Biblioteca i2c.h para 8051
Você deve copiar o código i2c.h (logo abaixo) e colar no JFE.
Em seguida, deverá clicar no menu File >
Save As... e salvar na pasta
8051\SDCC\INCLUDE e preencher o nome i2c.h e finalmente clicar no botão Salvar.
#define TEMPO 1
//***************************************************
void delay_i2c(char i){
int j;
while(i--) for(j=0;j<30;j++);
}
//***************************************************
void start(void){
sda=1;
delay_i2c(TEMPO);
scl=1;
delay_i2c(TEMPO);
sda=0;
}
//***************************************************
void stop(){
sda=0;
delay_i2c(TEMPO);
scl=1;
sda=1;
}
//***************************************************
void write(unsigned char dat){
unsigned char i;
for(i=0;i<8;i++){
scl=0;
sda=(dat&0x80>>i)?1:0;
delay_i2c(TEMPO);
scl=1;
} }
//***************************************************
void ack(void){
scl=0;
delay_i2c(TEMPO);
sda=1;
delay_i2c(TEMPO);
scl=1;
delay_i2c(TEMPO);
scl=0;
}
//***************************************************
void noack(void){
scl=0;
delay_i2c(TEMPO);
sda=1;
delay_i2c(TEMPO);
scl=1;
}
//***************************************************
unsigned char read(){
unsigned char i,buff=0;
sda=1;
for(i=0;i<8;i++){
scl=1;
if(sda) buff|=(0x80>>i);
scl=0;
}
return buff;
}
//***************************************************
void save_i2c(char id,char addr,char ch){
start(); write(id); ack(); write(addr);
ack(); write(ch); ack(); stop();
delay_i2c(100);
}
//***************************************************
char read_i2c(char id,char addr){
unsigned char buff;
start(); write(id); ack();
write(addr); ack(); start();
write(id|1); ack(); buff=read();
noack(); stop();
return buff;
}