Todos sabem que o Arduino conta com diversos recursos, portas digitais, analógicas, timers, PWM entre outros. Comparador analógico, Watchdog e EEPROM do Arduino são alguns recursos muito úteis porém pouco utilizados. São fáceis de serem utilizados e podem facilitar bastante a vida em alguns projetos. Este post descreve esses recursos e apresenta um exemplo de aplicação. Um projeto é montado para demonstrar o Watchdog e EEPROM do Arduino e outro para exemplificar o comparador analógico.
Watchdog e EEPROM do Arduino
Para exemplificar o Watchdog e EEPROM do Arduino, vamos precisar de:
Watchdog
O watchdog é um recurso simples mas com um objetivo vital para sistemas embarcados: garantir que o software volte para uma condição conhecida em caso de algum erro ou situação inesperada.
Trata-se de um timer que, uma vez ligado, precisa ser rearmado periodicamente. Se ele não for rearmado dentro de um tempo limite, o microcontrolador é automaticamente reiniciado.
Para usar o watchdog precisamos descer um nível abaixo da biblioteca do Arduino, usando a biblioteca do compilador C. Por este motivo, as informações abaixo se aplicam somente aos modelos baseados nos microprocessadores AVR (ATmega e ATtinny). No seu programa você precisa incluir o header wdt.h:
#include <avr/wdt.h>
Para ligar o watchdog, chame a função wdt_enable, informando o tempo limite:
wdt_enable(WDTO_2S);
O valor no exemplo acima corresponde a 2 segundos; estão também disponíveis WDTO_8S, WDTO_4S, WDTO_1S, WDTO_500MS, WDTO_250MS,WDTO_120MS, WDTO_60MS, WDTO_30MS e WDTO_15MS. A temporização do watchdog é fornecida por um oscilador interno ao microcontrolador e independe do clock de execução das instruções.
Para rearmar o watchdog, basta chamar wdt_reset(). Normalmente você irá colocar chamadas nos pontos normais de execução, para garantir que não ocorra o reset durante o funcionamento correto.
O exemplo abaixo permite ver o watchdog em funcionamento, para rodá-lo conecte um botão entre o pino 3 e GND. Se você mantiver o botão apertado por mais de 2 segundos o Arduino reiniciará.
#include <avr/wdt.h> const int pinBotao = 3; const int pinLED = 13; void setupt() { wdt_enable(WDTO_2S); pinMode (pinBotao, INPUT_PULLUP); pinMode (pinLED, OUTPUT); Serial.begin(9600); Serial.println("Reset"); digitalWrite (pinLED, HIGH); // para indicar o reset delay (500); digitalWrite (pinLED, LOW); } void loop() { wdt_reset(); while (digitalRead(pinBotao) == LOW) { delay (10); } }
EEPROM
Vamos olhar o terceiro tipo de memória no microcontrolador: a EEProm. Já estamos acostumados com a Flash (onde o programa é gravado conectando o Arduino a um micro e aí permanece mesmo sem alimentação) e a Ram (onde estão as nossas variáveis e cujo conteúdo é perdido quando desligamos o Arduino). A EEProm combina a não volatilidade da Flash (manutenção dos dados sem alimentação) com algo próximo à flexibilidade de leitura e escrita da Ram.
A EEProm deve ser usada para armazenar informações que queremos que sobrevivam a um desligamento, como parâmetros ou variáveis importantes. O ATmega328 possui generosos 1K de EEProm (generosos considerando que temos 2K de Ram!). Ao planejar o uso da EEProm, é preciso levar em conta que existe um limite de 100.000 escritas em cada posição (se você escrever a mesma posição a cada segundo ela começaria a falhar em pouco mais de um dia) e que a escrita e leitura são bem mais demoradas que na Ram (principalmente a escrita).
A EEProm é suportada pela IDE do Arduino através da biblioteca EEProm (https://www.arduino.cc/en/Reference/EEPROM).
No exemplo abaixo, vamos salvar na EEProm o número de vezes que foi apertado o botão conectado entre o Digital 3 e GND. Para evitar um número excessivo de escritas, vamos limitar o contador a 1000 e usar o método update() para atualizar a EEProm (este método só faz a gravação se o valor for diferente do já existentes). Uma preocupação no uso da EEProm é ter certeza que o conteúdo dela é válido. Neste exemplo a informação é gravada duas vezes, a segunda dela negada. No início do programa é conferido se as duas gravações são compatíveis, se não forem a contagem é iniciada com zero. Ao armazenar um conjunto mais complexo de dados você pode usar um checksum ou CRC para verificar a integridade dos dados.
#include <EEPROM.h> const int pinBotao = 3; const int endCont = 10; int contador = 0; void setup() { pinMode (pinBotao, INPUT_PULLUP); Serial.begin(9600); leContador(); mostraContador(); } void loop() { delay(100); if (digitalRead(pinBotao) == LOW) { if (contador < 1000) { contador++; } gravaContador(); mostraContador(); while (digitalRead(pinBotao) == LOW) { delay(100); } } } // Mostra valor atual do contador void mostraContador() { Serial.print("Contador: "); Serial.println(contador); } // Le o contador da EEProm void leContador() { uint16_t val1, val2; val1 = EEPROM.read(endCont) + (EEPROM.read(endCont+1) << 8); val2 = EEPROM.read(endCont+2) + (EEPROM.read(endCont+3) << 8); if (val1 != ~val2) { Serial.println("Nao encontrou valor valido, zerando contador"); contador = 0; } else { contador = val1; } } // Grava o contador na EEProm void gravaContador() { uint16_t val1, val2; val1 = (uint16_t) contador; val2 = ~val1; EEPROM.update(endCont, val1 & 0xFF); EEPROM.update(endCont+1, val1 >> 8); EEPROM.update(endCont+2, val2 & 0xFF); EEPROM.update(endCont+3, val2 >> 8); }
Comparador Analógico
É bastante comum o uso do Conversor Analógico Digital (ADC) para analisar uma entrada analógica, mas poucos sabem (ou lembram) que o ATmega possui também um Comparador Analógico que permite executar algumas tarefas de uma forma mais simples e rápida que o ADC.
Para exemplificar o comparador analógico, vamos precisar de:
O coração deste periférico é um comparador cuja saída é “1” quando uma de suas entradas analógicas (AIN0) for maior que a outra (AIN1). O sinal de saída pode gerar uma interrupção ou disparar o timer/contador1.
A entrada AIN0 pode ser o pino “Digital 6” do Arduino ou uma referência interna de 1.1V. Já a entrada AIN1 pode ser selecionada entre o pino “Digital 7” ou uma entradas analógicas do Arduino (A0 a A5). Atenção que para usar as entradas analógicas é necessário desligar o ADC.
A biblioteca do Arduino não possui rotinas de suporte ao comparador analógico, portanto é necessário interagir diretamente com os registradores do microcontrolador (o código a seguir é para o ATmega328 usado no Arduino UNO, Nano e Pro Mini). Para facilitar, criei uma classe para encapsular estes acessos aos registradores.
Como exemplo, vamos ligar um potenciômetro no pino A0 (que corresponderá a AIN1) e comparar a tensão obtida com a referência interna. O LED do Arduino sinaliza quando a tensão em A0 for menor que a referência. Repare que a comparação é feita toda pelo hardware, o software só é acionado quando a tensão cruza o limite. Um esquema parecido poderia ser usado, por exemplo, para detectar que uma bateria está baixa.
const int pinLED = 13; class C_COMPARADOR { public: typedef enum { SEM = bit(ACIS0), MUDANCA = 0, SUBIDA = bit(ACIS1) | bit(ACIS0), DESCIDA = bit(ACIS1) } COMP_INT ; // Inicia o comparador analógico // refInterna: true usa referência interna na entrada + do comparador // false usa AIN0 (Digital 6) na entrada + do comparador // entrada: -1 usa AIN1 (Digital 7) na entrada - do comparador // 0 a 5 usa o pino A0 a A5 na entrada - do comparador // interrup seleciona o modo de interrupcao void inicia(bool refInterna, int entrada, COMP_INT interrup) { byte val; cli(); // sem interrupções if (entrada == -1) { ADCSRB &= ~bit(ACME); // Não usar MUX } else { ADCSRA &= ~bit(ADEN); // ADC desligado para usar o MUX ADCSRB |= bit(ACME); // Usar MUX ADMUX = entrada; } val = bit(ACI); // Limpar interrupção if (refInterna) { val |= bit(ACBG); // Usar referência interna } if (interrup != SEM) { val |= bit(ACIE); // Habilitar interrupção val += interrup; } ACSR = val; // Programa o comparador sei(); // habilita interrupções } inline bool saida() { return (ACSR & bit(ACO)) == 0; } }; static C_COMPARADOR COMPARADOR; void setup() { pinMode (pinLED, OUTPUT); digitalWrite (pinLED, LOW); COMPARADOR.inicia (true, 0, COMPARADOR.MUDANCA); } void loop() { // nada a ser feito delay(100); } // Interrupção do comparador ISR (ANALOG_COMP_vect) { digitalWrite (pinLED, COMPARADOR.saida()? HIGH : LOW); }
Curtiu ver aprender sobre o Comparador analógico, Watchdog e EEPROM do Arduino? Ajude-nos a melhorar o blog comentando abaixo sobre este tutorial.
Parabens pelo ‘tutorial”. Ele me ajudou muitona compreensão e futuro uso destes recursos.
Olá, obrigado pelos exemplos. Eu tenho uma dúvida quanto ao watchdog: entendi aqui, com meus botões, que o acionamento do watchdog é hardware, não software. No entanto, você tem como confirmar isso para mim? Eu gostaria de saber, de fato, caso o processador trave (não alguma sub-rotina mal escrita), por exemplo se houver uma falha de alimentação ou estouro de memória, o watchdog atua também? Ou tudo para de funcionar?
Sim, é hardware. Ele vai ressetar o Arduino se o seu programa não chamar wdt_reset() no tempo configurado, qualquer que seja o motivo. Note que nem todos estouros de memória podem fazer o seu programa se perder a ponto de não chamar mais wdt_reset()… No caso da alimentação, existe um outro recurso de hardware, o Brown-out Detector, que reinicia o microcontrolador se a tensão de alimentação cair abaixo de um certo valor.
Olá DQ e Rosana da Equipe MakerHero. A função Watchdog é uma aplicação muito boa para resetar o sistema quando o loop trava. Estou com uma problema que até você poderiam criar um poste de como resolver. Eu não consigo remover ou desabilitar a macro (avr/wdt.h) após utiliza-lá. Você poderiam me ajudar?
Luciano, a macro wdt_disable() desliga o watchdog, desde que ele não esteja forçado através dos fuses.
Bom artigo! Obrigado.
Olá Fábio!
É muito bom saber que você gostou!
Abraço!
Rosana – Equipe MakerHero
Muuuuuito útil.
Simples e objetivo.
Obrigado.