Utilizando interrupções por nível lógico no GPIO do ESP8266

Utilizando interrupções por nível lógico no GPIO do ESP8266 4

Olá pessoal! Neste artigo, você irá aprender sobre interrupções por nível lógico no GPIO do ESP8266 nos modos RISING, FALLING e CHANGE. Elas são essenciais quando queremos registrar um evento no pino do microcontrolador, mesmo que ele esteja em outra rotina. Com a ISR (Interrupt Service Routine), você conseguirá “ficar de olho” naquele sensor ou acontecimento que não pode passar despercebido, enquanto seu projeto continua a fazer o que precisa ser feito no restante da programação.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Gif Montagem| Fonte: Autoral

Interrupção é um evento que obriga o microcontrolador a suspender, de maneira temporária, as suas atividades para atender exclusivamente uma rotina indicada pelo evento que o interrompeu. De maneira bem resumida, é o que veremos no fluxograma.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Fluxograma sintetizado da interrupção no microcontrolador | Fonte: Autoral

No caso do ESP8266, que possui somente um núcleo, quando a rotina de interrupção acaba, o microcontrolador volta de onde parou em sua sequência de trabalho.

Este post traz experimentos realizados com 03 modos de interrupção externa por nível lógico no GPIO do ESP8266, o modo RISING, FALLING e CHANGE.

Mas, antes de começarmos, quero trazer exemplos de aplicações utilizando rotinas de interrupção por nível lógico na entrada do microcontrolador.

  • Monitoramento de sensor de presença: em um sistema de segurança, a detecção de pessoas é essencial para o seu bom funcionamento. Então, uma interrupção é muito bem-vinda. Quando detectar a passagem de uma pessoa, o microcontrolador pausa o que está fazendo para executar a rotina que o projetista determinou para quando ocorrer esse evento;
  • Contagem de peças em uma linha de produção: digamos que um sistema embarcado, dentre as suas funções, tenha prioridade na leitura da quantidade de peças em uma linha de produção. Se é sua principal função, concorda que ele deve ser crítico, tendo alta prioridade? Então uma rotina de interrupção será bem útil;
  • Emergência de uma máquina: existem normas, como a NR12, que nos dão diretrizes e formalizam medidas necessárias para a segurança dos profissionais envolvidos na operação e manutenção de máquinas e equipamentos. Normalmente, existe um relé específico para segurança e um comando eletrotécnico cortando sinais quando ocorre o acionamento do botão de emergência, ou outra não conformidade na segurança do dispositivo. Mas, nesse nosso exemplo teórico, um sistema embarcado pode fazer uma redundância e até enviar para a nuvem, através da internet sem fio, a quantidade dessas ocorrências por dia, gerando um relatório para análise. Então uma interrupção também pode ser solução nesta aplicação.

Uma vez exemplificadas as aplicações, vamos à prática.

Material necessário para nosso experimento

Para reproduzir o experimento, você precisará de:

Obs.: O osciloscópio não é essencial para a reprodução. Ele será utilizado no ensaio em laboratório deste post com finalidade de demonstração funcional.

Montagem do Circuito

Iremos montar nosso projeto a partir do desenho fritzing abaixo:

Utilizando interrupções por nível lógico no GPIO do ESP8266
Esquemático montado no fritzing | Fonte: Autoral

Explicação do Ensaio

A função do Arduino Nano é gerar pulsos com frequência de 10Hz com ciclo de 50% ligado e 50% desligado (onda quadrada) no pino 13. Esses pulsos serão os sinais que o ESP8266 lerá no pino D2 (GPIO4) que está vinculado a uma rotina de interrupção. Lembrando que o nível lógico alto do Arduino é 5V e do ESP8266 é 3.3V. Ao reproduzir esse projeto, utilize um divisor resistivo ou outro método para reduzir essa tensão.

O ESP8266 terá em sua rotina principal a função de ligar e desligar o LED verde ligado no pino D6 (GPIO12) com uma frequência de 0,5Hz. No código, observe que utilizei delay de propósito, mostrando que a interrupção sobrepõe-se à função delay().

Quando ocorre a rotina de interrupção, que mostrarei nos três modos, muda o estado do LED ligado no pino D5 (GPIO14).

Outra observação importante a dizer é que o pino D0 (GPIO16) do ESP8266 não tem suporte para interrupções por entrada.

