Como otimizar o código do Arduino

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br


Nos sistemas de microcontroladores onde os recursos são limitados, os desenvolvedores investem muito tempo e recursos para produzir código eficiente. O objetivo principal é melhorar o desempenho geral do sistema. O artigo que apresentamos aqui explora várias técnicas para otimizar nosso código do Arduino. Categorizamos essas técnicas em duas: técnicas gerais e técnicas avançadas. Vamos dar uma descrição geral de cada método e, em seguida, daremos uma olhada em alguns exemplos.

Anúncio PCBWay

No contexto da programação do Arduino, a otimização de código é um método de melhorar o código para produzir um programa eficiente. A idéia é melhorar certos aspectos do programa, como tamanho do programa, consumo de memória, tempo de execução, taxa de transferência e consumo de energia. Idealmente, um programa otimizado é menor em formato, consome menos memória, tem menos tempo de execução e consome menos energia. Essas são considerações importantes de design que devem ser levadas em consideração ao escrever o código para o aplicativo em sistemas incorporados com recursos limitados. Mas o parâmetro mais crucial da otimização de código é que sua saída deve ser a mesma que a saída do código não otimizado.

Considerações sobre otimização de código

Existem alguns fatores, no entanto, que os desenvolvedores devem considerar antes de otimizar seu código. Um dos maiores desafios é saber o que e quando otimizar. Por exemplo, se a declaração do problema for desenvolver um programa Arduino para monitorar o nível de água em um tanque, a otimização será desnecessária, pois é improvável que você exceda os limites do Arduino de qualquer forma. Além disso, o processo de otimização de código custa dinheiro e tempo. Portanto, você deve otimizar o código que realmente precisa de otimização.

Dito isto, se outras soluções, como o uso de um Arduino maior, não forem possíveis, considere a otimização de código como uma opção para o seu projeto. Neste artigo, o autor se concentrará em técnicas para otimizar código no popular Arduino Uno. Os métodos que discutiremos aqui também podem ser aplicados a outras placas do Arduino.

Por que otimizar o código é importante

Aprendemos no artigo anterior que alguns dados são criados ao executar um programa Arduino. Esses dados estão relacionados a chamadas de função e rotinas de interrupção. Podem surgir problemas quando o esboço ou os dados gerados requerem mais espaço que o tamanho designado. Quando isso acontece, o programa Arduino pode falhar de maneiras diferentes. Portanto, é necessário otimizar o código e evitar uma situação, conforme mostrado no diagrama (b) abaixo.

Como otimizar o código do Arduino 1
Uma célula de memória

Como mencionamos anteriormente neste artigo, a otimização de código produz um código mais rápido. Esse resultado não é apenas necessário para executar cálculos, mas também para executar operações de entrada e saída. Por exemplo, essas operações podem incluir a comunicação com dispositivos de hardware externos ou apenas a atualização do status dos pinos analógicos e digitais. É necessário para aplicativos que exigem controle mais rígido ou aplicativos que exigem loops de feedback mais rápidos, como sistemas de controle automático.

Leia Também  Como o SEO mudou

Em resumo, esses são os benefícios de otimizar seu código Arduino:

  1. Código corre mais rápido
  2. Melhor legibilidade do código.
  3. Código de manutenção
  4. Economiza memória.

Técnicas para otimização de código

O diagrama abaixo mostra as diferentes técnicas que os desenvolvedores usam para otimizar seu código no Arduino. Algumas das técnicas são independentes da linguagem e outras são adaptadas para funcionar apenas no Arduino.

Como otimizar o código do Arduino 3
Diferentes técnicas de otimização de código

Removendo código desnecessário

Código desnecessário neste contexto refere-se a quaisquer funções, bibliotecas ou variáveis ​​não utilizadas que você possa ter incluído no seu esboço. Mesmo que não sejam usados ​​no esboço, eles ainda ocupam algum espaço na memória. Portanto, é necessário remover esse código morto no seu esboço para liberar um pouco de memória. É simples verificar se há variáveis ​​não utilizadas no seu esboço; aqui está um exemplo.

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  int myPin = 10;
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   
  delay(500);                      
  digitalWrite(LED_BUILTIN, LOW);    
  delay(500);                       
}

A variável inteira não utilizada (myPin), sinaliza um aviso na seção de mensagens do IDE do Arduino, como mostrado abaixo, e você pode remover a variável não utilizada.

C:UsersDellDocumentsArduinosketch_may28asketch_may28a.ino: In function 'void setup()':
C:UsersDellDocumentsArduinosketch_may28asketch_may28a.ino:7:7: warning: unused variable 'myPin' [-Wunused-variable]

   int myPin = 10;
       ^~~~~

