A memória é o lugar onde armazenamos informações ou dados. As memórias eletrônicas são circuitos usados para armazenar dados. Microcontroladores têm vários tipos de memória e quase todos os projetos ou equipamentos eletrônicos modernos as utilizam para armazenar dados. Isso é muito análogo ao cérebro humano, que tem uma memória onde armazenamos os dados. Neste artigo, aprenderemos como é uma memória eletrônica e como podemos usá-la para armazenar dados.

   Todas as instruções de um microcontrolador podem ser classificadas em 3 classes principais:

1 - instruções de salto condicional.

2 - Instruções de memória (movimentos variáveis).

3 - Instruções Aritméticas e Lógicas.

   Neste artigo vamos estudar e colocar em prática as Instruções de Movimentação de Dados na Memória. Começar a programar microcontroladores pode parecer uma tarefa que exige muito conhecimento ou conhecimento, mas com Arduino, vamos iniciar-nos no desenvolvimento de projetos e circuitos utilizando microcontroladores, de uma forma fácil e divertida. A primeira coisa que precisamos saber é como um microcontrolador é por dentro, de quais partes é formado, como funciona e qual é a relação com o compilador ou a linguagem de programação.

 

PARTES DE UM MICROCONTROLADOR.

   Existem três partes principais de um microcontrolador:

1 - A memória.

2 - CPU ou Unidade Central de Processamento.

3 - Os Periféricos.

   A figura 1 mostra as principais partes de um microcontrolador.

 

Figura 1_ Partes básicas de um microcontrolador
Figura 1_ Partes básicas de um microcontrolador

 

   

A memória é a parte do microcontrolador responsável pelo armazenamento de instruções e dados.

   A CPU é a parte do microcontrolador que é responsável por executar os comandos ou instruções do programa escrito em linguagem C.

   Periféricos, é a parte do microcontrolador que é responsável por remover ou inserir informações ou dados do lado de fora. Normalmente, ela é conhecida como entradas ou saídas (E / S).

 

MEMÓRIA.

   Para entender a memória, podemos fazer uma analogia com a mente humana. A mente armazena informações ou dados; O mesmo ocorre com a memória digital, ela é usada para armazenar informações ou dados. Podemos representar uma memória como uma série de gavetas ou gavetas onde a informação é colocada.

Na eletrônica digital existe um circuito chamado flip-flop tipo D ou data, este circuito armazena informações depois de receber um pulso digital. A figura 2 mostra um esquema desse circuito. O que este circuito faz é ler o nível lógico presente na entrada D e quando um pulso é recebido pela entrada Clock, armazenando-o na saída Q.

 

Figura 2 - Diagrama básico do Flip-Flop tipo D
Figura 2 - Diagrama básico do Flip-Flop tipo D

 

A Figura 3 mostra um exemplo do circuito, em um determinado estado, antes de receber um pulso na entrada Clock. Neste estado, a saída Q está no nível lógico 1 (alto) e a entrada no nível lógico 0 (baixo).

 

Figura 3_ Flip-Flop antes de receber o pulso de clock
Figura 3_ Flip-Flop antes de receber o pulso de clock

 

Agora, se tomarmos um pulso positivo (alto) na entrada do clock, o nível lógico presente na entrada D, é armazenado no flip-flop e seu valor é apresentado na saída Q. saída Podemos ver isso na Figura 4

 

Figura 4_ Flip-Flop depois de receber o pulso de clock
Figura 4_ Flip-Flop depois de receber o pulso de clock

 

Agora, podemos formar um circuito que possui 8 flip-flops tipo D o qual é representado pela Figura 5.

 

Figura 5 -Circuito com 8 Flip-Flops tipo D
Figura 5 -Circuito com 8 Flip-Flops tipo D

 

   

Temos 8 entradas D e 8 saídas Q independentes. O clock está em paralelo com os 8 flip-flops. A operação é exatamente a mesma para os 8 flip-flops, como explicado acima para um único flip-flop. Esse tipo de circuito é chamado de registro. A Figura 6 mostra um exemplo em um possível estado deste circuito, antes de receber um pulso através da entrada Clock.

 

Figura 6_8 Flip-Flops D antes de receber o pulso de clock
Figura 6_8 Flip-Flops D antes de receber o pulso de clock

 

   A figura 7 mostra o circuito após receber um pulso positivo na entrada Clock. Os valores são armazenados nas saídas Q.

 

Figura 7 - 8 Flip-Flops D após receber o pulso do relógio
Figura 7 - 8 Flip-Flops D após receber o pulso do relógio

 

   Assim, podemos armazenar dados ou informações digitais. Existem vários circuitos digitais que incorporam vários flip-flops e seguem a operação explicada acima.

   Para formar uma memória, podemos juntar vários flip-flops, organizados como mostrado na Figura 8.

 