Códigos

Existem duas programações, a do Arduino que somente gerará a onda quadrada na frequência de 10Hz, e a do ESP8266, que já teve sua explicação.

Arduino:

/* Programa para Artigo FilipeFlop:
 * Utilizando interrupções por nível lógico no GPIO do ESP8266 
 * SW: Gerador de ondas quadradas
 * Autor: Eduardo Castro
 * Arduino IDE: "1.8.13"
 * Placa: "Arduino nano"
*/
#define sinal 13
// ======================================================================
// --- void setup ---
void setup() 
{
   pinMode(sinal,OUTPUT);   // declara pino 3 como saída
}
// ======================================================================
// --- void loop ---
void loop() { // Fica oscilando o sinal com frequência de 10Hz
   digitalWrite (sinal, HIGH);
   delay(50);
   digitalWrite (sinal, LOW);
   delay(50);
}
// ======================================================================
// --- FIM ---

E a programação do ESP8266 NodeMCU:

/* Programa para Artigo FilipeFlop:
 * Utilizando interrupções por nível lógico no GPIO do ESP8266 
 * SW: Interrupção ESP8266
 * Autor: Eduardo Castro
 * Arduino IDE: "1.8.13"
 * Gerenciador de placas: "ESP8266 Community versão 2.7.4"
 * Placa: "NodeMCU 1.0 (ESP-12E Module)"
*/
// ======================================================================
#define pinInterrupcao D2  // D2 pino sinal Interrupção
#define pinLedVm D5        // D5 pino LED vermelho
#define pinLedVd D6        // D6 pino LED verde
bool statusLedVm = LOW;    // Variável booleana inicializada em LOW
// --- Declaração da Função ---
void ICACHE_RAM_ATTR funcaoInterrupcao(); //função de Interrupção armazenada na RAM
// --- void funcaoInterrupcao ---
void ICACHE_RAM_ATTR funcaoInterrupcao(){
  statusLedVm = !statusLedVm; // troca de LOW para HIGH vice e versa
 digitalWrite(pinLedVm, statusLedVm); // Define nível lógico dependendo do estado da variável
}
// ======================================================================
// --- void setup ---
void setup() {
  attachInterrupt(digitalPinToInterrupt(pinInterrupcao), funcaoInterrupcao, RISING);
//attachInterrupt(digitalPinToInterrupt(pinInterrupcao), funcaoInterrupcao, FALLING);  
//attachInterrupt(digitalPinToInterrupt(pinInterrupcao), funcaoInterrupcao, CHANGE);  
//RISING = Borda de Subida; FALLING = Borda de Descida; CHANGE = Tanto Subida quanto Descida
//Deve ser utilizada somente um modo de interrupção
  pinMode(pinLedVm, OUTPUT); // define pinLedVm como saída
  pinMode(pinLedVd, OUTPUT); // define pinLedVd como saída
}
// ======================================================================
// --- void loop ---
void loop() {  // Fica oscilando o LED verde com frequência de 0,5Hz
  digitalWrite(pinLedVd, HIGH);
  delay(1000);
  digitalWrite(pinLedVd, LOW);
  delay(1000);
}
// ======================================================================
// --- FIM ---

Desenvolvimento

Para executar a rotina de interrupção por nível lógico, seu código precisa estar estruturado. Vou te mostrar como fazer.

Primeiramente, você deve declarar uma rotina que o ESP8266 irá executar quando ocorrer a chamada. Ela tem o formato:

void ICACHE_RAM_ATTR nomedafunção();.

Após declarar, você deve escrever a rotina que será executada quando ocorrer a interrupção. Segue com o seguinte formato:

void ICACHE_RAM_ATTR funcaoInterrupcao() { }

Dentro dela, não devem existir rotinas ou chamadas que utilizem delay() ou yield(). Tarefas de longa duração (>1ms) em interrupções podem causar instabilidade ou travamentos.

Resumidamente, devem ser chamadas para instruções rápidas. Caso contrário, pode comprometer o funcionamento do seu microcontrolador.

Agora é hora de definir os parâmetros da interrupção dentro do setup.

Ela segue o formato:

attachInterrupt(digitalPinToInterrupt(pino), funcao, MODO);

No código do experimento ficou:

attachInterrupt(digitalPinToInterrupt(pinInterrupcao), funcaoInterrupcao, RISING);