Usando tipos de dados menores

No contexto da programação de computadores, uma variável é uma maneira de nomear e armazenar um valor na memória para uso posterior. Consiste em um tipo, um nome e, opcionalmente, um valor inicial. Primeiramente, precisamos entender os tipos de dados para representar variáveis ​​em um esboço do Arduino.

Tipo de dadosTamanho (bits)Valores
bool81 ou 0
Caracteres8-128 a127
char não assinado, byte80 a 255
curto, int16-327768 a 32767
int não assinado, palavra160 a 65535
longo32.-2147483648 a 2147483648
não assinado por muito tempo32.0 a 4294967295
flutuador, duplo32.1.175e-38 a 3.402e38

Como podemos ver na tabela acima, diferentes tipos de dados têm tamanhos diferentes. Portanto, deve-se sempre usar um tipo de dados que tenha o menor intervalo para acomodar os dados. Também é vantajoso ter uma idéia aproximada do escopo de seus dados, porque o uso de tipos de dados mais extensos resulta em um tamanho de esboço maior.

Usando funções em vez de repetir código

Podemos demonstrar esse princípio usando um exemplo de um sinal de socorro do Código Morse SOS. Três pontos representam S e O é representado por três traços. Portanto, o SOS no Código Morse será de 3 pontos, 3 traços e 3 pontos. Em nossa implementação, cada ponto tem 250 milissegundos e cada traço tem 1000 milissegundos. Este é um exemplo perfeito, pois o Código Morse contém código que se repete em intervalos regulares.

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250); 
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  delay(3000);
}

Isso pode ser reescrito como:

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(250);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
}

O uso de funções para esse problema resulta em cerca de 200 bytes de economia de memória. Isso ocorre principalmente porque uma única instância da função é criada na memória. E quando a função é chamada novamente, a CPU carrega o código novamente a partir de sua localização na memória, sem ter que recriar as variáveis.

Leia Também  Tutorial sobre sensor de temperatura Raspberry Pi DS18B20

Usando variáveis ​​locais em vez de variáveis ​​globais

Para entendermos os benefícios do uso de variáveis ​​locais, precisamos saber como o Arduino trata as variáveis ​​locais e globais. Mas, primeiro, uma variável é chamada de variável global se declarada antes do setup() e loop() funções. Enquanto uma variável local é uma variável que podemos manter e chamar dentro de uma função.

Uma variável global pode ser acessada por qualquer função dentro do esboço, enquanto uma variável local é visível apenas dentro dessa função. Os efeitos dessas variáveis ​​na funcionalidade do programa estão além do escopo deste artigo, pois estamos interessados ​​apenas nos possíveis problemas de desempenho de variáveis ​​globais versus variáveis ​​locais.

Pilha versus pilha

Variáveis ​​globais são alocadas para o heap, enquanto as variáveis ​​locais são alocadas para a pilha. A diferença entre esses dois locais é a velocidade de acesso, ou geralmente, o número de instruções da máquina necessárias para concluir uma tarefa. Devido às diferentes arquiteturas desses dois sistemas, a pilha tem tempos de acesso mais altos em comparação com o heap. Como resultado, é mais rápido acessar variáveis ​​locais do que variáveis ​​globais.

Como as variáveis ​​globais são declaradas antes de qualquer função, isso significa que elas são atribuídas apenas uma vez. Este valor é mantido por toda a duração do seu programa, enquanto as variáveis ​​locais são alocadas quando o aplicativo executa uma função específica. Esse valor é desalocado quando o programa conclui a execução da função. Isso significa que o tamanho das variáveis ​​globais permanece o mesmo durante todo o programa, enquanto a capacidade das variáveis ​​locais varia.

Para concluir, podemos usar variáveis ​​locais para otimizar o código do Arduino, pois a velocidade de acesso e o tamanho da memória afetam diretamente o desempenho geral do Arduino. No futuro, esperamos investigar o efeito da Alocação Dinâmica de Memória no desempenho do código Arduino.

F () Strings

Imprimir muitas seqüências de caracteres no monitor serial ou no LCD consome muita memória RAM. Para salvar a preciosa RAM, essas cadeias podem ser salvas na memória Flash. Para conseguir isso, o Arduino emprega a macro F (). Essa solução simples, porém poderosa, força o compilador a colocar a string incluída no PROGMEM. Aqui está um exemplo: Serial.print("Optimizing Code");

Podemos representar esse código como: Serial.print(F("Optimizing Code"));

Incluindo essa macro nessas duas palavras "Optimizing Code" , pode salvar até 16 bytes. No entanto, a macro F () funciona apenas em literais de seqüência de caracteres.

