



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)

Esse microcontrolador conta com modestos 8KB disponíveis na memória de programa, por isso ATtiny85.




* Caso o Arduino resete durante o processo de upload, basta colocar um capacitor de 10uF entre o pino GND e reset.
A gravação do bootloader no ATtiny85, desde que todas as etapas anteriores foram realizadas, é ilustrada no vídeo a seguir.
#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);
}
#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;
}
#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.
|
No projeto de hoje vamos juntar o ATtiny85 com um Arduino Pro-Mini, com ambos processando dados. Ficou curioso? Venha conferir!
Encontre tudo na Loja Eletrogate com frete grátis para compras acima de R$ 200