Figura 8_Estrutura de uma memória com flip-flops
Figura 8_Estrutura de uma memória com flip-flops

 

Os dados são colocados nas entradas D0, D1, D2, D3, D4, D5, D6, D7. Os dados são armazenados no respectivo grupo de flip-flops e exibidos ou removidos pelas saídas Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7. Uma vez que todas as saídas Q estejam em alta impedância ou seja, em estado de alta impedância, não há problemas com curto-circuitos e apenas o registro selecionado estará presente na saída Q

   O que precisamos agora é uma maneira de selecionar cada registro. Para isso, podemos usar um circuito digital chamado decodificador. Este circuito é formado por algumas portas NÃO e AND digitais. A Figura 9 mostra a operação lógica de uma porta NÃO (inversor). Este circuito inverte o nível lógico presente na entrada.

 

Figura 9_Porta NÃO
Figura 9_Porta NÃO

 

   

A Figura 10 mostra a operação lógica de uma porta AND. Isso quer dizer que somente quando todas as suas entradas estão no nível lógico 1 (alto), sua saída é colocada no nível lógico 1; caso contrário, sua saída terá um nível lógico de 0 (baixo). Com esses componentes, podemos formar um decodificador.

 

Figura 10_Portas AND
Figura 10_Portas AND

 

   A Figura 11 mostra um decodificador com 2 entradas e 4 saídas. Observe que quando as entradas estão no nível lógico 0, os inversores (negadores) selecionam a primeira saída. Agora vamos juntar o decodificador aos registradores para poder selecionar o desejado.

 

Figura 11_Decodificador de 2 entradas 4 saídas
Figura 11_Decodificador de 2 entradas 4 saídas

 

A figura 12 mostra o circuito.

 

 Figura 12_Decodificador de 2 entradas 4 para selecionar flip-flops (registros)
 Figura 12_Decodificador de 2 entradas 4 para selecionar flip-flops (registros)

 

 

   A figura 13 mostra o diagrama de blocos do circuito da figura.

 

 Figura 13_Diagrama em blocos do decodificador 2X4 para seleção de flip-flops
 Figura 13_Diagrama em blocos do decodificador 2X4 para seleção de flip-flops

 

   Agora precisamos de um circuito que nos permita usar o barramento de dados de entrada e saída em um único barramento. Para isso, usaremos um buffer tri-state (Transeiver). A Figura 14 mostra como um par de buffers Tri-State permite usar a mesma linha digital para que em um instante seja entrada e em outro instante a saída.

 

 Figura 14_Buffer tri-state
 Figura 14_Buffer tri-state

 

   Quando o nível de entrada na linha de controle está em 0, a saída da porta NÃO inverte para o nível lógico 1 e seleciona o estado tri-state para o buffer B, o que faz com que o valor de Q, apareça na linha I / O. Quando a linha de controle é 1 lógico , e porta inverte e estará presente a lógica 0, desabilitando o buffer Tri-State B. Mas agora, está ativado o buffer A, de modo que o nível lógico presente na linha I / O, está disponpivel na linha A.

   Com base nesse princípio, podemos fazer um circuito de controle para o circuito da figura 12. O circuito da figura 15 apresenta este circuito e a figura 16, seu respectivo diagrama de blocos.

 

Figura 15 -Controle do barramento de dados com buffer tri-state
Figura 15 -Controle do barramento de dados com buffer tri-state

 

 

Figura 16_ Diagrama de blocos do controle de barramento de dados com tri-state
Figura 16_ Diagrama de blocos do controle de barramento de dados com tri-state

 

 

PROGRAMA E MEMÓRIA DE DADOS.

   Os microcontroladores têm uma memória para armazenar instruções chamada memória de programa. Nela as instruções são armazenadas sequencialmente. A Unidade Central de Processamento (CPU) procura as instruções nesta memória e as executa. Veja a Figura 17.

 

 Figura 17 – Memória de Programa e Memória de Dados
 Figura 17 – Memória de Programa e Memória de Dados

 

   Os dados são armazenados na memória de dados. Os dados são valores que podem variar durante a execução do programa, por isso são chamados de variáveis.

   A memória do programa é não volátil, ou seja, quando o microcontrolador é desconectado da fonte de alimentação, a informação permanece. Como por, exemplo, ROM, PROM, EPROM, EEPROM, FLASH, etc. A memória de dados é volátil, o que significa que quando a fonte de alimentação é desconectada, a informação é perdida. Como exemplo temos a RAM dinâmica (DRAM) ou RAM estática (SRAM).

   As memórias são construídas com diferentes tipos de matrizes (arrays) ou arquiteturas, mas independentemente disso, todas armazenam as informações e seus princípios operacionais são os mesmos.

 

O COMPILADOR E O AMBIENTE DE DESENVOLVIMENTO (IDE).

   O compilador é o editor onde os programas são feitos. É principalmente um editor de texto e as instruções são escritas sequencialmente, dependendo do modo que você quer que o microcontrolador as execute. No ambiente de desenvolvimento do Arduino (IDE), a função main () é chamada automaticamente. A função principal chama a função setup () e loop (). Mas a maioria dos livros e textos microcontroladores usam a função main (), por isso para compreender melhor como as memórias trabalham dentro do microcontrolador, na seguinte explicação vai ajudar.

O código começa a executar em uma função chamada main (). O seguinte é o código para esta função:

int main ()

{

}

   

Para atribuir nomes aos endereços da memória de dados, isso é feito antes desta função. Por exemplo, a próxima linha diz ao compilador para separar um byte na memória de dados e atribuir a ele a contagem de nomes.

 

usigned char count;

 

   A linha de código a seguir diz ao compilador para gravar o valor 9 no endereço da memória count.

int main ()

  {

    count = 9;

  }

 

   Na Figura 18 podemos ver o que acontece antes de executar a instrução.

 

Figura 18_Antes de executar a instrução
Figura 18_Antes de executar a instrução

 

   A Figura 19 mostra quando a CPU lê a instrução e a Figura 20 depois de executar a instrução.

 

Figura 19_A CPU lê a instrução da memória do programa
Figura 19_A CPU lê a instrução da memória do programa

 

 

 

Figura 20_Depois de executar a instrução
Figura 20_Depois de executar a instrução

 

No exemplo a seguir, vamos separar 3 posições na memória de dados:

usigned char count;

usigned char temperature;

usigned char humidity;

 

O código a seguir atribui valores a essas posições de memória. Para a pozição de memória count é atribuído o valor 7, para a posição de memória temperature, é atribuído o valor 22 e para a posição de memória de humidity é atribuído o valor 16:

 

usigned char count;
usigned char temperature;
usigned char humidity;
 int main()
   {
      count = 7;
      temperature = 22;
      humidity = 16;
       while(1)
          {
          }
}

 

   Veja a Figura 21 para entender o que aconteceu na memória de dados. A instrução while (1) é um loop infinito que no momento não faz nada.

 

Figura 21_Exemplo do programa
Figura 21_Exemplo do programa

 

   No exemplo a seguir, vamos executar uma instrução de incremento na temperatura variável. A instrução:

temperature ++;

   

Aumenta em 1 o valor armazenado na variável temperatura. Em outras palavras, após o programa ter sido executado, a variável de temperatura permanecerá com o valor 23. A Figura 22 mostra este exemplo.

 

Figura 22_Exemplo de programa com instrução de incremento
Figura 22_Exemplo de programa com instrução de incremento

 

 

usigned char count;
usigned char temperature;
usigned char humidity;
 int main()
{
    count = 7;
    temperature = 22;
    humidity = 16;
    temperature++;
     while(1)
      {
      }
}

 

 

O OPERADOR DE ATRIBUIÇÃO (=).

   Na Figura 23 podemos ver a placa do Arduino Uno e na Figura 24 o IDE ou ambiente de desenvolvimento, que pode ser baixado do site do Arduino. Para atribuir um valor a uma variável, usamos o operador de atribuição (=). No exemplo a seguir, a variável do sensor é armazenada na variável de exibição.

 

 Figura 23_ Placa de desenvolvimento do Arduino Uno
 Figura 23_ Placa de desenvolvimento do Arduino Uno

 

 

Figura 24_Arduino Uno - ambiente de desenvolvimento
Figura 24_Arduino Uno - ambiente de desenvolvimento

 

display = sensor;

   Na instrução anterior, o valor contido na variável sensor é movido ou transferido para a localização da memória do displayr. Por exemplo, suponha que o valor da exibição da localização da memória seja 27 e o valor da variável do display seja 9. Após a execução da instrução display = sensor; o valor da variável de display será igual a 7. Assim, é como podemos mover dados de um local de memória para outro ou de uma porta para memória ou memória para uma porta.

   A linguagem C usa o operador de atribuição (=) para transferir dados de um local de memória para outro. Além disso, existem funções prontas para uso, como no caso da memória EEPROM. A seguir estão as funções para gravar e ler nesta memória. Os códigos de exemplo podem ser encontrados no menu: Files-> Examples-> EEPROM. Veja a Figura 25.

 