Movendo dados constantes para PROGMEM

Em geral, o Arduino armazena variáveis ​​na SRAM. Como observamos anteriormente, o tamanho dessas variáveis ​​pode mudar durante a execução do programa. Para evitar a falta de RAM, precisamos controlar os dados que entram nesse bloco de memória. Para isso, usamos a palavra-chave PROGMEM para armazenar os dados na memória do programa em vez da RAM. É particularmente útil para dados que nunca mudam, como constantes. A desvantagem, no entanto, é que isso é mais lento; mas a imagem maior é que economizamos RAM. Aqui está um exemplo de implementação do PROGMEM.

const int16_t chars[] = {200, 101, 521, 24, 892, 3012, 100};

torna-se: const int16_t chars[] PROGMEM = {200, 101, 521, 24, 892, 3012, 100};

Para ler a variável da memória do programa, usamos o seguinte código:

void ReadData() {
  unsigned int displayInt;
  
  for (byte k = 0; k < 7; k++) {
    displayChars = pgm_read_word_near(chars + k);
    Serial.println(displayChars);
  }
  Serial.println();
}

O loop for no código acima pressupõe que você saiba o tamanho dos dados em sua variável. No entanto, se essas informações não estiverem disponíveis, você poderá substituir o loop for pelo código abaixo:

for (byte k = 0; k < (sizeof(chars) / sizeof(chars[0])); k++) {
    displayInt = pgm_read_word_near(chars + k);
    Serial.println(displayInt);
  }

E obtemos os seguintes valores:

Como otimizar o código do Arduino 5

Usando reserve () para Strings

Outro método para otimizar o código do Arduino é gerenciar a memória de strings que crescem em tamanho. Para fazer isso, usamos a função reserve () para pré-alocar memória para essas strings. Se não controlarmos isso, poderão ocorrer erros relacionados à fragmentação da memória, resultando em desempenho reduzido. Aqui está o código para implementar o reserve() função.

  String string;
  string.reserve(50);

Para usar isso corretamente, declaramos uma variável do tipo Stringe, em seguida, transmita o número de bytes na memória para armazenar essa sequência.

Leia Também  Por que as impressoras HP ENVY são tão populares

Técnicas avançadas

Manipulação Direta de Portas

Podemos programar o Arduino usando C puro, pois o software é baseado no compilador avr-gcc e o hardware é baseado nos microcontroladores Atmel AVR. Cada pino no microcontrolador consiste nos seguintes bits de registro: PINxn, DDxn, e PORTxn. Uma descrição detalhada desses bits de registro pode ser encontrada na folha de dados da Atmega.

Duas melhorias principais podem ser trazidas pela manipulação direta da porta do seu Arduino. Em primeiro lugar, a sua velocidade. O controle direto da porta resulta em um controle de E / S muito mais rápido, economizando alguns microssegundos. Em segundo lugar, o controle direto da porta reduz a quantidade de memória que seu sketch utilizará. Por exemplo, os dois trechos de código abaixo implementam um programa simples de piscar de LED. O primeiro usa o Arduino digitalWrite() funções, enquanto o segundo usa controle direto de porta em C. puro

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   
  delay(1000);                       
  digitalWrite(LED_BUILTIN, LOW);    
  delay(1000);                       
}
void setup() {
  DDRB |= (1<

O tamanho do segundo programa é 488 bytes, comparado com o digitalWrite() programa, que leva cerca de 924 bytes de memória. No entanto, há muito debate sobre se a manipulação direta de portas em C puro conta como um método de otimização de código para o Arduino. O software Arduino foi escrito para simplificar o processo de programação do microcontrolador. As bibliotecas “escondem” esse C de baixo nível de nós. Portanto, reverter para C puro pode ser visto como negando o propósito de usar o Arduino. O autor mencionou apenas essa técnica para demonstrar os aspectos de memória do C puro em comparação com o estilo do Arduino.

Removendo o Carregador de Inicialização

A plataforma de prototipagem do Arduino simplifica o processo de transferência do seu código para o microcontrolador. Ele simplifica esse processo usando firmware, em vez de um programador externo. Esse firmware é chamado de carregador de inicialização e leva cerca de 2000 bytes de memória flash. Quando todas as opções estiverem esgotadas, você poderá ignorar o gerenciador de inicialização. Você pode usar o Arduino como ISP ou usar um programador externo para “gravar” seu código sem usar o gerenciador de inicialização. Quando o microcontrolador no Arduino é programado dessa maneira, a nova imagem substituirá o gerenciador de inicialização, deixando assim mais espaço para o seu esboço.

cupom com desconto - o melhor site de cupom de desconto cupomcomdesconto.com.br