Programação de microcontroladores com adição de interrupções
Assim como é o ser humano aprendendo a andar, no mundo da microeletrônica nossa caminhada se inicia com alguns projetos simples para começar a engatinhar na área e aperfeiçoar os conhecimentos para, então, criar projetos próprios. Por isso, atráves de uma série de artigos que se inicia agora, você aprenderá conhecimentos de programação intermediária para microcontroladores, que é uma junção de hardware com software.
Neste post, compatilharei também um pouco do conhecimento que adquiri enquanto trabalhei no segmento e ao longo da experiência como Analista de Sistemas da Vsoft, além disso, iremos utilizar o Atmega328p como microcontrolador, por ser barato e fácil de usar, estando presente em placas como Arduino Uno, Arduino Nano e Arduino Pro Mini e em suas versões genéricas.
Ao final do artigo, deixei um bônus para ajudar ainda mais durante o seu projeto. Boa leitura! 😊
Indo além na programação para microcontroladores
Neste conteúdo, você encontrará informações adequadas para atingir um nível intermediário de conhecimento. Iremos aumentar a precisão do microcontrolador no uso de rotinas que precisam ser executadas em frequências precisas que podem ser altas ou não, através do uso de interrupções por timer, e rotinas ativadas sem necessidade de uma espera ocupada do microcontrolador, deixando-o livre para executar outra parte do código, através de interrupção externa.
A partir de agora, você poderá pisar mais firme no mundo da microeletrônica. É nessa parte que o código precisa funcionar de forma mais sólida e robusta e mais linhas de código precisam ser adicionadas para lidar com erros e situações adversas fora do uso normal, porém mantendo alta eficiência.
E isto pode ser alcançado através do uso de todos os recursos possíveis do microcontrolador. Recursos esses que, muitas vezes, são bem comuns entre os microcontroladores, mudando apenas as formas de acessar e configurar, que devem ser sempre consultadas no datasheet do fabricante.
Por isso, para aproveitar este artigo, é necessário já ter um conhecimento básico de programação em C, C++, possuir um computador com acesso à internet, possuir a IDE da Arduino instalada e, posteriormente, o Atmel Studio.
Também é importante possuir uma placa de desenvolvimento que irá rodar os códigos criados através do conhecimento aqui adquirido, que possua o microcontrolador citado anteriormente, além de alguma experiência prévia e noções básicas sobre como criar projetos com placas de desenvolvimento.
Tipo de interrupções e como usá-las
Interrupções são sinais, que podem ser assíncronos ou síncronos, enviados para o processador, a fim de avisar que é necessário executar outra parte do código, salvando o ponto em que parou e retornando após realizar a sub-rotina pré configurada. Essas interrupções podem ser de dois tipos: externas ou por timer, entenda a seguir.
1. Interrupção externa (pinos disponíveis, registradores, inicialização)
Interrupções externas são sinais gerados a partir de sinais que são externos à arquitetura do microcontrolador, como por exemplo um botão, ou um interruptor de fim de curso. Em geral, são sinais enviados a um pino do microcontrolador, podendo ser detectado a borda de subida desse sinal (quando o pino está recebendo um sinal baixo e passa a receber um sinal alto, sendo no atmega328p abaixo de 2,5 volts e acima de 2,5 volts respectivamente), a borda de descida (quando o pino está recebendo um sinal alto e passa a receber um sinal baixo) ou ambos, permitindo assim saber a duração em que fico em alto ou que ficou em baixo.
No Atmega328p temos dois modos de utilizar a interrupção externa: utilizando o INT0 ou INT1 ou, no caso de precisar de mais de duas interrupções, podemos utilizar o modo de interrupção por mudança de pino, que nesse caso os pinos serão divididos em 3 grupos: B, C e D, onde será necessário saber qual pino gerou a interrupção dentro do grupo que disparou a interrupção.
Para interrupção usando INT0 ou INT1, é importante que:
● Os registradores para os pinos PD2 (INT0) e PD3(INT1) sejam limpos;
● Em seguida, usar o registrador PORTD para selecionar qual pino recebe pull-up ou pull down;
● E, então, ativar o bits no registrador EICRA (External Interrupt Control Register A) correspondente ao nível lógico para acionar a interrupção;
● Depois, é necessário configurar o registrador EIMSK (External Interrupt Mask) ativando o bit correspondente ao INT0 ou INT1 que será utilizado.
Abaixo um exemplo de como ativar o INT0 (Pino PD2 ou D2 na arduino):
No segundo modo, onde podemos utilizar mais pinos para interrupção, os registradores mudam. Agora é necessário realizar o seguinte passo:
● Configurar os registradores de mudança de porta, depois de limpar os pinos que serão utilizados. Aqui, usaremos o registrador PORTB, onde cada bit corresponde a um pino do grupo B, para ativar o Pull-up ou pull-down de cada pino que será utilizado;
● Em seguida, utilizaremos o registrador PCICR (Pin Change Interrupt Control Register) para ativar o escaneamento das portas. Assim, quando houver mudança de estado, uma interrupção será gerada correspondente àquele grupo (no caso do exemplo, o grupo B);
● Por fim, utilizaremos o registrador PCMSK0 (Pin Change Mask Register 0) para ativar a interrupção por mudança de estado.
Porém, como existem vários pinos no mesmo grupo, para utilizar esse modo de interrupção é necessário ler e armazenar os estados do pino do respectivo grupo a fim de determinar qual pino foi acionado.
Confira, abaixo, um código de exemplo trazido da rotina de trabalho e estudos da Vsoft para utilizar os pinos PB0, PB1, PB2 (na placa são os pinos D9, D10 e D11) para interrupção externa. Note que na função de subrotina de interrupção (ISR) é feita uma leitura de qual pino do grupo B foi acionado.
O código acima fará com que toda vez que o botão seja pressionado, o led acenda ou apague e, em alguns casos, apenas pisque devido à velocidade com que vai ser executado o código.
2. Interrupção por timer (cálculos, registradores, inicialização)
A interrupção por timer é feita a partir de registradores internos que são incrementados até que alcancem o valor de tempo decorrido pré-configurado em cada um. O incremento desses registradores se dá junto com o clock processador, porém, dependendo da aplicação, essa frequência pode ser alta demais, sendo necessário assim um divisor chamado de pre-scaler que pode ter valores de 2^0até 2^10, mas sempre potências de base 2 (consulte sempre o datasheet do seu microcontrolador para saber os valores possíveis).
Exemplo: em um caso de 16Mhz de clock do microcontrolador com pre-scaler de 1024, seriam necessários 15.625 pulsos para acionar a interrupção. E para a forma como esse registrador irá acionar a interrupção temos duas opções: overflow ou comparação.
No modo de overflow, é necessário primeiramente configurar os valores dos registradores para que funcionem no modo de overflow, limpar os registradores e, então, configurar o valor no qual o registrador irá dar overflow.
Esses registradores são chamados de Timer/Counter Control Registers (TCCR), que podem ser do timer 1 ou 2 e que são internamente divididos em grupo A ou B. Os valores de cada um mudam a forma como o timer vai operar (a tabela completa de funcionalidades pode ser encontrada no datasheet e não será incluída nessa seção por ser um pouco extensa).
O valor que será dado ao overflow é calculado a partir da seguinte fórmula:
65536-(Clock base do microcontrolador/prescaler/frequencia desejada em Hz)
No exemplo abaixo iremos fazer um blink com overflow para acionamento e desligamento a cada segundo (ou seja 1 Hz).
O registrador responsável pelo armazenamento do valor de clock, que irá acionar a interrupção e que deve ser sempre zerado manualmente é o TCNT1 (ou TCNT2 para o timer 2) e, por fim, o último registrador necessário é o que armazena a máscara de bits das interrupções ativas (TIMSK1 ou TIMSK2 para timer 2).
Lembre-se sempre de consultar as tabelas de pinos e registradores para saber quais podem ser escritos e quais valores podem, ou não, ser alterados. Para exemplificar vamos ativar o timer 1 no modo mais simples e com overflow passando os seguintes valores pros registradores:
Na interrupção por comparação, a configuração é parecida, porém será necessário atribuir outros valores aos registradores para que funcionem com comparação, e não é necessário zerar o timer, pois nesse modo ele é zerado automaticamente.
Veja um exemplo de como usar e configurar e note que o cálculo do valor a ser comparado é parecido com o anterior, sendo necessário apenas remover a subtração de 65536.
Aplicação na prática
Para melhor ilustrar, vamos trazer um exemplo prático para facilitar o entendimento do cenário e a solução demandada, assim como fazemos na análise de projetos aqui na Vsoft. Então, vamos considerar um sistema de resfriamento por temperatura crítica, assumindo que iremos controlar uma pequena estufa (estufa para plantas, chocadeira ou forno para desidratação).
Nessa estufa haverá botões para controle de temperatura, opções de desligar ou ligar o controle de temperatura, um display para mostrar as informações de temperatura interna e as opções do menu de configurações.
O funcionamento é simples: ao ser inserida a temperatura desejada no menu e acionado o controle de temperatura, o microcontrolador deverá realizar uma leitura frequente da temperatura interna, e caso esteja abaixo da ideal ela deverá ligar o aquecimento. Caso esteja superior, deverá acionar o ventilador para puxar ar frio de fora para resfriar o ambiente interno (não iremos nos preocupar agora com o caso em que a temperatura externa está maior que a interna).
Quando o microcontrolador estiver realizando esse ciclo, será necessário fazê-lo com frequência fixa e de preferência alta, para impedir que a temperatura mude demais entre uma leitura e outra, antes que possa ser tomada uma medida preventiva. Para isso, será necessário colocar o timer interno para 125 milissegundos e toda vez que o timer atingir essa duração de tempo, será gerada uma interrupção e executada uma função que irá ler a temperatura e decidir se é necessário esquentar, esfriar ou não fazer nada.
Ao mesmo tempo, o menu precisa continuar a executar continuamente a tarefa de atualizar a temperatura na tela ou as opções do menu. Para isso, colocaremos a rotina principal do microcontrolador como a responsável por fazer a atualização da tela, porém não faremos leitura dos botões pois iremos colocar os pinos conectados a eles como interrupção externa, assim, independente de qual parte da atualização da tela o microcontrolador estiver executando, os botões serão sempre lidos, pois cada interrupção dos botões executará uma função para descobrir quais comandos o usuário está desejando inserir.
No caso descrito, a maior prioridade ficará com os botões que podem interromper a rotina de controle de temperatura e realizar, por exemplo, um desligamento de emergência, sendo mais importante, portanto, saber que tipo de botão está sendo pressionado do que medir a temperatura.
A segunda maior prioridade ficará por conta do ciclo de medição de temperatura, pois a medição e controle de temperatura deve ser feito constantemente para evitar que se atinja temperaturas indesejadas, independente do que precisará ser exibido na tela (para entendimento da ordem de prioridades de interrupções basta consultar a tabela na seção material extra).
E, por último, fica a exibição dos dados na tela, que pode ser feita em partes, pois nesse caso, não tem problema se a tela demorar meio segundo a mais para atualizar, desde que a temperatura não fuja do controle e que sempre que for solicitado uma parada de emergência ou mudança de temperatura pelos botões, ela seja executada.
Para utilização do código exemplo, é necessário ter 3 botões conectados nos pinos D8 (Aumentar temperatura), D9 (desligar ou ligar o controle) e D10 (Diminuir a temperatura).
Código exemplo
Material extra
Atmega328p Datasheet:
Pode ser encontrado atráves do link.
Ou pode ser achado no site da Microchip através de uma busca rápida no Google.
Planilha para auxiliar durante o projeto, link.
Gostou do conteúdo? Acompanhe o LinkedIn da Vsoft para receber mais novidades sobre o assunto e acompanhar novos artigos publicados pelo time de ambassadors.
Moisés Cavalcanti
Posts relacionados
Todos os postsNão perca as nossas atualizações!
Assine para receber a newsletter da Vsoft e fique por dentro do mundo da identificação e tecnologia.