Figura 25_Menu do código de exemplo para memória EEPROM
Figura 25_Menu do código de exemplo para memória EEPROM

 

   Se você quiser incluir a biblioteca em qualquer projeto, vá ao menu: Programa-> Incluir Biblioteca-> EEPROM. Para isso, veja a Figura 26.

 

 

 Figura 26_Menu para incluir a biblioteca EEPROM
 Figura 26_Menu para incluir a biblioteca EEPROM

 

 

INSTRUÇÃO PARA ESCREVER NA MEMÓRIA EEPROM (EEPROM.write).

   A instrução EEPROM.write permite gravar um byte na memória EEPROM. Para isso usamos a função write da seguinte maneira:

EEPROM.write (endereço, valor)

 

Parâmetros:

address: o local a ser gravado, a partir de 0 (int)

valor: o valor para gravar de 0 a 255 (byte)

 

Retorno: nenhum

    No exemplo a seguir, armazenamos os valores lidos da entrada analógica 0, na memória EEPROM. Esses valores permanecerão na memória EEPROM, quando o circuito estiver desligado e puder ser lido por outro programa. A figura 27 mostra o circuito usado para este programa.

 

 Figura 27_Circuito para testar a memória EEPROM na placa do Arduino Uno
 Figura 27_Circuito para testar a memória EEPROM na placa do Arduino Uno

 

   O programa pode ser encontrado no menu: Files-> Examples-> EEPROM-> eeprom_read.

 


#include
/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/int addr = 0;void setup() {/** Empty setup. **/ } void loop() { /** Need to divide by 4 because analog inputs range from 0 to 1023 and each byte of the EEPROM can only hold a value from 0 to 255. **/ int val = analogRead(0) / 4; /** Write the value to the appropriate byte of the EEPROM these values will remain there when the board is turned off. **/ EEPROM.write(addr, val); /**Advance to the next address, when at the end restart at the beginning. Larger AVR processors have larger EEPROM sizes, E.g: - Arduno Duemilanove: 512b EEPROM storage. - Arduino Uno: 1kb EEPROM storage. - Arduino Mega: 4kb EEPROM storage. Rather than hard-coding the length, you should use the pre-provided length function. This will make your code portable to all AVR processors. **/ addr = addr + 1; if (addr == EEPROM.length()) { addr = 0; } /** As the EEPROM sizes are powers of two, wrapping (preventing overflow) of an EEPROM address is also doable by a bitwise and of the length - 1. ++addr &= EEPROM.length() - 1; **/ delay(100); }

 

INSTRUÇÕES PARA LER UM BYTE DA MEMÓRIA EEPROM (EEPROM.read)

   A instrução EEPROM.read permite ler um byte da memória EEPROM. Se por acaso a localização da memória for lida, nunca foi gravada, o valor retornado será 255 ou 0Xf em hexadecimal. O formato da instrução de leitura é o seguinte:

EEPROM.read (endereço)

 

Parâmetros:

address: o local a ser lido, a partir de 0 (int)

Retorno: O valor armazenado na localização (byte)

   O programa a seguir lê os valores de cada byte da memória EEPROM e imprime no computador. Para isso é necessário abrir o monitor serial. Você pode encontrar este programa no menu: Arquivos-> Exemplos-> EEPROM-> eeprom_read.

 

#include
// start reading from the first byte (address 0) of the EEPROM
int address = 0;
byte value;
void setup() {
   // initialize serial and wait for port to open:
   Serial.begin(9600);
   while (!Serial) {
       ; // wait for serial port to connect. Needed for native USB port only
     }
}
 
void loop() {
    // read a byte from the current address of the EEPROM
   value = EEPROM.read(address);
   Serial.print(address);
   Serial.print("\t");
   Serial.print(value, DEC);
   Serial.println();
   
/** Advance to the next address, when at the end restart at the beginning. Larger AVR processors have larger EEPROM sizes, E.g: - Arduno Duemilanove: 512b EEPROM storage. - Arduino Uno: 1kb EEPROM storage. - Arduino Mega: 4kb EEPROM storage. Rather than hard-coding the length, you should use the pre-provided length function. This will make your code portable to all AVR processors. **/
address = address + 1; if (address == EEPROM.length()) { address = 0; } /** As the EEPROM sizes are powers of two, wrapping (preventing overflow) of an EEPROM address is also doable by a bitwise and of the length - 1. ++address &= EEPROM.length() - 1; **/ delay(500); }

 

   Concluindo, podemos ver que para transferir dados de um local de memória para outro, usamos o operador de atribuição (=). Para transferir dados para a memória EEPROM, podemos usar funções como EEPROM.write ou EEPROM.read. Assim, é muito fácil transferir ou mover dados entre locais de memória.