O modo pode ser alterado para “FALLING” ou “CHANGE”, depende de como quer a interrupção.

E, assim, a estrutura do código estará pronta para interrupções.

Resultados

Deixei para falar sobre os modos de operação com os resultados, visto que será mais fácil compreender quando analisarmos os sinais respostas obtidos em cada um dos três tipos.

Devemos considerar em amarelo nosso sinal vindo do Arduino Nano como sinal para interrupção do ESP8266. Em verde a resposta da rotina da interrupção. Lembrando que ela basicamente altera o estado do LED quando chamada.

RISING

Neste modo de operação, o microcontrolador detecta a borda de subida como evento necessário para interrupção.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Sinal adquirido no modo RISING | Fonte: Autoral

Note que a resposta, em verde, altera o nível lógico a cada evento de subida do canal 1, em amarelo, caracterizando a famosa detecção de borda de subida.

FALLING

Ao contrário do anterior, o modo FALLING detecta a borda de descida como evento necessário para interrupção.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Sinal adquirido no modo FALLING | Fonte: Autoral

A resposta, em verde, acontece a cada vez que o sinal monitorado desce, caracterizando o evento como detecção de borda de descida.

CHANGE

Esse modo é a união dos dois anteriores. Ele detecta tanto a borda de subida quanto a de descida.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Sinal adquirido no modo CHANGE | Fonte: Autoral

Como o nome sugere, ele detecta a alteração do estado lógico do pino monitorado.

Legal, então a interrupção é instantânea?

Sim… mas também não.

Monitorei o tempo que o ESP8266 precisa para terminar a instrução que já estava em execução, detectar a interrupção e executar a instrução de alterar o estado do LED vermelho.

Utilizando interrupções por nível lógico no GPIO do ESP8266
Atraso do sinal | Fonte: Autoral

De fato, a interrupção é instantânea, mas foram necessários aproximadamente 2,4 microsegundos para alterar o estado no pino D5 (resultado da rotina de interrupção).

Na grande maioria dos casos isso não é crítico, considerando que, além de detectar o evento, a rotina altera o estado de uma variável e aciona um pino através do comando digitalWrite.

Conclusão

O leitor pôde conhecer alguns dos diversos recursos que o ESP8266 possui. É um conteúdo considerado intermediário que irá agregar aos entusiastas e projetistas que utilizam ou querem utilizar esse poderoso chip em seus projetos. Costumo pensar que, quanto mais ferramentas e recursos o hobbysta/profissional conhecer, mais alternativas e melhor serão os caminhos para solucionar problemas.

Com ensaios em laboratório, o entendimento tende a ser mais completo e a base lógica, reforçada, visto que as interrupções por borda de subida, descida e alteração de estado lógico foram literalmente vistas na prática.

E aí, pensou em alguma aplicação para as interrupções?

Gostou? Deixe seu comentário.

Realizou os ensaios dos projetos? Compartilhe a experiência. Sua participação é muito importante para o Universo Maker.

Faça seu comentário

Acesse sua conta e participe

4 Comentários

  1. EXCELENTE CONTEÚDO!! Gostaria de saber se existe uma solução para alterar o nível lógico do ESP durante o reset, pois observei que quando falta alimentação e na volta, as portas lógicas ficam todas em nível alto.

    1. Olá Glayson. Fico muito feliz que você tenha gostado do conteúdo!

      Sobre sua dúvida, pelo que entendi, os níveis lógicos (alto e baixo) das portas estão atrelados ao bootloader, assim sendo, mantém-se um padrão toda vez que é energizado (ou reenergizado).

      Eduardo de Castro Quintino
  2. Otimo conteudo. Não sabia que precisava declarar a chamada “ICACHE_RAM_ATTR “, para o código poder funcionar.
    No Arduino não requer esta chamada para que a interrupção funcione. Qual motivo? Muito obrigado mesmo.

    1. Olá Eder.

      Por mais que o Arduino e o NodeMCU utilizem a mesma IDE (como neste artigo), os microcontroladores ATmega328P e o ESP8266 possuem estruturação interna diferentes, por isso que utilizar interrupções em uma plataforma é diferente de utilizar na outra.

      Obrigado, fico feliz que tenha gostado do conteúdo.
      Abraço.

      Eduardo de Castro Quintino