Memória de programa AVR/ATmega328P

Sempre que utilizamos strings e arrays constantes em nossos programas, por padrão elas vão parar na memória RAM. Como tempo pouca memória RAM, seria ótimo conseguir colocar isso na memória de programa, liberando a preciosa RAM para as variáveis.

Neste post vemos como fazer isso nos microcontroladores AVR, como o ATmega328P do Arduino Uno.

Strings na memória de programa

Consideramos a seguinte função, que compara a string de entrada com uma string constante. Ela retorna verdadeiro se as strings são iguais e falso se as strings são diferentes. Este tipo de comparação é muito utilizada quando lendo comandos da serial, por exemplo.

#include <string.h>

bool is_hello(const char *str) {
  return !strcmp(str, "hello");
}

Agora que temos um exemplo para trabalhar, vejamos como colocar string de comparação na memória de programa.

Primeiro precisamos incluir o cabeçalho avr/pgmspace.h que contém as definições.

Para colocar a string constante na memória de programa basta envolver ela com a macro PSTR().

Se tentarmos executar o programa agora, ele não funcionará, pois a função strcmp() espera strings na memória RAM, e não na memória de programa.

Precisamos substituir a função strcmp() por outra, que faz a leitura da segunda string na memória de programa. A função que faz isso é strcmp_P().

Parabéns, você acabou de economizar preciosos 5 bytes de memória RAM!!!

O código final fica da forma a seguir.

#include <string.h>
#include <avr/pgmspace.h>

bool is_hello(const char *str) {
  return !strcmp_P(str, PSTR("hello"));
}

Há diversas funções de string alternativas, que fazem uso de strings na memória de programa. Inclusive versões para *printf()!

Você pode consultar os cabeçalhos avr/pgmspace.h e stdio.h para ver mais funções de string.

Arrays na memória de programa

Consideramos a seguinte função, que retorna um valor em uma tabela (array). Este é um exemplo simples que pode ser facilmente programado como uma função em vez de uma tabela. Colocar arrays na memória de programa é muito útil para CRC (códigos de verificação de erro), onde temos tabelas muito grande.

const unsigned char lut[16] = {
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

unsigned char get_lut(unsigned char pos) {
  return lut[pos];
}

Novamente, agora que temos um exemplo para trabalhar, vejamos como colocar a tabela na memória de programa.

Primeiro precisamos incluir o cabeçalho avr/pgmspace.h que contém as definições.

Para colocar a tabela (array) na memória de programa basta colocar PROGMEM na declaração. Note que é necessário declarar a tabela como constante.

Se tentarmos executar o programa agora, ele não funcionará, pois o programa tenta acessar a tabela na memória RAM, e não na memória de programa.

Em vez de usar lut[pos] para obter o valor, precisamos chamar uma função específica que faz a leitura do valor da memória de programa. A função que faz isso é pgm_read_byte() e para chamá-la passamos o endereço (&) do valor que queremos ler, ou seja,  pgm_read_byte(&lut[pos]).

Parabéns, você acabou de economizar preciosos 16 bytes de memória RAM!!!

O código final fica da forma a seguir.

#include <avr/pgmspace.h>

PROGMEM const unsigned char lut[16] = {
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};

unsigned char get_lut(unsigned char pos) {
  return pgm_read_byte(&lut[pos]);
}

Há várias funções para fazer leitura de valores na memória de programa:

  • pgm_read_byte – Ler byte (inteiro de 8 bits)
  • pgm_read_word – Ler word (inteiro de 16 bits)
  • pgm_read_dword – Ler double word (inteiro de 32 bits)
  • pgm_read_float – Ler float

Você pode consultar o cabeçalho avr/pgmspace.h ver mais funções.

Leia mais:

Veja mais posts da Categoria Programação!

Autor: Djones Boni

Engenheiro Eletricista.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *