Em sistemas eletrônicos profissionais, a depender da aplicação, nem sempre um único microcontrolador/microprocessador desempenha todas as tarefas que demandam processamento. Quando nos deparamos com circuitos mais complexos, é comum as tarefas serem distribuídas em distintos blocos computacionais.
Como exemplo, imaginemos um carro elétrico. O gerenciamento das baterias, painel de instrumentos, giro dos motores e resfriamento — tanto da cabine, quanto dos demais componentes — são algumas das funções cujo controle demandam certo processamento. É inteligente construir um sistema eletrônico descentralizado, assim, caso o controle do resfriamento da cabine leve ao travamento do controlador, o gerenciamento das baterias não é afetado. Logo, é interessante que cada subsistema possua o próprio núcleo computacional, comunicando a central do carro apenas o necessário, ou entregando dados pré-processados para uma tomada de decisão.
Além disso, desenvolvedores devem ser versáteis ao decidirem por qual hardware utilizar em determinado projeto. Processos mais simples podem ser desempenhados por microcontroladores com menos pinos, menos recursos e, consequentemente, menores custos.
Sendo assim, no projeto de hoje, vamos explorar uma combinação dos motivos citados anteriormente. Utilizaremos o ATtiny85, um mcu menos “poderoso”, para executar uma tarefa simples ao passo que descentralizamos o processamento de um sistema.
Os ATtinys compõem parte da família AVR, cujos microcontroladores são de 8 bits. Uma das branchs dessa família é a megaAVR/ATmega que contém o ATmega328p (mcu do Arduino Uno). Já o ATtiny85 integra a branch tinyAVR, cujo objetivo é ter menos memória, pinos e periféricos, prezando pelo baixo preço e facilidade de uso. (door Pierre, 2019)
ATtiny85 – Fonte: Loja virtual Eletrogate
O ATtiny85 é um microcontrolador pequeno, de alta performance e baixo consumo (Atmel, 2013). Possui 6 I/Os utilizáveis, incluindo 4 canais conversores analógico-digital e 2 canais PWM.
Com mais humildade nos periféricos, é válido destacar os 2 timers/counters de 8 bits e a Interface de Comunicação Serial (USI – Universal Serial Interface). Diferente do ATmega328p, que possui hardwares específicos para cada protocolo de comunicação como UART (Serial), SPI e TWI (I²C), o ATtiny85 possui um hardware mais geral — USI. Por esse motivo não possui Serial nativa, sendo necessário emulação, e demais protocolos precisam de maior atenção no software para funcionarem, mas nada que libs não resolvam.
Esse microcontrolador conta com modestos 8KB disponíveis na memória de programa, por isso ATtiny85.
Memória, tipos e quantidade
Posts anteriores já detalharam o papel do bootloader em microcontroladores (seção – Introdução), sendo assim, será abordado apenas o processo de instalação desse firmware. Nos apoiaremos no core desenvolvido por David A. Mellis, uma estrutura bem estável que serve de ponto de partida para cores mais novos.
É necessário colar o link abaixo na seção “URLs adicionais” da aba Preferências (Figura 1). O próximo passo é INSTALAR o pacote pelo gerenciador de placas (Figura 2), pesquisando por attiny (Figura 3).
Figura 1
Figura 2
Figura 3
Para fazer o upload — tanto do bootloader, quanto dos programas — para o ATtiny85, utilizaremos um Arduino como programador. Para isso, precisaremos do circuito a seguir e de um software previamente carregado na placa.
* Caso o Arduino resete durante o processo de upload, basta colocar um capacitor de 10uF entre o pino GND e reset.
O código do exemplo Arduino as ISP transforma qualquer placa Arduino em um programador AVRISP (AVR In-system programming). Ou seja, o Arduino atuará como uma ponte, entre o ambiente de desenvolvimento e o microcontrolador final, convertendo-se em um ferramenta que possibilita a programação de outros AVRs.
A gravação do bootloader no ATtiny85, desde que todas as etapas anteriores foram realizadas, é ilustrada no vídeo a seguir.
O projeto a ser desenvolvido será composto por dois núcleos de processamento: Arduino pro-mini e ATtiny85. Este último ficará responsável por checar, continuamente, a distância para um obstáculo — dist — e informar ao Arduino quando a distância for menor que um determinado valor. No ATtiny85, faremos uso de um potenciômetro e um canal ADC para definir, em tempo de execução do programa, a distância para um objeto que é considerada “perigosa”: dist_limite. Em outras palavras, quando dist < dist_limite o ATtiny emitirá um “aviso” à placa Arduino.
Além disso, exploraremos dois modos de “aviso” ao Arduino pro-mini. Na primeira forma, o ATtiny85 indicará 0 (zero), quando dist > dist_limite, e 1, quando dist < dist_limite. Nesse caso, o ATtiny se comporta como um sensor de output 0/1. Na segunda forma, assim que a distância para o objeto for menor que a distância limite, o ATtiny85 emitirá um pulso acionando a interrupção externa do pro-mini.
Quando o Arduino pro-mini receber a informação que o objeto encontra-se mais próximo que o ideal, seja da primeira ou segunda forma, um sinal sonoro será emitido.
Para alternar entre os modos de comunicação basta comentar/descomentar, de modo alternado, as seções de código entre as tags Primeira forma de comunicação ou Segunda forma de comunicação. Isto é, quando as seções Primeira forma de comunicação estiverem comentadas, as seções Segunda forma de comunicação deverão estar descomentadas e vice-versa. Feito isso, bastar fazer o upload do novo código para o mcu.
#define pino_trigger 1 //Pino usado para disparar os pulsos do HC-SR04 #define pino_echo 0 //Pino usado para ler a saida do HC-SR04 #define pino_output 2 //Pino usado como output #define pino_pot 3 //Pino usado para ler o poteciômetro #define Max 10 //Constante para filtro de média móvel const float VelocidadeSom_mporus = 0.000340; //Velocidade do som em metros por microsegundo float TempoEcho; float distancia, distancia_limite, dist_media; boolean make_interrupt = false; //Flag para controlar a emissão da interrupção void setup(){ pinMode(pino_output, OUTPUT); //Configurando como saída pinMode(pino_pot, INPUT); //Configurando como entrada //Configura pino de Trigger como saída e inicializa com nível baixo pinMode(pino_trigger, OUTPUT); digitalWrite(pino_trigger, LOW); pinMode(pino_echo, INPUT); //Configura pino ECHO como entrada } void loop() { DisparaPulsoUltrassonico(); //Envia pulso para o disparar o HC-SR04 TempoEcho = pulseIn(pino_echo, HIGH); //Mede o tempo de duração do sinal no pino de leitura(us) distancia = CalculaDistancia(TempoEcho)*100; //Calcula a distância em cm dist_media = mediamovel(distancia); //Média dos ultimos Max valores //Mapeia a distância limite pelo potenciômetro distancia_limite = map(analogRead(pino_pot), 0, 1023, 0, 30); // --- Primeira forma de comunicação --- if(distancia < distancia_limite) digitalWrite(pino_output, HIGH); else digitalWrite(pino_output, LOW); // --- Primeira forma de comuninação --- // --- Segunda forma de comunicação --- if((distancia < distancia_limite) && (!make_interrupt)){ //Se dist < dist_limite e interrupção não foi feita make_interrupt = true; //Indica que a interrupção foi realizada //Dá um pulso: HIGH - aguarda - LOW digitalWrite(pino_output, HIGH); delay(100); digitalWrite(pino_output, LOW); }else if(distancia > distancia_limite){ //Quando dist > dist_limite reseta a flag da interrupção make_interrupt = false; } // --- Segunda forma de comunicação --- delay(250); } // ----- Desenvolvimento de funções auxiliares ----- //----------------------------------------------------------------------------------------------------------------------------------------------------- //Função para enviar o pulso de trigger void DisparaPulsoUltrassonico(){ digitalWrite(pino_trigger, HIGH); delayMicroseconds(10); digitalWrite(pino_trigger, LOW); } //----------------------------------------------------------------------------------------------------------------------------------------------------- //Função para calcular a distancia em metros float CalculaDistancia(float tempo_us){ return((tempo_us*VelocidadeSom_mporus)/2); } //----------------------------------------------------------------------------------------------------------------------------------------------------- //Função de implementação do filtro digital - média móvel float mediamovel(float dist){ static float media[Max]; //Vetor circular static int Posicao = 0; //Posicao atual de leitura static float Soma = 0; //Soma total do buffer circular static float Media = 0; //A media, que é a saída da função static bool zera_vetor = 1; //A variavel para saber se é a primeira execução. Se for, ele zera todo o buffer circular. if (zera_vetor){ //Zerando todo o buffer circular, para que as subtrações das sobrescrição não atrapalhe o filtro for(int i = 0; i < Max; i++){ media[i] = 0; } zera_vetor = 0; } Soma = Soma - media[Posicao%Max] + dist; media[Posicao%Max] = dist; Media = (float)Soma/(float)(Max); Posicao = Posicao++%Max; return(Media); }
A mesma maneira de alternar entre os modos de comunicação explicados para o ATtiny85 também vale para o Arduino pro-mini.
#define Sensor_dist 2 //Pino usado para ler o output do ATtiny85 volatile bool interrupt = false; void Handle_interrupt(); void setup() { // --- Primeira forma de comuninação --- pinMode(Sensor_dist, INPUT); //Configura como entrada // --- Primeira forma de comuninação --- // --- Segunda forma de comunicação --- //Declara a interrupção, configurando o acionamento na borda de subida (RISING) attachInterrupt(digitalPinToInterrupt(Sensor_dist), Handle_interrupt, RISING); // --- Segunda forma de comunicação --- pinMode(LED_BUILTIN, OUTPUT); //Configura como saída digitalWrite(LED_BUILTIN, LOW); //Inicializa no nível baixo - desligado } void loop() { // --- Primeira forma de comuninação --- if(digitalRead(Sensor_dist)){ //Se o ATtiny indica a obstrução - liga o led/buzzer digitalWrite(LED_BUILTIN, HIGH); }else{ //Se o ATtiny não indica a obstrução - desliga o led/buzzer digitalWrite(LED_BUILTIN, LOW); } // --- Primeira forma de comuninação --- // --- Segunda forma de comuninação --- if(interrupt){ //Se a flag interrup é true interrupt = false; //Reseta a flag //Pisca o led/buzzer digitalWrite(LED_BUILTIN, HIGH); delay(3000); digitalWrite(LED_BUILTIN, LOW); } // --- Segunda forma de comuninação --- } //---------------------------------------------------------------------------------------------------------------------------------------------------------- //Interrupção aconteceu logo flag interrupt vai a true void Handle_interrupt(){ interrupt = true; }
No firmware do ATtiny temos alguns pontos principais: ajuste da distância limite, aquisição da distância real, filtro média móvel e a comunicação com Arduino pro-mini. A cada loop todos esses procedimentos são realizados, inclusive, é esse laço de repetição que permite a atualização da distância limite. A cada loop, o Arduino lê o canal analógico (0 – 1023), mapeando-o de 0 – 30 (em centímetros). Talvez essa funcionalidade não seja a ideal para a maioria das aplicações, pois demanda esforço computacional significativo, repetitivo e por vezes desnecessário. Poderia ser implementado de maneira estática, como é demonstrado a seguir, mas fica como ideia para algum projeto futuro.
#define dist_limite 15 if(dist < dist_limite){ /* demais procedimentos */ }
A aquisição da distância real e filtro média móvel foram implementados segundo posts anteriores, que recomendo a leitura. Logo, irei focar na comunicação com o Arduino pro-mini.
Na primeira forma de comunicação, o ATtiny85 se comporta como um dispositivo digital: quando percebe o obstáculo indica 1, quando não percebe indica 0. Para o Arduino pro-mini perceber ou não a presença da barreira, é necessário ler continuamente o estado do pino no qual localiza-se o ATtiny85. Se a porta digital estiver em High, há uma obstrução e, por consequência, a programação é desviada para tratar essa informação. No nosso projeto, o código é desviado para tocar o buzzer, mas em um robô poderia desencadear a ação de desvio.
Já na segunda forma, o ATtiny85 emite um pulso, ativando a interrupção externa do Arduino pro-mini. A interrupção externa é uma ferramenta para desencadear uma rotina de processamento de maneira assíncrona, ou seja, quando o evento acontecer, o mcu irá tratá-lo com prioridade. Com isso em mente, não é necessário o Arduino pro-mini checar, continuamente, o estado de uma porta digital. Pode realizar outras tarefas e quando o input (pulso) for recebido, a rotina de processamento é desviada para tratar a interrupção.
A 2º forma é mais interessante perante a 1º forma pois dispensa, no código do Arduino pro-mini, o uso de uma função condicional — if — que seria executada a cada loop. A tabela a seguir ilustra a ordem de prioridade das interrupções e é perceptível que as interrupções externas são superadas apenas pelo reset: essa característica garante que nenhum dado será perdido.
Fonte: ATmega328p Datasheet
Atualmente, existem situações que tiram vantagem dessa “descentralização” do processamento, mas em outras aplicações não é necessário e nem interessante. Apesar disso, exploramos um projeto no qual o ATtiny85 se encaixa na medida certa, mas ainda há um universo de possibilidades: outros sensores, distintas maneiras de comunicação e até mesmo o controle de atuadores.
Até a próxima!
Atmel Corporation. ATmega328p Datasheet. 2015. Disponível em: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
Atmel Corporation. ATtiny 25/v / ATtiny45/v / Attiny85/v Datasheet. 2013 Disponível em: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
door Pierre. What is in a name ? ATtiny version numbers. 2019. Disponível em: https://ictoblog.nl/2019/01/27/what-is-in-a-name-attiny-version-numbers
Tenha a Metodologia Eletrogate na sua Escola! Conheça nosso Programa de Robótica Educacional.
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!